核心摘要

在 Eino v0.9.21 中,多 Agent 协作最稳妥的默认方案不是无限制的 Swarm。责任归属可以由策略确定时使用确定性路由;必须让模型动态委派时,使用 ChatModelAgent,并通过 adk.NewAgentTool 暴露专家 Agent。Eino 仍保留基于 Transfer 的 Supervisor、Sequential、Parallel 和 Loop API,但源码已将它们标记为不推荐,原因是全上下文移交尚未证明有经验上的优势。每个 Agent 边界都应成为有类型契约、有预算、可观测的信任边界。

目录

  1. Eino v0.9.21 的关键变化
  2. 三种协作模式
  3. 推荐架构
  4. 可运行的 AgentTool 示例
  5. 上下文、状态与权限
  6. 故障与成本预算
  7. 评测协议
  8. 常见失败模式
  9. 常见问题
  10. 一手资料

Eino v0.9.21 的关键变化

Eino 当前 ADK 做出了一个重要设计区分:把专家 Agent 当作 Tool 调用是推荐的组合路径,全上下文 Agent Transfer 则是只适合特定需求的兼容路径。

v0.9.21 提供以下核心能力:

  • adk.NewChatModelAgent:构建模型驱动的 Agent 循环
  • adk.NewAgentTool:把 adk.Agent 包装为 Eino Tool
  • adk.NewRunner:运行 Agent 并消费事件流
  • adk.WithCallbacks:注入单次执行范围的可观测回调
  • Checkpoint 与 Resume:支持可中断、可恢复执行
  • Transfer、Supervisor、Sequential、Parallel 与 Loop:API 仍存在,但源码包含明确的“不推荐”说明

因此,旧式 Router、Supervisor、Swarm 教程不能再被理解为三个同等推荐的默认选项。它们描述的是协作形态,而不是统一最佳实践。

模式 控制方 传递上下文 适合场景 主要风险
确定性 Router 应用代码 显式请求契约 领域稳定、可按策略路由 规则陈旧或覆盖不全
Orchestrator + AgentTool 父 Agent 显式工具参数,必要时带历史 需要动态但受控的专家委派 嵌套成本与工具选择不透明
全上下文 Handoff 当前 Agent 会话历史 确实需要动态切换会话所有者 上下文、权限与故障耦合

可先通过 AI Agent 术语理解单 Agent 循环,再通过多智能体术语建立协作模型。

三种协作模式

确定性路由

当可信字段、权限、语言、产品域或经过验证的分类器可以决定处理者时,应优先使用确定性路由。它成本低、可测试,也最容易审计。

flowchart LR A["已校验请求"] --> B["策略路由器"] B -->|"账单"| C["账单专家"] B -->|"技术"| D["技术专家"] B -->|"未知"| E["安全兜底"] C --> F["标准化结果"] D --> F E --> F

不要让 LLM 处理精确规则已经能够决定的问题。只有标签存在语义歧义,并且分类错误已在代表性数据上被量化时,模型路由才有价值。

Orchestrator 配合 AgentTool

adk.NewAgentTool 为父 Agent 提供标准 Tool 边界。子 Agent 必须提供非空的 Name 和 Description;默认输入是包含 request 字符串的 JSON 对象。可以用 adk.WithAgentInputSchema 替换输入契约,也可以在确有必要时用 adk.WithFullChatHistoryAsInput 传递历史。

这个边界的执行语义是:

  1. 父 Agent 根据工具元数据选择专家。
  2. 子 Agent 默认接收受限输入,而不是继承全部消息。
  3. 子 Agent 通过 Tool Result 返回文本。
  4. 子 Agent 的 Exit、Transfer 和 BreakLoop 动作被限制在 Tool 边界内;Interrupt 可向上传播以支持 Checkpoint/Resume。

该模式更容易落实最小权限:每个专家只获得角色所需的工具、凭证、数据范围和执行预算。

全上下文移交

Handoff 会改变当前对话的所有者。它适合专家必须直接延续长对话的场景,但不应被当成通用协作原语。

完整历史会产生三类耦合风险:

  • 权限扩大:接收方可能看到角色范围之外的数据或指令。
  • 上下文稀释:无关历史与当前任务真正需要的事实争夺窗口。
  • 恢复歧义:重试与恢复必须知道每个副作用归哪个 Agent 所有。

如果保留 Handoff,必须定义允许的目标、最大移交次数、终止规则、上下文过滤和明确的所有权记录。

