修复总控智能体共识后仍被错判为继续派活的死循环
_find_dispatch_message 原实现会一路向前翻历史,直到撞到任意一条带 tool_calls 的 AIMessage,导致本轮 LLM 即使共识(tool_calls=[]),也会 被上一轮的派活 AI 翻出来再用一次,前端因此把同样的 task 又发给子智能体, 形成"嘴上共识、手上派活"的死循环。 改成只看最新一条 AIMessage(跳过末尾 skill_stop / clarification ack ToolMessage 即可),让 tool_calls 是否为空作为本轮的唯一权威信号。 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
1 parent
920fde9cd0
commit
eec7567ac6
2 files changed
+28
-13
No files matched your search
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"permissions": {
|
||||
"allow": [
|
||||
"PowerShell(uv --version)",
|
||||
"PowerShell(netstat -ano)",
|
||||
"PowerShell(Get-NetTCPConnection -LocalPort 8001 -ErrorAction SilentlyContinue)",
|
||||
"PowerShell(Get-Process -Id 20560 -ErrorAction SilentlyContinue | Select-Object Id,ProcessName,Path,StartTime; try { \\(Invoke-WebRequest -Uri \"http://127.0.0.1:8001/health\" -UseBasicParsing -TimeoutSec 5\\).Content } catch { \"health check failed: $_\" })"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -764,25 +764,30 @@ def _find_trailing_ai_text(messages: list[dict[str, Any]], *, exclude_text: str
|
||||
|
||||
|
||||
def _find_dispatch_message(messages: list[dict[str, Any]]) -> dict[str, Any]:
|
||||
"""Locate the latest AIMessage that carries dispatch tool calls.
|
||||
"""Locate the latest AIMessage from THIS turn.
|
||||
|
||||
The very last message can be a skill-stop ToolMessage injected by
|
||||
``SkillStopMiddleware`` when it intercepts the dispatch, or a clarification
|
||||
ToolMessage. Either way, the actionable tool_calls live on the AIMessage
|
||||
*before* it, so we walk backwards until we hit one with ``tool_calls``.
|
||||
Returns the latest AIMessage when none of them carry tool_calls so the
|
||||
caller can still extract its text content.
|
||||
Trailing ToolMessages (skill_stop / clarification ack injected by
|
||||
``SkillStopMiddleware`` or ``ClarificationMiddleware``) are skipped because
|
||||
the actionable tool_calls live on the AIMessage that emitted them. We stop
|
||||
at the **first AIMessage encountered** — its ``tool_calls`` (or lack
|
||||
thereof) is the authoritative signal for this turn.
|
||||
|
||||
历史踩坑:之前实现是"一直往前翻直到撞到带 tool_calls 的 AI",看似优雅,
|
||||
实际上让 leader 永远收敛不了:本轮 LLM 不论怎么共识,只要历史上派过活,
|
||||
上一轮的派活 AI 就会被翻出来再用一次,前端会把同样的 task 又发给子智能体,
|
||||
陷入死循环。现在停在最新一条 AI 上,把判定交给调用方:`tool_calls=[]` →
|
||||
共识分支,有 tool_calls → 派活分支。
|
||||
"""
|
||||
last_ai: dict[str, Any] = {}
|
||||
for msg in reversed(messages):
|
||||
if not isinstance(msg, dict):
|
||||
continue
|
||||
if (msg.get("type") or "").lower() in ("ai", "aimessage", "aimessagechunk"):
|
||||
if not last_ai:
|
||||
last_ai = msg
|
||||
if msg.get("tool_calls"):
|
||||
msg_type = (msg.get("type") or "").lower()
|
||||
if msg_type in ("tool", "toolmessage"):
|
||||
continue # 跳过末尾 skill_stop / clarification ack
|
||||
if msg_type in ("ai", "aimessage", "aimessagechunk"):
|
||||
return msg
|
||||
return last_ai
|
||||
break # 撞到 Human/其他类型,本轮没 AI(异常路径,留空让上游报错)
|
||||
return {}
|
||||
|
||||
|
||||
async def _leader_run(
|
||||
|
||||
Reference in new issue
Block a user