惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

量子位
博客园_首页
Google DeepMind News
Google DeepMind News
博客园 - Franky
The GitHub Blog
The GitHub Blog
GbyAI
GbyAI
有赞技术团队
有赞技术团队
Microsoft Azure Blog
Microsoft Azure Blog
G
Google Developers Blog
Recent Announcements
Recent Announcements
A
About on SuperTechFans
博客园 - 【当耐特】
博客园 - 三生石上(FineUI控件)
酷 壳 – CoolShell
酷 壳 – CoolShell
美团技术团队
罗磊的独立博客
IT之家
IT之家
博客园 - 聂微东
Stack Overflow Blog
Stack Overflow Blog
Jina AI
Jina AI
腾讯CDC
P
Proofpoint News Feed
Hugging Face - Blog
Hugging Face - Blog
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com

博客园 - zhang-yd

今日开源[第57期]beautiful-water源码解读 今日开源[第56期]human-atlas源码解读 今日开源[第55期]model-x-studio源码解读 今日开源[第54期]Spark(@sparkjsdev/spark)源码解读 今日开源[第53期]PlayCanvas Engine(playcanvas/engine)源码解读 今日开源[第52期]SPEEDBALL GI WebGPU Showcase(speedball-gi)源码解读 今日开源[第51期]Pi Agent Harness(pi)源码解读 今日开源[第50期]World Monitor(worldmonitor)游戏项目解读 今日开源[第49期]竹知了(zhuzhiliao)小玩具项目解读 今日开源[第48期]Operation Ironhold 游戏项目解读 今日开源[第47期]Academic Research Skills for Claude Code(ARS)项目skill解读 今日开源[第46期]AI-Research-SKILLs项目skill解读 今日开源[第45期]nature-research-skills项目skill解读 今日开源[第44期]findskills项目skill解读 今日开源[第43期]last30days-skill 今日开源[第42期]Cangjie Skill 今日开源[第41期]MoneyPrinterTurbo 今日开源[第39期]img2threejs 今日开源[第39期]Kronos 今日开源[第38期]Open Code Review (OCR) 今日开源[第37期]Openship 今日开源[第36期]croc 今日开源[第35期] Code-Review-Graph 今日开源[第34期] 《深入理解 AI Agent:设计原理与工程实践》 今日开源[第33期] Home Assistant Core 今日开源[第32期] Vibe-Trading 今日开源[第31期]RuView 今日开源[第30期]Chrome DevTools MCP 今日开源[第29期]RomM (ROM Manager) 今日开源[第28期]Page Agent
代码阅读笔记-OpenManus
zhang-yd · 2026-03-10 · via 博客园 - zhang-yd

项目介绍

OpenManus 是由 MetaGPT 团队孵化的开源通用 AI 智能体框架,核心目标是无需邀请码即可实现任意创意的智能体开发,支持多工具扩展、多智能体协作、强化学习(RL)调优(OpenManus-RL),并兼容 Google A2A 协议生态。

项目的定位是通用LLM智能体开发框架,支持远程和本地的工具调用。
项目代码的仓库地址:https://github.com/FoundationAgents/OpenManus

项目结构

整个项目使用分层架构设计
交互层: (终端输入,A2A Client, HuggingFace Demo) 接受用户任务输入,输出执行结果
启动层: (main.py run_mcp.py run_flow.py) 提供不同运行模式,单智能体,MCP工具,多智能体流程版
流程调度层: (多智能体规划 PlanningFlow) 任务规划,多智能体调度,步骤状态管理
智能体核心层: (Manus(通用智能体),DataAnalysis(专用智能体)) 通用智能体核心,管理工具集,MCP连接,浏览器上下文,决策逻辑
工具/协议扩展层: (本地工具,MCP远程工具, A2A协议工具) 内置本地工具,远程MCP工具,A2A协议工具

使用方法

One line for run OpenManus:

python main.py

Then input your idea via terminal!

For MCP tool version, you can run:

python run_mcp.py

For unstable multi-agent version, you also can run:

python run_flow.py

代码解读

智能体核心

代码在 app/agent/manus.py 中,核心类是 Manus,负责管理工具集,MCP连接,浏览器上下文,决策逻辑
关键的方法是:

  • initialize_mcp_servers:根据配置信息进行初始化MCP服务器
  • connect_mcp_server:分为connect_stdio 和 connect_sse 两种方式,分别对应标准输入输出和服务器发送事件。并将远程工具添加到 available_tools 列表中
  • think: 智能体决策核心:分析当前状态,若使用浏览器则格式化提示词,调用父类决策
  • cleanup: 资源清理:关闭浏览器、断开 MCP 服务器连接

