详情

首页手游攻略 源码深读 XAgent:6 个 Agent 怎么分工 工具失败不崩 死循环怎么防

源码深读 XAgent:6 个 Agent 怎么分工 工具失败不崩 死循环怎么防

佚名 2026-07-17 08:46:00

源码深读 XAgent:6 个 Agent 怎么分工?工具失败不崩、死循环怎么防?

一、大纲

  1. 整体执行模型:双层循环(Outer Loop + Inner Loop)
  2. Agent 全景:6 个 LLM Agent + 工作流编排类
  3. 为什么这么拆:职责分离、认知任务解耦
  4. 工具调用可靠性:LLM 层 / Schema 层 / ToolServer 层三层防护
  5. 防死循环与防扯皮:硬上限、单向推进、提交驱动
  6. 关键配置参数速查
  7. 技术债与未实现能力

二、整体执行模型:双层循环

XAgent 的核心编排器是 TaskHandlerXAgent/workflow/task_handler.py),它把一次用户 Query 的处理拆成两层:

源码深读 XAgent:6 个 Agent 怎么分工?工具失败不崩、死循环怎么防?

用户 Query→ PlanAgent.initial_plan_generation() [PlanGenerateAgent]→ while (下一个 TODO 子任务):[Outer Loop,中序遍历计划树] inner_loop()[Inner Loop,ReACT + ToolAgent] → 推理 → 工具调用 → 观察结果 → 直到 subtask_submit 或达步数上限 posterior_process()[ReflectAgent] working_memory.register_task() if need_for_plan_refine: plan_refine_mode()[PlanRefineAgent,最多 N 步] pop_next_subtask()[线性前进,不回溯]

Outer Loop 负责按子任务顺序推进全局计划;Inner Loop 负责在单个子任务内用 ReACT 范式反复「思考 → 行动 → 观察」。两层解耦是后续所有可靠性设计的基础:计划修改和工具执行不在同一个循环里打架。

三、Agent 全景:谁负责什么?

3.1 注册在 available_agents 的 4 个执行 Agent

XAgent/core.py 中注册:

Agent能力枚举核心职责
PlanGenerateAgentplan_generation将用户 query 分解为 2–4 个子任务的初始计划树
PlanRefineAgentplan_refinement子任务完成后,按 split/add/delete/exit 操作迭代修正未来子任务
ToolAgenttool_tree_searchInner Loop 内:推理 → 调工具 / subtask_submit
ReflectAgentreflection子任务结束后提取后验知识(summary、plan reflection、tool reflection)

3.2 元 Agent:DispatcherAgent

DispatcherAgent 不执行任务本身,而是为其他 Agent 按当前子任务动态生成附加 user prompt。它通过 XAgentDispatcher.dispatch() 被调用:

  1. 取目标 Agent 的 example prompt
  2. enable=True:调用 DispatcherAgent.parse() 生成定制化附加 prompt
  3. 解析失败则 fallback 到默认 prompt
  4. build_agent() 实例化目标 Agent

注意:当前默认配置 enable=Falsecore.py),生产路径直接使用静态 prompt 模板,不经 Dispatcher 动态 refine。

3.3 工作流编排类(非 BaseAgent,但承担 Agent 职能)

路径职责
PlanAgentworkflow/plan_exec.py计划生命周期:初始生成 →(可选)记忆 refine → plan_refine_mode
TaskHandlerworkflow/task_handler.py主编排器:outer loop + inner loop + posterior_process
ReACTChainSearchinner_loop_search_algorithms/ReACT.py单链 ReACT 执行器,驱动 ToolAgent
WorkingMemoryAgentworkflow/working_memory.py跨子任务记忆(chat_with_other_subtask 工具,handle 未完整实现)

3.4 摘要模块:summarize.py(函数式,非 Agent 类)

函数调用时机作用
summarize_action()ReACT 每步 make_message()压缩工具调用历史,提取 key_actions
summarize_plan()ReACT / PlanRefine / Reflect / TaskHandler 多处按 token 预算压缩计划树

