feat(chatter): 引入决策历史以增强上下文连续性

为提升语言模型在连续对话中的表现,本次更新引入了决策历史功能。该功能旨在为模型提供一个短期记忆,记录其最近的思考过程与采取的行动。

主要变更包括:
- 新增 `DecisionRecord` 数据模型,用于存储“思考-动作”对。
- 在 `StreamContext` 中添加 `decision_history` 字段,以维护每个聊天流的决策历史。
- 在 `plan_filter` 中实现决策记录的逻辑,并在构建提示词时将其注入,供模型参考。
- 添加 `enable_decision_history` 和 `decision_history_length` 配置项,允许用户启用此功能并调整历史记录的长度。

通过回顾近期的决策,模型可以更好地避免重复行为,并生成更具连贯性和创造性的响应。
This commit is contained in:
tt-P607
2025-10-29 18:58:28 +08:00
parent 76f33134df
commit 57794480b8
5 changed files with 99 additions and 2 deletions

View File

@@ -7,7 +7,7 @@ import asyncio
import time import time
from dataclasses import dataclass, field from dataclasses import dataclass, field
from enum import Enum from enum import Enum
from typing import TYPE_CHECKING, Optional from typing import TYPE_CHECKING, List, Optional
from src.common.logger import get_logger from src.common.logger import get_logger
from src.plugin_system.base.component_types import ChatMode, ChatType from src.plugin_system.base.component_types import ChatMode, ChatType
@@ -28,6 +28,14 @@ class MessageStatus(Enum):
PROCESSING = "processing" # 处理中 PROCESSING = "processing" # 处理中
@dataclass
class DecisionRecord(BaseDataModel):
"""决策记录"""
thought: str
action: str
@dataclass @dataclass
class StreamContext(BaseDataModel): class StreamContext(BaseDataModel):
"""聊天流上下文信息""" """聊天流上下文信息"""
@@ -56,6 +64,7 @@ class StreamContext(BaseDataModel):
triggering_user_id: str | None = None # 触发当前聊天流的用户ID triggering_user_id: str | None = None # 触发当前聊天流的用户ID
is_replying: bool = False # 是否正在生成回复 is_replying: bool = False # 是否正在生成回复
processing_message_id: str | None = None # 当前正在规划/处理的目标消息ID用于防止重复回复 processing_message_id: str | None = None # 当前正在规划/处理的目标消息ID用于防止重复回复
decision_history: List["DecisionRecord"] = field(default_factory=list) # 决策历史
def add_action_to_message(self, message_id: str, action: str): def add_action_to_message(self, message_id: str, action: str):
""" """

View File

@@ -143,6 +143,10 @@ class ChatConfig(ValidatedConfigBase):
dynamic_distribution_max_interval: float = Field(default=30.0, ge=5.0, le=300.0, description="最大分发间隔(秒)") dynamic_distribution_max_interval: float = Field(default=30.0, ge=5.0, le=300.0, description="最大分发间隔(秒)")
dynamic_distribution_jitter_factor: float = Field(default=0.2, ge=0.0, le=0.5, description="分发间隔随机扰动因子") dynamic_distribution_jitter_factor: float = Field(default=0.2, ge=0.0, le=0.5, description="分发间隔随机扰动因子")
max_concurrent_distributions: int = Field(default=10, ge=1, le=100, description="最大并发处理的消息流数量") max_concurrent_distributions: int = Field(default=10, ge=1, le=100, description="最大并发处理的消息流数量")
enable_decision_history: bool = Field(default=True, description="是否启用决策历史功能")
decision_history_length: int = Field(
default=3, ge=1, le=10, description="决策历史记录的长度,用于增强语言模型的上下文连续性"
)
class MessageReceiveConfig(ValidatedConfigBase): class MessageReceiveConfig(ValidatedConfigBase):

View File

