“框架层概念” vs “业务层概念”

一句话总结

  • 框架层概念 = Eino 框架已经帮你写好的代码和抽象,你直接 import 来用
  • 业务层概念 = 你自己写的(或示例项目帮你写的)代码,用来满足你的具体业务需求

用一个生活类比来理解

想象你在用微信开发框架做一个聊天应用:

框架层(微信SDK提供) 业务层(你自己写)
谁提供的 微信团队 你(开发者)
举例 发送消息API、接收消息API 聊天记录保存到数据库、用户管理
能不能换 不能换(框架就是这样设计的) 随便换(用MySQL还是Redis你自己决定)
所有人都一样吗 是的,所有开发者用同一套API 不是,每个项目的业务逻辑都不同

在 Eino 项目中的具体对应

框架层概念(Eino 框架提供的)

这些是你 import 进来就能用的,所有使用 Eino 的项目都一样

1
2
3
4
import (
"github.com/cloudwego/eino/adk" // 框架层
"github.com/cloudwego/eino/schema" // 框架层
)
框架层概念 说明 代码中的体现
schema.Message 消息的标准格式(角色+内容) schema.UserMessage(line)
adk.ChatModelAgent 智能体抽象 adk.NewChatModelAgent(...)
adk.Runner Agent 的执行器 runner.Run(ctx, history)
adk.AgentEvent 事件流中的事件单元 events.Next()
adk.AsyncIterator 流式事件迭代器 for { event, ok := events.Next() }

这些东西是 Eino 框架的核心设计,你不需要自己实现,也不能随意修改它们的行为。

业务层概念(示例项目自己写的)

这些是 mem/store.go示例项目自己实现的代码:

1
2
3
import (
"github.com/cloudwego/eino-examples/quickstart/chatwitheino/mem" // 业务层
)
业务层概念 说明 代码中的体现
mem.Store 管理多个会话的存储 store, err := mem.NewStore(sessionDir)
mem.Session 一次对话会话 session, err := store.GetOrCreate(sessionID)
session.Append() 追加消息到会话 session.Append(userMsg)
session.GetMessages() 获取历史消息 history := session.GetMessages()
JSONL 文件格式 消息的存储格式 磁盘上的 .jsonl 文件

这些东西是这个示例项目自己设计的,Eino 框架完全不知道它们的存在。


从代码中直观感受

main.go 中对话循环的核心代码(第 95~117 行):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// ========== 业务层:保存用户消息 ==========
userMsg := schema.UserMessage(line)
if err := session.Append(userMsg); err != nil { // ← 业务层:持久化到 JSONL 文件
...
}

// ========== 业务层 → 框架层的"交接点" ==========
history := session.GetMessages() // ← 业务层:从存储中取出消息列表
events := runner.Run(ctx, history) // ← 框架层:Runner 接收消息,调用模型

// ========== 框架层:消费事件流 ==========
content, err := printAndCollectAssistantFromEvents(events) // ← 框架层的 AgentEvent

// ========== 框架层 → 业务层的"交接点" ==========
assistantMsg := schema.AssistantMessage(content, nil)
if err := session.Append(assistantMsg); err != nil { // ← 业务层:把回复持久化
...
}

用一张流程图来看两层之间的边界

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
graph LR
subgraph 业务层["🟦 业务层(你自己写的)"]
A[session.Append<br/>保存用户消息] --> B[session.GetMessages<br/>取出历史消息]
E[session.Append<br/>保存AI回复]
end

subgraph 框架层["🟩 框架层(Eino 提供的)"]
C[runner.Run<br/>执行 Agent] --> D[events.Next<br/>消费事件流]
end

B -->|"history(消息列表)"| C
D -->|"content(AI回复文本)"| E

style 业务层 fill:#e3f2fd,stroke:#1565c0
style 框架层 fill:#e8f5e9,stroke:#2e7d32

交接点就是 []*schema.Message(消息列表)——这是两层之间唯一的”通信协议”。


为什么要区分这两层?

核心原因:框架不应该替你决定业务细节

Eino 框架的设计哲学是:

框架只负责”如何处理消息”,不管”如何存储消息”。

这意味着:

问题 谁负责 为什么
消息怎么发给模型? 框架层 所有项目都一样
流式输出怎么处理? 框架层 所有项目都一样
对话历史存在哪里? 业务层 每个项目不同
用什么格式存储? 业务层 每个项目不同
Session 怎么管理? 业务层 每个项目不同

实际好处:业务层可以随意替换

本章用的是 JSONL 文件存储,但你完全可以换成其他方案,框架层的代码一行都不用改

1
2
3
4
5
6
7
8
9
10
11
12
13
// 方案 1:JSONL 文件(本章的实现)
store, _ := mem.NewStore("./data/sessions")

// 方案 2:如果你换成 MySQL(假设你自己实现了 mysqlmem 包)
// store, _ := mysqlmem.NewStore("root:password@tcp(127.0.0.1:3306)/chat")

// 方案 3:如果你换成 Redis(假设你自己实现了 redismem 包)
// store, _ := redismem.NewStore("redis://localhost:6379")

// ↓↓↓ 以下代码完全不变 ↓↓↓
session, _ := store.GetOrCreate(sessionID)
history := session.GetMessages()
events := runner.Run(ctx, history) // 框架层不关心 history 从哪来的

对比第二章和第三章

维度 第二章 (ch02) 第三章 (ch03)
历史存储 history 变量(内存) session(JSONL 文件)
进程退出后 历史丢失 历史保留
能否恢复会话 ❌ 不能 --session <id> 恢复
框架层代码 runner.Run(ctx, history) runner.Run(ctx, history)
框架层有变化吗? 完全没变!

注意最后一行:框架层的代码完全一样。变化的只是业务层——从”内存变量”换成了”JSONL 文件”。这就是分层的好处。


总结

框架层 业务层
谁写的 Eino 框架团队 你(开发者)
在哪里 github.com/cloudwego/eino/... 你的项目代码(如 mem/store.go
能不能换 不能(框架的核心设计) 随便换(JSONL→MySQL→Redis…)
本章的例子 adk.Runnerschema.Messageadk.AgentEvent mem.Storemem.Session、JSONL 文件
职责 处理消息、调用模型、返回回复 存储消息、管理会话、持久化

一句话记住:框架管”算”,业务管”存”。两者通过 []*schema.Message(消息列表)这个”接口”连接起来。