中文资讯 掘金 AI Fri, 28 Aug 2026 正文 ok 652 words

Eino ADK:Agent 完整生命周期,从创建到中断恢复(第98篇-E84)

英文标题:Eino ADK——Agent 的完整生命周期:从创建到中断恢复(第98篇-E84)

Eino ADK(Agent Development Kit)通过 TypedAgent、ChatModelAgent、Runner 和 FlowAgent 等核心抽象,覆盖 Agent 从创建、执行、工具调用到中断恢复的完整生命周期。ChatModelAgent 内置 ReAct 循环,默认最大迭代次数为 20,并以 AsyncIterator 事件流而非一次性结果的形式输出执行过程。Runner 负责 checkpoint、流式执行及 Resume 恢复能力,使人工审批和外部输入等待等长流程任务可被安全暂停与继续。对开发者而言,ADK 将原本需手写的 Graph、消息历史、工具调用和错误恢复逻辑封装起来,可通过 `NewChatModelAgent` 与 `runner.Query` 快速启动 Agent。
打开原文相关 3 · 机会 1
media3.2 KB media589 B media5.4 KB

中文

上一篇讲了数据库迁移如何避免踩坑。但 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 的最小契约:

type TypedAgent[M MessageType] interface {
    Name(ctx context.Context) string
    Description(ctx context.Context) string
    Run(ctx context.Context, input *TypedAgentInput[M], options ...AgentRunOption) *AsyncIterator[*TypedAgentEvent[M]]
}

三个方法:

  • 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:

type TypedAgentEvent[M MessageType] struct {
    AgentName string
    RunPath   []RunStep
    Output    *TypedAgentOutput[M]
    Action    *AgentAction
    Err       error
}

(二)ChatModelAgent:最常用的 Agent 实现

ChatModelAgent 是 ADK 的默认实现,封装了 ReAct 循环。创建方法位于 adk/chatmodel.go:502-504:

func NewChatModelAgent(ctx context.Context, config *ChatModelAgentConfig) (*ChatModelAgent, error)

配置项位于 adk/chatmodel.go:260-369,核心字段如下:

字段 作用 默认值
Name Agent 名称 空
Description Agent 描述 空
Instruction 系统提示词 空
Model 聊天模型 必填
ToolsConfig 工具配置 空
MaxIterations 最大迭代次数 20
GenModelInput 输入转换函数 默认拼接 instruction + messages
Middlewares 中间件(已废弃) 空
Handlers 新版中间件 空

最简创建方式:

agent, _ := adk.NewChatModelAgent(ctx, &adk.ChatModelAgentConfig{
    Name:  "MyAgent",
    Model: myModel,
})

(三)ReAct 循环:Agent 如何“思考→行动→再思考”

ChatModelAgent 的核心是 ReAct 循环。状态定义于 adk/react.go:35-57:

type typedState[M MessageType] struct {
    Messages              []M
    ToolInfos             []*schema.ToolInfo
    HasReturnDirectly     bool
    ReturnDirectlyToolCallID string
    AgentName             string
    RemainingIterations   int
    
}

执行流程:

  1. 构建初始消息:system prompt(Instruction)+ 用户输入。
  2. 调用 ChatModel:传入消息历史与工具列表。
  3. 检查响应:
    • 无工具调用 → 作为最终回复,返回事件流。
    • 有工具调用 → 执行工具,并将结果追加到消息历史。
  4. 循环:回到步骤 2,直到不再有工具调用或超过 MaxIterations。

超过 MaxIterations 时,将返回 ErrExceedMaxIterations(adk/react.go:33):

var ErrExceedMaxIterations = errors.New("exceeds max iterations")

(四)Runner:Agent 的执行器

Runner 是 Agent 的执行器,负责管理 checkpoint、streaming 模式和中断恢复。定义于 adk/runner.go:50-59:

type TypedRunner[M MessageType] struct {
    a               TypedAgent[M]
    enableStreaming bool
    store           CheckPointStore
}

创建和运行方法位于 adk/runner.go:94-115:

func NewTypedRunner[M MessageType](conf TypedRunnerConfig[M]) *TypedRunner[M]

func (r *TypedRunner[M]) Run(ctx context.Context, messages []M, opts ...AgentRunOption) *AsyncIterator[*TypedAgentEvent[M]]

