适用读者:已经能跑单个 ReAct Agent、要做"分析→生成→审核"多条流水线,并要能中断、能观测、能扩展的人。
前置:S21-J06(编排)、B06(记忆与状态)、A02(安全与 HITL)。
覆盖旧文:A-3579(6. ADK 多智能体编排)、A-3578(7. ADK 高级特性)→ 2 篇 / 6.8 万字符。
扩展点:E-S21-022(四种 Agent 类型与嵌套规则)、E-S21-023(Runner 的输入/事件流/流式三件事)、E-S21-024(HITL 中断恢复与 StatefulInterrupt)、E-S21-025(Session Values 与 Middleware 的扩展点)。
上一篇:S21-J06 · Eino 编排:Chain、Graph 与 Workflow 字段映射
下一篇:S21-J08 · 现代 Agent 的定义:LLM + 上下文 + 工具。
1. 一句话结论
单 Agent 解决"下一步做什么",ADK 解决"几个 Agent 谁先谁后"。 四种 Agent 类型(ChatModel / Sequential / Parallel / Loop)共用同一个 Agent 接口,所以能像积木一样任意嵌套——这是 ADK 最关键的设计决策。而"能跑"到"能上线"之间,还差四件事:回调、中断恢复、共享状态、中间件。
出处:
A-3579、A-3578(秀才,Eino ADK,2026-04)。
2. 四种 Agent 类型(E-S21-022)
| 类型 | 做什么 | 典型场景 |
|---|---|---|
| ChatModelAgent | 增强版 ReAct:思考→行动→观察循环 | 意图识别、内容生成、调工具 |
| SequentialAgent | 按顺序串子 Agent,前一个输出给后一个 | 分析 → 生成 → 审核 |
| ParallelAgent | 并行跑子 Agent,汇总结果 | 多数据源并发搜索 |
| LoopAgent | 循环跑子 Agent 直到满足退出条件 | 迭代优化、自我纠错 |
嵌套示例:SequentialAgent
├─ ① ParallelAgent(3 个搜索 Agent)
├─ ② ChatModelAgent(生成报告)
└─ ③ LoopAgent(审核直到达标)
判据:先问"是顺序、并行还是循环",再问"每个格子放哪个 Agent"。硬用 Graph 手画节点,等于重复实现这四种模式。
2.1 ChatModelAgent 与裸 ReAct 的差别
| 项 | react.NewAgent |
ChatModelAgent |
|---|---|---|
| 循环控制 | 自己写 | 内置 |
| Instruction 动态变量 | 无 | 有(模板变量注入) |
| 与 Runner/事件流集成 | 需手工 | 原生 |
| 可被其他 Agent 嵌套 | 否 | 是(实现统一接口) |
3. Runner:输入、事件流、流式(E-S21-023)
runner := adk.NewRunner(adk.RunnerConfig{Agent: seq, EnableStreaming: true})
// ① 文本输入(最简)
events := runner.Query(ctx, "帮我分析这份财报")
// ② 消息输入(多轮/多模态)
events := runner.Run(ctx, []*schema.Message{{Role: schema.User, Content: "…"}})
for {
ev, ok := events.Next()
if !ok { break }
// 事件里有 Agent 名、输出、工具调用、错误……
}
| 能力 | 用途 | 注意 |
|---|---|---|
Query |
一句话进 | 内部自动包成 user 消息 |
Run |
完整消息列表 | 多轮上下文由你组织 |
| 事件流 | 观测每一步 | 必须消费完,否则阻塞 |
EnableStreaming |
首字更早 | 下游要能处理增量 |
判据:事件流既是"输出通道"也是"观测通道"。 生产里它还得喂给日志与评估(S21-A01)。
4. 中断与恢复:HITL(E-S21-024)
场景:Agent 要执行一个删除/转账类工具,必须先让人确认。
① Agent 触发工具 → 命中"需人工确认"策略
② 抛出中断(Interrupt),把当前状态存下来
③ 外部(前端/工单系统)拿到待确认信息
④ 人确认/拒绝 → 带 resume 数据重新进入
⑤ 从断点继续,而不是从头重跑
| 机制 | 存什么 | 适用 |
|---|---|---|
| 普通 Interrupt | 中断点标识 | 快速确认、无长上下文 |
| StatefulInterrupt | 中间状态(已算出的部分) | 长任务、代价高的前序步骤 |
判据:"能恢复"的底线是"不重复昂贵的前序工作"。 没有状态保存的中断,恢复等于重跑——长任务上等于白烧一次。
5. 共享状态:Session Values(E-S21-025)
// 外部注入
ctx = adk.WithSessionValues(ctx, map[string]any{"tenant": "t-42", "budget": 5})
// Agent 之间读写同一份会话状态
v, _ := adk.GetSessionValue(ctx, "tenant")
| 用法 | 好处 | 风险 |
|---|---|---|
| 注入租户/身份 | 子 Agent 不必层层传参 | 泄漏到日志 |
| 共享中间结果 | 避免重复计算 | 并发写冲突 |
| 预算/配额 | 全局约束 | 忘记扣减 |
判据:Session Values 是"会话级全局变量",用它是权衡不是免费。 并发写的 Agent 必须约定谁写、谁只读。
6. 中间件:不改代码扩展行为
type MyMiddleware struct{ adk.BaseAgentMiddleware }
func (m *MyMiddleware) BeforeModel(ctx context.Context, req *adk.ModelRequest) (*adk.ModelRequest, error) {
// 例:消息裁剪——超长历史只留最近 N 轮 + 系统提示
req.Messages = trim(req.Messages, 20)
return req, nil
}
| 内置/常见中间件 | 解决 |
|---|---|
| 消息裁剪 | 上下文超限(S20-J02) |
| 日志/追踪 | 观测 |
| 重试/降级 | 模型抖动 |
| 权限校验 | 工具调用前的护栏 |
判据:中间件是"横切关注点"的位置:日志、裁剪、限流、鉴权都不该写进每个 Agent。
7. 回调:先看清"哪个 Agent 出的问题"
handler := callbacks.NewHandlerBuilder().
OnStartFn(func(ctx context.Context, info *callbacks.RunInfo, in callbacks.CallbackInput) context.Context {
if info.Component == adk.ComponentOfAgent { log.Printf("start %s", info.Name) }
return ctx
}).
OnEndFn(func(ctx context.Context, info *callbacks.RunInfo, out callbacks.CallbackOutput) context.Context {
log.Printf("end %s", info.Name); return ctx
}).Build()
runner := adk.NewRunner(adk.RunnerConfig{Agent: seq, Callbacks: []callbacks.Handler{handler}})
判据:嵌套三层以上时,没有按组件过滤的回调,日志等于噪声。 用 RunInfo.Component 只留 Agent 事件。
8. 一个可中断的审批工作流(拼起来)
| 步 | 组件 | 作用 |
|---|---|---|
| 1 | SequentialAgent | 串起"起草→审阅→执行" |
| 2 | ChatModelAgent | 起草方案(带工具查询) |
| 3 | Interrupt | 执行前停下来等人确认 |
| 4 | StatefulInterrupt | 保存草稿与已查数据 |
| 5 | Middleware | 裁剪上下文、写审计日志 |
| 6 | Callbacks | 每步起止打点,供评估 |
9. 常见误区
| 误区 | 纠正 |
|---|---|
| "多 Agent 一定要手画 Graph" | 四种模式已覆盖大多数结构 |
| "ParallelAgent 一定更快" | 受工具限流与汇合约束 |
| "LoopAgent 加个计数器就行" | 要有明确退出条件,否则烧钱 |
| "中断就是抛异常" | 要能带状态恢复 |
| "Session Values 随便写" | 并发写会互相覆盖 |
10. 自测
- 四种 Agent 类型分别解决什么结构?
Query与Run的区别?事件流不消费会怎样?StatefulInterrupt比普通中断多存了什么?为什么重要?- 中间件适合放哪类逻辑?举两个例子。
答案
- 单体思考(ChatModel)、顺序(Sequential)、并行(Parallel)、循环(Loop)。
Query一句话自动包成 user 消息;Run接完整消息列表。不消费事件流会阻塞执行。- 中间状态;避免恢复时重跑昂贵的前序步骤。
- 横切关注点:消息裁剪、审计日志(还有限流、鉴权)。
11. 下一篇
S21-A01 · Agent 评估与可观测 —— 有了事件流与回调之后,怎么把它变成可度量的指标与可回归的评估集。
本篇属于系列《S21 · Agent 工程与 MCP》。旧文来源:A-3579/3578;扩展点 E-S21-022/023/024/025。ADK API 以官方文档当前版本为准。