ARTICLE DETAIL

资讯详情

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

OpenManus 整体代码架构分析:从 agent 到 toolcall 的 flow 拆解

OpenManus 整体代码架构分析:从 agent 到 toolcall 的 flow 拆解 1. 从一次任务落地说起OpenManus 的 flow 到底怎么跑OpenManus 是一个开源的多智能体agent执行框架核心能力是把一句自然语言任务拆成可执行步骤再通过 ReAct 循环驱动 toolcall 去调用工具最终把结果落地。它适合谁适合想读懂 agent 调度源码、想自己接一套 toolcall 执行链路、或者想把多智能体规划跑在本地的开发者。很多人第一次看 OpenManus 会被app目录下密密麻麻的模块劝退其实只要抓住一条主线输入 → PlanningFlow 拆解 → executor 选择 agent → ReAct 循环 think/act → toolcall 执行 → 状态回写整个架构就清晰了。我试过按这条主线把源码从头到尾走一遍发现它真正的骨架只有三层agent谁来做、flow怎么编排、tool用什么做。agent目录里base.py定义抽象基类react.py实现 think/act 抽象与 step 逻辑toolcall.py落地工具调用flow目录里base.py是流程基类planning.py实现 PlanningFlowflow_factory.py负责工厂创建tool目录则是所有可被 toolcall 触发的工具定义。再往外围sandbox提供 Docker 隔离执行环境mcp提供 MCP server 注册prompt集中管理提示词。理解这条链路后你就能回答一个关键问题一次任务从输入到落地中间到底经过了哪些函数调用。下面我会按「目录结构 → 前置准备 → 可复制配置 → 验证请求 → 常见报错 → 接入建议」的顺序拆解每一步都给出可复制的路径和命令方便你边看边在本地跑通。2. 前置准备把 OpenManus 跑起来需要什么在拆源码之前先把运行环境准备好否则读代码时无法验证调用路径。OpenManus 是 Python 项目依赖异步 IO 和 Docker沙箱执行需要所以本地需要 Python 3.10 和可用的 Docker 环境。如果你只是想读 agent 调度和 ReAct 循环不跑沙箱也可以先跳过 Docker 部分。模型接入方面OpenManus 需要一个兼容 OpenAI 接口的 LLM 服务来驱动 think 和 plan 生成。你可以用 TaoToken 提供的统一 API 入口它兼容 OpenAI SDK 的调用方式配置简单适合本地调试 agent 流程。官网地址是 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 基址是 https://taotoken.net/api 注意 API 地址不带 UTM 参数。拿到 API Key 的路径是先注册登录然后进入控制台创建密钥。控制台地址 https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewrite API Keys 管理页 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite 。创建后复制 Key后面配置环境变量会用到。注意API Key 只显示一次建议创建后立即保存到本地.env文件不要硬编码进源码提交到仓库。环境变量建议这样组织OpenManus 读取配置时通常会从环境变量或 config 文件加载# .env 示例 OPENAI_API_KEYsk-你的TaoToken密钥 OPENAI_BASE_URLhttps://taotoken.net/api OPENAI_MODELgpt-4o-mini如果你用的是其他兼容模型把OPENAI_MODEL换成对应模型名即可。配置好后先别急着跑完整 flow先用一个最小请求验证 Key 和网络是否通。3. 目录结构与关键模块调用路径先把app目录的骨架列出来这是理解整个 flow 的地图。下面这份结构说明可以直接对照你的本地仓库app/ ├── agent/ │ ├── base.py # BaseAgent 抽象基类定义 state_context、AgentState、max_steps │ ├── react.py # ReActAgent继承 BaseAgent定义 think/act 抽象与 step 执行 │ ├── toolcall.py # ToolCallAgent继承 ReActAgent实现 think/act工具调用基类 │ ├── browser.py # 浏览器 agent继承 ToolCallAgent │ ├── manus.py # 主 agent继承 ToolCallAgent │ ├── mcp.py # MCP agent继承 ToolCallAgent │ └── swe.py # SWE agent继承 ToolCallAgent ├── flow/ │ ├── base.py # BaseFlow 流程基类 │ ├── planning.py # PlanningFlow继承 BaseFlow任务规划与执行核心 │ └── flow_factory.py # 工厂创建 PlanningFlow 实例 ├── tool/ │ ├── base.py # BaseTool 工具基类 │ ├── planning.py # PlanningTool计划存储与状态更新 │ └── ... # 其他具体工具 ├── sandbox/ │ ├── manager.py # 多 Docker 沙箱生命周期管理 │ ├── sandbox.py # 单沙箱实现异步 Docker SDK 调用 │ ├── terminal.py # 异步 Docker 终端 │ └── client.py # 沙箱客户端接口 LocalSandboxClient ├── mcp/ │ └── server.py # MCPServer工具注册 register_tool 与 run ├── prompt/ # 各类提示词定义 └── ...调用路径可以这样串起来入口拿到input_text后flow_factory.py创建PlanningFlow实例PlanningFlow.execute()进入主循环先调_create_initial_plan()让 LLM 生成计划再通过_get_current_step_info()取当前步骤用get_executor(step_type)选执行 agent然后executor.run(step_prompt)进入 agent 侧agent 侧ToolCallAgent的step()驱动think()和act()act()里解析 toolcall 并调用tool目录下的具体工具执行完通过_mark_step_completed()回写状态循环直到计划完成。这条链路里最容易被忽略的是get_executor的动态代理分配它按step_type从self.agents里匹配专用 agent匹配不到才回退主 agent。这意味着你可以在计划里给不同步骤打上类型标签让浏览器任务走browser.py代码任务走swe.py。4. 可复制配置把 PlanningFlow 和 ToolCallAgent 接起来理解了路径接下来给一份可复制的配置把 flow 和 agent 接起来跑。核心是构造PlanningFlow时传入 agents 字典和 LLM 客户端。下面是一个最小可运行示例你可以放在项目根目录的run_flow.py里import asyncio from app.flow.flow_factory import FlowFactory from app.agent.manus import Manus from app.agent.browser import BrowserAgent from app.llm import LLM async def main(): llm LLM() # 读取 .env 中的 OPENAI_API_KEY / OPENAI_BASE_URL agents { manus: Manus(llmllm), browser: BrowserAgent(llmllm), } flow FlowFactory.create_flow( flow_typeplanning, agentsagents, llmllm, ) result await flow.execute(帮我查一下 OpenManus 的 agent 目录结构并总结) print(result) if __name__ __main__: asyncio.run(main())这段代码的关键点有三个。第一FlowFactory.create_flow内部会实例化PlanningFlow并把agents和llm注入进去所以你不必手动 new。第二agents字典的 key 就是step_type计划里步骤类型匹配到哪个 key就用哪个 agent 执行。第三flow.execute()是异步的必须用asyncio.run驱动。如果你要限制最大步数避免无限循环可以在 agent 构造时传max_steps。BaseAgent里通过max_steps限制单次执行的最大步数这是防止 ReAct 循环失控的第一道闸agent Manus(llmllm, max_steps15)max_steps的默认值通常在配置里建议本地调试时设小一点比如 10 到 15方便观察每一步的 think/act 输出。等流程稳定后再放宽。提示Config.extra allow这类配置允许你在 agent 上挂额外字段扩展自定义参数时不用改基类。5. 验证请求观察一次完整 flow 的成功结果配置好后跑一次验证请求重点观察三件事计划是否生成、executor 是否被正确选择、toolcall 是否执行并回写状态。运行上面的run_flow.py你会看到类似下面的输出结构[PlanningFlow] 生成初始计划 1. [ ] 查询 OpenManus agent 目录结构 2. [ ] 总结各模块职责 3. [ ] 输出最终报告 [PlanningFlow] 当前步骤 1executormanus [ToolCallAgent] think: 需要调用文件读取工具查看目录 [ToolCallAgent] act: toolcall - file_read(pathapp/agent) [PlanningTool] mark_step: step 1 - completed ... 最终结果OpenManus 的 agent 目录包含 base/react/toolcall 等模块...看到[✓]状态标记出现说明_mark_step_completed()成功调用了PlanningTool.execute(commandmark_step)状态被持久化。_generate_plan_text_from_storage()会生成带状态符号和进度百分比的可视化计划文本这是判断 flow 是否正常推进的直接依据。如果你想单独验证 toolcall 链路不走完整 flow可以直接实例化ToolCallAgent并调用step()async def test_toolcall(): llm LLM() agent Manus(llmllm, max_steps5) result await agent.run(读取 app/agent/base.py 的前 20 行) print(result) asyncio.run(test_toolcall())这一步能帮你确认think()是否正确解析出 toolcall、act()是否真的调到了工具。如果这里报错问题通常出在提示词或工具注册而不是 flow 编排。验证模型本身是否可用可以走模型对话入口快速测一下https://taotoken.net/model-chat?utm_sourcetaotoken_aicg_blog_endutm_contentmodel-chatutm_campaignrewrite 。如果模型对话正常但 flow 报错那问题就在代码侧不在 Key 或网络。6. 本篇常见错排查flow 跑不通时先看这几处第一个高频错误是get_executor返回了错误的 agent。原因通常是计划里步骤的step_type和agents字典的 key 对不上导致回退到主 agent。排查方法是打印step_info里的类型字段确认它和字典 key 一致。如果计划生成时 LLM 没按预期输出类型标签可以在 prompt 里明确要求步骤带类型。第二个错误是 ReAct 循环不终止一直 think 不 act。这通常是max_steps设得太大或者工具调用返回结果没被正确解析。先看ToolCallAgent.act()里 toolcall 的解析逻辑确认返回的 JSON 参数能被解析。如果解析失败检查提示词里工具描述是否完整。第三个错误是沙箱相关报错比如 Docker 容器创建失败或路径被过滤。sandbox.py里对路径做了..过滤并且默认network_modenone如果你的工具需要联网得显式放开。资源限制方面cpu_quotaint(100000 * cpu_limit)和mem_limit参数如果设得太小容器会启动失败。排查时先看manager.py的创建日志再确认 Docker 本身可用。第四个错误是状态回写失败计划一直显示未完成。检查PlanningTool.execute(commandmark_step)是否被调用以及PlanStepStatus的值是否传对。_mark_step_completed()里用的是PlanStepStatus.COMPLETED.value如果你手动传了枚举对象而不是 value可能匹配不上。第五个错误是异步调用阻塞。sandbox.py用asyncio.to_thread包装同步 Docker SDK 调用如果你在同步函数里直接调异步方法会报 event loop 相关错误。确保所有入口都用asyncio.run或已有 event loop 驱动。注意排障时优先看state_context里的异常捕获BaseAgent在关键操作点加了 try-except错误信息通常会被包装后返回别只看最外层报错。7. 接入建议与后续方向把 flow 跑通后下一步通常是接自己的工具或扩展 agent。工具侧在tool目录新增一个继承BaseTool的类实现execute方法然后在 agent 初始化时注册进去toolcall 就能识别。agent 侧如果要加专用执行器继承ToolCallAgent并实现自己的think/act再在agents字典里挂上对应step_type即可。如果你打算长期跑编码类或 Agent 类任务建议用 Coding Plan 做额度规划入口在 https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite 。接入文档在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 里面有完整的接口说明和参数对照。ClaudeCodeAnthropic 相关接入参考 https://taotoken.net/claudecode-anthropic?utm_sourcetaotoken_aicg_blog_endutm_contentclaudecode-anthropicutm_campaignrewrite 。最后给一个实用技巧读 OpenManus 源码时别从base.py一行行啃直接从PlanningFlow.execute()打断点跟着调用栈走一遍比静态阅读快得多。把max_steps设成 3跑一个简单任务观察每一步的 think/act 和状态回写整个 flow 的骨架自然就清楚了。
返回列表