两个 Agent 怎么协作:Host-Worker 模式(第91篇-E77)

作者:leeyi日期:2026/8/22

Part 16 开篇。前面 15 个 Part 讲的是一个 Agent 怎么做安全、怎么加记忆、怎么接知识库——但单个 Agent 的注意力是有限的。一个 Agent 面对"帮我查北京天气、订机票、比价酒店"这种多维度任务,会在上下文里来回跳,工具一多就乱。 这篇回答一个问题:两个 Agent 怎么协作——Host-Worker 模式,主 Agent 分派、子 Agent 执行,注意力该拆给谁。

两个问题串全篇:

  1. Eino 的 Host MultiAgent 怎么让 host LLM 把 specialist 当 tool 调?(分派机制)
  2. 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):

1agentTools := make([]*schema.ToolInfo, 0, len(config.Specialists))
2for i := range config.Specialists {
3    specialist := config.Specialists[i]
4    agentTools = append(agentTools, &schema.ToolInfo{
5        Name: specialist.Name,        //  tool 名就是 specialist 
6        Desc: specialist.IntendedUse, //  tool 描述就是 specialist 的用途
7        ParamsOneOf: schema.NewParamsOneOfByParams(map[string]*schema.ParameterInfo{
8            "reason": {Type: schema.String, Desc: "the reason to call this tool"},
9        }),
10    })
11}
12

NameIntendedUse 来自 AgentMetatypes.go:130-134):

1type AgentMeta struct {
2    Name        string //  multi-agent 系统内唯一
3    IntendedUse string // 用途声明,host LLM 据此决定调哪个
4}
5

这个设计很干净:IntendedUse 就是 specialist 的"简历"——host LLM 不需要知道 specialist 内部怎么实现,它只需要知道"这个 agent 能干什么"。和普通 tool 的区别是:普通 tool 是确定性函数(查天气 API),specialist 是完整的 Agent(有自己的 LLM + 工具链 + 推理循环)。

整个 Graph 的拓扑(compose.go:49-151,用节点名标注):

1START  Host(ChatModel)  Branch( tool_call?)
2                              ├─   END(直接回答)
3                              └─   converter  MultiBranch(哪个 specialist?)
4                                                    ├─ weather  collector  AfterBranch
5                                                    ├─ flight   collector  AfterBranch
6                                                              单意图  single_answer  END
7                                                              多意图  map2list  Summarizer  END
8                                                    └─ hotel    collector  AfterBranch
9

关键节点:

  • HostaddHostAgent, :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 验证分派:

1====== 场景 A:Host MultiAgent · 注册 tool 清单 ======
2  tool: weather    desc: 查询天气信息,包括温度、湿度、降水概率
3  tool: flight     desc: 查询航班信息,包括航班号、起降时间、延误情况
4  tool: hotel      desc: 查询酒店信息,包括价格、房型、评分
5
6====== 场景 B:单意图路由(只调天气)======
7  host 决策:  weather
8  specialist 结果: 北京今天晴,22°C,湿度 45%
9  单意图=true 多意图=false 直接回答=false
10
11====== 场景 C:多意图路由(同时调天气和航班)======
12  host 决策: 调了 2  specialist
13  [weather]: 北京今天晴,22°C,湿度 45%
14  [flight]: CA1234 北京→上海 08:00-10:30 准点
15  汇总结果: [weather]: 北京今天晴,22°C,湿度 45%
16[flight]: CA1234 北京→上海 08:00-10:30 准点
17  单意图=false 多意图=true 直接回答=false
18
19====== 场景 D:无匹配  host 直接回答 ======
20  host 决策: 无匹配 specialist
21  host 直接回答: host direct: 我无法处理这个请求
22  单意图=false 多意图=false 直接回答=true
23

三种路径:single intent(直接返回)/ multi intent(汇总)/ direct answer(host 自己回答)。实际 Eino 中 host LLM 的 tool call 决策比 demo 的关键词匹配复杂得多,但骨架上就是这三个分支。

HandOff 回调:观测"谁交给了谁"

callback.go 定义了 MultiAgentCallback.OnHandOff,每次 host 把任务交给 specialist 时触发:

1type HandOffInfo struct {
2    ToAgentName string // 交给了哪个 specialist
3    Argument    string // 带着什么理由
4}
5

这给观测层提供了一个干净的切面:不用翻 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 定义了六种步类型:

