手把手带你理解OpenManus

发布于 2025-3-25 00:31
浏览
0收藏

我之前演示过几个OpenManus的demo,其实也就是demo,包括manus,现在也就是demo阶段,复杂的plan和flow,现在的代码支撑和LLM的能力都有待改善,但是我们这期不是吐槽文章,是来把OpenManus给打开看看它的实现是怎么样的,其实Manus也都差不多,甚至OWL也都差不多,我们看一个就够了。

手把手带你理解OpenManus-AI.x社区

其他的几个目录也没啥特别需要看的,就看app。

手把手带你理解OpenManus-AI.x社区

app里面有这么几个结构:

1- agent 没啥好解释的

2- flow 就是来做multiagent的planning和管理任务框架的。

3- prompt 

手把手带你理解OpenManus-AI.x社区

都是这种形式,来定义系统提示词和agent的role。

4- tool

手把手带你理解OpenManus-AI.x社区

纯纯干活的了。

手把手带你理解OpenManus-AI.x社区

主要看最重要的目录agent。

大概总体分这么几个agent。

先看base:base.py 模块定义了抽象基类 BaseAgent,用于管理代理的状态、内存、执行循环(包括运行、步骤执行、卡住处理)和消息,并提供初始化和配置功能,为构建具有特定行为的代理提供基础框架。

在看planning:planning.py模块定义了PlanningAgent类,该代理通过PlanningTool和Terminate等工具创建、管理和执行任务计划。它具有初始化、计划创建(create_initial_plan)、思考(think)、行动(act)、计划状态更新(update_plan_status)、步骤跟踪(step_execution_tracker)等功能,并能根据工具执行结果动态调整计划,处理初始请求(run)并检索当前计划状态(get_plan)。

然后是 react.py:react.py模块定义了继承自BaseAgent的抽象类ReActAgent,它通过think(思考,决定下一步行动) 和act(执行行动) 两个抽象方法(需子类实现)以及step方法(整合think和act)来处理和执行任务,并提供基础的任务处理框架。

swe和tool就是 指从code和tool了

manus.py:manus.py模块定义了Manus类,一个继承自ToolCallAgent(实际上你前面提到了继承自 PlanningAgent, 请确认是哪个) 的通用智能代理。Manus具有预定义的名称、描述、系统提示、步骤限制(max_observe,max_steps),并利用包含PythonExecute、WebSearch、BrowserUseTool、FileSaver和Terminate等工具的available_tools集合来执行各种任务,并通过_handle_special_tool方法处理(如清理)BrowserUseTool的结果。

好的,我们来梳理一下这些代码的调用逻辑。这里涉及到多个Agent类(BaseAgent,ReActAgent,ToolCallAgent,PlanningAgent,SWEAgent,Manus),以及它们之间的继承关系和方法调用。我用文字描述,并结合伪代码(因为好解释)来表示调用流程。

1. 总体继承关系:

BaseAgent (抽象基类)
  └─ ReActAgent (抽象基类)
      └─ ToolCallAgent
          ├─ PlanningAgent
          ├─ SWEAgent
          └─ Manus
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.

2.BaseAgent(核心基类):

  • 核心方法:

__init__: 初始化Agent的基本属性(name, description, llm, memory, state, max_steps等)。

initialize_agent: 使用model_validator在对象创建后进行进一步初始化(设置默认的LLM和Memory)。

state_context: 一个异步上下文管理器,用于安全地更改Agent的状态(IDLE, RUNNING, ERROR等)。

update_memory: 将消息添加到Agent的内存中。

run: Agent的主执行循环。它会循环调用step()方法,直到达到max_steps或状态变为FINISHED。

step:抽象方法,必须由子类实现。执行Agent的单个步骤。

is_stuck: 检查智能体是否卡住。

handle_stuck_state: 处理卡住的状态。

messages: 属性,用于获取和设置消息。

  • 调用流程 (以run方法为例):

async run(request):
  if state != IDLE:
    raise RuntimeError


  if request:
    update_memory(USER, request)


  async with state_context(RUNNING):
    while current_step < max_steps and state != FINISHED:
      current_step += 1
      step_result = await step()  # 调用子类的 step() 方法
      if is_stuck():
         handle_stuck_state()


      results.append(step_result)


    if current_step >= max_steps:
      state = IDLE
      results.append("Terminated: Reached max steps")


  return "\n".join(results)
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.

3.ReActAgent(ReAct模式的基类):

  • 继承自:​BaseAgent
  • 核心方法:

think:抽象方法,由子类实现。思考下一步行动。

act:抽象方法,由子类实现。执行行动。

step: 实现了BaseAgent的step方法。依次调用think()和act()。

  • 调用流程 (以step方法为例):

4.ToolCallAgent(支持工具调用的Agent):

async step():
  should_act = await think()  # 调用子类的 think()
  if should_act:
    return await act()  # 调用子类的 act()
  else:
    return "Thinking complete - no action needed"
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.


  • 继承自:​ReActAgent
  • 核心方法:

__init__: 初始化工具相关的属性 (available_tools, tool_choices, special_tool_names, tool_calls等)。

think: 根据当前状态和可用工具,决定是否调用工具。使用LLM进行决策。

act: 执行工具调用,并处理工具的输出。

execute_tool: 执行具体的工具调用。

_handle_special_tool: 处理特殊工具。

  • 调用流程 (以think和act为例):

