两个 Agent 如何协作:Host-Worker 模式(第91篇-E77)
英文标题:两个 Agent 怎么协作:Host-Worker 模式(第91篇-E77)
本文对比了两种多 Agent 协作方式:Eino 的 Host MultiAgent 将 specialist Agent 注册为 tool,由 host LLM 通过 tool call 决定分派;DeepFlux 则将 subagent 作为 workflow 中可编排的步骤执行。Eino 支持单意图直接返回、多意图汇总以及 host 直接回答三条路径,并通过 HandOff 回调观测任务交接。DeepFlux 的 subagent 会接收图状态 JSON、调用外部 Agent,并将结果回写 workflow 状态,还支持在子 Agent 工具调用阶段进行 HITL 审批中断。对开发者而言,对话式任务更适合 Host MultiAgent,DAG 工作流编排更适合 DeepFlux subagent,而通用的 Agent 封装调用可使用 AgentTool。
5.4 KB
中文
Part 16 开篇。前面 15 个 Part 讲的是一个 Agent 怎么做安全、怎么加记忆、怎么接知识库——但单个 Agent 的注意力是有限的。一个 Agent 面对“帮我查北京天气、订机票、比价酒店”这种多维度任务,会在上下文里来回跳,工具一多就乱。这篇回答一个问题:两个 Agent 怎么协作——Host-Worker 模式,主 Agent 分派、子 Agent 执行,注意力该拆给谁。
两个问题串全篇:
- Eino 的 Host MultiAgent 怎么让 host LLM 把 specialist 当 tool 调?(分派机制)
- DeepFlux 的 subagent 步怎么把子 Agent 嵌入 workflow?(嵌入式调用)
(一)Eino Host MultiAgent:把 Agent 当 Tool 调
eino/flow/agent/multiagent/host/compose.go 核心只有 335 行。一句话概括:host LLM 把每个 specialist 看成一个 tool,tool 名就是 specialist 名,tool 描述就是 specialist 的职责声明。
先看怎么注册(compose.go:84-105):
agentTools := make([]*schema.ToolInfo, 0, len(config.Specialists))
for i := range config.Specialists {
specialist := config.Specialists[i]
agentTools = append(agentTools, &schema.ToolInfo{
Name: specialist.Name,
Desc: specialist.IntendedUse,
ParamsOneOf: schema.NewParamsOneOfByParams(map[string]*schema.ParameterInfo{
"reason": {Type: schema.String, Desc: "the reason to call this tool"},
}),
})
}
Name 和 IntendedUse 来自 AgentMeta(types.go:130-134):
type AgentMeta struct {
Name string
IntendedUse string
}
这个设计很干净:IntendedUse 就是 specialist 的“简历”——host LLM 不需要知道 specialist 内部怎么实现,它只需要知道“这个 agent 能干什么”。和普通 tool 的区别是:普通 tool 是确定性函数(如查询天气 API),specialist 则是完整的 Agent(拥有自己的 LLM、工具链和推理循环)。
整个 Graph 的拓扑(compose.go:49-151,用节点名标注):
START → Host(ChatModel) → Branch(有 tool_call?)
├─ 否 → END(直接回答)
└─ 是 → converter → MultiBranch(哪个 specialist?)
├─ weather → collector → AfterBranch
├─ flight → collector → AfterBranch
│ 单意图 → single_answer → END
│ 多意图 → map2list → Summarizer → END
└─ hotel → collector → AfterBranch
关键节点:
- Host(
addHostAgent, :187-203):就是带 tool list 的 ChatModel,system prompt 默认是"decide which tool is best for the task and call only the best tool." - directAnswerBranch(:205-220):检查 host 输出中有没有 tool call;没有则直接进入 END——由 host 自己回答。
- multiSpecialistsBranch(:222-244):读取 tool call 的 function name,匹配到对应的 specialist 节点;如果同时出现多个 tool call,则标记
isMultipleIntents=true。 - specialist 节点(
addSpecialistAgent, :154-185):每个 specialist 可以是一个 ChatModel,也可以是任意 Invokable/Streamable,例如react.Agent。 - multiIntentSummarizeNode(:286-335):多意图时汇总各 specialist 结果。如果配置了 Summarizer 则使用 LLM 汇总,否则直接拼接。
Demo(/tmp/e91demo,使用纯标准库复刻核心路由逻辑)通过三组 specialist 验证分派:
====== 场景 A:Host MultiAgent · 注册 tool 清单 ======
tool: weather desc: 查询天气信息,包括温度、湿度、降水概率
tool: flight desc: 查询航班信息,包括航班号、起降时间、延误情况
tool: hotel desc: 查询酒店信息,包括价格、房型、评分
====== 场景 B:单意图路由(只调天气)======
host 决策: 调 weather
specialist 结果: 北京今天晴,22°C,湿度 45%
单意图=true 多意图=false 直接回答=false
====== 场景 C:多意图路由(同时调天气和航班)======
host 决策: 调了 2 个 specialist
[weather]: 北京今天晴,22°C,湿度 45%
[flight]: CA1234 北京→上海 08:00-10:30 准点
汇总结果: [weather]: 北京今天晴,22°C,湿度 45%
[flight]: CA1234 北京→上海 08:00-10:30 准点
单意图=false 多意图=true 直接回答=false
====== 场景 D:无匹配 → host 直接回答 ======
host 决策: 无匹配 specialist
host 直接回答: host direct: 我无法处理这个请求
单意图=false 多意图=false 直接回答=true
三种路径分别是:single intent(直接返回)、multi intent(汇总)和 direct answer(host 自己回答)。实际 Eino 中,host LLM 的 tool call 决策比 Demo 的关键词匹配复杂得多,但从骨架上看就是这三个分支。
HandOff 回调:观测“谁交给了谁”
callback.go 定义了 MultiAgentCallback.OnHandOff,每次 host 将任务交给 specialist 时都会触发:
type HandOffInfo struct {
ToAgentName string
Argument string
}
这给观测层提供了一个干净的切面:不需要翻查 tool call 日志,直接查看 handoff 事件,就能知道“host 把什么任务交给了谁”。
(二)DeepFlux Subagent:把 Agent 嵌入 Workflow
Eino Host MultiAgent 是“Agent 之间的对话”——host 和 specialist 都是独立 Agent,通过 tool call 通信。DeepFlux 则走另一条路:subagent 是 workflow 中的一个步骤。
orchestration/domain/model/workflow.go:26-35 定义了六种步骤类型:
type StepKind string
const (
StepKindTask StepKind = "task"
StepKindApproval StepKind = "approval"
StepKindSubagent StepKind = "subagent"
StepKindSleep StepKind = "sleep"
StepKindMap StepKind = "map"
StepKindForeach StepKind = "foreach"
)
subagent 步的核心是 subagentLambda(einoexec/subagent.go:64-161),流程分为四步:
- 读取图状态:workflow 的图共享状态 → JSON,这就是子 Agent 的上下文。
- Agent First 指令:
step.Input有值时,将其作为 agent 指令并附加状态 JSON;为空时,则回落到纯状态 JSON,以兼容旧版WorkflowDef。 - 通过 AgentInvoker 闭包调用:装配根注入的闭包,同步调用 agent BC 的
RunSubAgentHandler.RunSync。 - 将结果写回图状态:子 Agent 输出 →
state.Data[step.Ref]。
Demo 的场景 E 验证了这个流程:
====== 场景 E:DeepFlux Subagent 步 · 图状态传递 ======
step.Input: 根据用户查询,返回天气信息
step.Ref: weather_agent
子 agent 输出: [weather_agent 执行结果] 北京晴 22°C (收到上下文 142 字节)
图状态[weather_agent]: [weather_agent 执行结果] 北京晴 22°C (收…
====== 场景 F:Agent First · step.Input 为空时回落纯状态 JSON ======
step.Input 为空,回落纯状态 JSON
子 agent 输出: [flight_agent 执行结果] 北京晴 22°C (收到上下文 178 字节)
场景 E 和 F 的区别在于:E 有 step.Input,子 Agent 收到的是 "根据用户查询,返回天气信息\n\n[上游状态]\n{...}";F 没有 step.Input,子 Agent 收到的是纯状态 JSON。输入大小差异(142 与 178 字节)反映了这一差异。
HITL 审批门:子 Agent 也能被叫停
subagent 步并不是简单地“调用完成即结束”。子 Agent 内部的 pre-tool hook 可以请求中断(subagent.go:129-144),触发 ErrSubagentInterrupt → Eino 中断 → Run 进入 SUSPENDED 状态。resume 时:
- 批准 → per-tool 定向放行(V5):只放行已批准的那一次工具调用,已执行的调用不会重跑。
- 拒绝 → 写入
denied标签供下游 Branch 分流,run 不会被终止。
这个审批门与 approval 步复用同一套 suspend/resume 机制,但语义不同:approval 步是“整个步骤等待人工审批”,而 subagent 审批门是“子 Agent 的某一次工具调用等待人工审批”。
(三)两种模式,一张表
| 维度 | Eino Host MultiAgent | DeepFlux Subagent |
|---|---|---|
| 通信方式 | host LLM 产出 tool call → specialist 节点 | 图状态 JSON → AgentInvoker 闭包 |
| specialist 是什么 | 图的节点(ChatModel 或 Agent) | 外部 agent config(独立 session) |
| host 的角色 | 也是 LLM,自己做决策 | workflow 引擎(编译器 + 图执行) |
| 上下文传递 | 消息列表(messages) | 图状态(map[string]string)→ JSON |
| 多 specialist | 并行调用 + summarizer 汇总 | 顺序步骤(DAG 编排) |
| 中断/审批 | 无内置 HITL | 子 agent 内 pre-tool hook 可中断 |
| 适用场景 | 对话式多 Agent 协作 | 工作流式多 Agent 编排 |
Host MultiAgent 适合“对话式”场景:用户说一句话,host 判断该找谁,找完之后返回结果。host 和 specialist 是平等的 Agent,只是职责不同。
DeepFlux subagent 适合“工作流式”场景:多个步骤按照 DAG 执行,subagent 只是其中一步;前面可能有 task 步进行数据预处理,后面可能有 approval 步等待人工审批。
(四)Eino ADK 的另外两种多 Agent 模式
除了 Host MultiAgent,Eino ADK 还提供两种多 Agent 模式,但文档均标注为 NOT RECOMMENDED。
Supervisor(adk/prebuilt/supervisor/supervisor.go)
该模式建立在 agent transfer 之上,supervisor 与 sub-agents 共享完整上下文。sub-agents 只能与 supervisor 通信,不能彼此直接通信。源码注释直言:
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.
AgentTool(adk/agent_tool.go)
该模式将任意 Agent 包装为一个 tool(NewAgentTool),使另一个 Agent 可以直接调用它。这是 ADK 推荐的替代方案:不使用 transfer 共享上下文,而是像调用普通 tool 一样调用子 Agent——子 Agent 拥有独立的 session 和 checkpoint。
AgentTool 的设计与 Host MultiAgent 中的 specialist 本质上是同一思路:Agent 就是 Tool。区别在于,AgentTool 更底层,可以作为任意 Agent 的 tool;Host MultiAgent 则是更高层的封装,自带 Graph 编排。
小结
| 问题 | 答案 | 关键源码 |
|---|---|---|
| Host 怎么分派 | specialist 注册为 tool(Name + IntendedUse → ToolInfo),host LLM 产出 tool call 匹配 | host/compose.go:84-105 |
| specialist 怎么执行 | 作为 Graph 节点,前置 handler 将消息注入 state,结果经 collector 汇聚 | host/compose.go:154-185 |
| 单意图 vs 多意图 | 单意图直接返回,多意图标记后进入 summarizer 汇总 | host/compose.go:222-335 |
| DeepFlux subagent 怎么嵌入 | 作为 workflow 步类型,图状态 JSON → AgentInvoker 闭包 → 结果写回 | einoexec/subagent.go:64-161 |
| 上下文怎么传 | Host MultiAgent 使用消息列表,DeepFlux 使用图状态 JSON | subagent.go:104-117 |
| Agent First 是什么 | step.Input 有值 → 作为 agent 指令并附加状态;为空 → 回落纯状态 JSON |
subagent.go:112-117 |
| 什么时候用哪个 | 对话式 → Host MultiAgent;工作流式 → DeepFlux subagent;通用 → AgentTool | 三种模式对比 |
几条设计判断:
- **Agent 就是 Tool。**Host MultiAgent 和 AgentTool 都在做同一件事:将 Agent 包装成可调用工具。区别在于,Host MultiAgent 自带 Graph 编排,AgentTool 则是留给调用方自行组装的裸 tool。
- **IntendedUse 是 specialist 的“简历”。**host LLM 不需要知道 specialist 内部有什么工具、什么 prompt,只需要知道“这个 agent 能干什么”。这与人类团队分工相同——不需要知道同事如何完成工作,只需要知道应将任务交给谁。
- **上下文传递方式决定耦合度。**消息列表(Host MultiAgent)是松耦合:host 和 specialist 各自维护自己的上下文。图状态 JSON(DeepFlux)则是紧耦合:subagent 能获得 workflow 的完整状态,但状态结构变化也会影响所有步骤。
- **ADK 文档的诚实标注值得注意。**Supervisor 和 DeterministicTransfer 都标注了 NOT RECOMMENDED,并提供替代方案(AgentTool/DeepAgent)。这并非“功能不好用”,而是“经验证明这个方向不如另一种方案”——工程文档中很少见到这种坦诚表达。
下一篇(E92)将拆解 MultiAgent Host 源码,以及 ADK prebuilt 的 supervisor、planexecute、deep 三种预制模式——从“怎么用”深入到“内部怎么实现”。E93 将讲解 Agent 间的 Transfer 交接,即用户如何在不同 Agent 之间无缝切换。
English / 原文
Part 16 开篇。前面 15 个 Part 讲的是一个 Agent 怎么做安全、怎么加记忆、怎么接知识库——但单个 Agent 的注意力是有限的。一个 Agent 面对"帮我查北京天气、订机票、比价酒店"这种多维度任务,会在上下文里来回跳,工具一多就乱。 这篇回答一个问题:两个 Agent 怎么协作 ——Host-Worker 模式,主 Agent 分派、子 Agent 执行,注意力该拆给谁。
两个问题串全篇:
- Eino 的 Host MultiAgent 怎么让 host LLM 把 specialist 当 tool 调?(分派机制)
- DeepFlux 的 subagent 步怎么把子 Agent 嵌入 workflow?(嵌入式调用)
(一)Eino Host MultiAgent:把 Agent 当 Tool 调
eino/flow/agent/multiagent/host/compose.go 核心只有 335 行。一句话概括:host LLM 把每个 specialist 看成一个 tool,tool 名就是 specialist 名,tool 描述就是 specialist 的职责声明 。
先看怎么注册(compose.go:84-105):
agentTools := make([]*schema.ToolInfo, 0, len(config.Specialists))
for i := range config.Specialists {
specialist := config.Specialists[i]
agentTools = append(agentTools, &schema.ToolInfo{
Name: specialist.Name,
Desc: specialist.IntendedUse,
ParamsOneOf: schema.NewParamsOneOfByParams(map[string]*schema.ParameterInfo{
"reason": {Type: schema.String, Desc: "the reason to call this tool"},
}),
})
}
Name 和 IntendedUse 来自 AgentMeta(types.go:130-134):
type AgentMeta struct {
Name string
IntendedUse string
}
这个设计很干净:IntendedUse 就是 specialist 的"简历" ——host LLM 不需要知道 specialist 内部怎么实现,它只需要知道"这个 agent 能干什么"。和普通 tool 的区别是:普通 tool 是确定性函数(查天气 API),specialist 是完整的 Agent(有自己的 LLM + 工具链 + 推理循环)。
整个 Graph 的拓扑(compose.go:49-151,用节点名标注):
START → Host(ChatModel) → Branch(有 tool_call?)
├─ 否 → END(直接回答)
└─ 是 → converter → MultiBranch(哪个 specialist?)
├─ weather → collector → AfterBranch
├─ flight → collector → AfterBranch
│ 单意图 → single_answer → END
│ 多意图 → map2list → Summarizer → END
└─ hotel → collector → AfterBranch
关键节点:
- Host (
addHostAgent, :187-203):就是带 tool list 的 ChatModel,system prompt 默认"decide which tool is best for the task and call only the best tool." - directAnswerBranch (:205-220):检查 host 输出有没有 tool call,没有就直接 END——host 自己回答了
- multiSpecialistsBranch (:222-244):读 tool call 的 function name,匹配到对应的 specialist 节点;如果多个 tool call 同时出现,标记
isMultipleIntents=true - specialist 节点 (
addSpecialistAgent, :154-185):每个 specialist 可以是一个 ChatModel,也可以是任意 Invokable/Streamable(如react.Agent) - multiIntentSummarizeNode (:286-335):多意图时把各 specialist 结果汇总。有 Summarizer 配置就用 LLM 汇总,否则纯拼接
demo(/tmp/e91demo,纯标准库复刻核心路由逻辑)用三组 specialist 验证分派:
====== 场景 A:Host MultiAgent · 注册 tool 清单 ======
tool: weather desc: 查询天气信息,包括温度、湿度、降水概率
tool: flight desc: 查询航班信息,包括航班号、起降时间、延误情况
tool: hotel desc: 查询酒店信息,包括价格、房型、评分
====== 场景 B:单意图路由(只调天气)======
host 决策: 调 weather
specialist 结果: 北京今天晴,22°C,湿度 45%
单意图=true 多意图=false 直接回答=false
====== 场景 C:多意图路由(同时调天气和航班)======
host 决策: 调了 2 个 specialist
[weather]: 北京今天晴,22°C,湿度 45%
[flight]: CA1234 北京→上海 08:00-10:30 准点
汇总结果: [weather]: 北京今天晴,22°C,湿度 45%
[flight]: CA1234 北京→上海 08:00-10:30 准点
单意图=false 多意图=true 直接回答=false
====== 场景 D:无匹配 → host 直接回答 ======
host 决策: 无匹配 specialist
host 直接回答: host direct: 我无法处理这个请求
单意图=false 多意图=false 直接回答=true
三种路径:single intent(直接返回)/ multi intent(汇总)/ direct answer(host 自己回答)。实际 Eino 中 host LLM 的 tool call 决策比 demo 的关键词匹配复杂得多,但骨架上就是这三个分支。
HandOff 回调:观测"谁交给了谁"
callback.go 定义了 MultiAgentCallback.OnHandOff,每次 host 把任务交给 specialist 时触发:
type HandOffInfo struct {
ToAgentName string
Argument string
}
这给观测层提供了一个干净的切面:不用翻 tool call 日志,直接看 handoff 事件就知道"host 把什么任务交给了谁"。
(二)DeepFlux Subagent:把 Agent 嵌入 Workflow
Eino Host MultiAgent 是"Agent 之间的对话"——host 和 specialist 都是独立 Agent,通过 tool call 通信。DeepFlux 走的是另一条路:subagent 是 workflow 的一个步 。
orchestration/domain/model/workflow.go:26-35 定义了六种步类型:
type StepKind string
const (
StepKindTask StepKind = "task"
StepKindApproval StepKind = "approval"
StepKindSubagent StepKind = "subagent"
StepKindSleep StepKind = "sleep"
StepKindMap StepKind = "map"
StepKindForeach StepKind = "foreach"
)
subagent 步的核心是 subagentLambda(einoexec/subagent.go:64-161),流程四步:
- 读图状态 :workflow 的图共享状态 → JSON——这是子 agent 的上下文
- Agent First 指令 :
step.Input有值 → 作为 agent 指令,状态 JSON 附后;为空 → 回落纯状态 JSON(向后兼容旧 WorkflowDef) - 经 AgentInvoker 闭包调用 :装配根注入的闭包,同步调用 agent BC 的
RunSubAgentHandler.RunSync - 结果写回图状态 :子 agent 输出 →
state.Data[step.Ref]
demo 场景 E 验证了这个流程:
====== 场景 E:DeepFlux Subagent 步 · 图状态传递 ======
step.Input: 根据用户查询,返回天气信息
step.Ref: weather_agent
子 agent 输出: [weather_agent 执行结果] 北京晴 22°C (收到上下文 142 字节)
图状态[weather_agent]: [weather_agent 执行结果] 北京晴 22°C (收…
====== 场景 F:Agent First · step.Input 为空时回落纯状态 JSON ======
step.Input 为空,回落纯状态 JSON
子 agent 输出: [flight_agent 执行结果] 北京晴 22°C (收到上下文 178 字节)
场景 E 和 F 的区别:E 有 step.Input,子 agent 收到的是 "根据用户查询,返回天气信息\n\n[上游状态]\n{...}";F 没有 step.Input,子 agent 收到的是纯状态 JSON。输入大小差异(142 vs 178 字节)反映了这个差异。
HITL 审批门:子 Agent 也能被叫停
subagent 步不是简单的"调完就完"。子 agent 内的 pre-tool hook 可以请求中断(subagent.go:129-144),触发 ErrSubagentInterrupt → Eino 中断 → Run→SUSPENDED。resume 时:
- 批准 → per-tool 定向放行(V5):只放行被批准的那次工具调用,已执行的不重跑
- 拒绝 → 写
denied标签供下游 Branch 分流,run 不杀
这个审批门和 approval 步用的是同一套 suspend/resume 机制,但语义不同:approval 步是"整个步等人批",subagent 审批门是"子 agent 的某个工具调用等人批"。
(三)两种模式,一张表
| 维度 | Eino Host MultiAgent | DeepFlux Subagent |
|---|---|---|
| 通信方式 | host LLM 产出 tool call → specialist 节点 | 图状态 JSON → AgentInvoker 闭包 |
| specialist 是什么 | 图的节点(ChatModel 或 Agent) | 外部 agent config(独立 session) |
| host 的角色 | 也是 LLM,自己做决策 | workflow 引擎(编译器 + 图执行) |
| 上下文传递 | 消息列表(messages) | 图状态(map[string]string)→ JSON |
| 多 specialist | 并行调用 + summarizer 汇总 | 顺序步(DAG 编排) |
| 中断/审批 | 无内置 HITL | 子 agent 内 pre-tool hook 可中断 |
| 适用场景 | 对话式多 Agent 协作 | 工作流式多 Agent 编排 |
Host MultiAgent 适合"对话式"场景 :用户一句话,host 判断该找谁,找完回来。host 和 specialist 是平等的 Agent,只是职责不同。
DeepFlux subagent 适合"工作流式"场景 :多个步骤按 DAG 执行,subagent 只是其中一步,前面可能有 task 步做数据预处理,后面可能有 approval 步等人审批。
(四)Eino ADK 的另外两种多 Agent 模式
除了 Host MultiAgent,Eino ADK 还提供了两种多 Agent 模式,但文档都标注了 NOT RECOMMENDED :
Supervisor(adk/prebuilt/supervisor/supervisor.go)
建立在 agent transfer 之上,supervisor 和 sub-agents 共享完整上下文。sub-agents 只能和 supervisor 通信(不能互相直接通信)。源码注释直言:
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.
AgentTool(adk/agent_tool.go)
把任意 Agent 包装成一个 tool(NewAgentTool),让另一个 Agent 可以直接调它。这是 ADK 推荐的替代方案:不用 transfer 共享上下文,而是像普通 tool 一样调用——子 Agent 有独立的 session 和 checkpoint。
AgentTool 的设计和 Host MultiAgent 的 specialist 本质上是一个思路:Agent 就是 Tool 。区别在于 AgentTool 更底层(可以作为任意 Agent 的 tool),Host MultiAgent 是更高层的封装(自带 Graph 编排)。
小结
| 问题 | 答案 | 关键源码 |
|---|---|---|
| Host 怎么分派 | specialist 注册为 tool(Name+IntendedUse→ToolInfo),host LLM 产出 tool call 匹配 | host/compose.go:84-105 |
| specialist 怎么执行 | 作为 Graph 节点,前置 handler 把消息注入 state,结果走 collector 汇聚 | host/compose.go:154-185 |
| 单意图 vs 多意图 | 单意图直接返回,多意图标记后走 summarizer 汇总 | host/compose.go:222-335 |
| DeepFlux subagent 怎么嵌入 | workflow 步类型,图状态 JSON→AgentInvoker 闭包→结果写回 | einoexec/subagent.go:64-161 |
| 上下文怎么传 | Host MultiAgent 用消息列表,DeepFlux 用图状态 JSON | subagent.go:104-117 |
| Agent First 是什么 | step.Input 有值→作 agent 指令+状态附后;空→回落纯状态 JSON | subagent.go:112-117 |
| 什么时候用哪个 | 对话式→Host MultiAgent;工作流式→DeepFlux subagent;通用→AgentTool | 三种模式对比 |
几条设计判断:
- Agent 就是 Tool。 Host MultiAgent 和 AgentTool 都在做同一件事:把 Agent 包装成可调用的工具。区别在于 Host MultiAgent 自带 Graph 编排,AgentTool 是裸 tool 留给调用方组装。
- IntendedUse 是 specialist 的"简历"。 host LLM 不需要知道 specialist 内部有什么工具、什么 prompt,它只需要知道"这个 agent 能干什么"。这和人类团队分工一样——你不需要知道同事怎么做,只需要知道该把任务交给谁。
- 上下文传递方式决定耦合度。 消息列表(Host MultiAgent)是松耦合——host 和 specialist 各自维护自己的上下文。图状态 JSON(DeepFlux)是紧耦合——subagent 能拿到 workflow 的完整状态,但也意味着状态结构变化会影响所有步。
- ADK 文档的诚实标注值得注意。 Supervisor 和 DeterministicTransfer 都标注了 NOT RECOMMENDED,并给出了替代方案(AgentTool/DeepAgent)。这不是"功能不好用",而是"经验证明这个方向不如另一个方向"——工程文档里很少见到这种诚实。
下一篇(E92)拆 MultiAgent Host 源码 + ADK prebuilt 的 supervisor/planexecute/deep 三种预制模式——从"怎么用"到"里面怎么实现的"。E93 讲 Agent 间的 Transfer 交接——用户在不同 Agent 间无缝切换。