)
上一篇 讲了两个 Agent 怎么协作——Host MultiAgent 和 DeepFlux Subagent 两种模式以及Agent 就是 Tool这个核心设计。那篇侧重怎么用这篇拆里面怎么实现。三个问题串全篇Host MultiAgent 的 Graph 内部怎么跑状态、流式、多意图汇总ADK 三种预制模式supervisor/planexecute/deep各自怎么实现为什么 supervisor 标注 NOT RECOMMENDED而 deep 是推荐方案一Host MultiAgent 源码拆解上一篇 讲过 Host MultiAgent 的 Graph 拓扑和 specialist 注册。这里深入源码内部看几个关键机制。1. 输入的注入map2list 把消息喂给 hostcompose.go:49-83的入口适配器把 Eino 的图状态map[string]any转成 LLM 能理解的消息列表。所有 specialist 的输出也通过同样的方式回流到消息列表。2. 状态管理state 结构体compose.go:30-47定义了图的内部状态typestatestruct{Messages[]*schema.Message IsMultipleIntentsboolSpecialistResultsmap[string]string}Messages当前消息列表host LLM 和 specialist 都往里写IsMultipleIntents多意图标记host 一次产出多个 tool call 时为 trueSpecialistResults各 specialist 的原始结果供 summarizer 汇总状态通过ProcessState在节点间流转。每个节点host/specialist/summarizer都能读写这个状态。3. 流式处理firstChunkStreamToolCallCheckertypes.go:182-204定义了一个重要的辅助函数typefirstChunkStreamToolCallCheckerstruct{doneboolhasCallbool}func(c*firstChunkStreamToolCallChecker)Check(chunk*schema.Message)bool{ifc.done{returnc.hasCall}c.donetruec.hasCalllen(chunk.ToolCalls)0returnc.hasCall}只检查第一个 chunk 是否有 tool call。为什么只看第一个因为流式场景下host LLM 要么在第一个 chunk 就决定调 tool要么就是不调。不需要等全部 chunk 到了再判断——这是性能优化也避免了等全部流式结果再决定的延迟。multiSpecialistsBranch:222-244用这个 checker 来分流ifchecker.Check(msg){// 有 tool call → 走 specialist 分支}else{// 无 tool call → 直接回答}4. 多意图汇总SummarizermultiIntentSummarizeNode:286-335处理多意图的汇总。逻辑分两层有 Summarizer 配置:303-318用 LLM 汇总各 specialist 的结果。把 specialist 结果拼成消息列表喂给 summarizer ChatModel产出最终回答。无 Summarizer 配置:319-333纯拼接——[weather]: 结果1\n[flight]: 结果2——不做 LLM 汇总。这个设计很实用不是所有场景都需要 LLM 汇总——简单场景下纯拼接就够了省一次 LLM 调用。5. 回调体系HandOff 的三层设计callback.go定义了三层回调MultiAgentCallback用户注册的回调接口OnHandOff(HandOffInfo)在每次 host 把任务交给 specialist 时触发ConvertCallbackHandlers把 MultiAgentCallback 转成 Eino 通用回调OnStart/OnEnd注入到 Graph 的节点回调中HandOffInfoToAgentNameArgument记录谁交给了谁、带着什么理由三层设计的好处用户只需关心 HandOff 事件不需要理解 Eino 内部的回调机制。二ADK Supervisor 模式transfer 机制supervisor/supervisor.go只有 121 行核心就两个动作1. 限制子 agent 只能回 supervisor// supervisor.go:101-108for_,subAgent:rangeconf.SubAgents{subAgentsappend(subAgents,adk.AgentWithDeterministicTransferTo(ctx,adk.DeterministicTransferConfig{Agent:subAgent,ToAgentNames:[]string{supervisorName},}))}AgentWithDeterministicTransferTo在每个子 agent 外面包一层限制它只能 transfer 到ToAgentNames列表中的 agent。这里的ToAgentNames只有 supervisor 的名字。效果子 agent 之间不能直接通信所有通信必须经过 supervisor。这是 supervisor 模式的核心约束——supervisor 是唯一的协调者。2. 统一追踪// supervisor.go:53-84typesupervisorContainerstruct{namestringinner adk.ResumableAgent}supervisorContainer把整个 supervisor 结构supervisor 所有子 agent包装成一个 agent。当 callback 注册时OnStart/OnEnd只触发一次创建单一 trace root。所有 agent 共享同一个 trace 上下文。3. 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.“经验证明这个方向不如另一个方向”。具体原因共享完整上下文transfer 时 supervisor 和子 agent 共享完整的消息历史上下文膨胀快缺乏隔离子 agent 能看到 supervisor 的所有历史包括不该它关心的信息替代方案更好AgentTool把 agent 当 tool 调独立 session和 DeepAgent内置 task tool 调度在经验上更有效三ADK Plan-Execute 模式三阶段循环plan_execute.go有 881 行是三个 prebuilt 中代码量最大的。核心是New函数:862-880funcNew(ctx context.Context,cfg*Config)(adk.ResumableAgent,error){loop,_:adk.NewLoopAgent(ctx,adk.LoopAgentConfig{Name:execute_replan,SubAgents:[]adk.Agent{cfg.Executor,cfg.Replanner},MaxIterations:maxIterations,})returnadk.NewSequentialAgent(ctx,adk.SequentialAgentConfig{Name:plan_execute_replan,SubAgents:[]adk.Agent{cfg.Planner,loop},})}结构是SequentialAgent(Planner, LoopAgent(Executor, Replanner))Planner生成计划 ↓ LoopAgent循环直到完成 ├─ Executor执行第一步 └─ Replanner决定继续 or 完成 ├─ 继续 → 更新计划 → 回到 Executor └─ 完成 → 退出状态传递Session Value四个 Session Key 在不同 agent 之间传递状态Key写入者读取者内容UserInputSessionKeyPlanner:329Executor、Replanner用户原始输入PlanSessionKeyPlanner:403、Replanner:762Executor、Replanner当前计划ExecutedStepSessionKeyExecutor通过 OutputKeyReplanner最新执行结果ExecutedStepsSessionKeyReplanner:671Executor、Replanner所有已执行步骤关键细节ExecutedStepsSessionKey的累积发生在 Replanner:667-671不是在 Executor。Replanner 把当前步骤的结果追加到历史列表然后决定下一步。工具调用PlanTool RespondToolReplanner 有两个工具:110-142PlanTool生成/更新计划参数steps[]步骤列表RespondTool生成最终响应参数response回复文本Replanner 的 prompt:192-238告诉 LLM 二选一要么调respond_tool结束要么调plan_tool更新计划继续。这是一个经典的工具驱动流程控制——LLM 通过选择不同的工具来决定流程走向。循环终止Replanner 调respond_tool时触发BreakLoopAction:748ifmsg.ToolCalls[0].Function.Namer.respondTool.Name{action:adk.NewBreakLoopAction(r.Name(ctx))generator.Send(adk.AgentEvent{Action:action})returnmsg,nil}BreakLoopAction告诉 LoopAgent “这个循环该停了”LoopAgent 收到后退出循环。四ADK Deep 模式内置工具 task tool 调度deep/deep.go只有 268 行但构造了一个完整的 agent 脚手架。核心是NewTyped:116-166funcNewTyped[M adk.MessageType](ctx context.Context,cfg*TypedConfig[M])(adk.TypedResumableAgent[M],error){// 1. 构建内置中间件handlers,_:buildTypedBuiltinAgentMiddlewares(ctx,cfg)// 2. 构建 task tool子 agent 调度tt,_:typedTaskToolMiddleware(ctx,...)handlersappend(handlers,tt)// 3. 创建 ChatModelAgentreturnadk.NewTypedChatModelAgent(ctx,adk.TypedChatModelAgentConfig[M]{...})}内置工具链Deep agent 启动时自动装配三个中间件buildTypedBuiltinAgentMiddlewares:209-232write_todos:244-267任务管理工具。参数todos[]每个 todo 含 content/activeForm/status状态三态pending → in_progress → completed。这是 Claude Code 的 TaskCreate 模式的复刻。filesystem 工具:219-229如果配置了 Backend/Shell/StreamingShell自动注册文件读写、glob、grep、shell 执行等工具。这些工具通过filesystem2.NewTyped中间件注入。task tooltask_tool.go:35-58子 agent 调度工具。把每个子 agent 包装为 AgentTool通过subagent_type参数路由。同时注入一个 prompt告诉主 agent 什么时候用 task tool。Task Tool把 Agent 当 Tool 调task_tool.go:61-124的typedNewTaskTool是 Deep agent 的核心functypedNewTaskTool[M](...)(tool.InvokableTool,error){t:typedTaskTool[M]{subAgents:map[string]tool.InvokableTool{},}// 1. 如果未禁用通用子 agent创建一个if!withoutGeneralSubAgent{generalAgent,_:adk.NewTypedChatModelAgent(ctx,...)t.subAgents[generalAgent.Name(ctx)]adk.NewTypedAgentTool(ctx,generalAgent)}// 2. 把用户提供的子 agent 也包装成 AgentToolfor_,a:rangesubAgents{t.subAgents[a.Name(ctx)]adk.NewTypedAgentTool(ctx,a)}returnt,nil}InvokableRun:156-175的路由逻辑很简单func(t*typedTaskTool[M])InvokableRun(ctx,argumentsInJSONstring,...)(string,error){input:taskToolArgument{}json.Unmarshal([]byte(argumentsInJSON),input)// 按 subagent_type 找对应的 AgentToola:t.subAgents[input.SubagentType]// 把 description 作为参数传给子 agentreturna.InvokableRun(ctx,marshal(map[string]string{request:input.Description}))}参数只有两个字段subagent_type选哪个子 agent和description子 agent 的任务描述。主 agent 调tasktool 时就像调度一个短生命周期的工作进程。通用子 agentgeneral-purposeagent:86-112是一个特殊设计它共享主 agent 的 instruction、tools、middlewares、handlers。这意味着通用子 agent 和主 agent 有相同的能力但有自己的独立 session。用途主 agent 把复杂子任务委托给这个分身自己聚焦在协调上。中英文双语 PromptDeep agent 的 prompt 是四种模式中最丰富的prompt.go有 685 行所有 prompt 都有中英文两套baseAgentInstruction/baseAgentInstructionChinese主 agent 的系统指令~110 行包含语气风格、安全策略、编码规范、工具使用策略taskPrompt/taskPromptChinesetask tool 的使用说明告诉主 agent 什么时候该用 task tooltaskToolDescription/taskToolDescriptionChinesetask tool 的描述模板变量{other_agents}填入可用子 agent 列表writeTodosToolDescription/writeTodosToolDescriptionChinesewrite_todos 工具的使用说明~180 行大量示例语言选择通过internal.SelectPrompt自动判断根据上下文语言选择对应版本。Deep Agent 的 prompt 来源Deep agent 的 prompt 设计借鉴了 LangChain 的 DeepAgents 项目和 Claude Codeprompt.go:21-23This file contains prompt templates and tool descriptions adapted from the DeepAgents project and ClaudeCode.这说明 Deep agent 不是凭空设计——它复刻了 Claude Code 在编码场景中验证过的 prompt 模式包括任务管理write_todos、子进程调度task tool、文件系统操作等。五三种 prebuilt 模式一张表维度SupervisorPlan-ExecuteDeep核心机制transfer 交接计划→执行→重规划循环task tool 子 agent 调度子 agent 通信只能和 supervisor通过 session state通过 task tool 参数上下文共享完整共享问题所在通过 session value 选择性传递task tool 启动独立 session流程控制supervisor LLM 决定Replanner 二选一工具主 agent LLM 决定调哪个 task内置工具无无write_todos filesystem task推荐程度NOT RECOMMENDED推荐推荐代码量121 行881 行268 行685 行 prompt小结问题答案关键源码Host MultiAgent 图状态怎么传state 结构体MessagesIsMultipleIntentsSpecialistResults通过 ProcessState 流转compose.go:30-47流式怎么判断 tool call只看第一个 chunkfirstChunkStreamToolCallChecker不等全量types.go:182-204多意图怎么汇总有 Summarizer→LLM 汇总无→纯拼接compose.go:286-335Supervisor 为什么 NOT RECOMMENDEDtransfer 共享完整上下文经验证明不如 AgentTool/DeepAgentsupervisor.go:42-44Plan-Execute 怎么循环SequentialAgent(Planner, LoopAgent(Executor, Replanner))Replanner 二选一工具plan_execute.go:862-880Deep agent 怎么调度子 agenttask tool 把子 agent 包装为 AgentTool通过 subagent_type 参数路由task_tool.go:61-175Deep agent 有哪些内置工具write_todos filesystem(r/w/glob/grep/shell) task tooldeep.go:209-232几条设计判断Transfer 共享上下文不如 AgentTool 独立 session。Supervisor 的 NOT RECOMMENDED 标注不是功能不好用而是经验证明这个方向不如另一个方向。AgentTool 给每个子 agent 独立 session 和 checkpoint隔离性更好。Plan-Execute 的本质是工具驱动流程控制。Replanner 不是通过代码分支决定继续 or 完成而是通过给 LLM 两个工具plan_tool / respond_tool让 LLM 自己选。这是一种把控制流交给模型的设计。Deep agent 是 Claude Code 的复刻。从 write_todos 的任务管理到 task tool 的子进程调度再到 filesystem 工具链Deep agent 把 Claude Code 在编码场景中验证过的模式搬到了 Eino ADK。prompt 就是代码。Deep agent 的 685 行 prompt 不是文档而是 agent 行为的核心逻辑。prompt 控制着主 agent 什么时候用 task tool、什么时候并行、什么时候写 todos——这些行为不是硬编码的而是软编码在 prompt 里。下一篇讲 Agent 间 Transfer 交接——用户在多个 Agent 间无缝切换以及 Eino ADK 的 transfer 机制源码。