Skip to content

Commit da0707e

Browse files
publish blog: 浅谈sag-在-zleap-ai-眼中-我们为什么需要-sag
1 parent bdaa793 commit da0707e

4 files changed

Lines changed: 295 additions & 0 deletions

File tree

public/blogs/categories.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
{
22
"categories": [
33
"实习学习日志",
4+
"RAG",
45
"学习周报",
56
"#DataStructures",
67
"ComputerNetworks",

public/blogs/index.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,16 @@
99
],
1010
"category": "实习学习日志"
1111
},
12+
{
13+
"slug": "浅谈sag-在-zleap-ai-眼中-我们为什么需要-sag",
14+
"title": "浅谈SAG:在 Zleap AI 眼中,我们为什么需要 SAG?",
15+
"date": "2026-08-23",
16+
"summary": "SAG,号称是一套可以替代传统 RAG 与 GraphRAG 的原创检索架构。",
17+
"tags": [
18+
"RAG"
19+
],
20+
"category": "RAG"
21+
},
1222
{
1323
"slug": "学习周报-2026-08-05-to-07",
1424
"title": "学习周报|2026-08-05-to-07",
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"title": "浅谈SAG:在 Zleap AI 眼中,我们为什么需要 SAG?",
3+
"date": "2026-08-23",
4+
"summary": "SAG,号称是一套可以替代传统 RAG 与 GraphRAG 的原创检索架构。",
5+
"tags": [
6+
"RAG"
7+
],
8+
"category": "RAG"
9+
}
Lines changed: 275 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,275 @@
1+
# 浅谈SAG:在 Zleap AI 眼中,我们为什么需要 SAG?
2+
<br>
3+
最近在上网冲浪的时候又了解到了一个ai新概念,SAG,号称是一套可以替代传统 RAG 与 GraphRAG 的原创检索架构。为了研究其是否真的有那么大作用,我点开了它的论文和GitHub仓库,开始研究了起来。
4+
5+
# 为什么世界需要SAG?
6+
7+
在现实生活中,对于需要通过查询来解答的问题,有时需要多步推理。
8+
9+
## 举个例子:
10+
11+
你的邻居们每天会把吃了几顿饭记录在日历上(哦,邻居们啊邻居们。真是吃饱了闲的)。除此之外,我们手里还有一份邻居档案,记录着每位邻居的性别、住址以及与我家的距离。
12+
13+
**问:离我最近的男邻居在前天吃了几顿饭?**
14+
15+
回答这样的问题,我们需要如下几步:
16+
17+
1. 查询邻居档案,明确离我最近的男邻居是谁。(哦,是家铭。)
18+
2. 明确前天对应的是日历哪一天。(好的,是8月19日)
19+
3. 查询家铭8月20日的日历记录。(天哪,他一天吃了五顿!)
20+
21+
我们把这样的问题,称为 **多跳问题(Multi-hop Questions)**
22+
23+
对于解答这样的问题,
24+
25+
### 传统RAG(NaiveRAG)会这么做:
26+
27+
```text
28+
Query: "离我最近的男邻居在前天吃了几顿饭?"
29+
30+
↓ 向量化
31+
32+
↓ 与知识库中的各个 chunk 计算语义相似度
33+
34+
↓ 返回Top-5最相似的记录(这里我们设置是前五)
35+
36+
37+
结果:
38+
记录1: "小红住在我家3米外,是女性" (相似度 0.78)
39+
记录2: "8月19日,小明吃了3顿饭" (相似度 0.75)
40+
记录3: "家铭住在我家5米外,是男性" (相似度 0.73)
41+
记录4: "8月20日,家铭吃了4顿饭" (相似度 0.71)
42+
记录5: "小明住在我家12米外,是男性" (相似度 0.69)
43+
```
44+
45+
问题来了:真正决定答案的另一条记录——**“8月19日,家铭吃了5顿饭”**——这一次恰好排在我们检索的 Top-5 之外。
46+
47+
传统RAG(Naive RAG)通常会把每个 chunk 独立地按照它与 Query 的语义相似度进行排序,但检索器本身并不会显式沿着:
48+
49+
> **男性 → 最近 → 家铭 → 8月19日**
50+
51+
这样的关系链一步步寻找证据。
52+
53+
如果完整的证据刚好都被检索进上下文,后面的 LLM 完全可能自己推理出答案。但Naive RAG 的检索阶段并不能保证完成多跳推理所需要的所有证据都能进入 Top-k。
54+
55+
---
56+
57+
### 知识图谱(GraphRAG)会这么做(以 Microsoft GraphRAG 这类方案为例):
58+
59+
```text
60+
文档
61+
62+
切 chunk
63+
64+
LLM 提取实体
65+
66+
LLM 提取关系
67+
68+
实体消歧 / 合并
69+
70+
建立图
71+
72+
社区检测
73+
74+
生成社区摘要
75+
76+
建立索引
77+
```
78+
79+
在构建实体阶段,GraphRAG 可能要让 LLM 提取出如下关系:
80+
81+
```text
82+
实体:
83+
84+
家铭
85+
8月19日
86+
87+
关系:
88+
家铭 --住在隔壁--> 我
89+
家铭 --性别--> 男性
90+
家铭 --距离我家--> 5米
91+
```
92+
93+
而对于“8月19日吃了5顿饭”这种带有时间和属性的信息,我们也可以把它建模成一个事件:
94+
95+
```text
96+
吃饭事件 E1
97+
├── person → 家铭
98+
├── date → 8月19日
99+
└── meal_count → 5
100+
```
101+
102+
这样一来,系统就可以利用图中的关系进行扩展:
103+
104+
```text
105+
男性邻居
106+
107+
比较距离
108+
109+
家铭
110+
111+
找到8月19日相关事件
112+
113+
meal_count = 5
114+
```
115+
116+
这看起来很不错,对吧?GraphRAG为了弥补纯向量检索缺乏显式关系的问题,一类 GraphRAG 方法会提前从文档中抽取实体与关系,再利用图结构辅助检索。
117+
118+
但在实际运用过程中,我们仍会发现如下问题:
119+
120+
#### 首先,是构建成本高:
121+
122+
1. 切出来的chunk越多,每个chunk都需要模型提取里面的实体和关系,这样的话处理成本就会很高。
123+
124+
2. 家铭可能有很多称呼:臭宝,小铭,jm…
125+
126+
这就会导致一个问题:**实体消歧(Entity Resolution)**:即GraphRAG 得下工夫判断这四个名字是不是同一个实体。比如臭宝和家铭是一个实体,那么就将他们合并成一个节点;家铭和小明不是一个实体,就要搞清楚小明到底指哪个实体。
127+
128+
3. 图建好以后,GraphRAG 还得下工夫去做 **社区检测(Community Detection)**,还需要去研究哪些节点彼此联系特别紧密。
129+
130+
#### 其次,是增量更新难:
131+
132+
新的邻居搬进来,就可能导致实体,关系,图索引,社区等一系列内容的修改,一改一大片。
133+
134+
这里拓展一下:在普通RAG中新增一篇文档,可能就只需要获取新“邻居”档案,然后chunk,接着embedding,再插进去,就结束了。
135+
136+
#### 最后,是语义碎片化:
137+
138+
一个完整事件在被拆成多个独立实体和关系后,其中的时间、条件、否定、计划、实际结果、因果等上下文信息,可能被削弱甚至丢失。
139+
140+
很多知识图谱方法会将自然语言中的信息抽取为实体、关系或三元组:
141+
142+
```text
143+
(subject, relation, object)
144+
```
145+
146+
举个例子:
147+
148+
家铭在8月19日的日历里写道:
149+
150+
```text
151+
“今天本来打算吃5顿,但下午那顿没来得及吃,所以最后一共只吃了4顿。”
152+
```
153+
154+
可见,记录的信息包含计划与实际结果,而知识图谱又习惯于把句子拆成三元组,一旦其没有把“本来打算”“最后”“实际”等限定信息一起保存下来,那么就会得出:
155+
156+
```text
157+
(家铭, 吃饭次数, 5)
158+
159+
160+
161+
(家铭, 吃饭次数, 4)
162+
```
163+
164+
就可能变成两条看起来同样成立的事实。
165+
166+
当然,这并不意味着知识图谱必然会出现语义碎片化。我们完全可以把关系设计得更加复杂,例如:
167+
168+
```text
169+
(家铭, planned_meal_count, 5)
170+
(家铭, actual_meal_count, 4)
171+
```
172+
173+
但新的问题也随之而来:图结构设计得简单,抽取和维护比较容易,但可能损失复杂上下文;图结构设计得复杂,能够保存更多完整语义,但抽取、构建、消歧和维护成本进一步上升。这样就很难同时兼顾。
174+
175+
---
176+
177+
## SAG来咯!
178+
179+
基于以上痛点,Zleap AI团队提出了一个新架构:**SAG**
180+
181+
他们发现,多跳推理所需的关联结构,其实“潜在地”存在于记录描述的事件和它们共享的实体中。与其提前把整个知识库画成一张巨大的图,不如先把事件和实体存好,等用户真正提问的时候,再通过 SQL 把当前问题需要的事件临时连接起来。
182+
183+
我们可以先把 SAG 的数据库极度简化成三张表。
184+
185+
```text
186+
第一张:
187+
188+
Events
189+
190+
E1 | 家铭是一名男性邻居,距离我家5米
191+
E2 | 小明是一名男性邻居,距离我家12米
192+
E3 | 8月19日,家铭一共吃了5顿饭
193+
E4 | 8月19日,小明一共吃了3顿饭
194+
195+
196+
第二张:
197+
198+
Entities
199+
200+
P1 | 家铭
201+
P2 | 小明
202+
P3 | 我
203+
P4 | 8月19日
204+
205+
206+
第三张最重要:
207+
208+
Event_Entity
209+
210+
E1 → 家铭
211+
E1 → 我
212+
213+
E2 → 小明
214+
E2 → 我
215+
216+
E3 → 家铭
217+
E3 → 8月19日
218+
219+
E4 → 小明
220+
E4 → 8月19日
221+
```
222+
223+
看到这里,SAG 最核心的东西其实已经出现了。
224+
225+
**注意:**
226+
227+
```text
228+
E1:
229+
家铭是男性邻居,距离我5米
230+
231+
E3:
232+
8月19日,家铭吃了5顿
233+
```
234+
235+
SAG 没有提前画一条边说:
236+
237+
```text
238+
E1 ─────────→ E3
239+
```
240+
241+
但是它们都有:
242+
243+
```text
244+
E1 → 家铭
245+
246+
E3 → 家铭
247+
```
248+
249+
也就是说:
250+
251+
```text
252+
E1
253+
\
254+
\
255+
家铭
256+
/
257+
/
258+
E3
259+
```
260+
261+
E1 和 E3 的关系其实已经存在了。
262+
263+
只不过这个关系没有被提前做成一张全局图。
264+
265+
这就是 SAG 所说的:
266+
267+
> **“潜在的关系”**
268+
269+
等有人问问题的时候,系统首先会围绕 Query 找到一些比较相关的 **Seed Event / Seed Entity**
270+
271+
---
272+
273+
SAG的确给我们打开了一个解决多跳问题的新思路。不过对于每一个新出来的技术,我们应该保持科学严谨的态度。在了解其原理后,检验其是否真的能够如其所说,那么“有效”。
274+
275+
**欢迎与我讨论!**

0 commit comments

Comments
 (0)