--- tags: [eino, ai-development, go, quickstart, memory, session] create time: 2026-04-29 15:30 title: 第三章:Memory 与 Session(持久化对话) weight: 3 --- ## 概述 在第二章掌握了多轮对话的实现后,我们面临一个关键问题:**对话历史只存在于内存中,进程退出后一切归零**。本章引入 **Memory 与 Session** 机制,让对话历史能够持久化保存并跨进程恢复,为构建真正的智能助手奠定基础。 > [!warning] 重要概念区分:业务层 vs 框架层 > 本章介绍的 **Memory、Session、Store 是业务层概念**,**不是 Eino 框架的核心组件**。Eino 框架只负责"如何处理消息",而"如何存储消息"完全由业务层决定。本章提供的实现只是一个参考示例,你可以根据自己的需求选择数据库、Redis、云存储等方案。 ## 代码位置 - 入口代码:[cmd/ch03/main.go](https://github.com/cloudwego/eino-examples/blob/main/quickstart/chatwitheino/cmd/ch03/main.go) - Store 实现:[mem/store.go](https://github.com/cloudwego/eino-examples/blob/main/quickstart/chatwitheino/mem/store.go) --- ## 前置条件 与第二章一致:需要配置一个可用的 ChatModel(OpenAI 或 Ark)。 ## 运行 在 `examples/quickstart/chatwitheino` 目录下执行: ```bash # 创建新会话 go run ./cmd/ch03 # 恢复已有会话 go run ./cmd/ch03 --session ``` 输出示例: ``` Created new session: 083d16da-6b13-4fe6-afb0-c45d8f490ce1 Session title: New Session Enter your message (empty line to exit): you> 你好,我是张三 [assistant] 你好张三!很高兴认识你... you> 我叫什么名字? [assistant] 你叫张三... Session saved: 083d16da-6b13-4fe6-afb0-c45d8f490ce1 Resume with: go run ./cmd/ch03 --session 083d16da-6b13-4fe6-afb0-c45d8f490ce1 ``` --- ## 从内存到持久化:为什么需要 Memory > [!question] 思考一下 > 第二章我们实现了多轮对话,但存在一个问题——如果进程退出、机器重启,之前聊的内容还会在吗? **内存存储的局限:** - 进程退出后,对话历史丢失 - 无法跨设备、跨进程恢复会话 - 无法实现会话管理(列表、删除、搜索等) **Memory 的定位:** - **Memory 是对话历史的持久化存储**:将对话保存到磁盘或数据库 - **Memory 支持 Session 管理**:每个 Session 代表一次完整的对话 - **Memory 与 Agent 解耦**:Agent 不关心存储细节,只关心消息列表 **简单类比:** | 方式 | 比喻 | 特点 | |------|------|------| | 内存存储 | "草稿纸" | 进程退出就没了 | | Memory | "笔记本" | 永久保存,随时翻阅 | --- ## 关键概念 > [!tip] 重要提示 > 以下 Session、Store 等概念都是**业务层实现**,用于管理对话历史的存储。Eino 框架本身不提供这些组件,而是由业务层负责管理消息列表,然后将消息传递给 `adk.Runner` 进行处理。 ### Session(业务层概念) `Session` 代表一次完整的对话会话: ```go type Session struct { ID string CreatedAt time.Time messages []*schema.Message // 对话历史 // ... } ``` **核心方法:** - `Append(msg)`:追加消息到会话,并持久化 - `GetMessages()`:获取所有消息 - `Title()`:从第一条用户消息生成会话标题 ### Store(业务层概念) `Store` 管理多个 Session 的持久化存储: ```go type Store struct { dir string // 存储目录 cache map[string]*Session // 内存缓存 } ``` **核心方法:** - `GetOrCreate(id)`:获取或创建 Session - `List()`:列出所有 Session - `Delete(id)`:删除 Session ### JSONL 文件格式 每个 Session 存储为一个 `.jsonl` 文件: ``` {"type":"session","id":"083d16da-...","created_at":"2026-03-11T10:00:00Z"} {"role":"user","content":"你好,我是谁?"} {"role":"assistant","content":"你好!我暂时不知道你是谁..."} {"role":"user","content":"我叫张三"} {"role":"assistant","content":"好的,张三,很高兴认识你!"} ``` **为什么用 JSONL?** | 特性 | 说明 | |------|------| | **简单** | 每行一个 JSON 对象,易于读写 | | **可扩展** | 可以追加新消息,无需重写整个文件 | | **可读性好** | 可以用文本编辑器直接查看 | | **容错性强** | 单行损坏不影响其他行 | ## Memory 的实现(业务层示例) 以下是一个简单的业务层实现示例,使用 JSONL 文件存储对话历史。这只是众多可能实现中的一种,你可以根据实际需求选择数据库、Redis 等其他存储方案。 ### 1. 创建 Store ```go sessionDir := "./data/sessions" store, err := mem.NewStore(sessionDir) if err != nil { log.Fatal(err) } ``` ### 2. 获取或创建 Session ```go sessionID := "083d16da-6b13-4fe6-afb0-c45d8f490ce1" session, err := store.GetOrCreate(sessionID) if err != nil { log.Fatal(err) } ``` ### 3. 追加用户消息 ```go userMsg := schema.UserMessage("你好") if err := session.Append(userMsg); err != nil { log.Fatal(err) } ``` ### 4. 获取历史并调用 Agent ```go history := session.GetMessages() events := runner.Run(ctx, history) content := collectAssistantFromEvents(events) ``` ### 5. 追加助手消息 ```go assistantMsg := schema.AssistantMessage(content, nil) if err := session.Append(assistantMsg); err != nil { log.Fatal(err) } ``` ### 关键代码解析 完整流程可以浓缩为以下核心片段: ```go // 1. 创建或恢复 Session session, err := store.GetOrCreate(sessionID) if err != nil { log.Fatal(err) } // 2. 读取用户输入并追加到会话 userMsg := schema.UserMessage(line) if err := session.Append(userMsg); err != nil { log.Fatal(err) } // 3. 获取全部历史,送入 Agent 处理 history := session.GetMessages() events := runner.Run(ctx, history) content := collectAssistantFromEvents(events) // 4. 收集回复并存回会话 assistantMsg := schema.AssistantMessage(content, nil) if err := session.Append(assistantMsg); err != nil { log.Fatal(err) } ``` > [!note] 简化说明 > 以上代码已省略错误处理之外的细节(如命令行参数解析、事件流消费等),不能直接运行。完整代码请参考 [cmd/ch03/main.go](https://github.com/cloudwego/eino-examples/blob/main/quickstart/chatwitheino/cmd/ch03/main.go)。 ## Session 与 Agent 的关系:业务层与框架层的协作 > [!tip] 理解要点 > - **Session 是业务层概念**:由你的代码实现和管理,负责存储和加载对话历史 > - **Agent(Runner)是框架层概念**:由 Eino 框架提供,负责处理消息并生成回复 > - **两者的交互点**:业务层通过 `session.GetMessages()` 获取消息列表,传递给 `runner.Run(ctx, history)` 进行处理 **数据流示意图:** ```mermaid flowchart TD A["用户输入"] --> B["session.Append()
保存用户消息"] B --> C["session.GetMessages()
获取完整历史"] C --> D["runner.Run(history)
Agent 处理消息"] D --> E["收集助手回复"] E --> F["session.Append()
保存助手消息"] style B fill:#e8f5e9 style C fill:#e8f5e9 style F fill:#e8f5e9 style D fill:#fff3e0 ``` **分层面貌:** ```mermaid graph LR subgraph biz_layer["业务层 — 你的代码"] S1["Session
持久化存储"] S2["GetMessages()"] S3["Append()
保存消息"] S1 --> S2 S3 -.->|"回写"| S1 end subgraph frame_layer["框架层 — Eino"] R1["runner.Run()"] end S2 --> R1 R1 -->|"助手回复"| S3 style S1 fill:#e3f2fd style S2 fill:#e3f2fd style S3 fill:#e3f2fd style R1 fill:#fff3e0 style biz_layer fill:none,stroke:#90caf9 style frame_layer fill:none,stroke:#ffcc80 ``` ## 本章小结 | 核心概念 | 定位 | 说明 | |----------|------|------| | **Memory** | 业务层 | 对话历史的持久化存储,支持跨进程恢复 | | **Session** | 业务层 | 一次完整的对话会话,包含 ID、创建时间、消息列表 | | **Store** | 业务层 | 管理多个 Session 的存储,支持创建、获取、列表、删除 | | **JSONL 格式** | 业务层 | 简单的文件格式,易于读写和扩展 | | **adk.Runner** | 框架层 | 接收消息列表,调用 ChatModel,返回回复 | > [!success] 学习成果 > 完成本章后,你应该能够: > - 理解 Memory/Session/Store 的业务层职责 > - 知道如何将对话历史持久化为 JSONL 文件 > - 明白业务层与框架层的协作边界 > - 根据业务需求选择合适的存储方案 --- ## 扩展思考:业务层存储方案的选择 本章提供的 JSONL 文件存储方案适合简单的单机应用。在实际业务中,你可能需要考虑其他存储方案:
存储方案适用场景优势劣势
JSONL 文件单机应用、开发调试零依赖,简单直观不支持并发、分布式
SQLite / LevelDB桌面端应用轻量级嵌入式数据库不适合高并发写入
MySQL / PostgreSQL服务端部署成熟稳定,功能丰富运维成本较高
Redis分布式、高频访问性能极高,支持过期策略数据需额外持久化
S3 / OSS海量冷数据归档成本极低,无限扩展不适合频繁查询
**高级功能展望:** - 会话过期清理(TTL 自动删除) - 会话全文搜索 - 会话导出 / 导入 - 会话分享(生成公开链接) > [!tip] Middleware 联动 > 当对话非常长时,单纯增加存储容量是不够的。第五章介绍的 **Summarization Middleware** 可以在调用 Agent 之前自动压缩历史消息,有效控制 Token 消耗。 --- ## 下一章预告 [[Eino/quick_start/chapter_04_tool_and_filesystem|第四章:Tool 与文件系统]] 将为 Agent 添加文件访问能力,让智能助手能够读取代码仓库中的真实内容。 ## 关联笔记 - [[Eino/quick_start/chapter_02_chatmodelagent_runner_agentevent]] - [[Eino/quick_start/chapter_04_tool_and_filesystem]]