func (r *TypedRunner[M]) Query(ctx context.Context, query string, opts ...AgentRunOption) *AsyncIterator[*TypedAgentEvent[M]]

Query 是 Run 的语法糖:内部会先将字符串封装为一条用户消息,再调用 Run。

消费事件流:

iter := runner.Query(ctx, "今天天气怎么样?")
for {
    event, ok := iter.Next()
    if !ok {
        break
    }
    if event.Err != nil {
        
    }
    if event.Output != nil {
        
    }
}

(五)Interrupt / Resume:Agent 的中断与恢复

Agent 并非每次都能一口气完成执行。人工确认、等待外部输入、审批等场景,都需要中断机制。

中断:Agent 返回 Action.Interrupted = true 的事件,暂停执行。

恢复:调用 Runner.Resume 或 Runner.ResumeWithParams,携带恢复数据后继续执行。

中断信息定义于 adk/interrupt.go:33-55:

type ResumeInfo struct {
    EnableStreaming bool
    *InterruptInfo
    WasInterrupted bool
    InterruptState any
    IsResumeTarget bool
    ResumeData     any
}

type InterruptInfo struct {
    Data             any
    InterruptContexts []*InterruptCtx
}

Runner 的 Resume 方法位于 adk/runner.go:124-149:

func (r *TypedRunner[M]) Resume(ctx context.Context, checkPointID string, opts ...AgentRunOption) (*AsyncIterator[*TypedAgentEvent[M]], error)

func (r *TypedRunner[M]) ResumeWithParams(ctx context.Context, checkPointID string, params *ResumeParams, opts ...AgentRunOption) (*AsyncIterator[*TypedAgentEvent[M]], error)

流程:

  1. Agent 执行过程中调用 TypedInterrupt,返回中断事件。
  2. Runner 检测到 Action.Interrupted,保存 checkpoint。
  3. 外部系统处理中断事项,例如人工审批。
  4. 调用 Resume/ResumeWithParams,携带数据后继续执行。
  5. Agent 从 checkpoint 恢复状态,继续执行。

(六)FlowAgent:多 Agent 编排

FlowAgent 是 ADK 的多 Agent 编排层。定义于 adk/flow.go:42-52:

type flowAgent struct {
    Agent
    subAgents                []*flowAgent
    parentAgent              *flowAgent
    disallowTransferToParent bool
    historyRewriter          HistoryRewriter
    checkPointStore          compose.CheckPointStore
}

关键能力:

  • 子 Agent 注册:通过 SetSubAgents 将 Agent 注入编排树。
  • Transfer:Agent 可以将任务转交给子 Agent 或父 Agent。
  • History Rewriting:在 Transfer 时重写消息历史,将上一个 Agent 的回复改写为用户消息。
  • Checkpoint:支持用于中断恢复的持久化能力。

注意:源码多处标注“NOT RECOMMENDED”,建议使用 AgentTool 或 DeepAgent 替代 Transfer 模式。实践中,Agent 之间全量共享上下文的效果不如预期。

(七)生命周期状态机

ChatModelAgent 内部使用 frozen 标记(通过 sync.Once 保证仅初始化一次)以及懒加载机制。但其对外暴露的生命周期可以简化为:

Created → Running → Completed
                  → Interrupted → Running → Completed
                  → Error
  • Created:NewChatModelAgent 返回后,尚未冻结。
  • Running:首次调用 Run 时触发 sync.Once 初始化,进入执行状态。
  • Completed:模型返回不含工具调用的最终回复。
  • Interrupted:Agent 发出中断事件,等待外部输入。
  • Error:超过 MaxIterations、模型错误或工具错误。

(八)事件流:AsyncIterator 的设计

ADK 没有使用 channel,而是采用 AsyncIterator 模式。其优势包括:

  • 不依赖 goroutine 通信。
  • 消费端可以进行同步迭代。
  • 支持通过 Next() 阻塞等待。

消费模式:

iter := agent.Run(ctx, input)
for {
    event, ok := iter.Next()
    if !ok {
        break
    }
    switch {
    case event.Err != nil:
        
    case event.Action != nil && event.Action.Interrupted:
        
    case event.Output != nil:
        
    }
}

小结