推荐架构

应用代码负责不可违反的约束,Agent 只负责确实需要语义判断的决策。

flowchart TD A["请求与身份"] --> B["认证与策略"] B --> C{"能否确定性路由"} C -->|"能"| D["选定专家"] C -->|"不能"| E["Orchestrator ChatModelAgent"] E --> F["专家 AgentTool"] D --> G["输出校验"] F --> G G --> H["审计与响应"]

父 Agent 应只看到一组规模较小、描述彼此可区分的工具。对子 Agent 的输入应是任务契约,而不是“帮忙处理一下”之类的模糊要求。

有效的委派契约至少包含:

字段 作用
objective 子 Agent 必须产出的精确结果
evidence 已知标识符与来源材料
constraints Scope、Tenant、Region 与禁止动作
output schema 输出字段与最大尺寸
deadline 剩余端到端时间预算
call budget 剩余模型与工具调用次数
idempotency key 对副作用进行去重

可运行的 AgentTool 示例

下面的程序基于 Eino v0.9.21,不依赖外部模型。它实现两个确定性专家 Agent,用 NewAgentTool 包装,再路由并调用选中的 Tool。需要模型动态委派时,可以把同样的 Tool 边界放进 ChatModelAgent.ToolsConfig。

go
package main

import (
	"context"
	"fmt"
	"log"
	"strings"

	"github.com/cloudwego/eino/adk"
	"github.com/cloudwego/eino/components/tool"
	"github.com/cloudwego/eino/schema"
)

type specialist struct {
	name        string
	description string
	prefix      string
}

func (s *specialist) Name(context.Context) string {
	return s.name
}

func (s *specialist) Description(context.Context) string {
	return s.description
}

func (s *specialist) Run(
	ctx context.Context,
	input *adk.AgentInput,
	_ ...adk.AgentRunOption,
) *adk.AsyncIterator[*adk.AgentEvent] {
	iter, gen := adk.NewAsyncIteratorPair[*adk.AgentEvent]()

	go func() {
		defer gen.Close()

		if err := ctx.Err(); err != nil {
			gen.Send(&adk.AgentEvent{Err: err})
			return
		}

		request := ""
		if len(input.Messages) > 0 {
			request = input.Messages[len(input.Messages)-1].Content
		}
		reply := fmt.Sprintf("%s handled: %s", s.prefix, request)
		gen.Send(adk.EventFromMessage(
			schema.AssistantMessage(reply, nil),
			nil,
			schema.Assistant,
			"",
		))
	}()

	return iter
}

func chooseTool(input string, tools map[string]tool.InvokableTool) tool.InvokableTool {
	if strings.Contains(strings.ToLower(input), "invoice") {
		return tools["billing"]
	}
	return tools["technical"]
}

func main() {
	ctx := context.Background()
	tools := map[string]tool.InvokableTool{
		"billing": adk.NewAgentTool(ctx, &specialist{
			name:        "billing",
			description: "Resolve invoice and payment questions.",
			prefix:      "billing",
		}).(tool.InvokableTool),
		"technical": adk.NewAgentTool(ctx, &specialist{
			name:        "technical",
			description: "Diagnose API and integration failures.",
			prefix:      "technical",
		}).(tool.InvokableTool),
	}

	request := "Why was invoice INV-42 charged twice?"
	selected := chooseTool(request, tools)
	result, err := selected.InvokableRun(
		ctx,
		`{"request":"Why was invoice INV-42 charged twice?"}`,
	)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println(result)
	// Output: billing handled: Why was invoice INV-42 charged twice?
}

模块依赖固定为:

text
require github.com/cloudwego/eino v0.9.21

需要模型驱动的 Orchestrator 时,把 Tool 放入 adk.ChatModelAgentConfig.ToolsConfig.Tools,设置有限的 MaxIterations,用 adk.NewChatModelAgent 创建 Agent,再通过 adk.NewRunner 执行。模型实现必须支持 Eino 的 model.WithTools Option。

上下文、状态与权限

多 Agent 状态应按语义拆分,而不是全部塞进共享对话。

状态类别 推荐归属 示例
不可变请求事实 Request Context 或类型化输入 Tenant、Locale、Trace ID
对话推理 各个 Agent Messages 与中间假设
持久工作流状态 事务性存储 审批、任务状态、Checkpoint
证据产物 对象或文档存储 搜索结果、报告、代码补丁
安全决策 Policy Engine Grant、Scope、对象级权限

