MultiAgent Host 源码 + ADK prebuilt 三种预制模式(第92篇-E78)

上一篇 讲了两个 Agent 怎么协作——Host MultiAgent 和 DeepFlux Subagent 两种模式,以及"Agent 就是 Tool"这个核心设计。那篇侧重"怎么用",这篇拆"里面怎么实现"。

三个问题串全篇:

  1. Host MultiAgent 的 Graph 内部怎么跑?(状态、流式、多意图汇总)
  2. ADK 三种预制模式(supervisor/planexecute/deep)各自怎么实现?
  3. 为什么 supervisor 标注 NOT RECOMMENDED,而 deep 是推荐方案?

(一)Host MultiAgent 源码拆解

上一篇 讲过 Host MultiAgent 的 Graph 拓扑和 specialist 注册。这里深入源码内部,看几个关键机制。

1. 输入的注入:map2list 把消息喂给 host

compose.go:49-83 的入口适配器把 Eino 的图状态(map[string]any)转成 LLM 能理解的消息列表。所有 specialist 的输出也通过同样的方式回流到消息列表。

2. 状态管理:state 结构体

compose.go:30-47 定义了图的内部状态:

type state struct {
    Messages          []*schema.Message
    IsMultipleIntents bool
    SpecialistResults map[string]string
}
  • Messages:当前消息列表,host LLM 和 specialist 都往里写
  • IsMultipleIntents:多意图标记,host 一次产出多个 tool call 时为 true
  • SpecialistResults:各 specialist 的原始结果,供 summarizer 汇总

状态通过 ProcessState 在节点间流转。每个节点(host/specialist/summarizer)都能读写这个状态。

3. 流式处理:firstChunkStreamToolCallChecker

types.go:182-204 定义了一个重要的辅助函数:

type firstChunkStreamToolCallChecker struct {
    done    bool
    hasCall bool
}

func (c *firstChunkStreamToolCallChecker) Check(chunk *schema.Message) bool {
    if c.done {
        return c.hasCall
    }
    c.done = true
    c.hasCall = len(chunk.ToolCalls) > 0
    return c.hasCall
}

只检查第一个 chunk 是否有 tool call。为什么只看第一个?因为流式场景下,host LLM 要么在第一个 chunk 就决定调 tool,要么就是不调。不需要等全部 chunk 到了再判断——这是性能优化,也避免了"等全部流式结果再决定"的延迟。

multiSpecialistsBranch:222-244)用这个 checker 来分流:

if checker.Check(msg) {
    // 有 tool call → 走 specialist 分支
} else {
    // 无 tool call → 直接回答
}

4. 多意图汇总:Summarizer

multiIntentSummarizeNode: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 定义了三层回调:

  1. MultiAgentCallback:用户注册的回调接口,OnHandOff(HandOffInfo) 在每次 host 把任务交给 specialist 时触发
  2. ConvertCallbackHandlers:把 MultiAgentCallback 转成 Eino 通用回调(OnStart/OnEnd),注入到 Graph 的节点回调中
  3. HandOffInfoToAgentName + Argument,记录"谁交给了谁、带着什么理由"

三层设计的好处:用户只需关心 HandOff 事件,不需要理解 Eino 内部的回调机制。

(二)ADK Supervisor 模式:transfer 机制

supervisor/supervisor.go 只有 121 行,核心就两个动作:

1. 限制子 agent 只能回 supervisor

