多 Agent 编排:Eino ADK 的三种协作模式(第57篇-E43)
系列「企业级 AI Agent 实现拆解」E43 篇,Part 10 生产工程篇第一章。Part 9 拆完了单个 Agent 的内部结构。这篇往上一层:多个 Agent 怎么协作?Eino ADK 提供了哪些模式,各有什么取舍?
读完这篇你会知道
Agent接口是什么,为什么它是 ADK 的核心抽象- 三种多 Agent 模式:AgentTool / Supervisor / Plan-Execute-Replan
- 为什么官方源码注释里明确写了 Supervisor 模式"NOT RECOMMENDED"
NewAgentTool:把 Agent 包成工具的边界设计AgentEvent流:内层 Agent 的输出如何穿透到外层
为什么需要多 Agent
单个 ReAct Agent 的问题:
- 工具太多会让 LLM “迷失”(context pollution),工具调用准确率下降
- 复杂任务需要不同专业知识(搜索/计算/写作),一个 Agent 难以都擅长
- 某些任务天然可以并行(研究 + 分析 + 撰写同时进行)
- 长任务的 context window 容量限制
多 Agent 的解法是分而治之:把一个大任务拆给多个专注的 Agent,通过某种协调机制组合结果。
Agent 接口:ADK 的核心抽象
Eino ADK 的多 Agent 体系围绕一个极简接口构建:
type Agent interface {
Name(ctx context.Context) string
Description(ctx context.Context) string
Run(ctx context.Context, input *AgentInput, options ...AgentRunOption) *AsyncIterator[*AgentEvent]
}
AgentInput 只有两个字段:
type AgentInput struct {
Messages []*schema.Message // 输入消息
EnableStreaming bool // 是否流式
}
AgentEvent 是执行中产生的事件流:
type AgentEvent struct {
AgentName string // 产生事件的 Agent 名
RunPath []RunStep // 从根到当前 Agent 的执行路径
Output *AgentOutput // 消息输出(模型生成 or 工具结果)
Action *AgentAction // 动作(退出/中断/转移/继续)
Err error
}
AgentAction 包含 Agent 可以触发的所有动作:
type AgentAction struct {
Exit bool // 正常退出
Interrupted *InterruptInfo // HITL 中断
TransferToAgent *TransferToAgentAction // 转移给另一个 Agent(不推荐)
BreakLoop *BreakLoopAction // 跳出循环
CustomizedAction any // 自定义
}
这套接口足够薄——只要实现了 Run,任何东西都能成为 Agent:一个 ReAct Agent、一个 Supervisor、一个确定性工作流,甚至一个简单的 LLM 调用。
模式一:AgentTool(官方推荐)
把 Worker Agent 包成一个工具,让 Orchestrator Agent 通过 tool_calls 来调用它。
// 创建专门的 Worker Agent
searchAgent, _ := adk.NewChatModelAgent(ctx, &adk.ChatModelAgentConfig{
Name: "research_agent",
Description: "搜索互联网获取信息", // LLM 用这个描述决定何时调用
Instruction: "你是一个研究专员...",
Model: model,
ToolsConfig: adk.ToolsConfig{...},
})
// 把 Worker Agent 包成工具
searchTool := adk.NewAgentTool(ctx, searchAgent)
// Orchestrator 把所有 Worker Tool 加进自己的工具集
orchestrator, _ := adk.NewChatModelAgent(ctx, &adk.ChatModelAgentConfig{
Name: "orchestrator",
Model: model,
ToolsConfig: adk.ToolsConfig{
ToolsNodeConfig: compose.ToolsNodeConfig{
Tools: []tool.BaseTool{searchTool, mathTool, writeTool},
},
},
})
工作原理:
Orchestrator LLM → tool_calls: [{name:"research_agent", args:{"request":"..."}}}]
↓
searchAgent.Run(ctx, &AgentInput{Messages: [...]})
↓ (完整的 ReAct 循环)
搜索工具 → 返回结果
↓
返回 string 给 Orchestrator
↓
Orchestrator LLM 收到工具结果 → 继续决策
边界隔离(关键设计):
// AgentTool 对 Action 的处理是有边界的
// 内层 Agent 的这些动作不会传播给父 Agent:
// - Exit: 只退出内层 Agent,不退出 Orchestrator
// - TransferToAgent: 在工具边界内消化
// - BreakLoop: 同上
// 只有 Interrupted (HITL) 会通过 CompositeInterrupt 穿透给父 Agent
这个设计很重要:Worker Agent 的内部决策(“我完成了”)不会意外终止 Orchestrator 的执行。
事件穿透(可选):
如果 ToolsConfig.EmitInternalEvents = true,Worker Agent 产生的 AgentEvent(包括模型流式输出、工具结果)会实时穿透到外层 Runner,用户可以实时看到内层 Agent 的思考过程。
模式二:Supervisor(不推荐,但要理解)
官方源码注释里有一段很罕见的坦诚:
// NOT RECOMMENDED: Agent transfer with full context sharing between agents has not proven
// to be more effective empirically. Consider using ChatModelAgent with AgentTool
// or DeepAgent instead for most multi-agent scenarios.
Supervisor 模式的结构:
sv := adk.NewChatModelAgent(ctx, &adk.ChatModelAgentConfig{
Name: "supervisor",
Instruction: `你管理两个 Agent:research_agent 和 math_agent。
分配任务给合适的 Agent,不要同时调用多个。`,
Model: model,
Exit: &adk.ExitTool{}, // 结束信号
})
// supervisor.New 做了两件事:
// 1. 给每个 sub-agent 注入 DeterministicTransferTo(supervisor)
// → sub-agent 跑完后必须把控制权还给 supervisor
// 2. 用 SetSubAgents 把 sub-agents 注册为 supervisor 可以 "transfer to" 的目标
result, _ := supervisor.New(ctx, &supervisor.Config{
Supervisor: sv,
SubAgents: []adk.Agent{searchAgent, mathAgent},
})
为什么 NOT RECOMMENDED?
Agent transfer 模式本质上是"共享完整 context 的消息传递":
Supervisor → (完整对话历史) → SubAgent A → (完整对话历史) → Supervisor → ...
每次转移都携带完整历史,越来越长,LLM 越来越难以关注当前任务。实际测试中,把 Worker 包成 AgentTool 效果更好——因为边界更清晰,每个 Worker 只看到自己需要的信息。
Supervisor 模式适合用于理解历史遗留设计,或者在特殊场景下需要 sub-agent 感知完整历史时用。
模式三:Plan-Execute-Replan(结构化工作流)
对于需要严格顺序的复杂任务(旅行规划、代码编写、多步研究),用预定义的工作流拆分职责:
// 三个专职 Agent
planAgent, _ := agent.NewPlanner(ctx) // 规划师:输出结构化计划
executeAgent, _ := agent.NewExecutor(ctx) // 执行者:按计划执行每一步
replanAgent, _ := agent.NewReplanAgent(ctx) // 重规划师:执行失败时调整计划
// planexecute.New 把三者组成一个确定性循环:
// Plan → Execute → (成功 → Done) or (失败 → Replan → Execute → ...)
entryAgent, _ := planexecute.New(ctx, &planexecute.Config{
Planner: planAgent,
Executor: executeAgent,
Replanner: replanAgent,
MaxIterations: 20,
})
执行循环:
用户输入 → Planner:生成步骤列表 [step1, step2, step3]
↓
Executor:执行 step1 → 成功
↓
Executor:执行 step2 → 失败
↓
Replanner:分析失败原因 → 修改计划
↓
Executor:执行调整后的 step2' → 成功
↓
所有步骤完成 → 返回结果
这个模式的优点是可预测:每一步都有明确的输入输出,失败可以定点重试,不依赖 LLM 在一次 ReAct 循环里"自己想到"要做什么。
三种模式对比
| AgentTool | Supervisor | Plan-Execute-Replan | |
|---|---|---|---|
| 推荐度 | ✅ 推荐 | ⚠️ 不推荐 | ✅ 推荐 |
| 边界清晰度 | 高(工具边界) | 低(共享 context) | 高(确定性步骤) |
| 灵活性 | 高(LLM 决定调用) | 中 | 低(预定义流程) |
| Context 增长 | 慢(每次调用独立) | 快(历史累积) | 受控(分步独立) |
| HITL 支持 | ✅ 穿透到父 Agent | ✅ | ✅ |
| 适合场景 | 通用 Agent 协作 | 特殊历史共享需求 | 长任务、需要回滚 |
DeepFlux 的选择
DeepFlux 的 Agent BC(server/internal/agent)使用了 AgentTool 模式:
- 知识库检索:
kbBC 暴露为一个 Tool,Agent 需要查资料时调用 - 记忆读写:
memoryBC 作为 Tool,Agent 自主决定何时存储/检索记忆 - HITL 审批:通过
StatefulInterrupt,穿透到外层等待人工处理
这种设计的好处是每个 Worker(kb/memory/tool)有完整的边界,可以独立扩容、独立测试,不会因为某个 Worker 的历史积累污染其他 Worker 的 context。
小结
Eino ADK 的多 Agent 体系核心是一个极简的 Agent 接口,三种协作模式本质上是三种组合方式:
- AgentTool:Agent 作为工具被调用,边界最清晰,官方推荐
- Supervisor:共享 context 的转移模式,直觉上"像指挥系统",但实测效果不如 AgentTool
- Plan-Execute-Replan:确定性工作流,适合步骤清晰、需要容错的长任务
选模式的原则很简单:能用 AgentTool 就用 AgentTool,需要严格顺序就用 Plan-Execute,只在真正需要共享完整历史时才考虑 Supervisor。
代码来源:eino/adk/interface.go · eino/adk/agent_tool.go · eino/adk/prebuilt/supervisor/supervisor.go
更多推荐
所有评论(0)