与 ReflectAgent 的分工:summarize 压缩原始轨迹,Reflect 输出结构化后验知识供下一子任务消费。

3.5 枚举存在但未实现的 Agent

  • task_evaluator:枚举在 utils.py,无实现类
  • summarization:有 SummarizationTrieTree 骨架,未接入主流程

四、为什么这么拆?设计哲学

4.1 能力分派,而非单体超级 Agent

每个 Agent 通过 RequiredAbilities 枚举声明单一能力集合,由 AgentDispatcher.agent_markets 按能力路由。好处:

  • Prompt 专精:PlanGenerate 专注全局分解,ToolAgent 专注逐步执行,互不污染
  • 可替换:同一能力可注册多个 Agent 实现,Dispatcher 选第一个
  • 可测试:每个 Agent 的输入输出边界清晰

4.2 计划生成 vs 计划修正:两种认知任务

PlanGeneratePlanRefine
时机任务开始子任务完成后
权限创建完整初始计划只能修改 subtask_id > now_dealing_task 的未来任务
操作一次性分解split / add / delete / exit,最多 3 步
约束2–4 个子任务树深 ≤ 3、树宽 ≤ 5

把「从零规划」和「局部修正」拆开,避免执行中的 Agent 随意改已完成任务,这是防扯皮的第一道防线。

4.3 执行与反思分离:ToolAgent 不自我辩论

ToolAgent 在 Inner Loop 只做三件事:思考、调工具、提交(subtask_submit)。它不修改计划,若发现后续子任务需要调整,通过 subtask_submit.suggestions_for_latter_subtasks_plan.need_for_plan_refine 把意图交给 PlanRefineAgent。

ReflectAgent 在子任务结束后单次调用,输出 action_list_summaryposterior_plan_reflection 等,供后续子任务读取。没有「ToolAgent 和 PlanRefine 同环对话」的设计,从根本上杜绝多 Agent 来回辩论。

4.4 FunctionHandler 统一中介

