核心摘要

ADK(Agent Development Kit)是 Eino 框架的最顶层抽象,提供开箱即用的 AI 智能体构建模式。本文从最简单的 ChatModelAgent 出发,逐步深入 Tool Use 循环、DeepAgent 深度推理、中断/恢复机制和状态管理,并通过一个完整的数据分析 Agent 实战案例,帮助你掌握用 Go 构建生产级智能体的核心技能。


目录

  1. 核心要点
  2. 什么是 ADK
  3. ChatModelAgent 详解
  4. Tool Use 完整流程
  5. DeepAgent 深度推理
  6. 人机协作:中断与恢复
  7. Agent 状态管理
  8. 预置 Agent 模式对比
  9. 实战:构建数据分析 Agent
  10. 最佳实践
  11. 常见问题
  12. 总结与相关资源

核心要点

  • ADK 定位:Eino 组件和编排能力之上的高层 Agent 抽象,封装了 Tool Use 循环、对话管理和状态持久化
  • ChatModelAgent:最简单的 Agent 模式——一个 ChatModel + 一组 Tools,自动处理多轮工具调用
  • DeepAgent:支持计划、反思和子任务分解的深度推理 Agent,适合复杂多步任务
  • 中断/恢复:原生支持人机协作(Human-in-the-Loop),Agent 可在任意步骤暂停等待人工审核
  • 状态管理:内置对话历史维护和 Checkpoint 机制,支持跨会话持久化

什么是 ADK

ADK(Agent Development Kit)是 Eino 框架的第三层能力,位于 Components(组件层)和 Composition(编排层)之上:

graph TB A["ADK - Agent 开发套件"] --> B["Composition - 编排层"] B --> C["Components - 组件层"] A --> D["ChatModelAgent"] A --> E["DeepAgent"] A --> F["Multi-Agent"] B --> G["Chain / Graph / Workflow"] C --> H["ChatModel / Tool / Retriever"]

ADK 解决的核心问题是:将 LLM 能力从「一次性问答」提升为「持续推理与行动」。它封装了以下关键能力:

  • Tool Use 循环:自动执行 LLM ↔ Tool 的多轮交互,直到任务完成
  • 对话记忆:自动维护多轮对话的上下文历史
  • 状态管理:支持 Checkpoint 序列化,实现中断后恢复
  • 预置模式:提供 ChatModelAgent、DeepAgent 等开箱即用的 Agent 模式

ChatModelAgent 详解

ChatModelAgent 是 ADK 中最简单也最常用的 Agent 模式,其核心组成为一个 ChatModel 加一组 Tools:

go
package main

