feat(proactive_thinking): 重构主动思考为由Planner直接决策

重构了主动思考的触发和决策流程。原有的通过生成特定prompt来启动思考循环的方式被移除,改为直接调用Planner的`PROACTIVE`模式。

- **Planner增强**:
  - 新增`PROACTIVE`聊天模式,用于处理主动思考场景。
  - 为`PROACTIVE`模式设计了专用的prompt模板,整合了长期记忆、当前状态等信息。
  - 引入`do_nothing`动作,允许Planner在分析后决定保持沉默。
  - 增加从海马体(长期记忆)获取上下文的功能,为决策提供更丰富的背景。

- **ProactiveThinker简化**:
  - 移除了原有的prompt生成和调用`observe`的逻辑。
  - 现在直接调用`action_planner.plan(mode=ChatMode.PROACTIVE)`来获取决策。
  - 根据Planner返回的动作(如`do_nothing`或具体行动),决定是保持沉默还是执行计划。

- **CycleProcessor & Tracker调整**:
  - `CycleProcessor`新增`execute_plan`方法,用于执行一个已经由Planner预先制定好的计划。
  - `CycleTracker`能够区分并标记由主动思考发起的循环(例如,cycle_id为 "1.p"),以便于追踪和分析。
This commit is contained in:
minecraft1024a
2025-08-21 19:29:14 +08:00
committed by Windpicker-owo
parent ddf023ff11
commit f53993e34a
6 changed files with 194 additions and 119 deletions

View File

@@ -130,6 +130,34 @@ class CycleProcessor:
return True
async def execute_plan(self, action_result: Dict[str, Any], target_message: Optional[Dict[str, Any]]):
"""
执行一个已经制定好的计划
"""
action_type = action_result.get("action_type", "error")
# 这里我们需要为执行计划创建一个新的循环追踪
cycle_timers, thinking_id = self.cycle_tracker.start_cycle(is_proactive=True)
loop_start_time = time.time()
if action_type == "reply":
# 主动思考不应该直接触发简单回复但为了逻辑完整性我们假设它会调用response_handler
# 注意:这里的 available_actions 和 plan_result 是缺失的,需要根据实际情况处理
await self._handle_reply_action(target_message, {}, None, loop_start_time, cycle_timers, thinking_id, {"action_result": action_result})
else:
await self._handle_other_actions(
action_type,
action_result.get("reasoning", ""),
action_result.get("action_data", {}),
action_result.get("is_parallel", False),
None,
target_message,
cycle_timers,
thinking_id,
{"action_result": action_result},
loop_start_time
)
async def _handle_reply_action(self, message_data, available_actions, gen_task, loop_start_time, cycle_timers, thinking_id, plan_result):
"""
处理回复类型的动作

View File

@@ -21,10 +21,13 @@ class CycleTracker:
"""
self.context = context
def start_cycle(self) -> Tuple[Dict[str, float], str]:
def start_cycle(self, is_proactive: bool = False) -> Tuple[Dict[str, float], str]:
"""
开始新的思考循环
Args:
is_proactive: 标记这个循环是否由主动思考发起
Returns:
tuple: (循环计时器字典, 思考ID字符串)
@@ -34,8 +37,11 @@ class CycleTracker:
- 生成唯一的思考ID
- 初始化循环计时器
"""
self.context.cycle_counter += 1
self.context.current_cycle_detail = CycleDetail(self.context.cycle_counter)
if not is_proactive:
self.context.cycle_counter += 1
cycle_id = self.context.cycle_counter if not is_proactive else f"{self.context.cycle_counter}.p"
self.context.current_cycle_detail = CycleDetail(cycle_id)
self.context.current_cycle_detail.thinking_id = f"tid{str(round(time.time(), 2))}"
cycle_timers = {}
return cycle_timers, self.context.current_cycle_detail.thinking_id

View File

@@ -1,5 +1,5 @@
import time
from typing import Optional, Dict, Any
from typing import Optional, Dict, Any, Union
from src.config.config import global_config
from src.common.logger import get_logger
@@ -23,7 +23,7 @@ class CycleDetail:
- 提供序列化和转换功能
"""
def __init__(self, cycle_id: int):
def __init__(self, cycle_id: Union[int, str]):
"""
初始化循环详情记录

View File

@@ -245,58 +245,23 @@ class ProactiveThinker:
Args:
silence_duration: 沉默持续时间(秒)
功能说明:
- 格式化沉默时间并记录触发日志
- 获取适当的思考提示模板
- 创建主动思考类型的消息数据
- 调用循环处理器执行思考和可能的回复
- 处理执行过程中的异常
"""
formatted_time = self._format_duration(silence_duration)
logger.info(f"{self.context.log_prefix} 触发主动思考,已沉默{formatted_time}")
try:
proactive_prompt = self._get_proactive_prompt(formatted_time)
# 直接调用 planner 的 PROACTIVE 模式
action_result_tuple, target_message = await self.cycle_processor.action_planner.plan(mode=ChatMode.PROACTIVE)
action_result = action_result_tuple.get("action_result")
thinking_message = {
"processed_plain_text": proactive_prompt,
"user_id": "system_proactive_thinking",
"user_platform": "system",
"timestamp": time.time(),
"message_type": "proactive_thinking",
"user_nickname": "系统主动思考",
"chat_info_platform": "system",
"message_id": f"proactive_{int(time.time())}",
}
logger.info(f"{self.context.log_prefix} 开始主动思考...")
await self.cycle_processor.observe(message_data=thinking_message)
logger.info(f"{self.context.log_prefix} 主动思考完成")
# 如果决策不是 do_nothing则执行
if action_result and action_result.get("action_type") != "do_nothing":
logger.info(f"{self.context.log_prefix} 主动思考决策: {action_result.get('action_type')}, 原因: {action_result.get('reasoning')}")
# 将决策结果交给 cycle_processor 的后续流程处理
await self.cycle_processor.execute_plan(action_result, target_message)
else:
logger.info(f"{self.context.log_prefix} 主动思考决策: 保持沉默")
except Exception as e:
logger.error(f"{self.context.log_prefix} 主动思考执行异常: {e}")
logger.error(traceback.format_exc())
def _get_proactive_prompt(self, formatted_time: str) -> str:
"""
获取主动思考的提示模板
Args:
formatted_time: 格式化后的沉默时间字符串
Returns:
str: 填充了时间信息的提示模板
功能说明:
- 优先使用自定义的提示模板(如果配置了)
- 根据聊天类型(群聊/私聊)选择默认模板
- 将格式化的时间信息填入模板
- 返回完整的主动思考提示文本
"""
if hasattr(global_config.chat, 'proactive_thinking_prompt_template') and global_config.chat.proactive_thinking_prompt_template.strip():
return global_config.chat.proactive_thinking_prompt_template.format(time=formatted_time)
chat_type = "group" if self.context.chat_stream and self.context.chat_stream.group_info else "private"
prompt_template = self.proactive_thinking_prompts.get(chat_type, self.proactive_thinking_prompts["group"])
return prompt_template.format(time=formatted_time)