fix(agent): 修复工具结果system_message注入夹断tool序列导致的API 400

- execute_tool_calls 循环内改为收集 pending_tool_system_messages,
  整轮工具结束后统一注入,避免并行 tool_calls 中途插入 user 消息
- terminate_sub_agent handler 摘除结果中的 system_message,
  主智能体自行终结子智能体时不再注入冗余 user 消息
This commit is contained in:
JOJO 2026-08-07 14:11:22 +08:00
parent dd2f00276d
commit 1cd3a742af
2 changed files with 32 additions and 11 deletions

View File

@ -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:

View File

@ -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)