// supervisor.go:101-108
for _, subAgent := range conf.SubAgents {
    subAgents = append(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-84
type supervisorContainer struct {
    name  string
    inner 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-880):

func New(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,
    })
    return adk.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(通过 OutputKey)Replanner最新执行结果
ExecutedStepsSessionKeyReplanner(:671Executor、Replanner所有已执行步骤

关键细节:ExecutedStepsSessionKey 的累积发生在 Replanner(:667-671),不是在 Executor。Replanner 把当前步骤的结果追加到历史列表,然后决定下一步。

工具调用:PlanTool + RespondTool

Replanner 有两个工具(:110-142):

  • PlanTool:生成/更新计划,参数 steps[](步骤列表)
  • RespondTool:生成最终响应,参数 response(回复文本)

Replanner 的 prompt(:192-238)告诉 LLM 二选一:要么调 respond_tool 结束,要么调 plan_tool 更新计划继续。这是一个经典的"工具驱动流程控制"——LLM 通过选择不同的工具来决定流程走向。

循环终止

Replanner 调 respond_tool 时触发 BreakLoopAction:748):

if msg.ToolCalls[0].Function.Name == r.respondTool.Name {
    action := adk.NewBreakLoopAction(r.Name(ctx))
    generator.Send(&adk.AgentEvent{Action: action})
    return msg, nil
}

BreakLoopAction 告诉 LoopAgent “这个循环该停了”,LoopAgent 收到后退出循环。

(四)ADK Deep 模式:内置工具 + task tool 调度

deep/deep.go 只有 268 行,但构造了一个完整的 agent 脚手架。核心是 NewTyped:116-166):

func NewTyped[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, ...)
    handlers = append(handlers, tt)
    
    // 3. 创建 ChatModelAgent
    return adk.NewTypedChatModelAgent(ctx, &adk.TypedChatModelAgentConfig[M]{...})
}

内置工具链

Deep agent 启动时自动装配三个中间件(buildTypedBuiltinAgentMiddlewares:209-232):

  1. write_todos:244-267):任务管理工具。参数 todos[](每个 todo 含 content/activeForm/status),状态三态:pending → in_progress → completed。这是 Claude Code 的 TaskCreate 模式的复刻。

  2. filesystem 工具:219-229):如果配置了 Backend/Shell/StreamingShell,自动注册文件读写、glob、grep、shell 执行等工具。这些工具通过 filesystem2.NewTyped 中间件注入。

  3. task tooltask_tool.go:35-58):子 agent 调度工具。把每个子 agent 包装为 AgentTool,通过 subagent_type 参数路由。同时注入一个 prompt,告诉主 agent 什么时候用 task tool。

Task Tool:把 Agent 当 Tool 调

task_tool.go:61-124typedNewTaskTool 是 Deep agent 的核心:

func typedNewTaskTool[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 也包装成 AgentTool
    for _, a := range subAgents {
        t.subAgents[a.Name(ctx)] = adk.NewTypedAgentTool(ctx, a)
    }
    return t, nil
}

InvokableRun:156-175)的路由逻辑很简单:

func (t *typedTaskTool[M]) InvokableRun(ctx, argumentsInJSON string, ...) (string, error) {
    input := &taskToolArgument{}
    json.Unmarshal([]byte(argumentsInJSON), input)
    // 按 subagent_type 找对应的 AgentTool
    a := t.subAgents[input.SubagentType]
    // 把 description 作为参数传给子 agent
    return a.InvokableRun(ctx, marshal(map[string]string{"request": input.Description}))
}

参数只有两个字段:subagent_type(选哪个子 agent)和 description(子 agent 的任务描述)。主 agent 调 task tool 时,就像调度一个短生命周期的工作进程。

通用子 agent

general-purpose agent(:86-112)是一个特殊设计:它共享主 agent 的 instruction、tools、middlewares、handlers。这意味着通用子 agent 和主 agent 有相同的能力,但有自己的独立 session。

用途:主 agent 把复杂子任务委托给这个"分身",自己聚焦在协调上。

中英文双语 Prompt

Deep agent 的 prompt 是四种模式中最丰富的(prompt.go 有 685 行),所有 prompt 都有中英文两套:

  • baseAgentInstruction / baseAgentInstructionChinese:主 agent 的系统指令(~110 行),包含语气风格、安全策略、编码规范、工具使用策略
  • taskPrompt / taskPromptChinese:task tool 的使用说明,告诉主 agent 什么时候该用 task tool
  • taskToolDescription / taskToolDescriptionChinese:task tool 的描述,模板变量 {other_agents} 填入可用子 agent 列表
  • writeTodosToolDescription / writeTodosToolDescriptionChinese:write_todos 工具的使用说明(~180 行),大量示例