1type StepKind string
2const (
3    StepKindTask     StepKind = "task"      // 直连工具
4    StepKindApproval StepKind = "approval"  // HITL 审批
5    StepKindSubagent StepKind = "subagent"  //  Agent
6    StepKindSleep    StepKind = "sleep"     // 定时等待
7    StepKindMap      StepKind = "map"       // 状态映射
8    StepKindForeach  StepKind = "foreach"   // 迭代
9)
10

subagent 步的核心是 subagentLambdaeinoexec/subagent.go:64-161),流程四步:

  1. 读图状态:workflow 的图共享状态 → JSON——这是子 agent 的上下文
  2. Agent First 指令step.Input 有值 → 作为 agent 指令,状态 JSON 附后;为空 → 回落纯状态 JSON(向后兼容旧 WorkflowDef)
  3. 经 AgentInvoker 闭包调用:装配根注入的闭包,同步调用 agent BC 的 RunSubAgentHandler.RunSync
  4. 结果写回图状态:子 agent 输出 → state.Data[step.Ref]

demo 场景 E 验证了这个流程:

1====== 场景 E:DeepFlux Subagent  · 图状态传递 ======
2  step.Input: 根据用户查询,返回天气信息
3  step.Ref: weather_agent
4   agent 输出: [weather_agent 执行结果] 北京晴 22°C (收到上下文 142 字节)
5  图状态[weather_agent]: [weather_agent 执行结果] 北京晴 22°C (收…
6
7====== 场景 F:Agent First · step.Input 为空时回落纯状态 JSON ======
8  step.Input 为空,回落纯状态 JSON
9   agent 输出: [flight_agent 执行结果] 北京晴 22°C (收到上下文 178 字节)
10

场景 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 MultiAgentDeepFlux 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 用图状态 JSONsubagent.go:104-117
Agent First 是什么step.Input 有值→作 agent 指令+状态附后;空→回落纯状态 JSONsubagent.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 间无缝切换。


两个 Agent 怎么协作:Host-Worker 模式(第91篇-E77)》 是转载文章,点击查看原文


相关推荐


漫话火山 Milvus|为 Agent、RAG 、语义搜索而生,AI 时代的极致性价比之选
火山引擎Agent社区2026/8/8

托管锁定、自建难扩容、性能和成本难以兼顾? 火山 Milvus,开源兼容、可自由迁移;自研算法优化算力开销,支持 Serverless;联动火山大模型生态,适配多模态 RAG 与 Agent,可控、性能、性价比一站式解决。 火山 Milvus,基于开源 Milvus 构建的全托管数据库服务,提供高效的非结构化数据检索能力,适用于多样化 AI 场景,客户无需再关心底层硬件资源,降低使用成本,提高整体效率。


CSS 现代布局终极方案:Grid 完整实战,替代浮动/弹性布局复杂场景
前端人类学2026/7/30

Hi,我是前端人类学! 从“圣杯”到“卡片墙”,Grid 正在重新定义我们对 Web 布局的想象。 如果你还在用 float 清浮动、用 margin 推间距、用 flex 小心翼翼地嵌套多行网格,那今天这篇文章,值得你多看两眼。 CSS Grid 不是来“卷”谁的,它是来解决问题的——那些你以前觉得“凑合能看但维护起来想删库”的布局问题。 文章目录 一、Grid 凭什么说自己是“终极方案”?二、干掉浮动和复杂 Flex:两个核心实战模式2.1 响应式卡片网格:一行代码,无需媒


算力赋能零售与创意新生态:视程空间Pandora,解锁线下场景智能化无限可能
视***间2026/7/22

智慧零售与创意开发正成为驱动消费升级与产业创新的核心引擎。从线下门店的智能导购、客流分析,到沉浸式体验场景的交互设计、创意内容生成,传统模式已难以满足高效、个性化、安全合规的发展需求。视程空间 Pandora 系列边缘算力盒子,凭借 NVIDIA Jetson Orin™ NX/Nano Super 的强劲算力、极致集成设计与全开放生态,成为智慧零售场景落地与创意开发实践的绝佳解决方案,为企业提供安全、高效、可定制的边缘 AI 底座,让零售更智能、让创意更自由。 一、智慧零售:从流量运营到体


基于 OpenClaw 构建医疗健康系统:智能问诊与用药管理的全链路实战
七夜zippoe2026/7/14

摘要:本文基于 OpenClaw v2.4 与 Python 3.11,以"医疗健康系统"为场景,完整演示如何用对话式 AI 框架落地智能问诊、健康监测、用药管理与电子病历四大模块。文章先拆解 OpenClaw 的核心抽象与医疗场景诉求,再用 Mermaid 图与可运行 Python 代码逐模块实现,并给出运行验证、阈值配置与合规边界。面向中高级后端与全栈开发者,无需医学背景即可照着落地;重点讲清"领域建模 + 合规护栏"的工程取舍,而非堆砌大模型。读完可掌握一套可复用的行业应用构建范式。


计算机导论_第4章_笔记
薄荷椰果抹茶2026/7/6

内容提要 程序设计语言翻译系统操作系统工具软件 第1节 程序设计语言翻译系统 1.1 概述 计算机硬件只能识别并执行机器指令,但人们普遍习惯于使用高级程序设计语言或汇编语言来编写程序。为了让计算机能够理解高级程序设计语言或汇编语言并执行用它编写的程序,必须要为它配备一个"翻译",这就是程序设计语言翻译系统。 定义:程序设计语言翻译系统是一类系统软件,它能够将使用某一种源语言编写的程序翻译成为与其等价的使用另一种目标语言编写的程序。 源程序:使用源语言编写的程序目标程序:使用目标语言编写的程序


图解 MongoDB 18|复制集拓扑:Primary、Secondary 和 Arbiter 的分工
十三Tech2026/6/28

存储引擎阶段回答了「数据怎么存、怎么不丢」,但留下一个致命问题:单机宕机了怎么办。一台 mongod 进程挂掉,不管是硬件故障、进程崩溃还是网络隔离,所有读写都会中断。这在生产环境是不可接受的。 MongoDB 解决单点故障的方式是复制集(Replica Set)——把数据复制到多个节点,一台挂了自动切换到另一台。这一阶段(18–23)就围绕复制集展开,从拓扑结构、复制原理、延迟、选举到读写关注,把 MongoDB 的高可用讲透。 先把机制边界说清楚 复制集是一组维护相同数据集的 mongod


百度 C++/PHP 研发一二面:一面扫八股和算法,二面开始逼近 Redis、MySQL 和秒杀设计
TechPioneer_lp2026/6/19

这篇百度 C++/PHP 研发面经很适合作为“后端基础到系统设计过渡”的样本来看。 它的一面还比较像常规校招: 算法 OS 计网 HTTP Redis 静态 / 动态链接 C++ 基础 但二面明显开始往: 智能指针 TCP 窗口和拥塞 Redis 各种底层结构 MySQL 索引、隔离级别、锁 秒杀系统和高并发设计 去推进了。 校招大礼包获取:入口 可能是至今最全,最好,最实用的校招大礼包,减少信息差,预期漫步无敌的刷提,不如有的放矢,针对性的准备,这


从单机到分布式:用 Go + Eino + DeepSeek V4 构建生产级 Code Review Agent
银河技术2026/6/11

从单机到分布式:用 Go + Eino + DeepSeek V4 构建生产级 Code Review Agent 不是把大模型接到 GitHub Webhook 上,就叫生产级 Code Review Agent。真正决定系统上限的,是任务编排、规则前置、上下文治理、并发隔离与可观测性。 引言:为什么团队越来越需要“生产级” Code Review Agent 在小团队里,Code Review 通常是一个“人盯人”的流程:开发者提 PR,Reviewer 看 diff,提几点


Webpack如何实现万物皆可import?loader的使用/配置/手写实践
漂流瓶jz2026/6/4

Webpack是前端历史上具有统治地位的打包工具,应用非常广泛。虽然现在逐渐被性能更强的工具替代,但是依然有很多工程使用。loader是Webpack中的一种重要的外部插入配置工具,负责对源代码进行转换。Webpack本身只能理解JavaScript和JSON文件,其它类型的文件不能处理。正是使用各种loader,Webpack才有了将各种格式的资源和代码识别和引入的能力。当然,loader的能力也并不仅限于此。 loader使用示例 为了了解loader的作用和使用方式,我们举例一些现有的知名


Android 离线优先架构实践:网络只是本地数据库的同步触发器
潜龙勿用之化骨龙2026/5/29

本文基于对 Learn-Kotlin-Coroutines 的工程化重构,记录从「请求式架构」走向「响应式单一数据源(SSOT)」的完整思路与实现方案。 引言:被网络绑架的 UI 过去很多 Android 项目的数据流都是这样的: 请求网络 → 拿到数据 → 更新 UI 这种模式在网络稳定时看起来没有问题。但一旦网络变慢、接口超时、页面频繁切换,问题就会迅速暴露: 页面白屏等待 数据状态不一致 本地缓存形同虚设 根本原因在于:UI 的命运被网络状态决定了。 现代 Android 架构正

首页编辑器站点地图

本站内容在 CC BY-SA 4.0 协议下发布

Copyright © 2026 聚合阅读