Implement chat type-aware processing for private and group chats

pull/1045/head
Cursor Agent 2025-06-17 01:21:05 +00:00
parent 918aa57b01
commit 6fce7f8dcd
3 changed files with 103 additions and 45 deletions

View File

@ -383,8 +383,21 @@ class HeartFChatting:
task_to_name_map = {}
processor_time_costs = {} # 新增: 记录每个处理器耗时
# 获取聊天类型
chat_stream = await asyncio.to_thread(chat_manager.get_stream, self.stream_id)
is_group_chat = chat_stream.group_info is not None
# 根据聊天类型选择处理器
selected_processors = []
for processor in self.processors:
processor_name = processor.__class__.log_prefix
# 私聊时跳过某些处理器
if not is_group_chat and processor_name in ["MindProcessor", "ToolProcessor"]:
continue
selected_processors.append(processor)
for processor in selected_processors:
processor_name = processor.__class__.log_prefix
async def run_with_timeout(proc=processor):
return await asyncio.wait_for(
@ -465,9 +478,14 @@ class HeartFChatting:
self.all_observations = observations
# 获取聊天类型
chat_stream = await asyncio.to_thread(chat_manager.get_stream, self.stream_id)
is_group_chat = chat_stream.group_info is not None
with Timer("调整动作", cycle_timers):
# 处理特殊的观察
await self.action_modifier.modify_actions(observations=observations)
if is_group_chat: # 只在群聊中调整动作
await self.action_modifier.modify_actions(observations=observations)
await self.action_observation.observe()
observations.append(self.action_observation)
@ -497,59 +515,41 @@ class HeartFChatting:
}
with Timer("规划器", cycle_timers):
plan_result = await self.action_planner.plan(all_plan_info, running_memorys)
# 私聊时使用简单规划
if not is_group_chat:
plan_result = await self.action_planner.plan_simple(all_plan_info, running_memorys)
else:
plan_result = await self.action_planner.plan(all_plan_info, running_memorys)
loop_plan_info = {
"action_result": plan_result.get("action_result", {}),
"current_mind": plan_result.get("current_mind", ""),
"observed_messages": plan_result.get("observed_messages", ""),
}
loop_plan_info = {
"action_result": plan_result,
}
with Timer("执行动作", cycle_timers):
action_type, action_data, reasoning = (
plan_result.get("action_result", {}).get("action_type", "error"),
plan_result.get("action_result", {}).get("action_data", {}),
plan_result.get("action_result", {}).get("reasoning", "未提供理由"),
action_taken, action_command, action_reason = await self._handle_action(
action=plan_result["action_type"],
reasoning=plan_result["reasoning"],
action_data=plan_result["action_data"],
cycle_timers=cycle_timers,
thinking_id=thinking_id,
)
if action_type == "reply":
action_str = "回复"
elif action_type == "no_reply":
action_str = "不回复"
else:
action_str = action_type
loop_action_info = {
"action_taken": action_taken,
"command": action_command,
"reason": action_reason,
}
logger.debug(f"{self.log_prefix} 麦麦想要:'{action_str}', 原因'{reasoning}'")
success, reply_text, command = await self._handle_action(
action_type, reasoning, action_data, cycle_timers, thinking_id
)
loop_action_info = {
"action_taken": success,
"reply_text": reply_text,
"command": command,
"taken_time": time.time(),
}
loop_info = {
return {
"loop_observation_info": loop_observation_info,
"loop_processor_info": loop_processor_info,
"loop_plan_info": loop_plan_info,
"loop_action_info": loop_action_info,
}
return loop_info
except Exception as e:
logger.error(f"{self.log_prefix} FOCUS聊天处理失败: {e}")
logger.error(traceback.format_exc())
return {
"loop_observation_info": {},
"loop_processor_info": {},
"loop_plan_info": {},
"loop_action_info": {"action_taken": False, "reply_text": "", "command": ""},
}
logger.error(f"{self.log_prefix} 循环处理出错: {e}")
traceback.print_exc()
raise
async def _handle_action(
self,

View File

@ -16,6 +16,8 @@ from src.chat.utils.prompt_builder import Prompt, global_prompt_manager
from src.individuality.individuality import individuality
from src.chat.focus_chat.planners.action_manager import ActionManager
from json_repair import repair_json
from src.chat.focus_chat.info.chat_info import ChattingInfo
from src.chat.focus_chat.info.workingmemory_info import WorkingMemoryInfo
logger = get_logger("planner")
@ -85,6 +87,61 @@ class ActionPlanner:
self.action_manager = action_manager
async def plan_simple(self, all_plan_info: List[InfoBase], running_memorys: List[Dict[str, Any]]) -> Dict[str, Any]:
"""
为私聊提供的简单规划方法
主要关注ChattingInfo和WorkingMemory不考虑复杂的心智状态和工具使用
Args:
all_plan_info: 所有处理器提供的信息
running_memorys: 当前激活的记忆
Returns:
Dict[str, Any]: 包含action_type, action_data和reasoning的字典
"""
# 提取聊天信息
chatting_info = None
working_memory_info = None
for info in all_plan_info:
if isinstance(info, ChattingInfo):
chatting_info = info
elif isinstance(info, WorkingMemoryInfo):
working_memory_info = info
if not chatting_info:
return {
"action_type": "no_reply",
"action_data": {},
"reasoning": "没有检测到新的聊天信息",
}
# 检查是否有新消息需要回复
if not chatting_info.new_messages:
return {
"action_type": "no_reply",
"action_data": {},
"reasoning": "没有新消息需要回复",
}
# 获取最新消息
latest_message = chatting_info.new_messages[-1]
# 检查工作记忆中是否有相关上下文
context = ""
if working_memory_info and working_memory_info.related_memory:
context = working_memory_info.related_memory
# 构建回复动作
return {
"action_type": "reply",
"action_data": {
"message": latest_message,
"context": context,
"style": "direct", # 私聊使用直接回复风格
},
"reasoning": "收到新的私聊消息,直接回复",
}
async def plan(self, all_plan_info: List[InfoBase], running_memorys: List[Dict[str, Any]]) -> Dict[str, Any]:
"""
规划器 (Planner): 使用LLM根据上下文决定做出什么动作

View File

@ -219,7 +219,7 @@ class SubHeartflowManager:
"""
接收来自 HeartFChatting 的请求将特定子心流的状态转换为 NORMAL
通常在连续多次 "no_reply" 后被调用
对于私聊和群聊转换为 NORMAL
对于群聊转换为 NORMAL对于私聊转换为 ABSENT
Args:
subflow_id: 需要转换状态的子心流 ID
@ -234,8 +234,9 @@ class SubHeartflowManager:
current_state = subflow.chat_state.chat_status
if current_state == ChatState.FOCUSED:
target_state = ChatState.NORMAL
log_reason = "转为NORMAL"
# 根据聊天类型决定目标状态
target_state = ChatState.NORMAL if subflow.is_group_chat else ChatState.ABSENT
log_reason = "转为NORMAL" if subflow.is_group_chat else "转为ABSENT(私聊)"
logger.info(
f"[状态转换请求] 接收到请求,将 {stream_name} (当前: {current_state.value}) 尝试转换为 {target_state.value} ({log_reason})"