从零开始构建 AI Agent(一):理解 Eino 的 Component 抽象与流式对话

用 Go 语言快速构建可扩展、可维护的 AI 应用 ------ 第一章:ChatModel 与 AgenticMessage

AI 应用开发正从"调用一次 API"演变为"编排复杂工作流"。我们需要处理多种大语言模型提供商、管理对话状态、支持工具调用、实现流式输出...... 如何避免代码变成一堆难以维护的 if-else 和硬编码?Eino 给出了答案。

Eino 是一个基于 Go 的 AI 应用开发框架(Agent Development Kit),它通过统一的 Component 接口 抽象出模型、工具、检索器等能力单元,并提供了 Agent、Graph、Chain 等编排抽象,让开发者能够像搭积木一样构建生产级 AI Agent。

本系列将带你从零开始,逐步实现一个完整的智能助手 ------ ChatWithEino 。今天的第一章,我们从最基础的 ChatModel 调用开始,理解 Eino 的核心设计思想,并亲手写出第一个支持流式输出的对话程序。


一、为什么需要 Component 接口?

在传统代码中,调用 OpenAI 和调用 Ark 可能是两套完全不同的 API:

go 复制代码
// 硬编码 OpenAI
resp, _ := openai.CreateChatCompletion(...)

// 换成 Ark 需要重写所有调用代码
resp, _ := ark.Generate(...)

Eino 定义了统一的 BaseModel 接口:

go 复制代码
type BaseModel[M any] interface {
    Generate(ctx context.Context, input []M, opts ...Option) (M, error)
    Stream(ctx context.Context, input []M, opts ...Option) (*schema.StreamReader[M], error)
}

好处显而易见

  • 实现可替换:eino-ext 提供了 OpenAI、Ark、Claude、Ollama 等多种实现,业务代码只依赖接口。切换模型只需修改构造逻辑,业务逻辑一行不改。
  • 编排可组合:Agent、Graph 等高层抽象只依赖接口,不关心具体实现。你可以自由组合不同提供商的组件。
  • 测试可 Mock:接口天然支持 mock,单元测试不需要真实调用模型,也不会消耗 API 额度。

本章只涉及 ChatModel,后续会逐步引入 Tool、Retriever 等组件,但它们都遵循同样的接口哲学。


二、AgenticMessage:对话的基本单位

Eino 的 Quickstart 系列使用 schema.AgenticMessage 作为统一的消息结构。它比普通的文本消息更强大,能够表达模型在一次回复中的多个有序事件:

go 复制代码
type AgenticMessage struct {
    Role          AgenticRoleType          // system / user / assistant
    ContentBlocks []*ContentBlock          // 有序的内容块
    ResponseMeta  *AgenticResponseMeta     // 元信息
    Extra         map[string]any           // 扩展字段
}

ContentBlock 可以包含推理内容(reasoning)、普通文本、工具调用、工具结果等。这对于支持 推理时调用工具(如 DeepSeek‑R1、Claude 的 tool use)至关重要。

常用的构造函数简化了创建:

go 复制代码
schema.SystemAgenticMessage("You are a helpful assistant.")
schema.UserAgenticMessage("What is the weather today?")

// 手动构造 assistant 回复
&schema.AgenticMessage{
    Role: schema.AgenticRoleTypeAssistant,
    ContentBlocks: []*schema.ContentBlock{
        schema.NewContentBlock(&schema.AssistantGenText{Text: "I don't know."}),
    },
}

角色语义与 OpenAI 一致:system 给出系统指令,user 为用户输入,assistant 为模型回复。


三、实战:第一个流式对话程序

我们将编写一个简单的命令行程序,接收用户问题,调用 ChatModel 的流式接口,并逐词打印回复。

前置准备

  1. 获取代码 (本系列基于 eino-examples 仓库):
bash 复制代码
git clone https://github.com/cloudwego/eino-examples.git
cd eino-examples/quickstart/chatwitheino
  1. Go 环境:1.21 或更高版本。

  2. 选择一个模型提供商(以 OpenAI 为例):

bash 复制代码
export OPENAI_API_KEY="your-api-key"
export OPENAI_MODEL="gpt-4.1-mini"   # 或 gpt-4o, gpt-4o-mini
# 可选:OPENAI_BASE_URL(代理或兼容服务)
# 可选:OPENAI_BY_AZURE=true(使用 Azure OpenAI)

代码解析

入口文件位于 cmd/ch01/main.go。下面是核心逻辑的简化版本:

go 复制代码
func runTyped[M adk.MessageType](ctx context.Context, instruction, query string) {
    // 1. 创建 ChatModel(根据环境变量自动选择 OpenAI/Ark)
    cm, err := chatmodel.NewModel[M](ctx)
    if err != nil {
        log.Fatal(err)
    }

    // 2. 构造消息:system + user
    messages := []M{
        msgops.NewSystem[M](instruction),
        msgops.NewUser[M](query),
    }

    // 3. 调用流式接口
    stream, err := cm.Stream(ctx, messages)
    if err != nil {
        log.Fatal(err)
    }
    defer stream.Close()

    // 4. 逐帧读取并打印
    for {
        frame, err := stream.Recv()
        if errors.Is(err, io.EOF) {
            break
        }
        if err != nil {
            log.Fatal(err)
        }
        fmt.Print(msgops.AssistantDeltaText(frame)) // 只输出增量文本
    }
}

执行命令

bash 复制代码
go run ./cmd/ch01 -- "用一句话解释 Eino 的 Component 设计解决了什么问题?"

输出示例(流式逐步打印):

复制代码
[assistant] Eino 的 Component 设计通过定义统一接口,让开发者可以无缝切换不同 LLM 提供商而不影响业务代码。

关键点说明

  • chatmodel.NewModel[M] :根据 MODEL_TYPE 环境变量,自动构造 OpenAI 或 Ark 的 agentic model 实例。这就是接口可替换性的体现。
  • msgops.NewSystem/NewUser :封装了 AgenticMessage 的创建,返回泛型 M(即 *schema.AgenticMessage)。
  • cm.Stream :返回 *schema.StreamReader[M],它是一个可迭代的流。每一帧通常是一个包含 ContentBlock 的消息片段。
  • msgops.AssistantDeltaText:从一帧消息中提取出本次新增的助手文本。因为流式输出可能将一段文字拆成多帧。

四、从本章到下一章

现在,我们已经能够完成单次对话了。但现实中的 Agent 需要记住多轮对话历史、支持用户跨会话恢复、调用外部工具......

接下来的章节将逐一引入:

章节 核心能力 解决的问题
第二章 Agent + Runner 多轮对话,对话状态管理
第三章 Memory + Session 持久化历史,会话恢复
第四章 Tool 文件读取、代码搜索等工具调用
... ... ...

每一章都在前一章的基础上增加一个核心能力,让你看到架构如何从简单演变为健壮的生产系统。


五、总结

本章我们学到了:

  1. Eino 的 Component 接口 带来了可替换、可组合、可测试的架构优势。
  2. AgenticMessage 是一个能够表达复杂对话事件(推理、工具调用、文本)的统一消息结构。
  3. 通过 BaseModel.Stream 可以轻松实现流式对话,提升用户体验。
  4. 整个 Quickstart 系列遵循渐进式构建的理念,从最简单的 ChatModel 开始,逐步叠加功能。

下一章,我们将引入 Agent 与 Runner,让程序具备多轮对话的能力。敬请期待!


附:本章完整代码

eino-examples/quickstart/chatwitheino/cmd/ch01/main.go

相关资源


本文由 ChatWithEino 智能助手协助撰写,它本身也是用 Eino 构建的 😊

本系列文档基于 Eino v0.3+,Go 1.21+。

相关推荐
IT_陈寒1 小时前
Vite项目build后路由404了?你可能漏了这个小配置
前端·人工智能·后端
海兰1 小时前
【小程序】基于 AI 大语言模型驱动的中国古典诗词 Web 应用详细设计指南
人工智能·语言模型·小程序
团象科技1 小时前
从一线运营场景观察 海外云 独立站的跨境效能释放实践路径
大数据·人工智能
今日综合2 小时前
2026免费AI自动抠图工具汇总:全平台+电脑在线全方案,无水印零套路
人工智能·电脑
宸津-代码粉碎机2 小时前
Spring AI企业级实战|从RAG优化到Agent多工具调度
java·大数据·人工智能·后端·python·spring
小柒儿3362 小时前
汪进进:深水区里以质立身,做长期价值的践行者
大数据·人工智能
救救孩子把2 小时前
88-机器学习与大模型开发数学教程-8-6 矩阵分解与低秩近似在推荐系统中的应用
人工智能·机器学习·矩阵
阿里云大数据AI技术2 小时前
Agentic Search + Memory:当企业研究遇上"会思考的搜索"
人工智能·elasticsearch