07多智能体:先跑通两条路线
对照体验 supervisor(集中协调)vs DeepAgent(agent-as-tool)与 plan-execute。
adk/prebuilt/supervisor/supervisor.go:101adk/prebuilt/deep/deep.go:171adk/prebuilt/planexecute/plan_execute.go:862一个 Agent 不够用的时候
单个 Agent 什么都想干,往往什么都干不好:工具太多它选不准,指令太长它抓不住重点,不同领域的知识互相干扰。自然的解法是分工——把大问题拆给多个专精的 Agent。
但”多个 Agent 怎么协作”这件事,藏着 Eino 全书最重要的一个设计分歧。这一章我们先把两条主流路线各跑通一次,亲手感受差别;下一部分(第 9 章)再上升到设计哲学的高度去辩论。
对照上面的可视化:左边是 transfer(转移),控制权像接力棒一样从一个 Agent 交到另一个,大家共享同一份对话上下文;右边是 agent-as-tool(智能体即工具),主 Agent 像调用普通工具一样”调用”子 Agent,子 Agent 在隔离的上下文里跑完,只把结果交回来。这两种拓扑,就是本章要跑通的两条路线。
路线一:Supervisor —— 集中协调
第一条路线是 supervisor(主管):一个居中的主管 Agent 负责调度,把任务派给各个子 Agent,子 Agent 只能和主管对话、彼此之间不直接通信。这是经典的中心辐射(hub-and-spoke)结构。
import "github.com/cloudwego/eino/adk/prebuilt/supervisor"
sup, _ := supervisor.New(ctx, &supervisor.Config{ Supervisor: coordinatorAgent, // 居中调度的主管 SubAgents: []adk.Agent{researcher, writer, reviewer}, // 被调度的专家})
runner := adk.NewRunner(ctx, adk.RunnerConfig{Agent: sup})iter := runner.Query(ctx, "写一篇关于 Eino 的技术博客")它的构造(adk/prebuilt/supervisor/supervisor.go:101)做了一件关键的事:给每个子 Agent 包一层 AgentWithDeterministicTransferTo,强制它们干完活后转移回主管。这样就形成了”主管派活 → 子 Agent 干活 → 交回主管 → 主管再决策”的循环。
// New creates a supervisor-based multi-agent system with the given configuration.//// In the supervisor pattern, a designated supervisor agent coordinates multiple sub-agents.// The supervisor can delegate tasks to sub-agents and receive their responses, while// sub-agents can only communicate with the supervisor (not with each other directly).// This hierarchical structure enables complex problem-solving through coordinated agent interactions.//// The returned agent is wrapped in an internal container that provides unified tracing.// When used with Runner and callbacks, all agents within the supervisor structure will// share the same trace root, making it easy to observe the entire multi-agent execution// as a single logical unit.//// NOT RECOMMENDED: Supervisor is built on agent transfer with full context sharing,// which has not proven to be more effective empirically. Consider using// ChatModelAgent with AgentTool or DeepAgent instead for most multi-agent scenarios.func New(ctx context.Context, conf *Config) (adk.ResumableAgent, error) { subAgents := make([]adk.Agent, 0, len(conf.SubAgents)) supervisorName := conf.Supervisor.Name(ctx) for _, subAgent := range conf.SubAgents { subAgents = append(subAgents, adk.AgentWithDeterministicTransferTo(ctx, &adk.DeterministicTransferConfig{ Agent: subAgent, ToAgentNames: []string{supervisorName}, })) }
inner, err := adk.SetSubAgents(ctx, conf.Supervisor, subAgents) if err != nil { return nil, err }
return &supervisorContainer{ name: supervisorName, inner: inner, }, nil}⚠️ 源码给 Supervisor 标了 NOT RECOMMENDED
注意
supervisor.New的文档注释(adk/prebuilt/supervisor/supervisor.go:98)明确写着 NOT RECOMMENDED:supervisor 建立在 agent transfer + 完整上下文共享之上,经验上并未被证明更有效,官方建议多数场景改用ChatModelAgent+AgentTool或DeepAgent。为什么共享上下文是问题?这正是第 9 章要展开的核心辩论。我们先把它跑通、感受它,再理解为什么不推荐。// New creates a supervisor-based multi-agent system with the given configuration.//// In the supervisor pattern, a designated supervisor agent coordinates multiple sub-agents.// The supervisor can delegate tasks to sub-agents and receive their responses, while// sub-agents can only communicate with the supervisor (not with each other directly).// This hierarchical structure enables complex problem-solving through coordinated agent interactions.//// The returned agent is wrapped in an internal container that provides unified tracing.// When used with Runner and callbacks, all agents within the supervisor structure will// share the same trace root, making it easy to observe the entire multi-agent execution// as a single logical unit.//// NOT RECOMMENDED: Supervisor is built on agent transfer with full context sharing,// which has not proven to be more effective empirically. Consider using// ChatModelAgent with AgentTool or DeepAgent instead for most multi-agent scenarios.func New(ctx context.Context, conf *Config) (adk.ResumableAgent, error) {subAgents := make([]adk.Agent, 0, len(conf.SubAgents))supervisorName := conf.Supervisor.Name(ctx)for _, subAgent := range conf.SubAgents {subAgents = append(subAgents, adk.AgentWithDeterministicTransferTo(ctx, &adk.DeterministicTransferConfig{Agent: subAgent,ToAgentNames: []string{supervisorName},}))}inner, err := adk.SetSubAgents(ctx, conf.Supervisor, subAgents)if err != nil {return nil, err}return &supervisorContainer{name: supervisorName,inner: inner,// … 这是 L86–118 的片段(该行不在任何顶层声明内);点击上方「浏览完整文件」
路线二:DeepAgent —— 智能体即工具
第二条路线是 DeepAgent,它代表 Eino 更推崇的组合方式:把子 Agent 当工具用。
import "github.com/cloudwego/eino/adk/prebuilt/deep"
agent, _ := deep.New(ctx, &deep.Config{ Name: "lead", ChatModel: chatModel, Instruction: "你是团队负责人,把子任务派给合适的专家。", SubAgents: []adk.Agent{researcher, writer, reviewer},})构造入口是 deep.New(adk/prebuilt/deep/deep.go:171,内部走泛型版 NewTyped adk/prebuilt/deep/deep.go:116)。它的巧妙之处在于:所有子 Agent 不是被平铺成一堆工具,而是被收敛成一个统一的 task 工具(adk/prebuilt/deep/deep.go:131 的 task tool middleware)。主 Agent 想派活时就调用 task,由它把子任务分发给对应专家。
// New creates a new Deep agent instance with the provided configuration.// This function initializes built-in tools, creates a task tool for subagent orchestration,// and returns a fully configured ChatModelAgent ready for execution.func New(ctx context.Context, cfg *Config) (adk.ResumableAgent, error) { return NewTyped(ctx, cfg)}// NewTyped creates a new typed Deep agent instance with the provided configuration.// This function initializes built-in tools, creates a task tool for subagent orchestration,// and returns a fully configured TypedChatModelAgent ready for execution.func NewTyped[M adk.MessageType](ctx context.Context, cfg *TypedConfig[M]) (adk.TypedResumableAgent[M], error) { handlers, err := buildTypedBuiltinAgentMiddlewares(ctx, cfg) if err != nil { return nil, err }
instruction := cfg.Instruction if len(instruction) == 0 { instruction = internal.SelectPrompt(internal.I18nPrompts{ English: baseAgentInstruction, Chinese: baseAgentInstructionChinese, }) }
if !cfg.WithoutGeneralSubAgent || len(cfg.SubAgents) > 0 { tt, err := typedTaskToolMiddleware( ctx, cfg.TaskToolDescriptionGenerator, cfg.SubAgents,
cfg.WithoutGeneralSubAgent, cfg.ChatModel, instruction, cfg.ToolsConfig, cfg.MaxIteration, cfg.Middlewares, append(handlers, cfg.Handlers...), cfg.ModelFailoverConfig, ) if err != nil { return nil, fmt.Errorf("failed to new task tool: %w", err) } handlers = append(handlers, tt) }
return adk.NewTypedChatModelAgent(ctx, &adk.TypedChatModelAgentConfig[M]{ Name: cfg.Name, Description: cfg.Description, Instruction: instruction, Model: cfg.ChatModel, ToolsConfig: cfg.ToolsConfig, MaxIterations: cfg.MaxIteration, Middlewares: cfg.Middlewares, Handlers: append(handlers, cfg.Handlers...),
// … 省略 6 行;完整声明 L113–166,点击上方「浏览完整文件」这和 supervisor 的本质区别是:子 Agent 在隔离的上下文里执行,主 Agent 只看到它的最终产出,而不是它满地的中间草稿。这就是可视化右侧那条路线——干净的输入进、干净的结果出。
🔑 本章的设计钥匙
两条路线的分水岭是上下文的边界。Supervisor 让所有 Agent 泡在同一份共享上下文里(transfer 语义),彼此的中间过程互相可见、互相污染;DeepAgent 把每个子 Agent 关进隔离的上下文(agent-as-tool 语义),只交换”输入→输出”。隔离带来三样东西:可组合(子 Agent 能被任意嵌套复用)、可控(主 Agent 不被子 Agent 的噪声淹没)、中断可传播(子 Agent 里的中断能穿透工具边界冒到顶层——这一点第 16 章会看到实现)。记住这个”边界”直觉,第 9 章的辩论就水到渠成。
路线三(进阶):Plan-Execute-Replan
除了这两条主线,ADK 还预置了第三种更结构化的范式:先规划、再执行、按需重规划。它的构造(adk/prebuilt/planexecute/plan_execute.go:862)其实是把前几章学过的编排原语拼起来:
// New creates a new plan-execute-replan agent with the given configuration.// The plan-execute-replan pattern works in three phases:// 1. Planning: Generate a structured plan with clear, actionable steps// 2. Execution: Execute the first step of the plan// 3. Replanning: Evaluate progress and either complete the task or revise the plan// This approach enables complex problem-solving through iterative refinement.func New(ctx context.Context, cfg *Config) (adk.ResumableAgent, error) { maxIterations := cfg.MaxIterations if maxIterations <= 0 { maxIterations = 10 } loop, err := adk.NewLoopAgent(ctx, &adk.LoopAgentConfig{ Name: "execute_replan", SubAgents: []adk.Agent{cfg.Executor, cfg.Replanner}, MaxIterations: maxIterations, }) if err != nil { return nil, err }
return adk.NewSequentialAgent(ctx, &adk.SequentialAgentConfig{ Name: "plan_execute_replan", SubAgents: []adk.Agent{cfg.Planner, loop}, })}// 概念结构:Sequential(Planner, Loop(Executor, Replanner))peAgent, _ := planexecute.New(ctx, &planexecute.Config{ Planner: planexecute.NewPlanner(ctx, ...), // 先出一份可执行计划 Executor: planexecute.NewExecutor(ctx, ...), // 执行计划的第一步 Replanner: planexecute.NewReplanner(ctx, ...), // 评估进度,改计划或收尾})它是一个 Sequential(顺序):先跑 Planner 出计划,再进入一个 Loop(循环)反复”执行一步 → 重规划”。Replanner(adk/prebuilt/planexecute/plan_execute.go:807)在判断任务完成时,通过 respond 工具触发 NewBreakLoopAction 跳出循环。默认最多循环 10 轮(adk/prebuilt/planexecute/plan_execute.go:851)。
// NewReplanner creates a plan-execute-replan agent wired with plan and respond tools.// It configures the provided ToolCallingChatModel with the tools and returns an Agent.func NewReplanner(_ context.Context, cfg *ReplannerConfig) (adk.Agent, error) { planTool := cfg.PlanTool if planTool == nil { planTool = &PlanToolInfo }
respondTool := cfg.RespondTool if respondTool == nil { respondTool = &RespondToolInfo }
chatModel, err := cfg.ChatModel.WithTools([]*schema.ToolInfo{planTool, respondTool}) if err != nil { return nil, err }
planParser := cfg.NewPlan if planParser == nil { planParser = defaultNewPlan }
return &replanner{ chatModel: chatModel, planTool: planTool, respondTool: respondTool, genInputFn: cfg.GenInputFn, newPlan: planParser, }, nil}// Config provides configuration options for creating a plan-execute-replan agent.type Config struct { // Planner specifies the agent that generates the plan. // You can use provided NewPlanner to create a planner agent. Planner adk.Agent
// Executor specifies the agent that executes the plan generated by planner or replanner. // You can use provided NewExecutor to create an executor agent. Executor adk.Agent
// Replanner specifies the agent that replans the plan. // You can use provided NewReplanner to create a replanner agent. Replanner adk.Agent
// MaxIterations defines the maximum number of loops for 'execute-replan'. // Optional. If not provided, 10 will be used as the default. MaxIterations int}这个范式的价值在于显式的计划:适合步骤多、需要中途纠偏的任务。它也顺带印证了一件事——多智能体不是只有”谁调用谁”这一个维度,还有”如何编排它们的时序”。这三种预置 Agent(supervisor / DeepAgent / plan-execute)各代表一种组合范式,第 11 章会系统对比。
两条路线,先建立手感
现在你已经把三种预置多智能体都跑通了。回到开头的可视化再看一眼:transfer 是”交接控制权+共享上下文”,agent-as-tool 是”调用+隔离上下文”。这不是两种等价的写法,而是两种世界观——它决定了系统能否组合、能否控制、中断能否传播。
不必现在就下判断。Part I 的目标是”会用”和”有感觉”;我们已经把单 Agent → 工具 → 记忆 → 中断续跑 → 中间件 → 多智能体这条主线完整走了一遍。接下来 Part II 才正式进入”为什么这样设计”的正文。
本章小结
- 单 Agent 力不从心时,用多智能体分工;协作方式主要有两条路线。
- Supervisor(
adk/prebuilt/supervisor/supervisor.go:101):中心辐射、共享上下文、transfer 语义——源码标注 NOT RECOMMENDED。 - DeepAgent(
adk/prebuilt/deep/deep.go:171):agent-as-tool、隔离上下文、子 Agent 收敛成单个task工具——官方推荐路线。 - Plan-Execute-Replan(
adk/prebuilt/planexecute/plan_execute.go:862):Sequential(Planner, Loop(Executor, Replanner)),显式计划+按需重规划。 - 分水岭是上下文的边界:隔离带来可组合、可控、中断可传播——这是第 9 章辩论的伏笔。
Part I 到此结束。下一部分,我们打开设计的正文:从 ADK 的世界观(Agent = 事件流生成器)讲起,直抵全教程的高潮——组合哲学之争。