@@ -105,6 +105,23 @@ class ChatterPlanFilter:
thinking = item.get("thinking", "未提供思考过程") thinking = item.get("thinking", "未提供思考过程")
actions_obj = item.get("actions", {}) actions_obj = item.get("actions", {})
# 记录决策历史
if hasattr(global_config.chat, "enable_decision_history") and global_config.chat.enable_decision_history:
action_types_to_log = []
actions_to_process_for_log = []
if isinstance(actions_obj, dict):
actions_to_process_for_log.append(actions_obj)
elif isinstance(actions_obj, list):
actions_to_process_for_log.extend(actions_obj)
for single_action in actions_to_process_for_log:
if isinstance(single_action, dict):
action_types_to_log.append(single_action.get("action_type", "no_action"))
if thinking != "未提供思考过程" and action_types_to_log:
await self._add_decision_to_history(plan, thinking, ", ".join(action_types_to_log))
# 处理actions字段可能是字典或列表的情况 # 处理actions字段可能是字典或列表的情况
if isinstance(actions_obj, dict): if isinstance(actions_obj, dict):
action_type = actions_obj.get("action_type", "no_action") action_type = actions_obj.get("action_type", "no_action")
@@ -141,6 +158,65 @@ class ChatterPlanFilter:
return plan return plan
async def _add_decision_to_history(self, plan: Plan, thought: str, action: str):
"""添加决策记录到历史中"""
try:
from src.chat.message_receive.chat_stream import get_chat_manager
from src.common.data_models.message_manager_data_model import DecisionRecord
chat_manager = get_chat_manager()
chat_stream = await chat_manager.get_stream(plan.chat_id)
if not chat_stream:
return
if not thought or not action:
logger.debug("尝试添加空的决策历史,已跳过")
return
context = chat_stream.context_manager.context
new_record = DecisionRecord(thought=thought, action=action)
# 添加新记录
context.decision_history.append(new_record)
# 获取历史长度限制
max_history_length = getattr(global_config.chat, "decision_history_length", 3)
# 如果历史记录超过长度,则移除最旧的记录
if len(context.decision_history) > max_history_length:
context.decision_history.pop(0)
logger.debug(f"已添加决策历史,当前长度: {len(context.decision_history)}")
except Exception as e:
logger.warning(f"记录决策历史失败: {e}")
async def _build_decision_history_block(self, plan: Plan) -> str:
"""构建决策历史块"""
if not hasattr(global_config.chat, "enable_decision_history") or not global_config.chat.enable_decision_history:
return ""
try:
from src.chat.message_receive.chat_stream import get_chat_manager
chat_manager = get_chat_manager()
chat_stream = await chat_manager.get_stream(plan.chat_id)
if not chat_stream:
return ""
context = chat_stream.context_manager.context
if not context.decision_history:
return ""
history_records = []
for i, record in enumerate(context.decision_history):
history_records.append(f"- 思考: {record.thought}\n - 动作: {record.action}")
history_str = "\n".join(history_records)
return f"{history_str}"
except Exception as e:
logger.warning(f"构建决策历史块失败: {e}")
return ""
async def _build_prompt(self, plan: Plan) -> tuple[str, list]: async def _build_prompt(self, plan: Plan) -> tuple[str, list]:
""" """
根据 Plan 对象构建提示词。 根据 Plan 对象构建提示词。
@@ -166,6 +242,9 @@ class ChatterPlanFilter:
chat_mood = mood_manager.get_mood_by_chat_id(plan.chat_id) chat_mood = mood_manager.get_mood_by_chat_id(plan.chat_id)
mood_block = f"你现在的心情是:{chat_mood.mood_state}" mood_block = f"你现在的心情是:{chat_mood.mood_state}"
# 构建决策历史
decision_history_block = await self._build_decision_history_block(plan)
# 构建已读/未读历史消息 # 构建已读/未读历史消息
read_history_block, unread_history_block, message_id_list = await self._build_read_unread_history_blocks( read_history_block, unread_history_block, message_id_list = await self._build_read_unread_history_blocks(
plan plan
@@ -239,6 +318,7 @@ class ChatterPlanFilter:
mood_block=mood_block, mood_block=mood_block,
time_block=time_block, time_block=time_block,
chat_context_description=chat_context_description, chat_context_description=chat_context_description,
decision_history_block=decision_history_block,
read_history_block=read_history_block, read_history_block=read_history_block,
unread_history_block=unread_history_block, unread_history_block=unread_history_block,
actions_before_now_block=actions_before_now_block, actions_before_now_block=actions_before_now_block,

View File

@@ -30,6 +30,9 @@ def init_prompts():
{actions_before_now_block} {actions_before_now_block}
## 🤔 最近的决策历史 (回顾你之前的思考与动作,可以帮助你避免重复,并做出更有趣的连贯回应)
{decision_history_block}
## 📜 已读历史(仅供理解,不可作为动作对象) ## 📜 已读历史(仅供理解,不可作为动作对象)
{read_history_block} {read_history_block}

View File

@@ -137,7 +137,8 @@ dynamic_distribution_min_interval = 1.0 # 最小分发间隔(秒)
dynamic_distribution_max_interval = 30.0 # 最大分发间隔(秒) dynamic_distribution_max_interval = 30.0 # 最大分发间隔(秒)
dynamic_distribution_jitter_factor = 0.2 # 分发间隔随机扰动因子 dynamic_distribution_jitter_factor = 0.2 # 分发间隔随机扰动因子
max_concurrent_distributions = 10 # 最大并发处理的消息流数量可以根据API性能和服务器负载调整 max_concurrent_distributions = 10 # 最大并发处理的消息流数量可以根据API性能和服务器负载调整
enable_decision_history = true # 是否启用决策历史功能
decision_history_length = 3 # 决策历史记录的长度,用于增强语言模型的上下文连续性
[message_receive] [message_receive]