Manus 继承自 ToolCallAgent, 代码在 app/agent/toolcall.py 中, ToolCallAgent 是一个通用的智能体框架,提供了一些基础的工具调用功能,如调用工具、管理工具集等。Manus 在 ToolCallAgent 的基础上,添加了更多的功能,如管理浏览器上下文、管理MCP连接等。
关键的方法有:

  • think: 先使用message和工具集合,去chat LLM,得到工具调用的方法
  • act: 循环所有的工具,进行工具调用
  • execute_tool: 执行单个工具调用,并进行可靠的错误处理
  • cleanup: 将self.available_tools.tool_map中的所有工具进行清理
  • run: 执行父类的run(request)的方法,执行有错则执行cleanup进行清理

ToolCallAgent 继承自 ReActAgent, ReActAgent的代码在 中,ReActAgent是很简单的基础类,包含了三个方法,分别是 think,act,step,其中think 和 act 方法都是没有实现的,step中简单构建了整个框架,先进行think,根据返回内容看是否需要act,或者进行 act ,或者直接返回think的结果。

Agent类

基础的Agent类,BaseAgent 类,代码是在 app/agent/base.py
BaseAgent类的有几个核心的属性:

  • system_prompt
  • next_step_prompt
  • LLM
  • memory
  • state
  • max_steps current_step

浏览器上下文Agent
结合browser和Agent类,记录当前的Agent的state和Memory的信息,共同进行下一步的规划。

数据分析Agent
代码是 app/agent/data_analysis.py 中,仅仅包含了工具集合为:NormalPythonExecute, VisualizationPrepare, DataVisualization, Terminate四类工具

代码编程 SWEAgent
代码在 app/agent/swe.py 下,包含了自动化编码的prompt,和相关的工具集合,bash,StrReplaceEditor,Terminate三个工具。

流程管理

流程的代码在 app.flow下,有三个文件,base.py flow_factory.py planning.py

base.py 中定义了 BaseFlow 类,是所有流程的基类,包含了简单地管理Agent的方法等。
如 属性primary_agent_key 是多个agent中的主agent,
还有 get_agent, add_agent, execute 方法,来管理agent。其中execute方法未定义。

flow_factory.py 中定义了 FlowFactory 类,是流程工厂类,负责创建流程。目前只支持一个流程,PlanningFlow。

planning.py 中定义了核心类 PlanningFlow 类,是流程的具体实现类,继承自 BaseFlow。
具有的属性有:

  • llm:大语言模型的实例
  • planning_tool:上下文
  • executor_keys:
  • activate_plan_id:状态
  • current_step_index: 当前执行步骤的索引

核心方法只有一个,就是 execute方法,该方法会根据当前状态,在多个Agent上执行不同的操作,如调用工具、更新状态等。
核心代码为

    async def execute(self, input_text: str) -> str:
        """Execute the planning flow with agents."""
        try:
            if not self.primary_agent:
                raise ValueError("No primary agent available")

            # Create initial plan if input provided  # 如果提供了输入,则说明是新的任务或者有新内容增加,则需要重新制定执行计划。
            if input_text:
                await self._create_initial_plan(input_text)

                # Verify plan was created successfully
                if self.active_plan_id not in self.planning_tool.plans:
                    logger.error(
                        f"Plan creation failed. Plan ID {self.active_plan_id} not found in planning tool."
                    )
                    return f"Failed to create plan for: {input_text}"

            result = ""
            while True:
                # Get current step to execute
                self.current_step_index, step_info = await self._get_current_step_info()

                # Exit if no more steps or plan completed
                if self.current_step_index is None:
                    result += await self._finalize_plan()
                    break

                # Execute current step with appropriate agent
                step_type = step_info.get("type") if step_info else None
                executor = self.get_executor(step_type)
                step_result = await self._execute_step(executor, step_info)
                result += step_result + "\n"

                # Check if agent wants to terminate
                if hasattr(executor, "state") and executor.state == AgentState.FINISHED:
                    break

该方法的主要的做的内容是

  • 根据输入内容进行制定执行计划
  • 根据执行计划划分得到多个执行步骤,从而进行分步骤执行。

方法 def _create_initial_plan 将要prompt转为Message,并带有工具调用去chat LLM,得到tool_calls后

PlanningFlow 在execute 中的最常调用的是 PlanningTool 这个类的 execute 函数。
核心函数代码为:

def execute(self, *, command: Literal["create", "update", "list", "get", "set_active", "mark_step", "delete"],
        plan_id: Optional[str] = None,
        title: Optional[str] = None,
        steps: Optional[List[str]] = None,
        step_index: Optional[int] = None,
        step_status: Optional[Literal["not_started", "in_progress", "completed", "blocked"]] = None,
        step_notes: Optional[str] = None,
        **kwargs,
    ):
        """
        Execute the planning tool with the given command and parameters.
        Parameters:
        - command: The operation to perform
        - plan_id: Unique identifier for the plan
        - title: Title for the plan (used with create command)
        - steps: List of steps for the plan (used with create command)
        - step_index: Index of the step to update (used with mark_step command)
        - step_status: Status to set for a step (used with mark_step command)
        - step_notes: Additional notes for a step (used with mark_step command)
        """

        if command == "create":
            return self._create_plan(plan_id, title, steps)
        elif command == "update":
            return self._update_plan(plan_id, title, steps)
        elif command == "list":
            return self._list_plans()
        elif command == "get":
            return self._get_plan(plan_id)
        elif command == "set_active":
            return self._set_active_plan(plan_id)
        elif command == "mark_step":
            return self._mark_step(plan_id, step_index, step_status, step_notes)
        elif command == "delete":
            return self._delete_plan(plan_id)
        else:
            raise ToolError()

这个类主要在管理 plans 格式为dict{dict_name: plan_info_dict}的形式。

LLM模块

代码在 app/llm.py 下,使用单例模式来实现,每一个配置名只会创建一次的实例。
LLM类会首先进行tokenier 的初始化
代码为:self.tokenizer = tiktoken.encoding_for_model(self.model)
根据指定的模型名称返回对应的预训练分词器。不在集合中的模型,则统一使用 cl100k_base 预训练模型。

LLM类的几个关键方法
def ask(self,
messages: List[Union[dict, Message]],
system_msgs: Optional[List[Union[dict, Message]]] = None,
stream: bool = True,
temperature: Optional[float] = None,
) -> str:
发送一段Prompt给LLM以返回 response。
先将List[Message]的列表格式中的转为 List[dict{'role':'', 'content':''}]的格式。
然后,计算总的token的长度,判断是否需要满足不超过最大token数量。
最后,调用LLM的chat,分streaming 和 not stream的方式分别处理。

方法
def ask_with_images(self,
messages: List[Union[dict, Message]],
images: List[Union[str, dict]],
system_msgs: Optional[List[Union[dict, Message]]] = None,
stream: bool = False,
temperature: Optional[float] = None,
) -> str
方法
def ask_tool(self,
messages: List[Union[dict, Message]],
system_msgs: Optional[List[Union[dict, Message]]] = None,
timeout: int = 300,
tools: Optional[List[dict]] = None,
tool_choice: TOOL_CHOICE_TYPE = ToolChoice.AUTO, # type: ignore
temperature: Optional[float] = None,
** kwargs,
) -> ChatCompletionMessage | None
这两个方法的实现都类似 chat,只是增加对image和工具调用的支持。

还有一个有意思的类是 TokenCounter 类,负责计算token的数量。基于上面创建的tokenizer对象,来计算每一个文本的token,TokenCounter实现封装,可以计算纯文本的,也可以计算带图片的,还支持本项目的Message格式的token数量的计算。

沙箱sandbox agent代理

该模块的核心类是SandboxManus类。
代码是在 app/agent/sandbox_agent.py 上。一个多功能代理,可以使用多个沙盒工具(包括基于MCP的工具)解决各种任务
该类的核心方法有几个
create方法,根据输入的参数进行 SandboxManus的创建。只有三个步骤:

  • 类初始化;
  • initialize_mcp_servers:根据服务的类型(sse或者stdio)来进行mcp服务器的链接
  • initialize_sandbox_tools: 初始化沙箱,基于Python的daytona来实现。然后设置属性initialized 为True,从而完成创建。
    SandboxManus类同样跟 Manus 继承自 ToolCallAgent。

沙箱sandbox

该模式是在 app/sandbox 文件夹下,核心类是 LocalSandboxClient ,代码在 app/sandbox/client.py 下
类LocalSandboxClient下有很多关键的方法:

  • create 创建一个 DockerSandbox 对象,以提供一个容器化的执行环境。(确保资源限制,文件操作,指令执行的安全和独立)
  • run_command
  • copy_from 把文件从容器拷贝到运行的本地
  • copy_to 将文件从本地拷贝到容器内部
  • read_file 从容器中读取文件,直接调用 self.sandbox.read_file 完成读取
  • write_file 往容器中写入文件
  • clearnup 清理sandbox
    LocalSandboxClient类就是简单对 app/sandbox/core/sandbox.py 下的 DockerSandbox 进行了简单的封装

DockerSandbox类的解析
初始化该类的时候传入两个参数,config和volume_bindings,其中config是Sandbox的配置,volume_bindings是格式为{host_path: container_path}的卷映射

  • create 方法,根据docker创建一个docker容器,首先是 host_config来创建docker容器的配置,然后再使用 asyncio.to_thread 异步创建一个docker容器,最后创建一个 terminal 终端来连接到该docker终端,绑定好该docker容器并保持session。
  • run_command 方法,将命令cmd交给 self.terminal 进行执行
  • read_file 从容器中读取文件,另外起一个进程来读取docker中的文件,
