11 KiB
tags, create time, date, lastmod, title, weight
| tags | create time | date | lastmod | title | weight | ||||
|---|---|---|---|---|---|---|---|---|---|
|
2026-04-29 14:30 | 2026-03-24 | 第一章:ChatModel 与 Message(Console) | 1 |
概述
本章是 Eino 快速入门系列的第一章,带你理解 Eino 的 Component 抽象设计,并通过最简代码实现一次 ChatModel 调用(支持流式输出)。你将掌握 schema.Message 的基本用法,为后续构建完整的 ChatWithEino Agent 打下基础。
Eino 框架简介
[!question] 思考一下 如果你要开发一个 AI 应用,需要支持 OpenAI、Claude、豆包等多个模型,你会如何设计代码架构才能让切换模型变得简单?
Eino 是什么?
Eino 是一个 Go 语言实现的 AI 应用开发框架(Agent Development Kit),旨在帮助开发者快速构建可扩展、可维护的 AI 应用。
Eino 解决什么问题?
- 模型抽象:统一不同 LLM 提供商的接口(OpenAI、Ark、Claude 等),切换模型无需修改业务代码
- 能力组合:通过 Component 接口实现可替换、可组合的能力单元(对话、工具、检索等)
- 编排框架:提供 Agent、Graph、Chain 等编排抽象,支持复杂的多步骤 AI 工作流
- 运行时支持:内置流式输出、中断与恢复、状态管理、Callback 可观测性等能力
Eino 的主要仓库:
- eino(本仓库):核心库,定义接口、编排抽象和 ADK
- eino-ext:扩展库,提供各类 Component 的具体实现(OpenAI、Ark、Milvus 等)
- eino-examples:示例代码库,包含本 quickstart 系列
ChatWithEino:与 Eino 文档对话的智能助手
ChatWithEino 是什么?
ChatWithEino 是一个基于 Eino 框架构建的智能助手,能够帮助开发者学习 Eino 框架并编写 Eino 代码。它通过访问 Eino 仓库的源码、注释和示例,为用户提供最准确、最及时的技术支持。
核心能力:
- 对话交互:理解用户关于 Eino 的问题,提供清晰的解答
- 代码访问:直接读取 Eino 源码、注释和示例,基于真实实现回答问题
- 持久化会话:支持多轮对话,记住上下文,可跨进程恢复会话
- 工具调用:能够执行文件读取、代码搜索等操作
技术架构:
- ChatModel:与大语言模型通信(OpenAI、Ark、Claude 等)
- Tool:文件系统访问、代码搜索等能力扩展
- Memory:对话历史持久化存储
- Agent:统一的执行框架,协调各组件协同工作
Quickstart 文档系列:从零构建 ChatWithEino
本系列文档通过循序渐进的方式,带你从最基础的 ChatModel 调用开始,逐步构建一个功能完整的 ChatWithEino Agent。
学习路径:
| 章节 | 主题 | 核心内容 | 能力提升 |
| 第一章 | ChatModel 与 Message | 理解 Component 抽象,实现单次对话 | 基础对话能力 |
| 第二章 | Agent 与 Runner | 引入执行抽象,实现多轮对话 | 会话管理能力 |
| 第三章 | Memory 与 Session | 持久化对话历史,支持会话恢复 | 持久化能力 |
| 第四章 | Tool 与文件系统 | 添加文件访问能力,读取源码 | 工具调用能力 |
| 第五章 | Middleware | 中间件机制,统一处理横切关注点 | 扩展性增强 |
| 第六章 | Callback | 回调机制,监控 Agent 执行过程 | 可观测性 |
| 第七章 | Interrupt 与 Resume | 中断与恢复,支持长时间任务 | 可靠性增强 |
| 第八章 | Graph 与 Tool | 使用 Graph 编排复杂工作流 | 复杂编排能力 |
| 第九章 | A2UI | Agent 到 UI 的集成方案 | 生产级应用 |
为什么这样设计?
每一章都在前一章的基础上增加一个核心能力,让你:
- 理解每个组件的作用:不是一次性展示所有功能,而是逐步引入
- 看到架构演进过程:从简单到复杂,理解为什么需要每个抽象
- 掌握实际开发技能:每章都有可运行的代码,可以动手实践
本章目标:理解 Eino 的 Component 抽象,用最小代码调用一次 ChatModel(支持流式输出),并掌握 schema.Message 的基本用法。
代码位置
- 入口代码:cmd/ch01/main.go
为什么需要 Component 接口
Eino 定义了一组 Component 接口(ChatModel、Tool、Retriever、Loader 等),每个接口描述一类可替换的能力:
type BaseChatModel interface {
Generate(ctx context.Context, input []*schema.Message, opts ...Option) (*schema.Message, error)
Stream(ctx context.Context, input []*schema.Message, opts ...Option) (
*schema.StreamReader[*schema.Message], error)
}
接口带来的好处:
- 实现可替换:
eino-ext提供了 OpenAI、Ark、Claude、Ollama 等多种实现,业务代码只依赖接口,切换模型只需改构造逻辑。 - 编排可组合:Agent、Graph、Chain 等编排层只依赖 Component 接口,不关心具体实现。你可以把 OpenAI 换成 Ark,编排代码无需改动。
- 测试可 Mock:接口天然支持 mock,单元测试不需要真实调用模型。
本章只涉及 ChatModel,后续章节会逐步引入 Tool、Retriever 等 Component。
classDiagram
class Component {
<<interface>>
}
class ChatModel {
<<interface>>
+Generate()
+Stream()
}
class Tool {
<<interface>>
+Execute()
}
class Retriever {
<<interface>>
+Retrieve()
}
Component <|-- ChatModel
Component <|-- Tool
Component <|-- Retriever
note for ChatModel "本章重点学习"
schema.Message:对话的基本单位
[!tip] 核心概念
Message是 Eino 对话系统的基石。理解 Message 的结构和角色语义,是掌握整个对话流程的关键。
Message 是 Eino 里对话数据的基本结构:
type Message struct {
Role RoleType // system / user / assistant / tool
Content string // 文本内容
ToolCalls []ToolCall // 仅 assistant 消息可能有
// ...
}
graph LR
A[Message] --> B[Role]
A --> C[Content]
A --> D[ToolCalls]
B --> E["system / user / assistant / tool"]
C --> F["文本内容"]
D --> G["工具调用指令"]
常用构造函数:
schema.SystemMessage("You are a helpful assistant.")
schema.UserMessage("What is the weather today?")
schema.AssistantMessage("I don't know.", nil) // 第二个参数是 ToolCalls
schema.ToolMessage("tool result", "call_id")
角色语义:
| 角色 | 用途 | 典型位置 |
|---|---|---|
system |
系统指令,定义模型行为 | messages 最前面 |
user |
用户输入 | 交替出现 |
assistant |
模型回复 | 交替出现 |
tool |
工具调用结果 | 工具调用后(后续章节) |
[!warning] 常见错误 注意
system消息应该放在 messages 数组的最前面,而不是中间或末尾。这是大多数 LLM 的要求。
前置条件
获取代码
git clone https://github.com/cloudwego/eino-examples.git
cd eino-examples/quickstart/chatwitheino
- Go 版本:Go 1.21+(见
go.mod) - 一个可调用的 ChatModel(默认使用 OpenAI;也支持 Ark)
方式 A:OpenAI(默认)
export OPENAI_API_KEY="..."
export OPENAI_MODEL="gpt-4.1-mini" # OpenAI 2025 年新模型,也可用 gpt-4o、gpt-4o-mini 等
# 可选:
# OPENAI_BASE_URL(代理或兼容服务)
# OPENAI_BY_AZURE=true(使用 Azure OpenAI)
方式 B:Ark
export MODEL_TYPE="ark"
export ARK_API_KEY="..."
export ARK_MODEL="..."
# 可选:ARK_BASE_URL
运行
在 examples/quickstart/chatwitheino 目录下执行:
go run ./cmd/ch01 -- "用一句话解释 Eino 的 Component 设计解决了什么问题?"
输出示例(流式逐步打印):
[assistant] Eino 的 Component 设计通过定义统一接口...
入口代码做了什么
按执行顺序:
flowchart TD
A[开始] --> B[创建 ChatModel]
B --> C["构造 messages 数组"]
C --> D[调用 Stream 方法]
D --> E{接收数据块}
E -->|EOF| F[结束]
E -->|有数据| G[打印 chunk.Content]
G --> E
- 创建 ChatModel:根据
MODEL_TYPE环境变量选择 OpenAI 或 Ark 实现 - 构造输入 messages:
SystemMessage(instruction)+UserMessage(query) - 调用 Stream:所有 ChatModel 实现都必须支持
Stream(),返回StreamReader[*Message] - 打印结果:迭代
StreamReader逐帧打印 assistant 回复
关键代码片段(注意:这是简化后的代码片段,不能直接运行,完整代码请参考 cmd/ch01/main.go):
// 1. 构造输入消息数组
// system 消息定义模型行为,user 消息包含用户问题
messages := []*schema.Message{
schema.SystemMessage(instruction), // 系统指令
schema.UserMessage(query), // 用户输入
}
// 2. 调用 Stream 方法(所有 ChatModel 都必须实现)
// 返回 StreamReader,用于逐块读取流式输出
stream, err := cm.Stream(ctx, messages)
if err != nil {
log.Fatal(err)
}
defer stream.Close() // 确保资源释放
// 3. 循环接收流式数据
for {
chunk, err := stream.Recv() // 读取下一个数据块
if errors.Is(err, io.EOF) {
break // 流结束
}
if err != nil {
log.Fatal(err)
}
fmt.Print(chunk.Content) // 实时打印内容(不换行)
}
[!note] 流式输出的优势 使用
Stream而不是Generate,可以让用户更早看到响应,提升交互体验。这类似于 ChatGPT 的逐字显示效果。
本章小结
| 核心概念 | 说明 | 本章要点 |
|---|---|---|
| Component 接口 | 定义可替换、可组合、可测试的能力边界 | 通过接口实现解耦,支持多模型切换 |
| Message | 对话数据的基本单位,通过角色区分语义 | 使用构造函数创建消息 |
| ChatModel | 最基础的 Component | 提供 Generate 和 Stream 方法 |
| 实现选择 | 通过环境变量或配置切换不同实现 | 业务代码无需改动 |
[!success] 学习成果 完成本章后,你应该能够:
- 理解 Eino 的 Component 设计理念
- 使用 ChatModel 进行单次对话调用
- 掌握 Message 的构造和使用方法
- 运行示例代码并观察流式输出
下一章预告
Eino/quick_start/chapter_02_agent_and_runner 将引入执行抽象,实现多轮对话和会话管理。