所有工具调用(含 intrinsic tools:subtask_submitask_human_for_help)都经 FunctionHandler.handle_tool_call() 路由,集中做:

  • 命令分发
  • 状态码映射(ToolCallStatusCode
  • ToolServer 超时重试
  • 长结果压缩(long_result_summary

Agent 层不直接碰 HTTP,降低耦合。

4.5 summarize 独立:控制 Token 预算

ReACT 每步可能积累大量工具输出。summarize_action / summarize_plan 在写入 message history 前做压缩(clip_text、LLM 摘要),防止上下文爆炸导致 LLM 调用失败或成本失控。

五、工具调用可靠性:三层防护

5.1 LLM 层:重试 + 上下文降级

位置机制配置
ToolAgent.parse()@retry(stop=stop_after_attempt(max_retry_times))max_retry_times: 3
openai.py chatcompletion_requesttenacity 指数退避 61–293s;不重试 Auth/Permission/BadRequestmax_retry_times + 3
obj_generator.py chatcompletionSchema 校验失败重试 3 次内置

上下文超长时自动降级到更大模型(gpt-4gpt-4-32k 等),见 openai.py 第 69–90 行。

此外,obj_generator.chatcompletion() 通过 recorder.query_llm_inout() 查缓存;experiment.redo_action: false 时优先用缓存,避免重复 LLM 调用。

5.2 Schema 层:幻觉工具名与坏 JSON 修复

obj_generator.py

  • function_call_refine():校验 function name 是否在允许列表;幻觉工具名时注入 system error message 再抛 FunctionCallSchemaError 触发重试
  • dynamic_json_fixes():broken JSON 时让 LLM 修复后重新校验
  • load_args_with_schema_validation():参数 schema 失败时自动修复一次

ToolAgent.parse()(OpenAI 模式):

# tool_agent/agent.py 第 116–125 行jsonschema.validate(tool_call_args, schema)# 失败 → objgenerator.dynamic_json_fixes() → 再 validate

双重校验确保 LLM 输出的 function call 结构合法,减少「调了个不存在的工具」的情况。

5.3 ToolServer 层:状态码映射 + 超时重试

HTTP 状态码 → ToolCallStatusCode

HTTP状态码含义
200TOOL_CALL_SUCCESS成功
404HALLUCINATE_NAME工具名不存在
422FORMAT_ERROR参数格式错误
450TIMEOUT_ERROR超时
500TOOL_CALL_FAILED执行失败
503SERVER_ERROR服务不可用(抛异常)

超时重试(function_handler.py 第 219–231 行):

MAX_RETRY = 10while retry_time < MAX_RETRY and status == TIMEOUT_ERROR and detail['type'] == 'retry':time.sleep(3)# 用 detail['next_calling'] + detail['arguments'] 重试

ToolServer 可通过异常 type='retry' 告知客户端「这次超时但可重试」,最多 10 次、每次间隔 3 秒。超过上限则返回友好错误信息,不抛异常阻断主流程。

关键设计:失败不阻断 ReACT 循环

工具失败时,tool_status_code 写入 ToolNode.data,结果作为 system message 追加到 history,ReACT 继续下一步(除非触发 subtask_submit)。Agent 可以从错误中学习,而不是一失败就 crash。

5.4 长结果处理

  • long_result_summary():网页类工具调用 parse_web_text 压缩;字符串 >2000 字符有 summarize 占位
  • clip_text(file_archi, 1000) 限制文件系统结构展示
  • summary.single_action_max_length: 4096max_return_length: 8192 控制摘要输出

5.5 尚未实现的可靠性机制

  • 无 Circuit Breaker:ToolServer 503 直接抛异常
  • TIME_LIMIT_EXCEEDED 枚举存在但未使用
  • tool_call_count 在 TaskHandler 中初始化为 0 但从未递增
  • ToolServer 请求 timeout 硬编码 10–20s,非统一配置

六、防死循环与防扯皮

6.1 ReACT Inner Loop:硬步数上限

ReACT.py 第 220–312 行:

while now_node.get_depth() < config.max_subtask_chain_length:# 默认 15...if now_node.get_depth() == config.max_subtask_chain_length - 1:function_call = {"name": "subtask_submit"}# 最后一步强制提交if tool_output_status_code == SUBMIT_AS_SUCCESS:breakelif tool_output_status_code == SUBMIT_AS_FAILED:break

机制值/行为
硬循环上限max_subtask_chain_length: 15
最后一步强制提交不允许无限调工具
Prompt 预算提醒"You can at most use {{max_length}} steps"
多链重试max_try=1(默认不重试整条链)
退出条件subtask_submit 的 success/failed

尽管类名含 Search 且维护 TaskSearchTree,实际是单链而非树搜索分支——没有 backtracking,不会在同一子任务内反复探索多条路径。

6.2 Plan Refine:有限修正 + 显式退出

plan_exec.py plan_refine_mode()

while modify_steps < max_step:# max_plan_refine_chain_length: 3...if operation == 'exit':output_status_code = PLAN_REFINE_EXITif output_status_code in (PLAN_REFINE_EXIT, MODIFY_SUCCESS):returnmodify_steps += 1

  • 最多 3 步 refine
  • exit 操作可随时退出
  • 一次 MODIFY_SUCCESS 即 return
  • 计划树深度 ≤ 3、宽度 ≤ 5 在代码中强制校验

6.3 子任务单向推进:不回溯、不辩论

  • 中序遍历 + 单向前进:Plan.pop_next_subtask() 只取当前任务之后第一个 TODO 子任务,不回溯已完成任务
  • Plan Refine 编辑边界:deal_subtask_split/delete/addcan_edit 逻辑限制只能改未来子任务
  • 无多 Agent 对话环:Reflect 仅在子任务结束后单次调用;ToolAgent 不与 PlanRefine 同环运行

这三条合在一起,解决了 Agent 系统里最常见的两类「扯皮」:

  1. 执行中反复改计划 → 执行与修正分离,修正有步数上限
  2. 多 Agent 互相推翻结论 → 单向流水线,后验知识只写一次

6.4 人机交互中断(非死循环,但可打断)

  • interaction.interrupt 时用户可改写 goal(outer loop)或 thoughts(inner loop)
  • ask_human_for_help 是同步阻塞等待用户输入,然后继续循环——这是「等人」,不是 Agent 间辩论

6.5 上下文压缩(间接防无限膨胀)

enable_summary: true 时 ReACT 每步用 summarize_action / summarize_plan 压缩历史,避免 message 无限增长导致 LLM 反复因 context 超长而失败重试,形成隐性死循环。

七、关键配置参数速查

来源:assets/gpt-3.5-turbo_config.yml

max_retry_times: 3# LLM 调用重试max_subtask_chain_length: 15# ReACT 单链子任务最大步数max_plan_refine_chain_length: 3 # 计划修正最大迭代max_plan_tree_depth: 3# 计划树深度上限max_plan_tree_width: 5# 计划树宽度上限max_plan_length: 4096enable_summary: true# 上下文压缩开关summary:single_action_max_length: 4096max_return_length: 8192enable_ask_human_for_help: falseexperiment:redo_action: false# false = 使用 LLM/Tool 缓存

八、架构关系图

flowchart TBsubgraph Entry[&#34;入口&#34;]Server[&#34;XAgentServer&#34;]endsubgraph Core[&#34;XAgentCoreComponents&#34;]Dispatcher[&#34;XAgentDispatcher<br/>(enable=False)&#34;]FH[&#34;FunctionHandler&#34;]TSI[&#34;ToolServerInterface&#34;]WM[&#34;WorkingMemoryAgent&#34;]endsubgraph OuterLoop[&#34;Outer Loop — TaskHandler&#34;]PA[&#34;PlanAgent&#34;]PGen[&#34;PlanGenerateAgent&#34;]PRef[&#34;PlanRefineAgent&#34;]Refl[&#34;ReflectAgent&#34;]endsubgraph InnerLoop[&#34;Inner Loop — ReACTChainSearch&#34;]TA[&#34;ToolAgent&#34;]Sum[&#34;summarize.py&#34;]endServer --> TH[&#34;TaskHandler&#34;]TH --> PAPA -->|plan_generation| Dispatcher --> PGenTH -->|per subtask| InnerLoopInnerLoop --> Dispatcher --> TATA --> FH --> TSIInnerLoop --> SumTH -->|posterior| ReflTH -->|plan_refine| PRefTH --> WM

九、技术债与展望

缺口影响
SummarizationTrieTree 未接入主流程Trie 增量摘要能力闲置
task_evaluator 无实现无法自动评估子任务质量
tool_call_count 未使用无法做全局工具调用预算
无 Circuit BreakerToolServer 故障可能拖垮整条链
Dispatcher 默认关闭动态 prompt 能力未在生产路径启用
ReACTChainSearch 实为单链命名与实现不符,无真正树搜索
WorkingMemoryAgent handle 未完整跨子任务对话能力预留但未落地

十、总结

XAgent 的多 Agent 拆分遵循一条清晰原则:不同认知任务用不同 Agent,用硬上限和单向流水线约束协作边界。

  • 可靠性:LLM 重试 + Schema 修复 + ToolServer 超时重试 + 失败写入 history 继续执行
  • 防死循环:ReACT 15 步硬上限 + 最后一步强制 submit + Plan Refine 3 步上限
  • 防扯皮:执行/修正/反思三阶段分离 + 子任务单向推进 + 只能改未来计划

如果你在做自己的 Agent 框架,最值得抄的三件事是:FunctionHandler 统一中介、subtask_submit 驱动计划变更、summarize 控制 token 预算。其余部分(Dispatcher 动态 prompt、WorkingMemory 跨任务通信)在源码里还是半成品,正好留给你改进的空间。

点击查看更多
推荐专题
热门阅读