context.Context 适合传递取消、Deadline、Trace 和小型请求级值,不是持久业务状态仓库。Eino Session Value 可以支持运行时指令格式化,但授权仍必须来自可信的服务端策略。

不要把父 Agent 的完整 Transcript 复制给每个专家。只传递子任务必需的证据,附带来源,再返回紧凑的结构化结果。这样既减少 Prompt Injection 的传播,也更容易定位评测结果。

故障与成本预算

多 Agent 会放大依赖数量,因此每次执行必须共享一份总预算。

text
deadline = min(request deadline, workflow SLA)
remaining calls = global call budget - completed calls
remaining tokens = global token budget - measured usage
allowed depth = configured maximum - current depth

至少需要执行:

  • 通过 context.Context 传播端到端 Deadline
  • 模型与 Tool 超时短于父级 Deadline
  • 限制 Agent 深度、模型迭代次数和工具调用次数
  • 在模型供应商与下游服务边界限制并发
  • 子结果重新进入模型上下文前限制字节数
  • 根据幂等性和错误类型决定是否重试
  • 为长任务定义取消传播和 Checkpoint 规则

不能给每个子 Agent 独立的完整预算。五个子 Agent 各有十次调用额度,会把看似十次的父级预算放大成五十次。

评测协议

所有协作模式都应在同一个冻结任务集上比较。必须保留单 Agent 基线,因为多 Agent 可能只增加 Token 消耗而没有改善最终结果。

维度 示例指标
结果质量 任务 Pass Rate 或 Rubric Score
路由质量 Owner 命中率、Fallback Rate、拒答质量
证据质量 可支持结论比例与引用精度
效率 Wall Time、模型 Token、Tool Call、子 Agent 数
可靠性 Timeout、Retry、重复副作用、恢复成功率
安全 未授权调用拦截率、跨租户泄漏
可运维性 Parent-Child Trace 归因完整度

Anthropic 的生产报告显示,多 Agent 对广度优先的研究任务有明显收益,但也报告了显著更高的 Token 消耗,以及对强依赖任务的较弱适配性。这些数字只证明对应工作负载,不能直接套用到其他系统;你需要在自己的任务分布上复现实验。

常见失败模式

每个角色都能看到所有工具

工具过载会损害选择质量并扩大爆炸半径。每个专家只应获得最小 Tool Allowlist,而且白名单必须由代码执行。

Router 静默走默认分支

Fallback 会掩盖分类漂移。应记录原因、置信度、最终路由,以及人工或确定性规则是否覆盖了模型选择。

重试整个工作流

全局重试可能重复已完成的副作用。持久化步骤状态和 Idempotency Key,并只在契约允许时重试失败操作。

委派没有完成契约

可以无限搜索的子 Agent 会耗尽总预算。必须规定目标、输出 Schema、停止条件和最大调用数。

把 Handoff 当成授权

获得控制权不等于获得数据权限。每次工具调用都要重新校验身份、Tenant、对象和动作权限。

常见问题

路由应该写在 Eino Graph 还是应用代码中?

可信字段或稳定规则可以决定路由时,直接使用应用代码。路由属于更大规模的类型化工作流,并涉及 Branch、持久化或组件复用时,可使用 Eino Graph。只有语义歧义足以抵消模型成本和错误率时,才使用模型分类。

什么时候适合向 AgentTool 传递完整历史?

只有子 Agent 必须理解对话本身时,才使用 adk.WithFullChatHistoryAsInput。对于数据查询、计算、审阅或抽取任务,窄而明确的类型化请求通常更安全,也更省 Token。

并行 Agent 一定更快吗?

不一定。只有子任务足够独立时,并行才有价值。共享依赖、限流、汇总成本和最慢子任务都可能抵消延迟收益。

应该追踪哪些信息?

应追踪父 Run、路由决策、子 Agent 调用、模型调用、Tool 调用、Retry 和最终校验。记录标识符和有界指标,不要记录原始 Prompt、凭证、个人信息和未经限制的 Tool 输出。

总结

可靠的 Eino 多 Agent 系统依赖明确边界,而不是 Agent 数量。先使用确定性路由,再用 AgentTool 实现受控委派;只有实测工作负载能够证明价值时,才采用全上下文 Transfer。共享事实与总预算,隔离推理和权限,并始终与更简单的单 Agent 基线比较。

相关资源

一手资料