From 1cd3a742af8d090d09311204b110e23b2b135a23 Mon Sep 17 00:00:00 2001 From: JOJO <1498581755@qq.com> Date: Fri, 7 Aug 2026 14:11:22 +0800 Subject: [PATCH] =?UTF-8?q?fix(agent):=20=E4=BF=AE=E5=A4=8D=E5=B7=A5?= =?UTF-8?q?=E5=85=B7=E7=BB=93=E6=9E=9Csystem=5Fmessage=E6=B3=A8=E5=85=A5?= =?UTF-8?q?=E5=A4=B9=E6=96=ADtool=E5=BA=8F=E5=88=97=E5=AF=BC=E8=87=B4?= =?UTF-8?q?=E7=9A=84API=20400?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - execute_tool_calls 循环内改为收集 pending_tool_system_messages, 整轮工具结束后统一注入,避免并行 tool_calls 中途插入 user 消息 - terminate_sub_agent handler 摘除结果中的 system_message, 主智能体自行终结子智能体时不再注入冗余 user 消息 --- core/main_terminal_parts/tools_execution.py | 6 ++++ server/chat_flow_tool_loop.py | 37 +++++++++++++++------ 2 files changed, 32 insertions(+), 11 deletions(-) diff --git a/core/main_terminal_parts/tools_execution.py b/core/main_terminal_parts/tools_execution.py index 8fbafd91..eee33a73 100644 --- a/core/main_terminal_parts/tools_execution.py +++ b/core/main_terminal_parts/tools_execution.py @@ -2062,6 +2062,12 @@ class MainTerminalToolsExecutionMixin: result = self.sub_agent_manager.terminate_sub_agent( agent_id=arguments.get("agent_id") ) + # 主智能体主动终结时,tool 结果(message)已包含结论, + # 摘掉 system_message 避免工具循环再注入一条冗余 user 消息 + # (且「已被手动关闭」措辞对此场景是误导)。 + # 前端 UI 手动终止走 server/conversation.py API 直调 manager,不受影响。 + if isinstance(result, dict): + result.pop("system_message", None) # 多智能体模式:同步状态到 MultiAgentState if getattr(self, "multi_agent_mode", False): try: diff --git a/server/chat_flow_tool_loop.py b/server/chat_flow_tool_loop.py index 0f30f26e..5a066923 100644 --- a/server/chat_flow_tool_loop.py +++ b/server/chat_flow_tool_loop.py @@ -352,6 +352,9 @@ async def execute_tool_calls(*, web_terminal, tool_calls, sender, messages, clie # 执行每个工具 pending_runtime_mode_notices: List[str] = [] + # 工具结果附带的 system_message 先收集、整轮工具循环结束后统一注入, + # 避免并行 tool_calls 中途插入 user 消息夹断 assistant.tool_calls 的 tool 序列。 + pending_tool_system_messages: List[Dict[str, Any]] = [] last_completed_tool_call_id: Optional[str] = None deep_compression_pending = False for tool_call in tool_calls: @@ -1229,17 +1232,13 @@ async def execute_tool_calls(*, web_terminal, tool_calls, sender, messages, clie debug_log(f"💾 增量保存:工具结果 {function_name}") system_message = result_data.get("system_message") if isinstance(result_data, dict) else None if system_message: - inject_runtime_user_message( - web_terminal=web_terminal, - messages=messages, - text=system_message, - source="sub_agent", - sender=sender, - conversation_id=conversation_id, - after_tool_call_id=tool_call_id, - inline=False, - extra_metadata={"task_id": result_data.get("task_id")}, - ) + # 不在此立即注入:若本轮是并行 tool_calls,其余 tool 结果尚未写入, + # 此时插入 user 消息会夹断 assistant.tool_calls 的 tool 序列,触发 API 400 + # (tool_calls must be followed by tool messages)。统一延迟到循环结束后注入。 + pending_tool_system_messages.append({ + "text": system_message, + "task_id": result_data.get("task_id"), + }) # 自动深层压缩(工具调用后触发) current_context_tokens = web_terminal.context_manager.get_current_context_tokens(conversation_id) @@ -1262,6 +1261,22 @@ async def execute_tool_calls(*, web_terminal, tool_calls, sender, messages, clie await asyncio.sleep(0.2) + # 工具结果附带的 system_message:必须等待同一轮全部 tool_call 的 tool 消息 + # 都已写入 messages/历史后再注入,避免夹断 assistant.tool_calls 的 tool 序列。 + # 注意放在深层压缩分支之前,防止压缩提前 return 导致通知丢失。 + for pending_system_message in pending_tool_system_messages: + inject_runtime_user_message( + web_terminal=web_terminal, + messages=messages, + text=pending_system_message["text"], + source="sub_agent", + sender=sender, + conversation_id=conversation_id, + after_tool_call_id=last_completed_tool_call_id, + inline=False, + extra_metadata={"task_id": pending_system_message.get("task_id")}, + ) + # 自动深层压缩必须等待同一轮全部 tool_call 的 tool 消息都已写入 messages/历史后再触发。 if deep_compression_pending and not web_terminal.context_manager.is_compression_in_progress(): web_terminal.context_manager._set_meta_flag("is_ultra_long_conversation", True)