async think():
  # ... (准备消息和工具信息) ...
  response = await llm.ask_tool(...) # 让LLM决定是否调用工具,以及调用哪个工具
  if response.tool_calls:
     self.tool_calls = response.tool_calls
     return True # 需要执行工具
  else:
     return False


async act():
   if self.tool_calls:
      tool_call = self.tool_calls.pop(0) # 取出第一个工具调用
      result = await execute_tool(tool_call)
      await _handle_special_tool(tool_call.function.name,result) # 如果是特殊工具,则处理。
      update_memory(TOOL, result, tool_call_id=tool_call.id)
      return result
   else:
       return "No tool to execute"
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.

5.PlanningAgent(规划型Agent):

  • 继承自:​ToolCallAgent
  • 核心方法:
  • initialize_plan_and_verify_tools: 初始化计划ID并验证是否有PlanningTool。
  • think: 获取当前计划状态,并让LLM决定下一步行动(包括是否调用工具)。
  • act: 执行工具调用,并更新计划状态。
  • get_plan: 获取当前计划。
  • create_initial_plan: 根据用户请求创建初始计划。
  • update_plan_status: 根据工具执行结果更新计划步骤的状态。
  • _get_current_step_index: 获取当前计划中第一个未完成步骤的索引。
  • 调用流程 (更详细,以run->think->act为例):

async run(request):
  if request:
     await create_initial_plan(request)
  return await super().run()  # 调用 ToolCallAgent.run() -> ReActAgent.run() -> BaseAgent.run()




async create_initial_plan(request):
  # 1. 构造消息,让 LLM 创建计划
  messages = [...]
  response = await llm.ask_tool(..., tool_choice=ToolChoice.AUTO)


  # 2. 处理 LLM 的响应,提取工具调用(应该是 planning 工具的调用)
  for tool_call in response.tool_calls:
    if tool_call.function.name == "planning":
      result = await execute_tool(tool_call) # 执行 planning 工具
      # 3. 将工具执行结果(计划)存入内存
      update_memory(TOOL, result, tool_call_id=tool_call.id)




async think():
    prompt = f"CURRENT PLAN STATUS:\n{await self.get_plan()}\n\n{self.next_step_prompt}"
    self.messages.append(Message.user_message(prompt))
    self.current_step_index = await self._get_current_step_index()
    result = await super().think() # 调用 ToolCallAgent 的 think()


    if result and self.tool_calls:
       # 记录工具和步骤的关联
       latest_tool_call = self.tool_calls[0]
       if latest_tool_call 不是 planning tool 且 不是 special tool:
           self.step_execution_tracker[latest_tool_call.id] = {
               "step_index": self.current_step_index,
               "tool_name": latest_tool_call.function.name,
                "status": "pending"
           }
    return result


async act():
   result = await super().act() # 调用 ToolCallAgent 的 act()
   if self.tool_calls:
       latest_tool_call = self.tool_calls[0]
       if latest_tool_call.id in self.step_execution_tracker:
          self.step_execution_tracker[latest_tool_call.id]["status"] = "completed"
          self.step_execution_tracker[latest_tool_call.id]["result"] = result


          if latest_tool_call 不是 planning tool 且 不是 special tool:
              await self.update_plan_status(latest_tool_call.id)
   return result




async update_plan_status(tool_call_id):
  # 1. 检查 tool_call_id 是否在 tracker 中,以及状态是否为 completed
  # 2. 调用 planning 工具的 mark_step 命令,将对应步骤标记为 completed


async _get_current_step_index():
   # 1. 获取当前计划 (文本)
   # 2. 解析计划文本,找到第一个 [ ] 或 [→] 的步骤
   # 3. 调用 planning 工具的 mark_step 命令,将当前步骤设置为 in_progress
   # 4. 返回步骤索引
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.

6.SWEAgent(软件工程师Agent):

  • 继承自:​ToolCallAgent
  • 核心方法:

think: 更新工作目录,并使用格式化的next_step_prompt。然后调用父类的think。

  • 调用流程 (以think为例):

async think():
  self.working_dir = await self.bash.execute("pwd")  # 获取当前工作目录
  self.next_step_prompt = self.next_step_prompt.format(current_dir=self.working_dir) # 更新提示
  return await super().think()  # 调用 ToolCallAgent 的 think()
  • 1.
  • 2.
  • 3.
  • 4.


7.Manus(通用Agent):

  • 继承自:​ToolCallAgent
  • 特点:拥有更广泛的工具集 (PythonExecute, WebSearch, BrowserUseTool, FileSaver, Terminate)。
  • _handle_special_tool: 重写此方法,处理BrowserUseTool的清理工作。

那么好,我们总结一下:

  • BaseAgent提供了Agent的基本框架和执行循环。
  • ReActAgent引入了 "思考-行动" 模式。
  • ToolCallAgent增加了工具调用的能力。
  • PlanningAgent、SWEAgent和Manus是ToolCallAgent的具体实现,分别针对不同的任务类型。
  • 调用逻辑主要通过继承和重写think、act和step方法来实现。
  • PlanningAgent具有更复杂的内部状态管理,用于跟踪计划的执行。


画出来就是下图这样:

手把手带你理解OpenManus-AI.x社区


总体调用的抽象感觉就是下图这样:

手把手带你理解OpenManus-AI.x社区

好了今天这节课就解释到这,大家可以结合我的解释自己去run一下代码,甚至自己按着这个逻辑来新写一套multi-agents的框架也不是特别难的事。

本文转载自​​熵减AI​​,作者:周博洋


收藏
回复
举报


回复
相关推荐