Eino ADK 的 Agent 生命周期由四个核心概念覆盖:

  1. TypedAgent 接口:Name + Description + Run → AsyncIterator。
  2. ChatModelAgent:封装 ReAct 循环、工具调用和迭代限制。
  3. Runner:管理执行、checkpoint、streaming 和中断恢复。
  4. FlowAgent:负责多 Agent 编排、transfer 和 history rewriting。

最终,一行 NewChatModelAgent + 一行 runner.Query,Agent 即可运行。中断恢复、事件流和多 Agent 编排,均由 ADK 统一管理。

下一篇(E99)将介绍两个“周边组件”:ACP 协议如何将 Eino Agent 桥接至外部编辑器,以及 devops 模块如何将编译后的 Graph 转化为可视化画布。

English / 原文

上一篇 讲了数据库迁移怎么不翻车。但 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 的最小契约:

type TypedAgent[M MessageType] interface {
    Name(ctx context.Context) string
    Description(ctx context.Context) string
    Run(ctx context.Context, input *TypedAgentInput[M], options ...AgentRunOption) *AsyncIterator[*TypedAgentEvent[M]]
}

三个方法:

  • 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:

type TypedAgentEvent[M MessageType] struct {
    AgentName string
    RunPath   []RunStep
    Output    *TypedAgentOutput[M]
    Action    *AgentAction
    Err       error
}

(二)ChatModelAgent:最常用的 Agent 实现

ChatModelAgent 是 ADK 的默认实现,封装了 ReAct 循环。创建方式在 adk/chatmodel.go:502-504:

func NewChatModelAgent(ctx context.Context, config *ChatModelAgentConfig) (*ChatModelAgent, error)

配置项在 adk/chatmodel.go:260-369,核心字段:

字段 作用 默认值
Name Agent 名称 空
Description Agent 描述 空
Instruction 系统提示词 空
Model 聊天模型 必填
ToolsConfig 工具配置 空
MaxIterations 最大迭代次数 20
GenModelInput 输入转换函数 默认拼接 instruction + messages
Middlewares 中间件(已废弃) 空
Handlers 新版中间件 空

最简创建:

agent, _ := adk.NewChatModelAgent(ctx, &adk.ChatModelAgentConfig{
    Name:  "MyAgent",
    Model: myModel,
})

(三)ReAct 循环:Agent 怎么"思考→行动→再思考"

ChatModelAgent 的核心是 ReAct 循环。状态定义在 adk/react.go:35-57:

type typedState[M MessageType] struct {
    Messages              []M
    ToolInfos             []*schema.ToolInfo
    HasReturnDirectly     bool
    ReturnDirectlyToolCallID string
    AgentName             string
    RemainingIterations   int
    
}

执行流程:

  1. 构建初始消息 :system prompt(Instruction)+ 用户输入
  2. 调用 ChatModel :传入消息历史 + 工具列表
  3. 检查响应 :
    • 无工具调用 → 最终回复,返回事件流
    • 有工具调用 → 执行工具,将结果追加到消息历史
  4. 循环 :回到步骤 2,直到无工具调用或超过 MaxIterations

超过 MaxIterations 时返回 ErrExceedMaxIterations(adk/react.go:33):

var ErrExceedMaxIterations = errors.New("exceeds max iterations")

(四)Runner:Agent 的执行器

Runner 是 Agent 的执行器,管理 checkpoint、streaming 模式、中断恢复。定义在 adk/runner.go:50-59:

type TypedRunner[M MessageType] struct {
    a               TypedAgent[M]
    enableStreaming bool
    store           CheckPointStore
}

创建和运行在 adk/runner.go:94-115:

func NewTypedRunner[M MessageType](conf TypedRunnerConfig[M]) *TypedRunner[M]


func (r *TypedRunner[M]) Run(ctx context.Context, messages []M, opts ...AgentRunOption) *AsyncIterator[*TypedAgentEvent[M]]


func (r *TypedRunner[M]) Query(ctx context.Context, query string, opts ...AgentRunOption) *AsyncIterator[*TypedAgentEvent[M]]

Query 是 Run 的语法糖,内部把字符串包装成一条用户消息后调用 Run。

消费事件流:

iter := runner.Query(ctx, "今天天气怎么样?")
for {
    event, ok := iter.Next()
    if !ok {
        break
    }
    if event.Err != nil {
        
    }
    if event.Output != nil {
        
    }
}

