ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

OpenManus框架解析(二)ToolCallAgent工作机制:从ReActAgent到工具调用的配置骨架

OpenManus框架解析(二)ToolCallAgent工作机制:从ReActAgent到工具调用的配置骨架 1. 从一次「工具调用卡住」说起ToolCallAgent 到底在做什么如果你正在本地调试 OpenManus 的多工具 Agent大概率遇到过这种场景Agent 明明识别出了要调用工具但循环停在某一步不动了或者工具返回结果后 Agent 没有继续推理直接输出一段无关的话就结束了。这类问题的根因往往不在工具本身而在 ToolCallAgent 与 ReActAgent 的衔接点上——think() 返回了什么、act() 怎么消费 tool_calls、特殊工具什么时候触发 FINISHED这三件事只要有一处配置不对整个闭环就断了。ToolCallAgent 是 OpenManus 里所有「能调工具的 Agent」的公共基类。它继承自抽象类 ReActAgent把 ReActReasoning Acting循环从纸面逻辑落成了可执行代码think() 负责让 LLM 决策要不要调工具、调哪个act() 负责真正执行并把 observation 写回 memory下一轮 think() 再基于新记忆继续推理。适合谁看正在给 OpenManus 写自定义 Agent、想搞清楚 available_tools / tool_choices / special_tool_names 这几个属性怎么配的人以及需要定位「ToolCall 与 ReAct 衔接点」的调试者。这篇不重复讲类继承图而是给出一份可以直接复制的 Agent 配置骨架和 settings.json 片段再演示一次完整的工具调用链验证动作让你能亲手确认 think → act → observe 这条链路是通的。模型侧我用的是 TaoToken 的兼容接口来跑 ask_tool()下面会把接入配置一并写清楚。2. 前置把模型接口和依赖准备好ToolCallAgent 的 think() 阶段会调用 LLM 的 ask_tool()这个方法要求后端支持标准的 tool/function calling 协议。所以第一步是准备一个能返回 tool_calls 字段的模型接口。我用 TaoToken 的 API 来做这件事它的接口形态和主流 OpenAI 兼容协议一致ask_tool() 不需要改代码就能对接。先去控制台拿一个 API Key地址是 https://taotoken.net/api-keys 登录后在密钥管理页新建即可。拿到 key 之后把它写进环境变量别硬编码进代码export TAOTOKEN_API_KEYsk-你的key export TAOTOKEN_BASE_URLhttps://taotoken.net/api如果你还没决定用哪个模型跑 Agent 循环可以先去模型对话页试一下工具调用是否正常返回地址 https://taotoken.net/models 选一个支持 function calling 的模型发一条带 tools 参数的请求看返回里有没有 tool_calls。这一步能提前排除「模型不支持工具调用」这个最常见的坑。依赖方面OpenManus 本体按官方仓库装好即可重点是确认 tenacity 和 pydantic 版本匹配因为 think() 里对 TokenLimitExceeded 的处理依赖 tenacity 的 RetryError 包装行为。装完依赖后建议先跑一次最小 Agent确认基础循环能起来再往下改配置。3. 可复制的 ToolCallAgent 配置骨架下面这份骨架是我在本地调试时常用的最小可用版本。它继承 ToolCallAgent只覆盖必要的属性把工具集合、提示词、步数控制都显式写出来方便你逐项对照排查。from app.agent.toolcall import ToolCallAgent from app.tool import ToolCollection, Terminate from app.tool.create_chat_completion import CreateChatCompletion from app.prompt.toolcall import SYSTEM_PROMPT, NEXT_STEP_PROMPT class MyToolAgent(ToolCallAgent): name: str my_tool_agent description: str a local agent for multi-tool debugging system_prompt: str SYSTEM_PROMPT next_step_prompt: str NEXT_STEP_PROMPT # 关键显式声明可用工具子类在这里扩展 available_tools: ToolCollection ToolCollection( CreateChatCompletion(), Terminate(), ) # AUTO 让 LLM 自行决定是否调工具调试期可临时改 REQUIRED tool_choices: str auto # 调用这些工具后 Agent 进入 FINISHED special_tool_names: list [terminate] max_steps: int 30 max_observe: int | None 4000 # 截断过长 observation避免撑爆上下文几个属性值得单独说。available_tools 默认只注册 CreateChatCompletion 和 Terminate你加自己的工具时直接往 ToolCollection 里塞实例就行工具名会进 tool_mapexecute_tool() 靠它做校验。tool_choices 有三个值none 不允许调工具、auto 自动、required 强制必须调调试衔接问题时我一般先用 required逼着 LLM 一定返回 tool_calls确认 act() 能正常消费再切回 auto。max_observe 建议设一个具体值。设成 None 表示不截断工具返回一大段日志时下一轮 think() 的 prompt 会瞬间变长很容易触发 token 超限然后你就看到 RetryError 被解包后 Agent 优雅终止——看起来像「莫名其妙停了」其实是上下文爆了。对应的 settings.json 片段把模型和 key 配好{ llm: { base_url: https://taotoken.net/api, api_key: ${TAOTOKEN_API_KEY}, model: 你的模型名, max_tokens: 4096, temperature: 0.3 }, agent: { max_steps: 30, max_observe: 4000 } }temperature 调低一点工具调用场景下模型更需要稳定输出结构化的 tool_calls而不是发挥创意。4. 验证一次工具调用链think → act → observe配置写好后别急着上复杂任务先用一个「必然触发工具调用」的请求验证链路。我通常让 Agent 调用 CreateChatCompletion 做一次简单补全再让它调 Terminate 结束这样能同时验证普通工具和特殊工具两条路径。import asyncio from my_agent import MyToolAgent async def main(): agent MyToolAgent() result await agent.run(请调用工具完成一次聊天补全然后终止。) print(FINAL:, result) asyncio.run(main())跑起来后重点观察三处输出。第一think() 阶段日志里应该出现 tool_calls 列表每个元素带 function.name 和 arguments如果这里是空的说明模型没返回工具调用检查 tool_choices 和模型是否支持 function calling。第二act() 阶段应该打印每个工具的执行结果格式是格式化后的 observation 字符串如果看到 Error: ... 开头说明 execute_tool() 在 JSON 解析或工具执行时抛了异常但注意它不会中断循环而是把错误当结果写回 memory。第三调用 Terminate 后Agent 状态应变为 FINISHEDrun() 的 finally 块执行 cleanup()日志里能看到工具清理动作。一个实测下来很有用的验证技巧在 execute_tool() 里临时加一行打印把 name 和 tool_input 打出来确认 JSON 参数解析正确。很多「工具没反应」的问题其实是 LLM 返回的 arguments 不是合法 JSON被 json.JSONDecodeError 捕获后返回了 Error 字符串而 Agent 把这个错误当成了正常 observation 继续跑表面看就是「工具调了但没效果」。5. 本篇常见错排查报错一RetryError 包裹 TokenLimitExceededAgent 直接停。这是 think() 里最典型的退出路径。tenacity 把原始异常包进 RetryError代码通过hasattr(e, __cause__)解包后终止。排查方向调小 max_observe、精简 system_prompt、或者换上下文窗口更大的模型。别去改异常处理逻辑先看是不是 prompt 太长。报错二REQUIRED 模式下抛 ValueError。act() 开头会检查如果无 tool_calls 且 mode 为 REQUIRED直接抛 ValueError。这说明你强制要求调工具但 LLM 没返回 tool_calls。要么模型不支持要么提示词没引导好。调试期可以先把 tool_choices 切回 auto 确认模型本身能返回工具调用。报错三工具名校验失败。execute_tool() 第一步校验 function.name 非空第二步校验工具是否在 available_tools.tool_map 里。如果你新加了工具但忘了注册进 ToolCollection就会走到「工具不在 map 中」的分支。检查 available_tools 的构造确保每个工具实例都传进去了。报错四特殊工具没触发终止。special_tool_names 默认只有 terminate且 _should_finish_execution() 默认返回 True。如果你自定义了终止工具但没加进 special_tool_names调用后 Agent 不会进 FINISHED会继续下一轮 think()。把工具名加进列表即可。报错五cleanup() 没执行导致资源泄漏。run() 用 try-finally 保证 cleanup() 一定跑但前提是你的工具实现了异步 cleanup() 方法。如果工具持有连接或文件句柄却没实现 cleanup资源不会自动释放。给这类工具补上 cleanup() 实现。6. 把链路跑通之后往哪走ToolCallAgent 的调试核心就一句话think() 决定调不调、act() 负责调得对不对、memory 决定下一轮怎么想。你把这三段的日志都打开任何衔接问题都能定位到具体是哪一段断了。上面那份骨架和 settings.json 可以直接拿去改先跑通一次 CreateChatCompletion Terminate 的最小链路再逐步加自己的工具。如果你要长期跑编码类或 Agent 类任务反复手动配 key 和模型比较麻烦可以看一下 Coding Plan地址 https://taotoken.net/coding-plan 它更适合这种持续性的开发场景。接入文档在 https://taotoken.net/doc ask_tool() 对接细节和参数说明都在里面。工具调用链跑通后下一步通常是给 Agent 加更多工具并处理工具间的依赖顺序那部分我们放到下一篇讲。
返回列表