{"schema_version":1,"id":"article:openmanus-teardown","kind":"article","slug":"openmanus-teardown","title":"深度拆解 OpenManus 技术框架：与 Manus 的差距分析","author":"晨旭","url":"https://chenxu.xin/writing/openmanus-teardown","date":"2025-11-15","updated":"2025-11-15","description":"从代码层拆解 OpenManus 的四层架构与 ReAct 骨架，用一次真实的旅游规划任务暴露它的僵尸 Agent 和内存污染缺陷，再对照官方 Manus 与 Anthropic，看清开源框架和商业产品之间那道叫上下文工程的鸿沟。","position":null,"group":null,"series":"AI技术理解","tags":["agent","product-teardown","context-engineering","learning-method"],"publication_status":"public","maturity":"evergreen","body_sha256":"112382360232f4c52f879866e96e25d819c1f13547eebdefdaf8f2d1af166a8c","paragraph_rule":"src/lib/paragraphs.mjs@1","paragraphs":[{"id":"p1","kind":"paragraph","text":"MetaGPT 团队开源了 OpenManus，一个通用的 AI Agent 框架。它的目标是提供一套强大的架构，让 Agent 能够执行浏览器操作、代码执行、文件操作等复杂任务。","raw":"MetaGPT 团队开源了 OpenManus，一个通用的 AI Agent 框架。它的目标是提供一套强大的架构，让 Agent 能够执行浏览器操作、代码执行、文件操作等复杂任务。"},{"id":"p2","kind":"paragraph","text":"那么，这个框架是怎么搭建的？它和我们熟知的 Manus 有什么异同呢？","raw":"那么，这个框架是怎么搭建的？它和我们熟知的 Manus 有什么异同呢？"},{"id":"p3","kind":"heading","text":"PM 视角：为什么要扒开源代码？","raw":"## PM 视角：为什么要扒开源代码？"},{"id":"p4","kind":"paragraph","text":"现在 AI 几乎可以秒懂任何代码库，程序员的技术黑盒在逐渐白盒化，看代码就像是看文档。产品经理可以，也应该，深入前沿技术，进行「黑客级竞品调研」。","raw":"现在 AI 几乎可以秒懂任何代码库，程序员的技术黑盒在逐渐白盒化，看代码就像是看文档。产品经理可以，也应该，深入前沿技术，进行「黑客级竞品调研」。"},{"id":"p5","kind":"paragraph","text":"它不是简单地罗列功能，而是建立一个交叉验证的闭环：","raw":"它不是简单地罗列功能，而是建立一个交叉验证的闭环："},{"id":"p6","kind":"list","text":"- 实测体验：从用户视角，感知竞品（如商业版 Manus）的效果。\n- 技术解密：深入挖掘竞品的技术博客、论文、访谈，理解其技术原理。\n- 开源复现：找到一个业界最接近的开源项目（如 OpenManus），在内部跑起来，观察其实现过程，哪怕这个开源项目只有商业 Manus 效果的 50%。","raw":"- **实测体验**：从用户视角，感知竞品（如商业版 Manus）的效果。\n- **技术解密**：深入挖掘竞品的技术博客、论文、访谈，理解其技术原理。\n- **开源复现**：找到一个业界最接近的开源项目（如 OpenManus），在内部跑起来，观察其实现过程，哪怕这个开源项目只有商业 Manus 效果的 50%。"},{"id":"p7","kind":"paragraph","text":"那为什么要复现一个可能落后的开源项目？因为没有对比，就没有分析。通过复现 OpenManus，我们是作为开发者的角度，能看到 Agent 所有的思考日志。这让我们能精准定位到它技术架构的优劣势，并看到它和真 Manus 的差距。","raw":"那为什么要复现一个可能落后的开源项目？因为没有对比，就没有分析。通过复现 OpenManus，我们是作为开发者的角度，能看到 Agent 所有的思考日志。这让我们能精准定位到它技术架构的优劣势，并看到它和真 Manus 的差距。"},{"id":"p8","kind":"paragraph","text":"看到差的，才能真正理解好的牛在何处，才有要优化的方向。","raw":"看到差的，才能真正理解好的牛在何处，才有要优化的方向。"},{"id":"p9","kind":"heading","text":"整体技术架构概览","raw":"## 整体技术架构概览"},{"id":"p10","kind":"paragraph","text":"从代码和设计来看，OpenManus 采用了经典的分层设计，可以概括为四个核心层次：","raw":"从代码和设计来看，OpenManus 采用了经典的分层设计，可以概括为四个核心层次："},{"id":"p11","kind":"list","text":"- 基础设施层：最底层，提供 LLM 抽象、沙箱环境、配置管理和日志。\n- 工具层：中间层，提供 Agent 动手的能力，如浏览器、代码执行、文件操作、搜索等。\n- Agent 层：核心控制层，负责思考，决定何时调用什么工具。\n- 应用入口层：最上层，负责接收用户指令，启动 Agent。","raw":"- **基础设施层**：最底层，提供 LLM 抽象、沙箱环境、配置管理和日志。\n- **工具层**：中间层，提供 Agent 动手的能力，如浏览器、代码执行、文件操作、搜索等。\n- **Agent 层**：核心控制层，负责思考，决定何时调用什么工具。\n- **应用入口层**：最上层，负责接收用户指令，启动 Agent。"},{"id":"p12","kind":"paragraph","text":"简化的流程是：用户指令 → Manus Agent（Agent 层）→ LLM（基础设施层）思考并决定调用工具 → 工具集合（工具层）↔ 沙箱 / 浏览器 / 外部系统。","raw":"简化的流程是：用户指令 → Manus Agent（Agent 层）→ LLM（基础设施层）思考并决定调用工具 → 工具集合（工具层）↔ 沙箱 / 浏览器 / 外部系统。"},{"id":"p13","kind":"heading","text":"1、统一的 LLM 抽象层（app/llm.py）","raw":"### 1、统一的 LLM 抽象层（`app/llm.py`）"},{"id":"p14","kind":"paragraph","text":"这是 Agent 的大脑中枢。框架没有锁定单一模型：","raw":"这是 Agent 的大脑中枢。框架没有锁定单一模型："},{"id":"p15","kind":"list","text":"- 兼容多家模型：OpenAI、Azure、Ollama（本地模型）、AWS Bedrock 都可以通过 config.toml 无缝切换。\n- 统一 API：封装了统一的 ask 和 ask_tool 接口。\n- 内置健壮性：自带 tenacity 重试策略，能处理常见的速率限制和 API 错误。\n- 成本控制：内置 Token 统计，包括图片 Token 估算，能有效防止上下文超限。","raw":"- **兼容多家模型**：OpenAI、Azure、Ollama（本地模型）、AWS Bedrock 都可以通过 `config.toml` 无缝切换。\n- **统一 API**：封装了统一的 `ask` 和 `ask_tool` 接口。\n- **内置健壮性**：自带 `tenacity` 重试策略，能处理常见的速率限制和 API 错误。\n- **成本控制**：内置 Token 统计，包括图片 Token 估算，能有效防止上下文超限。"},{"id":"p16","kind":"heading","text":"2、强大的工具集合（app/tool/）","raw":"### 2、强大的工具集合（`app/tool/`）"},{"id":"p17","kind":"paragraph","text":"这是 Agent 的双手，内置了极为丰富的工具集：","raw":"这是 Agent 的双手，内置了极为丰富的工具集："},{"id":"p18","kind":"list","text":"- 环境控制：BrowserUseTool（基于 Playwright 和 BrowserGym 的浏览器自动化）、ComputerUseTool（配合 Daytona Sandbox 实现 GUI 层面的电脑控制）。\n- 代码与文件：PythonExecute / Bash（在沙箱中安全执行代码和 Shell 命令）、FileOperators / StrReplaceEditor（文件读写、列举、编辑）。\n- 信息获取：WebSearch（封装 Google / Bing / Baidu）、Crawl4ai（结构化抓取网页内容）。\n- 其他能力：DataAnalysis + ChartVisualization（数据分析与图表可视化）、AskHuman（在关键节点引入人类确认）。","raw":"- **环境控制**：`BrowserUseTool`（基于 Playwright 和 BrowserGym 的浏览器自动化）、`ComputerUseTool`（配合 Daytona Sandbox 实现 GUI 层面的电脑控制）。\n- **代码与文件**：`PythonExecute` / `Bash`（在沙箱中安全执行代码和 Shell 命令）、`FileOperators` / `StrReplaceEditor`（文件读写、列举、编辑）。\n- **信息获取**：`WebSearch`（封装 Google / Bing / Baidu）、`Crawl4ai`（结构化抓取网页内容）。\n- **其他能力**：`DataAnalysis` + `ChartVisualization`（数据分析与图表可视化）、`AskHuman`（在关键节点引入人类确认）。"},{"id":"p19","kind":"heading","text":"3、隔离的沙箱环境（app/sandbox/）","raw":"### 3、隔离的沙箱环境（`app/sandbox/`）"},{"id":"p20","kind":"paragraph","text":"安全是 Agent 框架的重中之重。默认使用 DockerSandbox，将代码执行和 Shell 命令限制在隔离的 Docker 容器中，可以配置 CPU 和内存限制防止资源滥用，也支持连接 Daytona 远程开发环境实现更复杂的 GUI 控制。","raw":"安全是 Agent 框架的重中之重。默认使用 `DockerSandbox`，将代码执行和 Shell 命令限制在隔离的 Docker 容器中，可以配置 CPU 和内存限制防止资源滥用，也支持连接 Daytona 远程开发环境实现更复杂的 GUI 控制。"},{"id":"p21","kind":"heading","text":"4、独特的能力：环境感知与协议支持","raw":"### 4、独特的能力：环境感知与协议支持"},{"id":"p22","kind":"paragraph","text":"浏览器环境感知：在 Agent 操作浏览器时，这个模块会自动将当前页面的 DOM 摘要、页面信息等上下文喂给 LLM，极大提升了模型对当前环境的感知力。","raw":"**浏览器环境感知**：在 Agent 操作浏览器时，这个模块会自动将当前页面的 DOM 摘要、页面信息等上下文喂给 LLM，极大提升了模型对当前环境的感知力。"},{"id":"p23","kind":"paragraph","text":"A2A / MCP 协议支持：MCPClientTool 支持模型上下文协议，允许 Agent 动态地从外部服务加载和调用工具；protocol/a2a 支持将 Manus Agent 整体封装成一个 A2A 服务，让其他系统远程调用它的能力。","raw":"**A2A / MCP 协议支持**：`MCPClientTool` 支持模型上下文协议，允许 Agent 动态地从外部服务加载和调用工具；`protocol/a2a` 支持将 Manus Agent 整体封装成一个 A2A 服务，让其他系统远程调用它的能力。"},{"id":"p24","kind":"heading","text":"核心工作流拆解：Agent 如何思考与行动","raw":"## 核心工作流拆解：Agent 如何思考与行动"},{"id":"p25","kind":"paragraph","text":"OpenManus 的核心是一套基于 ReAct 模式的 Agent 引擎。无论上层是哪种 Agent，它们几乎都共享同一套骨架。","raw":"OpenManus 的核心是一套基于 ReAct 模式的 Agent 引擎。无论上层是哪种 Agent，它们几乎都共享同一套骨架。"},{"id":"p26","kind":"heading","text":"1、通用骨架：BaseAgent → ReAct → ToolCall","raw":"### 1、通用骨架：BaseAgent → ReAct → ToolCall"},{"id":"p27","kind":"paragraph","text":"BaseAgent 定义了 Agent 的通用生命周期：run(request) 是总入口，负责管理状态（IDLE → RUNNING）、初始化 memory、启动多步循环；step() 是循环中的核心，每一步都会调用一次，直到达到 max_steps 或状态变为 FINISHED；cleanup() 在任务结束时清理资源。","raw":"**`BaseAgent`** 定义了 Agent 的通用生命周期：`run(request)` 是总入口，负责管理状态（IDLE → RUNNING）、初始化 `memory`、启动多步循环；`step()` 是循环中的核心，每一步都会调用一次，直到达到 `max_steps` 或状态变为 FINISHED；`cleanup()` 在任务结束时清理资源。"},{"id":"p28","kind":"paragraph","text":"ReActAgent 继承 BaseAgent，注入 ReAct 思想，把 step() 拆分成了 think()（思考）和 act()（行动）两个方法。","raw":"**`ReActAgent`** 继承 `BaseAgent`，注入 ReAct 思想，把 `step()` 拆分成了 `think()`（思考）和 `act()`（行动）两个方法。"},{"id":"p29","kind":"paragraph","text":"ToolCallAgent 继承 ReActAgent，是最关键的实现层：","raw":"**`ToolCallAgent`** 继承 `ReActAgent`，是最关键的实现层："},{"id":"p30","kind":"list","text":"- think() 负责调用 LLM。它打包好 memory 中的历史消息和 available_tools 的工具列表，调用 llm.ask_tool(...)，等待 LLM 返回思考文本和工具调用计划。\n- act() 负责执行工具。它解析 think() 阶段拿到的 tool_calls，通过 execute_tool() 真正去调用工具，并拿到执行结果。","raw":"- `think()` 负责调用 LLM。它打包好 `memory` 中的历史消息和 `available_tools` 的工具列表，调用 `llm.ask_tool(...)`，等待 LLM 返回思考文本和工具调用计划。\n- `act()` 负责执行工具。它解析 `think()` 阶段拿到的 `tool_calls`，通过 `execute_tool()` 真正去调用工具，并拿到执行结果。"},{"id":"p31","kind":"paragraph","text":"总结成一个闭环就是：think → act → 把 ToolResult 写回 memory → 进入下一步循环。","raw":"总结成一个闭环就是：`think` → `act` → 把 `ToolResult` 写回 `memory` → 进入下一步循环。"},{"id":"p32","kind":"heading","text":"2、单 Agent 流程：各司其职","raw":"### 2、单 Agent 流程：各司其职"},{"id":"p33","kind":"paragraph","text":"基于上述骨架，OpenManus 派生出了多种专职 Agent，共享骨架但拥有不同的工具集：","raw":"基于上述骨架，OpenManus 派生出了多种专职 Agent，共享骨架但拥有不同的工具集："},{"id":"p34","kind":"list","text":"- 通用 Manus Agent：默认的全能型 Agent，工具集包括浏览器、代码、文件、AskHuman、Terminate，以及动态加载的 MCP 工具。create() 时会根据配置初始化 MCP 客户端；think() 之前会检查是否需要注入浏览器上下文。\n- SandboxManus Agent：Computer Use 专家，专为 Daytona 等远程 GUI 沙箱设计，工具是沙箱特供版，能力更强。create() 时创建远程沙箱，cleanup() 时销毁。\n- DataAnalysis Agent：数据分析师，工具是跑分析脚本、生成图表配置、调用服务绘图。\n- SWEAgent：程序员，工具集极简，只有 Bash 和 StrReplaceEditor，专注于「运行测试 → 发现错误 → 修改代码」的循环。","raw":"- **通用 Manus Agent**：默认的全能型 Agent，工具集包括浏览器、代码、文件、AskHuman、Terminate，以及动态加载的 MCP 工具。`create()` 时会根据配置初始化 MCP 客户端；`think()` 之前会检查是否需要注入浏览器上下文。\n- **SandboxManus Agent**：Computer Use 专家，专为 Daytona 等远程 GUI 沙箱设计，工具是沙箱特供版，能力更强。`create()` 时创建远程沙箱，`cleanup()` 时销毁。\n- **DataAnalysis Agent**：数据分析师，工具是跑分析脚本、生成图表配置、调用服务绘图。\n- **SWEAgent**：程序员，工具集极简，只有 `Bash` 和 `StrReplaceEditor`，专注于「运行测试 → 发现错误 → 修改代码」的循环。"},{"id":"p35","kind":"heading","text":"3、多 Agent 编排","raw":"### 3、多 Agent 编排"},{"id":"p36","kind":"paragraph","text":"当任务复杂需要多种 Agent 协作时，PlanningFlow 出场：","raw":"当任务复杂需要多种 Agent 协作时，`PlanningFlow` 出场："},{"id":"p37","kind":"list","text":"1. 创建 Flow：注册多个 Agent，例如 {\"manus\": Manus(), \"data_analysis\": DataAnalysis()}。\n2. 生成计划：先用 PlanningTool（内部调用 LLM）根据用户输入生成一个包含多个步骤的任务计划。\n3. 分发执行：循环遍历步骤，检查 Step 的 type 字段，调用 get_executor(step_type) 找到最合适的 Agent；没有匹配的就用默认的 manus 兜底。\n4. 执行并更新：把当前步骤的 step_prompt 交给选定的 Agent 运行，完成后标记为 COMPLETED。\n5. 汇总：所有步骤完成后返回结果。","raw":"1. **创建 Flow**：注册多个 Agent，例如 `{\"manus\": Manus(), \"data_analysis\": DataAnalysis()}`。\n2. **生成计划**：先用 `PlanningTool`（内部调用 LLM）根据用户输入生成一个包含多个步骤的任务计划。\n3. **分发执行**：循环遍历步骤，检查 `Step` 的 `type` 字段，调用 `get_executor(step_type)` 找到最合适的 Agent；没有匹配的就用默认的 `manus` 兜底。\n4. **执行并更新**：把当前步骤的 `step_prompt` 交给选定的 Agent 运行，完成后标记为 COMPLETED。\n5. **汇总**：所有步骤完成后返回结果。"},{"id":"p38","kind":"heading","text":"以旅游规划为例：一次真实的协作流","raw":"## 以旅游规划为例：一次真实的协作流"},{"id":"p39","kind":"paragraph","text":"为了测试协作能力，我用 run_flow.py 的多 Agent 模式下达了一个典型的复杂指令：","raw":"为了测试协作能力，我用 `run_flow.py` 的多 Agent 模式下达了一个典型的复杂指令："},{"id":"p40","kind":"quote","text":"我想要 11.15-11.20 去北京游玩，我和我妈妈两个人，请你帮我制定一下旅游规划，预算一万五左右","raw":"> 我想要 11.15-11.20 去北京游玩，我和我妈妈两个人，请你帮我制定一下旅游规划，预算一万五左右"},{"id":"p41","kind":"heading","text":"一、启动 PlanningFlow","raw":"### 一、启动 PlanningFlow"},{"id":"p42","kind":"paragraph","text":"Agent 接收到指令后，第一件事不是动手，而是启动规划流程。规划器调用 LLM，把模糊指令分解为一份清晰有序的行动计划，几秒钟后生成了 7 步：","raw":"Agent 接收到指令后，第一件事不是动手，而是启动规划流程。规划器调用 LLM，把模糊指令分解为一份清晰有序的行动计划，几秒钟后生成了 7 步："},{"id":"p43","kind":"code","text":"Plan: 北京5日游规划（11.15-11.20，2人预算一万五左右）\n0. [ ] 前期准备：预订机票/高铁票、酒店、景点门票\n1. [ ] Day 1 (11.15)：抵达北京，入住酒店，游览天安门广场、前门大街\n2. [ ] Day 2 (11.16)：故宫博物院深度游，景山公园俯瞰全景\n3. [ ] Day 3 (11.17)：八达岭长城一日游\n4. [ ] Day 4 (11.18)：颐和园游览，圆明园遗址参观\n5. [ ] Day 5 (11.19)：天坛公园，王府井购物，品尝北京烤鸭\n6. [ ] Day 6 (11.20)：自由活动，整理行李，返程","raw":"```text\nPlan: 北京5日游规划（11.15-11.20，2人预算一万五左右）\n0. [ ] 前期准备：预订机票/高铁票、酒店、景点门票\n1. [ ] Day 1 (11.15)：抵达北京，入住酒店，游览天安门广场、前门大街\n2. [ ] Day 2 (11.16)：故宫博物院深度游，景山公园俯瞰全景\n3. [ ] Day 3 (11.17)：八达岭长城一日游\n4. [ ] Day 4 (11.18)：颐和园游览，圆明园遗址参观\n5. [ ] Day 5 (11.19)：天坛公园，王府井购物，品尝北京烤鸭\n6. [ ] Day 6 (11.20)：自由活动，整理行李，返程\n```"},{"id":"p44","kind":"heading","text":"二、调度执行者：从在线预订到被迫放弃","raw":"### 二、调度执行者：从在线预订到被迫放弃"},{"id":"p45","kind":"paragraph","text":"计划制定后，PlanningFlow 开始调度默认的 Manus Agent 完成第 0 步「前期准备」，Agent 获得了 20 步的执行权限。接下来是一场教科书式的挣扎与妥协。","raw":"计划制定后，`PlanningFlow` 开始调度默认的 Manus Agent 完成第 0 步「前期准备」，Agent 获得了 20 步的执行权限。接下来是一场教科书式的挣扎与妥协。"},{"id":"p46","kind":"paragraph","text":"尝试 1（Web 搜索）：调用 browser_use (action: \"web_search\")，报错 Cannot navigate to invalid URL。","raw":"**尝试 1（Web 搜索）**：调用 `browser_use (action: \"web_search\")`，报错 `Cannot navigate to invalid URL`。"},{"id":"p47","kind":"paragraph","text":"尝试 2（访问携程）：Agent 自救，改为 go_to_url 直接访问携程，报错 Timeout 30000ms exceeded。","raw":"**尝试 2（访问携程）**：Agent 自救，改为 `go_to_url` 直接访问携程，报错 `Timeout 30000ms exceeded`。"},{"id":"p48","kind":"paragraph","text":"关键缺陷（幻觉）：下一步的思考日志写着「Great! I can see we've successfully navigated to Ctrip」。这是最致命的一点——尽管上一步明确超时失败，Agent 却幻觉自己成功了，并基于这个错误状态继续执行 extract_content、click_element，结果自然是连续失败。","raw":"**关键缺陷（幻觉）**：下一步的思考日志写着「Great! I can see we've successfully navigated to Ctrip」。这是最致命的一点——尽管上一步明确超时失败，Agent 却幻觉自己成功了，并基于这个错误状态继续执行 `extract_content`、`click_element`，结果自然是连续失败。"},{"id":"p49","kind":"paragraph","text":"最后的妥协：在连续 18 步失败后，Agent 似乎意识到浏览器工具不可靠，日志写着「The website seems to be having navigation issues. Let me try a different approach」，转而调用文件编辑工具，成功创建了《北京5日游预订规划.md》和《北京5日游预算计算器.py》。","raw":"**最后的妥协**：在连续 18 步失败后，Agent 似乎意识到浏览器工具不可靠，日志写着「The website seems to be having navigation issues. Let me try a different approach」，转而调用文件编辑工具，成功创建了《北京5日游预订规划.md》和《北京5日游预算计算器.py》。"},{"id":"p50","kind":"paragraph","text":"最终 Agent 耗尽了 20 步，PlanningFlow 把第 0 步标记为完成。","raw":"最终 Agent 耗尽了 20 步，`PlanningFlow` 把第 0 步标记为完成。"},{"id":"p51","kind":"heading","text":"三、关键缺陷：僵尸 Agent 与健忘的 Planner","raw":"### 三、关键缺陷：僵尸 Agent 与健忘的 Planner"},{"id":"p52","kind":"paragraph","text":"前期准备完成后，PlanningFlow 指派同一个 Manus Agent 实例去执行第 1 步。Agent 重复了「挣扎—妥协」循环，最后用文件工具产出了《Day1详细行程.md》，并成功调用 terminate 结束任务。","raw":"前期准备完成后，`PlanningFlow` 指派**同一个** Manus Agent 实例去执行第 1 步。Agent 重复了「挣扎—妥协」循环，最后用文件工具产出了《Day1详细行程.md》，并成功调用 `terminate` 结束任务。"},{"id":"p53","kind":"paragraph","text":"但从第 2 步开始，系统暴露了严重的设计缺陷。","raw":"但从第 2 步开始，系统暴露了严重的设计缺陷。"},{"id":"p54","kind":"paragraph","text":"状态管理缺陷：调度第 2 步「Day 2 故宫」时，日志显示 Executing step 11/20。注意，这是一个新任务，但 Agent 的 current_step 计数器却从 11 开始——PlanningFlow 复用了 Agent 实例，却没有重置它的内部状态。","raw":"**状态管理缺陷**：调度第 2 步「Day 2 故宫」时，日志显示 `Executing step 11/20`。注意，这是一个新任务，但 Agent 的 `current_step` 计数器却从 11 开始——`PlanningFlow` 复用了 Agent 实例，却没有重置它的内部状态。"},{"id":"p55","kind":"paragraph","text":"内存管理缺陷（致命）：Agent 成功创建了《Day2故宫深度游.md》后，执行第二项操作时系统崩溃：","raw":"**内存管理缺陷（致命）**：Agent 成功创建了《Day2故宫深度游.md》后，执行第二项操作时系统崩溃："},{"id":"p56","kind":"code","text":"ERROR | app.llm:ask_tool - OpenAI API error: Error code: 400\n{'error': {'message': \"Messages with role 'tool' must be a response to\na preceding message with 'tool_calls'\"}}","raw":"```text\nERROR | app.llm:ask_tool - OpenAI API error: Error code: 400\n{'error': {'message': \"Messages with role 'tool' must be a response to\na preceding message with 'tool_calls'\"}}\n```"},{"id":"p57","kind":"paragraph","text":"这个 400 错误是 Agent 开发中最经典的错误：消息历史被污染了。上一个任务的最后一条消息是一条 tool 结果，而新任务没有正确清空历史，导致 think() 时发送了一个无效的消息序列。","raw":"这个 400 错误是 Agent 开发中最经典的错误：消息历史被污染了。上一个任务的最后一条消息是一条 `tool` 结果，而新任务没有正确清空历史，导致 `think()` 时发送了一个无效的消息序列。"},{"id":"p58","kind":"paragraph","text":"错误处理缺陷：此时 Agent 实例已经僵尸化，think() 会永远 400 报错。而 PlanningFlow 捕获了错误之后……继续调度第 3 步。接下来的日志就是这个僵尸循环的不断重复：调度 Day 3 → Agent 成功写一个文件 → think() 时 400 报错 → Planner 捕获错误，继续调度 Day 4。","raw":"**错误处理缺陷**：此时 Agent 实例已经僵尸化，`think()` 会永远 400 报错。而 `PlanningFlow` 捕获了错误之后……继续调度第 3 步。接下来的日志就是这个僵尸循环的不断重复：调度 Day 3 → Agent 成功写一个文件 → `think()` 时 400 报错 → Planner 捕获错误，继续调度 Day 4。"},{"id":"p59","kind":"paragraph","text":"小结：","raw":"**小结**："},{"id":"p60","kind":"paragraph","text":"优点是规划能力清晰（成功把复杂任务分解成逻辑清晰的步骤）、实现了指挥家—执行者的调度模式、有一定弹性（浏览器工具连续失败时能妥协转向文件工具，曲线救国）。","raw":"优点是规划能力清晰（成功把复杂任务分解成逻辑清晰的步骤）、实现了指挥家—执行者的调度模式、有一定弹性（浏览器工具连续失败时能妥协转向文件工具，曲线救国）。"},{"id":"p61","kind":"paragraph","text":"缺陷是工具脆弱（browser_use 和 python_execute 高频超时）、状态管理有漏洞（复用实例不重置）、内存管理有漏洞（切任务不清空 memory）、错误处理缺乏健壮机制（不重试也不重新规划，只是记录后继续）。","raw":"缺陷是工具脆弱（`browser_use` 和 `python_execute` 高频超时）、状态管理有漏洞（复用实例不重置）、内存管理有漏洞（切任务不清空 memory）、错误处理缺乏健壮机制（不重试也不重新规划，只是记录后继续）。"},{"id":"p62","kind":"heading","text":"与 Manus 的差距，以及 MCP 的演进","raw":"## 与 Manus 的差距，以及 MCP 的演进"},{"id":"p63","kind":"paragraph","text":"Manus 官方博客揭示了这套朴素流程在真实复杂任务（平均 50 次工具调用）中会遇到的核心瓶颈——上下文管理。这正是最大的差距所在，官方 Manus 的核心护城河就是上下文工程。","raw":"Manus 官方博客揭示了这套朴素流程在真实复杂任务（平均 50 次工具调用）中会遇到的核心瓶颈——上下文管理。这正是最大的差距所在，官方 Manus 的核心护城河就是上下文工程。"},{"id":"p64","kind":"heading","text":"差距一：上下文管理","raw":"### 差距一：上下文管理"},{"id":"p65","kind":"paragraph","text":"OpenManus 的做法非常朴素：把每一步的观察完整地、只增不减地追加到 memory 列表，累积到 Token 上限时 LLM 就报错，任务失败。","raw":"**OpenManus 的做法**非常朴素：把每一步的观察完整地、只增不减地追加到 `memory` 列表，累积到 Token 上限时 LLM 就报错，任务失败。"},{"id":"p66","kind":"paragraph","text":"官方 Manus 的做法是把上下文当作成本最高昂的资源来管理，其策略不是存储，而是调度。核心理念是——文件系统才是真正的上下文：","raw":"**官方 Manus 的做法**是把上下文当作成本最高昂的资源来管理，其策略不是存储，而是调度。核心理念是——文件系统才是真正的上下文："},{"id":"p67","kind":"list","text":"- 上下文卸载：LLM 的上下文窗口只是一个短暂的暂存器，Manus 被训练成按需读写文件，将文件系统用作结构化的外部记忆。\n- 上下文缩减：不存储完整结果，而是区分 Full 和 Compact。一个工具调用的原始结果（Full）可能非常长，Manus 会把它存入文件系统，只在 memory 中保留一个文件路径（Compact）。","raw":"- **上下文卸载**：LLM 的上下文窗口只是一个短暂的暂存器，Manus 被训练成按需读写文件，将文件系统用作结构化的外部记忆。\n- **上下文缩减**：不存储完整结果，而是区分 Full 和 Compact。一个工具调用的原始结果（Full）可能非常长，Manus 会把它存入文件系统，只在 `memory` 中保留一个文件路径（Compact）。"},{"id":"p68","kind":"heading","text":"差距二：工具调用与 KV 缓存成本","raw":"### 差距二：工具调用与 KV 缓存成本"},{"id":"p69","kind":"paragraph","text":"OpenManus 的做法：think() 阶段每次都把 available_tools 里的所有工具都塞给 LLM。","raw":"**OpenManus 的做法**：`think()` 阶段每次都把 `available_tools` 里的所有工具都塞给 LLM。"},{"id":"p70","kind":"paragraph","text":"官方 Manus 的做法围绕 KV 缓存命中率来设计：","raw":"**官方 Manus 的做法**围绕 KV 缓存命中率来设计："},{"id":"p71","kind":"list","text":"- 问题：如果在循环中动态添加或删除工具，会导致 LLM 的 KV 缓存失效，推理成本和延迟飙升十倍。\n- 方案：遮蔽，而非移除。Manus 的工具列表是固定的，它不会移除工具，而是在解码时通过遮蔽或预填充的方式，强制 LLM 只能从一个特定子集（比如 browser_*）中选择。\n- 卸载到沙箱：Manus 只保留极少数（不到 20 个）原子工具，其他所有复杂功能都被卸载到沙箱中，通过 Bash 以命令行方式调用。这极大简化了工具集，进一步保证了 KV 缓存的稳定。","raw":"- **问题**：如果在循环中动态添加或删除工具，会导致 LLM 的 KV 缓存失效，推理成本和延迟飙升十倍。\n- **方案：遮蔽，而非移除**。Manus 的工具列表是固定的，它不会移除工具，而是在解码时通过遮蔽或预填充的方式，强制 LLM 只能从一个特定子集（比如 `browser_*`）中选择。\n- **卸载到沙箱**：Manus 只保留极少数（不到 20 个）原子工具，其他所有复杂功能都被卸载到沙箱中，通过 `Bash` 以命令行方式调用。这极大简化了工具集，进一步保证了 KV 缓存的稳定。"},{"id":"p72","kind":"heading","text":"差距三：多 Agent 编排","raw":"### 差距三：多 Agent 编排"},{"id":"p73","kind":"paragraph","text":"OpenManus 的做法：PlanningFlow 先让 LLM 生成计划，然后按步骤为每个步骤选择执行者，把 step_prompt 传过去。","raw":"**OpenManus 的做法**：`PlanningFlow` 先让 LLM 生成计划，然后按步骤为每个步骤选择执行者，把 `step_prompt` 传过去。"},{"id":"p74","kind":"paragraph","text":"官方 Manus 的做法：多 Agent 的首要目标是上下文隔离，而不是模仿人类角色，因为 LLM 没有人类的认知局限。它的情景式上下文共享比简单的 step_prompt 传递灵活得多：","raw":"**官方 Manus 的做法**：多 Agent 的首要目标是上下文隔离，而不是模仿人类角色，因为 LLM 没有人类的认知局限。它的情景式上下文共享比简单的 `step_prompt` 传递灵活得多："},{"id":"p75","kind":"list","text":"- 简单任务：Planner 只传递指令，子 Agent 在干净的上下文中执行后返回结果。\n- 复杂任务：例如子 Agent 需要读写 Planner 正在操作的文件，Planner 会把完整上下文加共享文件系统一起传过去，确保子 Agent 在需要时拥有完整记忆，同时又能在自己的隔离环境中思考。","raw":"- **简单任务**：Planner 只传递指令，子 Agent 在干净的上下文中执行后返回结果。\n- **复杂任务**：例如子 Agent 需要读写 Planner 正在操作的文件，Planner 会把完整上下文加共享文件系统一起传过去，确保子 Agent 在需要时拥有完整记忆，同时又能在自己的隔离环境中思考。"},{"id":"p76","kind":"paragraph","text":"小结一下：OpenManus 提供了一个功能完整、模块清晰的 Agent 骨架，让我们看清了 Agent 是如何基于 think → act → observe 循环工作的。但官方 Manus 展示了，从一个「能跑」的开源框架到一个「好用」的商业产品，中间隔着一条由上下文工程组成的深深护城河。","raw":"小结一下：OpenManus 提供了一个功能完整、模块清晰的 Agent 骨架，让我们看清了 Agent 是如何基于 think → act → observe 循环工作的。但官方 Manus 展示了，从一个「能跑」的开源框架到一个「好用」的商业产品，中间隔着一条由上下文工程组成的深深护城河。"},{"id":"p77","kind":"heading","text":"作为服务：Agent 能力的 API 化","raw":"### 作为服务：Agent 能力的 API 化"},{"id":"p78","kind":"paragraph","text":"OpenManus 提供了两种把能力暴露给外部的方式：A2A 协议把整个 Agent 封装成一个标准服务，让外部系统远程调用它作为黑盒执行者；MCP 服务器则把工具层打包成 MCP 服务器，实现 Tools-as-a-Service。","raw":"OpenManus 提供了两种把能力暴露给外部的方式：A2A 协议把整个 Agent 封装成一个标准服务，让外部系统远程调用它作为黑盒执行者；MCP 服务器则把工具层打包成 MCP 服务器，实现 Tools-as-a-Service。"},{"id":"p79","kind":"paragraph","text":"但用 Anthropic 提出的 Agent Skills 和 MCP Code Execution 来审视，会发现两者在设计哲学上的巨大差异。OpenManus 实现了「API 化」，而 Anthropic 正在构建一个「可扩展的能力生态」。","raw":"但用 Anthropic 提出的 Agent Skills 和 MCP Code Execution 来审视，会发现两者在设计哲学上的巨大差异。OpenManus 实现了「API 化」，而 Anthropic 正在构建一个「可扩展的能力生态」。"},{"id":"p80","kind":"paragraph","text":"概念辨析：接口 vs 技能包","raw":"**概念辨析：接口 vs 技能包**"},{"id":"p81","kind":"paragraph","text":"OpenManus 的 AgentSkill 在 A2A 协议中仅仅是一个可远程调用的能力接口。它告诉外部系统「我有一个叫 python_execute 的功能」，但并不包含如何用好这个功能的知识。","raw":"OpenManus 的 AgentSkill 在 A2A 协议中仅仅是一个可远程调用的能力接口。它告诉外部系统「我有一个叫 `python_execute` 的功能」，但并不包含如何用好这个功能的知识。"},{"id":"p82","kind":"paragraph","text":"Anthropic 的 Agent Skill 则是一个「知识 + 代码」的经验包，是一个包含 SKILL.md 和辅助脚本的完整文件夹，核心是渐进式披露：启动时只加载元数据，Agent 认为相关时才主动读取 SKILL.md 学习怎么做，需要时再进一步读取其他脚本。","raw":"Anthropic 的 Agent Skill 则是一个「知识 + 代码」的经验包，是一个包含 `SKILL.md` 和辅助脚本的完整文件夹，核心是渐进式披露：启动时只加载元数据，Agent 认为相关时才主动读取 `SKILL.md` 学习怎么做，需要时再进一步读取其他脚本。"},{"id":"p83","kind":"paragraph","text":"差距在于：OpenManus 的 Skill 是一个固定的功能点，而 Anthropic 的 Skill 是一个可被 Agent 学习和发现的知识包。","raw":"差距在于：OpenManus 的 Skill 是一个固定的功能点，而 Anthropic 的 Skill 是一个可被 Agent 学习和发现的知识包。"},{"id":"p84","kind":"paragraph","text":"架构对比：直接调用 vs 代码执行","raw":"**架构对比：直接调用 vs 代码执行**"},{"id":"p85","kind":"paragraph","text":"OpenManus 把自己的工具注册为 MCP 工具，任何客户端 Agent 都要在上下文中加载这些工具的完整定义，然后直接调用。","raw":"OpenManus 把自己的工具注册为 MCP 工具，任何客户端 Agent 都要在上下文中加载这些工具的完整定义，然后直接调用。"},{"id":"p86","kind":"paragraph","text":"Anthropic 明确指出了直接调用的两个致命缺陷：定义过载（工具成百上千时，所有 schema 会塞爆上下文）和结果过载（工具的中间结果，比如一份两小时的会议转录稿，必须完整流入上下文再传给下一个工具）。","raw":"Anthropic 明确指出了直接调用的两个致命缺陷：**定义过载**（工具成百上千时，所有 schema 会塞爆上下文）和**结果过载**（工具的中间结果，比如一份两小时的会议转录稿，必须完整流入上下文再传给下一个工具）。"},{"id":"p87","kind":"paragraph","text":"它的解决方案是把 MCP 工具作为代码 API 暴露在文件系统上，Agent 不再直接调用工具，而是编写代码来与 MCP 交互。这种模式的优势是压倒性的：","raw":"它的解决方案是把 MCP 工具作为代码 API 暴露在文件系统上，Agent 不再直接调用工具，而是编写代码来与 MCP 交互。这种模式的优势是压倒性的："},{"id":"p88","kind":"list","text":"- 渐进式披露：Agent 通过 ls ./servers 发现工具，只在需要时 cat 读取特定工具的代码。\n- 上下文高效：Agent 可以在沙箱中用代码处理数据（过滤、聚合），只把最终的小结果返回给 LLM。\n- 能力持久化：Agent 可以把自己写的好用代码保存为新文件，再加一个 SKILL.md，这就创造了一个全新的 Agent Skill。","raw":"- **渐进式披露**：Agent 通过 `ls ./servers` 发现工具，只在需要时 `cat` 读取特定工具的代码。\n- **上下文高效**：Agent 可以在沙箱中用代码处理数据（过滤、聚合），只把最终的小结果返回给 LLM。\n- **能力持久化**：Agent 可以把自己写的好用代码保存为新文件，再加一个 `SKILL.md`，这就创造了一个全新的 Agent Skill。"},{"id":"p89","kind":"heading","text":"最后","raw":"## 最后"},{"id":"p90","kind":"paragraph","text":"OpenManus 不仅仅是一个能运行的 Agent，它是一个设计清晰、高度模块化的 AI 代理框架。","raw":"OpenManus 不仅仅是一个能运行的 Agent，它是一个设计清晰、高度模块化的 AI 代理框架。"},{"id":"p91","kind":"paragraph","text":"但通过与官方 Manus 和 Anthropic 的深度对比，我们能更清醒地看到「能跑的开源框架」与「好用的商业产品」之间的真正鸿沟。","raw":"但通过与官方 Manus 和 Anthropic 的深度对比，我们能更清醒地看到「能跑的开源框架」与「好用的商业产品」之间的真正鸿沟。"},{"id":"p92","kind":"paragraph","text":"这正回应了开篇提到的黑客级竞品调研。拆解 OpenManus，看的不仅是它如何思考与如何行动，更是为了通过复现它的脆弱与缺陷，去反推那些商业产品到底解决了多少看不见的难题。","raw":"这正回应了开篇提到的黑客级竞品调研。拆解 OpenManus，看的不仅是它如何思考与如何行动，更是为了通过复现它的脆弱与缺陷，去反推那些商业产品到底解决了多少看不见的难题。"},{"id":"p93","kind":"paragraph","text":"对于 PM 而言，这就是深入代码的价值。","raw":"对于 PM 而言，这就是深入代码的价值。"}],"authored_claims":[{"id":"read-code-as-competitive-research","text":"程序员的技术黑盒正在白盒化，PM 可以也应该做「黑客级竞品调研」——实测体验、技术解密、开源复现三者交叉验证。看到差的，才能真正理解好的牛在何处。","stance":"judgment","confidence":"high","supersedes":null,"note":null,"formed_at":"2025-11-15"},{"id":"agent-hallucinates-tool-success","text":"实测中最致命的一幕：浏览器导航明确超时失败后，Agent 在下一步思考里幻觉自己成功了，并基于这个错误状态继续操作。工具失败后的状态确认是 Agent 可靠性的关键缺口。","stance":"fact","confidence":"high","supersedes":null,"note":null,"formed_at":"2025-11-15"},{"id":"zombie-agent-from-memory-pollution","text":"PlanningFlow 复用 Agent 实例时既不重置 current_step，也不清空 memory，导致新任务发出无效消息序列而 API 400 报错，Agent 从此僵尸化，而 Planner 只是记录错误继续调度下一步。","stance":"fact","confidence":"high","supersedes":null,"note":null,"formed_at":"2025-11-15"},{"id":"manus-moat-is-context-engineering","text":"OpenManus 和官方 Manus 之间最大的差距是上下文管理。OpenManus 只增不减地往 memory 里追加观察，撑到 Token 上限就失败；Manus 把上下文当最昂贵的资源来调度，文件系统才是它真正的上下文。","stance":"judgment","confidence":"high","supersedes":null,"note":null,"formed_at":"2025-11-15"},{"id":"mask-tools-dont-remove-them","text":"在循环中动态增删工具会让 KV 缓存失效，推理成本和延迟可能飙升十倍。正确做法是保持工具列表固定，在解码时通过遮蔽强制模型只从特定子集中选择。","stance":"fact","confidence":null,"supersedes":null,"note":null,"formed_at":"2025-11-15"}],"claim_ids":["read-code-as-competitive-research","agent-hallucinates-tool-success","zombie-agent-from-memory-pollution","manus-moat-is-context-engineering","mask-tools-dont-remove-them"],"revision":"e5a1cf2cab9b1b6b327f862599c50a0f1a7cde57da72e5cdf07ccbd411672f2d"}