(五)Interrupt / Resume:Agent 的中断与恢复

Agent 不是每次都能一口气跑完。需要人工确认、等待外部输入、审批——这些场景需要中断机制。

中断 :Agent 返回 Action.Interrupted = true 的事件,暂停执行。

恢复 :调用 Runner.Resume 或 Runner.ResumeWithParams,携带恢复数据继续。

中断信息定义在 adk/interrupt.go:33-55:

type ResumeInfo struct {
    EnableStreaming bool
    *InterruptInfo
    WasInterrupted bool
    InterruptState any
    IsResumeTarget bool
    ResumeData     any
}

type InterruptInfo struct {
    Data             any
    InterruptContexts []*InterruptCtx
}

Runner 的 Resume 在 adk/runner.go:124-149:

func (r *TypedRunner[M]) Resume(ctx context.Context, checkPointID string, opts ...AgentRunOption) (*AsyncIterator[*TypedAgentEvent[M]], error)


func (r *TypedRunner[M]) ResumeWithParams(ctx context.Context, checkPointID string, params *ResumeParams, opts ...AgentRunOption) (*AsyncIterator[*TypedAgentEvent[M]], error)

流程:

  1. Agent 执行中调用 TypedInterrupt 返回中断事件
  2. Runner 检测到 Action.Interrupted,保存 checkpoint
  3. 外部系统处理中断(人工审批等)
  4. 调用 Resume/ResumeWithParams 携带数据继续
  5. Agent 从 checkpoint 恢复状态,继续执行

(六)FlowAgent:多 Agent 编排

FlowAgent 是 ADK 的多 Agent 编排层。定义在 adk/flow.go:42-52:

type flowAgent struct {
    Agent
    subAgents                []*flowAgent
    parentAgent              *flowAgent
    disallowTransferToParent bool
    historyRewriter          HistoryRewriter
    checkPointStore          compose.CheckPointStore
}

关键能力:

  • 子 Agent 注册 :SetSubAgents 把 Agent 注入到编排树
  • Transfer :Agent 可以把任务转交给子 Agent 或父 Agent
  • History Rewriting :Transfer 时重写消息历史,把上一个 Agent 的回复改写成用户消息
  • Checkpoint :支持中断恢复的持久化

注意 :源码多处标注"NOT RECOMMENDED",建议用 AgentTool 或 DeepAgent 替代 Transfer 模式。Agent 间全量上下文共享在实践中效果不如预期。

(七)生命周期状态机

ChatModelAgent 内部有 frozen 标记(sync.Once 保证只初始化一次)和懒加载机制。但对外暴露的生命周期可以简化为:

Created → Running → Completed
                  → Interrupted → Running → Completed
                  → Error
  • Created :NewChatModelAgent 返回,未冻结
  • Running :首次 Run 调用触发 sync.Once 初始化,进入执行
  • Completed :模型返回无工具调用的最终回复
  • Interrupted :Agent 发出中断事件,等待外部输入
  • Error :超过 MaxIterations、模型错误、工具错误

(八)事件流:AsyncIterator 的设计

ADK 没有用 channel,而是用 AsyncIterator 模式。好处:

  • 不依赖 goroutine 通信
  • 消费端可以同步迭代
  • 支持 Next() 阻塞等待

消费模式:

iter := agent.Run(ctx, input)
for {
    event, ok := iter.Next()
    if !ok {
        break
    }
    switch {
    case event.Err != nil:
        
    case event.Action != nil && event.Action.Interrupted:
        
    case event.Output != nil:
        
    }
}

小结

Eino ADK 的 Agent 生命周期用四个核心概念覆盖:

  1. TypedAgent 接口 :Name + Description + Run → AsyncIterator
  2. ChatModelAgent :封装 ReAct 循环,工具调用,迭代限制
  3. Runner :管理执行、checkpoint、streaming、中断恢复
  4. FlowAgent :多 Agent 编排、transfer、history rewriting

结果是:一行 NewChatModelAgent + 一行 runner.Query,Agent 就能跑起来。中断恢复、事件流、多 Agent 编排——ADK 全管了。

下一篇(E99)讲两个"周边件":ACP 协议怎么把 Eino Agent 桥接到外部编辑器,devops 模块怎么把编译后的 Graph 变成可视化画布。