Manus的多Agent架构怎么做?
在本文主要讨论两个问题:
1、像Manus这样的多Agent系统,可以有哪些实现方式
2、多Agent系统中,如何实现信息的管理和共享
我在用LangGraph开发一个多Agent系统,LangGraph基于Langchain开发,是一个基于图的控制流框架,它可以更好的实现动态循环调用工作流。
LangChain的优点我们就不赘述,可以说是早期的完善的对开发者非常友好的框架。但LangGraph的控制流是静态的,它需要预先定义好所有可能的节点和边。而我们用的所有的AI产品都希望可以动态执行工作流,这就是我们的核心问题:
如何用LangGraph实现动态控制流?
OpenAI 目前尚未公开其应用产品中多智能体(multi-agent)系统的具体架构细节(例如 ChatGPT 内部是否使用多个 Agent、是否使用静态图 LangGraph 或动态控制流等),但我们可以从 已公开的系统设计模式、相关研究趋势,以及 OpenAI 生态中类似产品(如 Manuscripts、AutoGPT、LangGraph 等)来合理推测其可能的实现方式。以下是系统性回答:
多 Agent 系统的两种主流结构
A. 静态图结构(LangGraph、AutoGen)
核心机制:任务和 Agent 之间的流程是预定义好的(如图结构),控制流是**“可变但边界清晰”**的。
代表性框架:LangGraph、AutoGen、CrewAI
优势:可视化强、易于维护、debug-friendly。
限制:灵活性不足,不擅长处理复杂的、嵌套的动态决策链。
B. 动态执行控制(BOSS Agent / ReAct 式 Planner)
核心机制:一个主控 Agent(通常称为 BOSS、Manager、Planner)基于输入和上下文动态决定调用哪些工具或子 Agent。
代表性范式:ReAct、Toolformer、AutoGPT/Manuscripts 风格系统。
优势:灵活应对复杂任务,自我调整路径。
限制:调试成本高,稳定性依赖于 Prompt 工程和记忆能力。
Manus 的控制流推测(或类似系统):基于 BOSS Agent 的动态任务调度
根据 Manus 等项目的描述,其内部系统大概率采用如下结构:
┌─────────────┐
│ User Input │
└─────┬───────┘
↓
┌─────────────┐
│ Boss Agent │ ← 推理/规划中心,基于上下文决定下一步
└─────┬───────┘
↓
┌───────────────────┐
│ Tool/Agent Routing│ ← 动态分配给专门 Agent(如Writer, Coder, Searcher等)
└────┬──────────────┘
↓
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ Agent: Writer│ │ Agent: Coder │ │ Agent: Critic│ ← 子Agent,工具化
└──────────────┘ └──────────────┘ └──────────────┘
控制任务流的方式
Boss Agent 拥有上下文窗口与记忆能力,能观察输入 + 当前任务状态。
基于策略(Prompt 编程或微调),它会选择:
调用哪个子 Agent;
传什么内容;
是否 loop 回来做 refinement;
是否中止 / 输出最终结果。
Agent 之间可能通过一种「任务容器」交换信息,例如:
中间 JSON 格式(task + result + metadata)
或封装在 LLM 内部 Token 流中(隐式表示)
这类似于 OpenAI GPT 工具调用中使用 function_calling + tool_choice。
目前动态路由方案并不好用,我们可以使用其他类似的方式
方案1: 使用调度器节点模式,然后在工作流定义中使用条件路由。 方案2: 状态驱动执行计划
可能问题
- 可能会产生节点路由错误,为什么会产生这个错误?
StateGraph 的执行模型一次“执行”或“调度步”(step)内,StateGraph 会并发调用所有可执行的节点(Node)。
每个节点执行完毕后,都会返回一个字典,表示它要写入状态(state)里的哪些键(key)和对应的值(value)。
单值字段的保护策略
默认情况下,每个状态键(例如 mcp_results、messages、response)都被视为“单值”字段。
如果多个节点在同一个 step 返回了同一个键的不同值,StateGraph 无法判断应该采用哪一个,也不能安全地自动合并,就会认为这是并发更新冲突
这个错误本质上是 LangGraph(LangChain 的 StateGraph 引擎)为了保证状态的一致性,对同一个状态字段在同一步(step)里只允许一个节点写入而抛出的保护性警告。
“Use an Annotated key to handle multiple values”
这是多Agent在冲突时会报的错误,意味着你可以用 Python 的 typing.Annotated + Pydantic 的 Field(default_factory=…) 来声明这个字段是“可合并”的(通常是列表类型),让框架知道它可以接收来自多个节点的值,并以“追加”的方式合并它们。
什么时候会触发
并行拆分路径:当你在工作流图里,用多个并行分支(比如并行查询不同 API、并行调用不同检索 Agent)都输出到同一个字段。
广播型任务:多个 Agent 需要把自己的结果写到同一个“收集器”字段里,比如不同检索源的结果、不同专家 Agent 的反馈等。
为什么 Annotated 合并有效?
Pydantic 元信息:LangGraph 会读取 Pydantic 模型中 Field(default_factory=list) 的元信息,识别这是个“可合并”字段。
合并策略:当检测到多个返回值,它就知道要对列表进行拼接,而不是覆盖。
无需手动聚合:你不用在每个节点里先读旧值再写新值,框架自动帮你在 step 结束后统一合并。
2、 即便使用了Annotated,还是会报错,为什么?
我就遇到了这个问题,困扰了我很久,最终我找到了问题来源。StateGraph 会自动查找并行节点,即使你使用了Annotated,静态节点还是会导致他报错。
修改方式:在使用Annotated的前提下,修改LangGraph的节点逻辑,用循环来处理多个节点的排序。我想我已经讲的很清楚了。
1 | state = TravelState(user_intent=..., messages=[...]) |
它的核心要点是:
-静态图并行 → 可能同时触发多个节点,导致多个写入冲突。
-流式(顺序)执行 → 每次只跑一个 Agent,写入立刻合并,天然无并发。
-BOSS Agent 负责“生成”顺序,把原本图上的并行分支转化成一条条可控的序列。
这样既保留了“BOSS 动态决策”能力,又避开了 LangGraph 静态并行下的写入冲突。