import (
    "context"
    "fmt"

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

func main() {
    ctx := context.Background()

    // 初始化 ChatModel(以 OpenAI 为例)
    chatModel := initOpenAIChatModel()

    // 定义工具
    searchTool := createSearchTool()
    calculatorTool := createCalculatorTool()

    // 创建 ChatModelAgent
    agent, err := adk.NewChatModelAgent(ctx, &adk.ChatModelAgentConfig{
        Model:        chatModel,
        Tools:        []tool.BaseTool{searchTool, calculatorTool},
        SystemPrompt: "你是一个有帮助的助手,可以搜索信息和进行计算。",
        MaxSteps:     10,
    })
    if err != nil {
        panic(err)
    }

    // 运行 Agent
    result, err := agent.Run(ctx, "北京今天的气温是多少?换算成华氏度。")
    if err != nil {
        panic(err)
    }
    fmt.Println(result.Content)
}

配置参数说明

参数 类型 说明
Model ChatModel 底层大模型实例
Tools []tool.BaseTool 可用工具列表
SystemPrompt string 系统提示词,定义 Agent 行为
MaxSteps int 最大推理步数,防止无限循环

Tool Use 完整流程

Tool Use 是 Agent 的核心执行机制。以下是完整的交互流程:

sequenceDiagram participant User as 用户 participant Agent as Agent(LLM) participant Framework as Eino Framework participant Tool as 外部工具 User->>Agent: 发送消息 loop Tool Use 循环(直到 MaxSteps 或无 tool_call) Agent->>Framework: 返回 tool_call 消息 Framework->>Tool: 执行工具调用 Tool-->>Framework: 返回工具结果 Framework->>Agent: 将 tool_result 反馈给 LLM end Agent-->>User: 生成最终回复

执行步骤详解

  1. 用户输入:用户发送消息到 Agent
  2. LLM 决策:Agent 内部的 LLM 根据系统提示和对话历史决定下一步动作
  3. 工具调用:若 LLM 返回 tool_call,框架自动解析并执行对应工具
  4. 结果反馈:工具执行结果以 tool_result 消息形式反馈给 LLM
  5. 循环判断:LLM 可能继续调用工具或生成最终回复
  6. 终止条件:达到 MaxSteps 或 LLM 返回不含 tool_call 的消息时循环结束
go
// 自定义工具的实现示例
func createSearchTool() tool.BaseTool {
    return tool.NewSimpleTool(
        "web_search",
        "搜索互联网上的实时信息",
        func(ctx context.Context, params map[string]interface{}) (string, error) {
            query := params["query"].(string)
            // 调用搜索 API
            results, err := searchAPI.Search(ctx, query)
            if err != nil {
                return "", err
            }
            return formatResults(results), nil
        },
        tool.WithStringParam("query", "搜索关键词", true),
    )
}

DeepAgent 深度推理

DeepAgent 相比 ChatModelAgent 增加了计划、反思和子任务分解能力,适合处理复杂多步任务:

go
deepAgent, err := adk.NewDeepAgent(ctx, &adk.DeepAgentConfig{
    Model:        chatModel,
    Tools:        tools,
    SystemPrompt: "你是一个数据分析专家,擅长分解复杂问题并逐步解决。",
    MaxSteps:     25,
    Planning:     true,  // 启用任务规划
    Reflection:   true,  // 启用执行反思
})

DeepAgent 的推理流程

  1. 任务理解:分析用户请求,识别目标和约束
  2. 计划制定:将复杂任务拆解为有序的子任务列表
  3. 逐步执行:按计划依次执行子任务,每步可调用工具
  4. 中间反思:检查中间结果是否符合预期,必要时调整计划
  5. 结果整合:汇总所有子任务结果,生成最终回复

人机协作:中断与恢复

ADK 原生支持 Human-in-the-Loop(人机协作)模式,允许 Agent 在关键步骤暂停执行,等待人工审核后恢复:

go
// 注册工具调用拦截器
agent.OnToolCall(func(ctx context.Context, call *schema.ToolCall) (*schema.ToolResult, error) {
    // 对敏感操作(如发送邮件)进行拦截
    if call.Name == "send_email" {
        recipient := call.Args["to"].(string)
        subject := call.Args["subject"].(string)
        // 返回 Interrupt 暂停 Agent 执行
        return nil, adk.Interrupt(fmt.Sprintf(
            "请确认发送邮件:\n收件人: %s\n主题: %s",
            recipient, subject,
        ))
    }
    return nil, nil // 其他工具正常执行
})

// 执行 Agent(可能在某步中断)
result, err := agent.Run(ctx, "给张三发一封会议通知邮件")
if errors.Is(err, adk.ErrInterrupted) {
    // Agent 已中断,获取 Checkpoint
    checkpoint := agent.GetCheckpoint()
    // 持久化 checkpoint(如存入 Redis)
    saveCheckpoint(checkpoint)
    // 展示中断信息给用户
    fmt.Println("等待审核:", checkpoint.InterruptMessage)
}

// --- 人工审核后 ---

// 读取 checkpoint
checkpoint := loadCheckpoint()

// 构造人工审批结果
approvedResult := &schema.ToolResult{
    Name:    "send_email",
    Content: "邮件已确认发送",
}

// 恢复 Agent 执行
result, err := agent.Resume(ctx, checkpoint, approvedResult)
fmt.Println(result.Content) // "邮件已成功发送给张三"

中断/恢复的典型场景

  • 敏感操作确认(发送邮件、转账、删除数据)
  • 中间结果审核(报告生成、数据修改)
  • 分阶段长任务(需人工补充信息后继续)

Agent 状态管理

Agent 在执行过程中维护以下状态信息:

go
// Checkpoint 结构示意
type AgentCheckpoint struct {
    ConversationHistory []schema.Message   // 完整对话历史
    CurrentStep         int                // 当前执行步骤
    PendingToolCalls    []schema.ToolCall  // 待执行的工具调用
    InterruptMessage    string             // 中断原因
    Metadata            map[string]any     // 自定义元数据
}

// 状态持久化示例
func saveCheckpoint(cp *adk.AgentCheckpoint) error {
    data, err := json.Marshal(cp)
    if err != nil {
        return err
    }
    return redis.Set(ctx, "agent:checkpoint:"+sessionID, data, 24*time.Hour).Err()
}

// 状态恢复
func loadCheckpoint(sessionID string) (*adk.AgentCheckpoint, error) {
    data, err := redis.Get(ctx, "agent:checkpoint:"+sessionID).Bytes()
    if err != nil {
        return nil, err
    }
    var cp adk.AgentCheckpoint
    return &cp, json.Unmarshal(data, &cp)
}

状态管理最佳实践

  • 对话历史自动维护,无需手动追加消息
  • Checkpoint 支持 JSON 序列化,可存储于任意持久化层
  • 建议为 Checkpoint 设置 TTL,避免长期累积
  • 通过 Metadata 注入业务上下文(如用户 ID、会话 ID)

预置 Agent 模式对比

模式 适用场景 推理能力 复杂度 Tool Use
ChatModelAgent 简单工具调用、问答增强 单步推理 支持
DeepAgent 复杂分析、研究调研 多步推理+反思 支持
Multi-Agent 多角色协作、任务分工 协调推理 支持

选择建议:

  • 80% 的场景使用 ChatModelAgent 即可满足
  • 需要任务分解和反思时升级为 DeepAgent
  • 需要多个专业角色协作时使用 Multi-Agent(详见下一篇文章

实战:构建数据分析 Agent

以下是一个完整的数据分析 Agent 示例,集成了数据查询、JSON 处理和报告生成能力:

go
package main

import (
    "context"
    "encoding/json"
    "fmt"
    "log"

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

// 数据查询工具
func createQueryTool() tool.BaseTool {
    return tool.NewSimpleTool(
        "query_database",
        "执行 SQL 查询获取数据",
        func(ctx context.Context, params map[string]interface{}) (string, error) {
            sql := params["sql"].(string)
            rows, err := db.QueryContext(ctx, sql)
            if err != nil {
                return "", fmt.Errorf("查询失败: %w", err)
            }
            defer rows.Close()
            results := scanRows(rows)
            data, _ := json.Marshal(results)
            return string(data), nil
        },
        tool.WithStringParam("sql", "SQL 查询语句", true),
    )
}

// 数据分析工具
func createAnalysisTool() tool.BaseTool {
    return tool.NewSimpleTool(
        "analyze_data",
        "对数据进行统计分析(均值、中位数、趋势等)",
        func(ctx context.Context, params map[string]interface{}) (string, error) {
            data := params["data"].(string)
            method := params["method"].(string)
            result := performAnalysis(data, method)
            return result, nil
        },
        tool.WithStringParam("data", "JSON 格式的数据", true),
        tool.WithStringParam("method", "分析方法: mean/median/trend/distribution", true),
    )
}

// 报告生成工具
func createReportTool() tool.BaseTool {
    return tool.NewSimpleTool(
        "generate_report",
        "根据分析结果生成 Markdown 格式报告",
        func(ctx context.Context, params map[string]interface{}) (string, error) {
            findings := params["findings"].(string)
            title := params["title"].(string)
            report := fmt.Sprintf("# %s\n\n%s\n", title, findings)
            return report, nil
        },
        tool.WithStringParam("title", "报告标题", true),
        tool.WithStringParam("findings", "分析发现的内容", true),
    )
}

func main() {
    ctx := context.Background()

    // 初始化模型
    chatModel, err := openai.NewChatModel(ctx, &openai.ChatModelConfig{
        Model:  "gpt-4o",
        APIKey: os.Getenv("OPENAI_API_KEY"),
    })
    if err != nil {
        log.Fatal(err)
    }

    // 创建数据分析 Agent
    agent, err := adk.NewDeepAgent(ctx, &adk.DeepAgentConfig{
        Model: chatModel,
        Tools: []tool.BaseTool{
            createQueryTool(),
            createAnalysisTool(),
            createReportTool(),
        },
        SystemPrompt: `你是一个专业的数据分析师。你的工作流程是:
1. 理解用户的分析需求
2. 编写 SQL 查询获取数据
3. 对数据进行统计分析
4. 生成结构化分析报告

注意:SQL 查询必须包含 LIMIT 子句防止数据量过大。`,
        MaxSteps:   20,
        Planning:   true,
        Reflection: true,
    })
    if err != nil {
        log.Fatal(err)
    }

    // 添加敏感操作拦截
    agent.OnToolCall(func(ctx context.Context, call *schema.ToolCall) (*schema.ToolResult, error) {
        if call.Name == "query_database" {
            sql := call.Args["sql"].(string)
            if containsDangerousSQL(sql) {
                return nil, adk.Interrupt("检测到危险 SQL 操作,请人工审核: " + sql)
            }
        }
        return nil, nil
    })

    // 执行分析任务
    result, err := agent.Run(ctx, "分析过去 30 天的用户注册趋势,按日统计并识别异常波动")
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println(result.Content)
}

func containsDangerousSQL(sql string) bool {
    // 检测 DROP、DELETE、UPDATE 等危险操作
    dangerousKeywords := []string{"DROP", "DELETE", "UPDATE", "TRUNCATE", "ALTER"}
    upperSQL := strings.ToUpper(sql)
    for _, keyword := range dangerousKeywords {
        if strings.Contains(upperSQL, keyword) {
            return true
        }
    }
    return false
}

该示例展示了如何将 JSON 数据结构与 Agent 工具链结合,构建端到端的数据分析流水线。


最佳实践

工具设计原则

  • 每个工具职责单一,描述清晰准确(LLM 依赖描述决定何时调用)
  • 工具返回值使用结构化格式(JSON),便于 LLM 解析
  • 对耗时工具添加 Context 超时控制

Agent 配置建议

  • SystemPrompt 明确指定 Agent 角色、能力边界和工作流程
  • MaxSteps 根据任务复杂度合理设置(简单 5-10,复杂 15-25)
  • 生产环境必须配置 Callback 监控每步执行耗时和 Token 消耗

错误处理

  • 工具执行失败时返回有意义的错误信息(LLM 可据此调整策略)
  • 为 Agent 添加全局超时,防止异常情况下无限执行
  • 使用 Eino 的回调系统记录执行链路

常见问题

Q: ADK 和直接使用 Eino 组件有什么区别?

ADK 是 Eino 组件和编排能力之上的高层抽象。直接使用组件需要你手动处理 Tool Use 循环、对话历史管理和错误重试;ADK 将这些封装为开箱即用的 Agent 模式,让你专注于业务逻辑而非基础设施。

Q: ChatModelAgent 和 DeepAgent 应该选哪个?

ChatModelAgent 适合简单的工具调用场景(如搜索、计算、API 查询);DeepAgent 适合需要多步推理、任务分解和反思的复杂任务(如数据分析、报告生成、研究调研)。选择依据是任务的推理深度。

Q: 中断/恢复机制在生产环境中如何持久化?

Checkpoint 数据可以序列化为 JSON 并存储在 Redis、数据库或文件系统中。恢复时读取 checkpoint 反序列化后传入 agent.Resume() 即可。建议给 checkpoint 设置 TTL 避免无限累积。

Q: MaxSteps 设置为多少合适?

简单工具调用场景建议 5-10 步;复杂多工具协作场景建议 15-25 步。设置过小会导致 Agent 无法完成任务,设置过大可能导致无限循环消耗 Token。建议配合 Callback 监控实际步数分布后调优。

Q: Eino ADK 支持流式输出吗?

支持。ChatModelAgent 和 DeepAgent 均可通过 StreamRun 方法启用流式输出,配合 Eino 的 Callback 系统可以实时追踪每一步推理过程。详见本系列第四篇关于流式与回调的文章。


总结与相关资源

ADK 是 Eino 框架中将 LLM 能力转化为实际行动力的关键层。通过 ChatModelAgent 快速构建工具增强型 Agent,通过 DeepAgent 处理复杂推理任务,再配合中断/恢复机制实现安全的人机协作——这套组合拳覆盖了绝大多数生产级 Agent 场景。

下一步学习:Eino 多 Agent 协调模式将深入探讨如何让多个 Agent 协作完成更复杂的任务。

相关资源