上一篇 讲了数据库迁移怎么不翻车。但 Agent 本身是怎么跑起来的?创建、执行、工具调用、中断、恢复——这些生命周期节点 Eino ADK 怎么管?
这篇拆解 Eino ADK(Agent Development Kit)的 Agent 完整生命周期。从 NewChatModelAgent 到 Runner.Run,从 ReAct 循环到 Interrupt/Resume。源码在 eino/adk/ 下,核心文件四个:interface.go、chatmodel.go、runner.go、flow.go。
为什么需要 ADK
直接用 Eino 的 compose.Graph 也能跑 Agent,但每个 Agent 都要手写 Graph、管消息历史、处理工具调用、做错误恢复。ADK 把这些封装成可复用的 Agent 抽象,一行 NewChatModelAgent + 一行 runner.Query 就能跑。
(一)Agent 是什么:TypedAgent 接口
adk/interface.go:453-464 定义了 Agent 的最小契约:
1type TypedAgent[M MessageType] interface { 2 Name(ctx context.Context) string 3 Description(ctx context.Context) string 4 Run(ctx context.Context, input *TypedAgentInput[M], options ...AgentRunOption) *AsyncIterator[*TypedAgentEvent[M]] 5} 6
三个方法:
- Name:Agent 名字,用于日志、回调、多 Agent 编排
- Description:Agent 描述,其他 Agent 通过它判断该不该 transfer
- Run:执行入口,返回
AsyncIterator——事件流,不是一次性结果
AsyncIterator 是关键设计。Agent 不是"输入→输出"的黑盒,而是"输入→事件流"的管道。每个事件可以是:
- 模型回复(
Output.Message) - 工具调用/结果(
Output.Role == Tool) - 中断请求(
Action.Interrupted) - 转移请求(
Action.TransferToAgent) - 退出(
Action.Exit) - 错误(
Err)
事件结构定义在 adk/interface.go:419-435:
1type TypedAgentEvent[M MessageType] struct { 2 AgentName string 3 RunPath []RunStep 4 Output *TypedAgentOutput[M] 5 Action *AgentAction 6 Err error 7} 8
(二)ChatModelAgent:最常用的 Agent 实现
ChatModelAgent 是 ADK 的默认实现,封装了 ReAct 循环。创建方式在 adk/chatmodel.go:502-504:
1func NewChatModelAgent(ctx context.Context, config *ChatModelAgentConfig) (*ChatModelAgent, error) 2
配置项在 adk/chatmodel.go:260-369,核心字段:
| 字段 | 作用 | 默认值 |
|---|---|---|
| Name | Agent 名称 | 空 |
| Description | Agent 描述 | 空 |
| Instruction | 系统提示词 | 空 |
| Model | 聊天模型 | 必填 |
| ToolsConfig | 工具配置 | 空 |
| MaxIterations | 最大迭代次数 | 20 |
| GenModelInput | 输入转换函数 | 默认拼接 instruction + messages |
| Middlewares | 中间件(已废弃) | 空 |
| Handlers | 新版中间件 | 空 |
最简创建:
1agent, _ := adk.NewChatModelAgent(ctx, &adk.ChatModelAgentConfig{ 2 Name: "MyAgent", 3 Model: myModel, 4}) 5
(三)ReAct 循环:Agent 怎么"思考→行动→再思考"
ChatModelAgent 的核心是 ReAct 循环。状态定义在 adk/react.go:35-57:
1type typedState[M MessageType] struct { 2 Messages []M 3 ToolInfos []*schema.ToolInfo 4 HasReturnDirectly bool 5 ReturnDirectlyToolCallID string 6 AgentName string 7 RemainingIterations int 8 // ... 9} 10
执行流程:
- 构建初始消息:system prompt(Instruction)+ 用户输入
- 调用 ChatModel:传入消息历史 + 工具列表
- 检查响应:
- 无工具调用 → 最终回复,返回事件流
- 有工具调用 → 执行工具,将结果追加到消息历史
- 循环:回到步骤 2,直到无工具调用或超过
MaxIterations
超过 MaxIterations 时返回 ErrExceedMaxIterations(adk/react.go:33):
1var ErrExceedMaxIterations = errors.New("exceeds max iterations") 2
(四)Runner:Agent 的执行器
Runner 是 Agent 的执行器,管理 checkpoint、streaming 模式、中断恢复。定义在 adk/runner.go:50-59:
1type TypedRunner[M MessageType] struct { 2 a TypedAgent[M] 3 enableStreaming bool 4 store CheckPointStore 5} 6
创建和运行在 adk/runner.go:94-115:
1// 创建 2func NewTypedRunner[M MessageType](conf TypedRunnerConfig[M]) *TypedRunner[M] 3 4// 执行 5func (r *TypedRunner[M]) Run(ctx context.Context, messages []M, opts ...AgentRunOption) *AsyncIterator[*TypedAgentEvent[M]] 6 7// 便捷方法:字符串查询 8func (r *TypedRunner[M]) Query(ctx context.Context, query string, opts ...AgentRunOption) *AsyncIterator[*TypedAgentEvent[M]] 9
Query 是 Run 的语法糖,内部把字符串包装成一条用户消息后调用 Run。
消费事件流:
1iter := runner.Query(ctx, "今天天气怎么样?") 2for { 3 event, ok := iter.Next() 4 if !ok { 5 break 6 } 7 if event.Err != nil { 8 // 处理错误 9 } 10 if event.Output != nil { 11 // 处理输出 12 } 13} 14
(五)Interrupt / Resume:Agent 的中断与恢复
Agent 不是每次都能一口气跑完。需要人工确认、等待外部输入、审批——这些场景需要中断机制。
中断:Agent 返回 Action.Interrupted = true 的事件,暂停执行。
恢复:调用 Runner.Resume 或 Runner.ResumeWithParams,携带恢复数据继续。
中断信息定义在 adk/interrupt.go:33-55:
1type ResumeInfo struct { 2 EnableStreaming bool 3 *InterruptInfo 4 WasInterrupted bool 5 InterruptState any 6 IsResumeTarget bool 7 ResumeData any 8} 9 10type InterruptInfo struct { 11 Data any 12 InterruptContexts []*InterruptCtx 13} 14
Runner 的 Resume 在 adk/runner.go:124-149:
1// 简单恢复:所有中断点都恢复 2func (r *TypedRunner[M]) Resume(ctx context.Context, checkPointID string, opts ...AgentRunOption) (*AsyncIterator[*TypedAgentEvent[M]], error) 3 4// 精确恢复:指定恢复目标和数据 5func (r *TypedRunner[M]) ResumeWithParams(ctx context.Context, checkPointID string, params *ResumeParams, opts ...AgentRunOption) (*AsyncIterator[*TypedAgentEvent[M]], error) 6
流程:
- Agent 执行中调用
TypedInterrupt返回中断事件 - Runner 检测到
Action.Interrupted,保存 checkpoint - 外部系统处理中断(人工审批等)
- 调用
Resume/ResumeWithParams携带数据继续 - Agent 从 checkpoint 恢复状态,继续执行
(六)FlowAgent:多 Agent 编排
FlowAgent 是 ADK 的多 Agent 编排层。定义在 adk/flow.go:42-52:
1type flowAgent struct { 2 Agent 3 subAgents []*flowAgent 4 parentAgent *flowAgent 5 disallowTransferToParent bool 6 historyRewriter HistoryRewriter 7 checkPointStore compose.CheckPointStore 8} 9
关键能力:
- 子 Agent 注册:
SetSubAgents把 Agent 注入到编排树 - Transfer:Agent 可以把任务转交给子 Agent 或父 Agent
- History Rewriting:Transfer 时重写消息历史,把上一个 Agent 的回复改写成用户消息
- Checkpoint:支持中断恢复的持久化
注意:源码多处标注"NOT RECOMMENDED",建议用
AgentTool或DeepAgent替代 Transfer 模式。Agent 间全量上下文共享在实践中效果不如预期。
(七)生命周期状态机
ChatModelAgent 内部有 frozen 标记(sync.Once 保证只初始化一次)和懒加载机制。但对外暴露的生命周期可以简化为:
1Created → Running → Completed 2 → Interrupted → Running → Completed 3 → Error 4
- Created:
NewChatModelAgent返回,未冻结 - Running:首次
Run调用触发sync.Once初始化,进入执行 - Completed:模型返回无工具调用的最终回复
- Interrupted:Agent 发出中断事件,等待外部输入
- Error:超过 MaxIterations、模型错误、工具错误
(八)事件流:AsyncIterator 的设计
ADK 没有用 channel,而是用 AsyncIterator 模式。好处:
- 不依赖 goroutine 通信
- 消费端可以同步迭代
- 支持
Next()阻塞等待
消费模式:
1iter := agent.Run(ctx, input) 2for { 3 event, ok := iter.Next() 4 if !ok { 5 break 6 } 7 switch { 8 case event.Err != nil: 9 // 错误 10 case event.Action != nil && event.Action.Interrupted: 11 // 中断 12 case event.Output != nil: 13 // 输出 14 } 15} 16
小结
Eino ADK 的 Agent 生命周期用四个核心概念覆盖:
- TypedAgent 接口:Name + Description + Run → AsyncIterator
- ChatModelAgent:封装 ReAct 循环,工具调用,迭代限制
- Runner:管理执行、checkpoint、streaming、中断恢复
- FlowAgent:多 Agent 编排、transfer、history rewriting
结果是:一行 NewChatModelAgent + 一行 runner.Query,Agent 就能跑起来。中断恢复、事件流、多 Agent 编排——ADK 全管了。
下一篇(E99)讲两个"周边件":ACP 协议怎么把 Eino Agent 桥接到外部编辑器,devops 模块怎么把编译后的 Graph 变成可视化画布。