语言选择通过 internal.SelectPrompt 自动判断,根据上下文语言选择对应版本。

Deep Agent 的 prompt 来源

Deep agent 的 prompt 设计借鉴了 LangChain 的 DeepAgents 项目和 Claude Code(prompt.go:21-23):

This 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 结构体(Messages+IsMultipleIntents+SpecialistResults),通过 ProcessState 流转compose.go:30-47
流式怎么判断 tool call只看第一个 chunk(firstChunkStreamToolCallChecker),不等全量types.go:182-204
多意图怎么汇总有 Summarizer→LLM 汇总,无→纯拼接compose.go:286-335
Supervisor 为什么 NOT RECOMMENDEDtransfer 共享完整上下文,经验证明不如 AgentTool/DeepAgentsupervisor.go:42-44
Plan-Execute 怎么循环SequentialAgent(Planner, LoopAgent(Executor, Replanner)),Replanner 二选一工具plan_execute.go:862-880
Deep agent 怎么调度子 agenttask tool 把子 agent 包装为 AgentTool,通过 subagent_type 参数路由task_tool.go:61-175
Deep 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 机制源码。

标题基于SpringBoot的校园创客空间管理系统设计与实现AI更换标题第1章引言介绍校园创客空间管理系统的研究背景、意义、国内外研究现状、论文方法与创新点。1.1研究背景与意义阐述校园创客空间的发展现状及其对管理系统的需求。1.2国内外研究现状分析国内外校园创客空间管理系统的研究进展。1.3研究方法及创新点概述本文采用的研究方法及系统设计的创新点。第2章相关理论总结SpringBoot框架及相关技术,确立系统设计的理论基础。2.1SpringBoot框架概述介绍SpringBoot框架的特点、优势及在Web开发中的应用。2.2相关技术总结概述数据库技术、前端技术及系统安全技术等。2.3理论基础确立基于上述理论,确立校园创客空间管理系统的设计基础。第3章系统需求分析详细分析校园创客空间管理系统的功能需求、性能需求及用户需求。3.1功能需求分析列举系统应具备的核心功能,如用户管理、项目管理等。3.2性能需求分析分析系统对响应时间、并发处理能力等性能指标的要求。3.3用户需求分析从用户角度出发,分析用户对系统的期望和需求。第4章系统设计详细介绍系统的架构设计、数据库设计及界面设计。4.1系统架构设计给出系统的整体架构,包括前后端分离、微服务架构等。4.2数据库设计设计系统的数据库结构,包括表结构、索引及关系等。4.3界面设计展示系统的用户界面设计,包括页面布局、交互设计等。第5章系统实现与测试阐述系统的实现过程,包括编码实现、系统集成及测试验证。5.1编码实现介绍系统各模块的编码实现过程及关键技术点。5.2系统集成系统各模块之间的集成方式及集成测试过程。5.3测试验证通过单元测试、集成测试等方法验证系统的功能和性能。第6章结论与展望总结系统设计与实现的主要成果,提出未来研究方向。6.1研究结论概括系统设计与实现的主要成果,包括功能实现、性能优化等。6.2展望指出系统存在的不足及
内容概要:本文系统研究了基于分布式模型预测控制(DMPC)的多个固定翼无人机一致性控制问题,提出并实现了相应的Matlab仿真方案。通过建立固定翼无人机精确的动力学模型,结合分布式控制架构,采用模型预测控制策略,使多个无人机在无中央协调的情况下实现状态一致性,如位置、速度和航向的协同收敛。文中详尽阐述了系统建模、控制算法设计、一致性协议构建及Matlab代码实现过程,重点解决了通信延迟、局部信息交互受限以及动态环境适应性等关键技术难点,并通过大量仿真实验验证了该方法的有效性、鲁棒性与可扩展性。; 适合人群:具备一定现代控制理论基础和Matlab编程能力,从事自动化、航空航天、机器人学、集群智能或智能系统等相关领域的研究生、科研人员及工程技术人员。; 使用场景及目标:①应用于多无人机协同飞行、编队控制、集群作业与自主导航等实际场景;②服务于高校科研项目、课程设计、学位论文研究及工程原型开发,旨在深入掌握分布式控制与模型预测控制的融合机制与工程实现;③帮助读者复现已有算法成果,并为进一步研究复杂通信拓扑、障碍规避与任务分配等高级功能提供坚实基础。; 阅读建议:建议读者结合Matlab代码与文中的理论推导同步学习,动手搭建仿真模型,重点关注状态一致性收敛过程、代价函数设计与控制参数调优策略,同时可尝试拓展至不同初始条件、通信拓扑结构及外部干扰场景下的性能测试与分析。
内容概要:本文围绕基于蜣螂优化算法(DBO)的无线传感器网络(WSN)覆盖优化问题展开研究,旨在通过智能优化算法提升网络覆盖率与资源利用效率。研究采用Matlab进行算法实现,通过对传感器节点的部署位置进行全局寻优,最大化监测区域的覆盖范围并减少覆盖盲区。文中系统阐述了蜣螂优化算法的原理、WSN覆盖模型的构建、适应度函数的设计及仿真流程,并通过对比实验验证了DBO算法在收敛速度、优化精度和稳定性方面相较于传统优化方法的优越性。研究不仅提供了完整的代码实现,还探讨了算法在物联网、环境监测等实际场景中的应用潜力,突出了智能优化技术在解决复杂工程问题中的价值。; 适合人群:具备一定编程基础,熟悉Matlab编程环境,从事无线传感器网络、智能优化算法、物联网应用或相关领域研究的科研人员、工程技术人员及研究生。; 使用场景及目标:①解决无线传感器网络中节点部署导致的覆盖不均与资源浪费问题;②提升复杂环境下WSN的监测覆盖率与系统稳定性;③为智能优化算法在通信网络布局、环境监控、智慧农业等领域的工程应用提供可复现的技术范例; 阅读建议:建议结合提供的Matlab代码进行仿真实验,深入理解蜣螂优化算法的参数设置、迭代机制与收敛特性,同时可尝试将其迁移应用于其他组合优化问题如路径规划、任务调度或多目标优化中,以拓展算法的应用边界。
源码下载地址: https://pan.quark.cn/s/7385d689617d AIS解码算法达成6位码的数据获取 AIS(Automatic Identification System,自动识别系统)是一种用于船舶自动识别和追踪的系统,它运用6位码对信息进行编码和传输。在实际操作中,我们需要将AIS传输的信息解密并提取出有用的内容。下面我们将阐述AIS解码算法的实现过程。 一、将ASCII码转换为6位二进制数值 在AIS系统中,信息是采用6位码进行编码的,因此我们需要将ASCII码转换为6位二进制数值。这个过程可以通过bool EightByteToSix(BYTE inEight, BYTE &outSix)函数完成。该函数将ASCII码转换为6位二进制数值,并将结果存储在outSix中。 函数的实现可以分为三个阶段: 1. 验证输入的ASCII码是否合法。 2. 将ASCII码转换为6位二进制数值。 3. 如果SUM大于10000000,则加上101000,否则加上101000。 二、将ASCII码字符串转换为6位二进制数值数组 在实际操作中,我们需要将ASCII码字符串转换为6位二进制数值数组。这可以通过bool EightStrToSix(CString inEight, LPBYTE outSix)函数实现。该函数将ASCII码字符串转换为6位二进制数值数组,并将结果存储在outSix中。 函数的实现可以分为五个步骤: 1. 将ASCII码字符串转换为6位二进制数值。 2. 将6位二进制数值保存到字节中。 3. 将字节保存到输出数组中。 4. 处理每个ASCII码的转换过程。 5. 将结果保存到输出数组中。 三、AIS解码算法的实现过程 AI...
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值