asyncio.to_thread(self.container.get_archive, resolved_path)

然后从该对象中读取tar流,并使用临时文件保存下该tar流,然后一次性 tar.extractfile 得到文件内容。

  • write_file 写入文件,如果没有该路径,则进行创建路径,然后创建tar流,将文件内容以tar流的形式写入到容器对应的文件下。
await asyncio.to_thread(self.container.put_archive, parent_dir or "/", tar_stream)
  • cleanup 清理,先关闭 self.terminal, 然后 异步调用 asyncio.to_thread(self.container.stop, timeout=5) 等待容器关闭。
  • copy_to copy_from 这两个拷贝文件操作,其实就是对读写文件的一种封装

AsyncDockerizedTerminal类的解析
主要是维护一个DockerSession,通过init函数初始化好Terminal的session,以传递命令到docker中。
负责在client和Docker之间传输命令

SandboxManager类
代码是在 app/sandbox/core/manager.py ,该类主要负责管理多个SandBox,

  • 提供访问 SandBox 的锁机制,
  • 创建SandBox 并加入到管理字典中
  • 清理所有的SandBox,先获得全局锁,然后将所有使用过的SandBox进行清理和释放,并释放相关的锁
  • 提供两种清理的方式, 清理IDEA状态的SandBox,另一种是全部清理(先等任务执行完成后再进行清理)

mcp服务

启动脚本是根目录下的 run_mcp.py,其中核心是 MCPRunner 类
MCPRunner类负责封装 MCPAgent,提供了三个核心方法:

  • initialize方法: 代表 MCPAgent 进行初始化 stdio 和 see 两个方式
  • run 和 run的衍生方法(run_interactive, run_default, run_single_prompt)都是封装了 MCPAgent.run 函数。
    其中 run_single_prompt 传入单个prompt从而实现一次的执行
    其中 run_interactive 交互式地传入prompt的对话
    其中 run_default 默认的执行模式,传入一次prompt后输出

MCPAgent 是核心类,继承于ToolCallAgent

prompt提示词

代码在app/prompt文件夹。该文件夹下存放的是各个模块的prompt。
在browser.py, manus.py, mcp.py, planning.py, swe.py, toolcall.py, visualization.py 下,有 SYSTEM_PROMPT, NEXT_STEP_PROMPT 两个提示词。
整个项目的核心在于这些提示词的质量。

工具

所有的工具集合都在这个文件夹 app/tool 下。
所有工具类的基础类是 BaseTool,用于组合BaseModel和工具功能的所有工具

AskHuman类,代码在 app/tool/ask_human.py 中,让LLM在信息不够详细的时候需求使用人类的介入

网络搜索工具,代码在 app/tool/search 文件夹下,含有多个不同的网络搜索,baidu,bing,google,duckduckgo

  • baiduSearch: 直接调用baiduSearch的lib实现简单封装
  • bingSearch: 使用requests库基于URL的方式来实现搜索结果的返回
  • googleSearch:也使用GoogleSearch的库来实现search的简单封装
  • duckduckgoSearch: 开源的搜索引擎,使用duckduckgo_search.DDGS实现搜索

SandBox工具
基于daytona来实现SandBox的管理

  • sb_vision_tool 要执行的视觉动作,将SandBox中的图片进行下载下来并压缩读取
  • sb_shell_tool 一个基于tmux session的命令行管理工具,支持四种方式,"execute_command","check_command_output", "terminate_command", "list_commands"
  • sb_files_tool 支持在安全的SandBox环境下进行文件操作,支持 create_file, str_replace, full_file_rewrite, delete_file 四个操作。
  • sb_browser_tool 浏览器操作工具,在SandBox下支持和Web进行多种的互动操作,"navigate_to","go_back", "wait", "click_element", "input_text", "send_keys", "switch_tab", "close_tab", "scroll_down", "scroll_up", "scroll_to_text", "get_dropdown_options", "select_dropdown_option", "click_coordinates", "drag_drop"

bash工具,直接在项目本地进行bash操作,安全性较差的方式
通过 asyncio.create_subprocess_shell 的方式完成

浏览器使用工具 browser_use_tool.py ,基于Python的browser_use library库来实现

文件操作工具 file_operators.py

mcp服务封装 mcp.py

任务规划和计划管理模块 planning.py

Python代码执行模型,python_execute.py 直接开启线程调用 exec 来执行代码并返回

工具收集模块 tool_collection.py 收集和管理所有的工具

网络搜索模块 web_search.py 管理所有的搜索引擎,并设置不同的搜索方式。

总结

OpenManus相比最近火热的OpenClaw来说,仅仅是和LLM交互的仍然为prompt,后续的Skills也就是prompt的另一种表现形式。