引言
"图谱 RAG 的标准做法是离线构建全局图谱,然后用 PageRank 做查询时的评分扩展------但 PageRank 有衰减,链条越长分数越低。SAG 转而用 SQL JOIN 在查询时动态扩展,没有衰减,也不需要提前构建全局图谱。"
这是"每日一个开源项目"系列的第152篇文章 。今天的主角是 SAG(Structured Agentic Graph)------Zleap-AI 开源的新一代多跳 RAG 框架,有 arXiv 论文(2606.15971)背书。
多跳检索的核心难题:问题的答案需要把来自多个不同文档段落的信息串联起来。标准向量检索找到语义最相近的 chunk,但"语义最相近"不等于"能回答需要多步推理的问题"。GraphRAG 和 HippoRAG 用知识图谱解决这个问题,但有两个代价:离线构建全局图谱成本高,PageRank 评分在长链条下存在衰减。
SAG 换了一条路:不提前构建全局图谱,在查询时用 SQL JOIN 做图结构扩展。每个 chunk 提取一个"事件"(语义摘要)和若干实体,事件和实体之间的关联关系通过关系型数据库的外键表达,多跳扩展就是 JOIN 操作,确定性强,没有衰减。
你将学到什么
- SAG 的核心数据模型:chunk → event + entities 的设计逻辑
- 查询时三步流程:种子检索 → SQL 扩展 → 双路径最终选择
- 为什么 SQL JOIN 比 PageRank 在长链多跳上表现更好
- 与 HippoRAG 2、GraphRAG 的对比:优势在哪里,短板在哪里
- MCP 集成:把 SAG 项目暴露为 Agent 可调用的工具
- 全栈实现:TypeScript + PostgreSQL + pgvector + React
前置知识
- 了解 RAG 和多跳检索的基本概念
- 了解知识图谱(节点、边、多跳推理)的基本概念
- 了解 SQL JOIN 的概念
项目背景
项目简介
SAG(Structured Agentic Graph)是一个文档检索系统,用事件-实体图结构做知识组织,通过 SQL JOIN 实现多跳检索,专为 Agent 工作流设计。
"Structured"指的是把非结构化文档转化成事件+实体的结构化表示;"Agentic"指的是每个 SAG 项目都内置 MCP 服务器,可以作为 Agent 的工具端点使用;"Graph"指的是通过 SQL JOIN 实现的查询时图遍历。
作者/团队介绍
- 团队: Zleap-AI
- 官网 : zleap.com
- arXiv: 2606.15971
- License: MIT
- 基准测试仓库 : Zleap-AI/SAG-Benchmark
项目数据
- ⭐ GitHub Stars: 1,900+
- 🍴 Forks: 87+
- 📄 License: MIT
核心设计:chunk → event + entities
SAG 的基础数据模型只有三种元素:
markdown
一个文档 chunk
↓
一个 Event(语义事件)
↑↕↓
多个 Entities(实体)
Event(事件):对这个 chunk 的语义摘要。每个 chunk 对应恰好一个 event,event 保留了完整的语义。
Entity(实体) :从 chunk 里提取的命名实体,作为图遍历的锚点。11 种类型:时间、地点、人物、组织、群体、话题、作品、产品、动作、度量指标、标签。实体本身不携带独立语义,只是"连接不同事件的索引和扩展点"。
隐式超边:一个 event 加上它所有的 entities,构成一条隐式超边。这与 HyperGraphRAG 的思路类似,但 SAG 不显式构建超图,而是通过关系型数据库的外键关联来实现等效效果。
vbnet
为什么不用三元组(Triple)而用 Event?
三元组方式:
chunk → (Alice, works_at, Google)
(Alice, attended, Meeting_001)
(Bob, attended, Meeting_001)
关系被分解,重建完整上下文需要多跳
Event 方式:
chunk → Event: "Alice 和 Bob 在 Meeting_001 上讨论了产品路线图"
Entity: Alice, Bob, Meeting_001
完整语义保留在 event,entity 作为连接点
论文的消融实验证实:在 MuSiQue 上,超边(event)方案 Recall@5 = 80.0%,三元组方案 = 77.1%。
两阶段架构
离线阶段(文档摄取)
csharp
文档 (Markdown / TXT)
↓
文本分块(chunking)
↓
LLM 提取(并行,可配置并发数):
每个 chunk → 1 个 event + N 个 entities
↓
写入两个后端(同步):
PostgreSQL → 结构化存储(事件表、实体表、关联表)
pgvector → 向量索引(事件向量、实体向量)
全文搜索索引 → BM25 实体匹配
关键特性:只有追加写入,没有全局图谱的重建。新文档的摄取不影响已有数据,不需要重新跑 PageRank 或重建图结构。
在线阶段(三步检索)
Step 1:种子检索(双路径并行)
css
Path A:实体引导的结构化召回
1. LLM 从查询里提取实体 → 种子实体集 U_q
2. 向量相似度搜索实体索引(阈值 0.9)→ 扩展实体集 Û_q
3. SQL JOIN:找到所有包含这些实体的事件
ℰR_entity = {e | ∃u ∈ Û_q : SQL-Join(e, u)}
Path B:直接事件召回
查询向量 → 事件向量相似度搜索(阈值 0.4)
合并:ℰR = ℰR_entity ∪ ℰR_direct
Step 2:查询时扩展(SQL 多跳)
这是 SAG 的核心机制:
ini
从种子事件集 ℰR 出发:
↓
反向 JOIN:取出种子事件关联的所有实体(实体前沿)
↓
正向 JOIN:找到所有包含这些实体的新事件(跨文档扩展)
↓
重复 H 跳(默认 H=1)
结果:ℰ_cand = ℰR ∪ ℰE
这就是"多跳"的实现方式。如果文档 A 提到了实体 X,文档 B 也提到了实体 X,那么从文档 A 的事件出发,一次 JOIN 就能找到文档 B 里的相关事件------即使这两个文档在向量空间里相距很远。
Step 3:最终选择(双路径融合)
css
候选集 ℰ_cand(100 个候选事件)
↓
结构路径:LLM 对候选链条重排序 → 取 top 5 事件 → 映射到 chunks
语义路径:查询向量直接检索 chunk → 取 top 5
↓
合并去重 → 最终 10 个 chunks
SQL JOIN vs. PageRank:为什么 SAG 在长链多跳上更好
HippoRAG 2 的做法:离线构建全局知识图谱,查询时用 Personalized PageRank(PPR)从种子节点出发,分数沿图边传播。
问题出在 PPR 的阻尼因子(damping factor):每经过一跳,分数乘以 (1-d)(d 通常是 0.85),也就是保留 15% 的概率。经过 2 跳,节点得分乘以 0.15²=0.0225;3 跳后几乎清零。
css
HippoRAG 2 长链多跳的问题:
答案需要路径:A → B → C → D → E(4跳)
经过 PPR:E 的得分 ≈ seed分数 × 0.15⁴ ≈ 0.0005 × seed分数
远端节点分数太低,竞争不过直接相关的近端节点
SAG 的 SQL JOIN 不传播分数,只传播可达性:实体 X 关联的所有事件,JOIN 结果里一视同仁,不论它们在图谱上距离种子有多远。
MuSiQue 是这个差异的集中体现:MuSiQue 专门设计了需要 4 步推理链的多跳问题,HippoRAG 2 在这个数据集上明显弱于 SAG(65.1% vs 80.0%)。
SAG 的短板在 2WikiMultiHop:SAG 有一个固定的实体前沿裁剪预算(50 个),低频的桥接实体在裁剪时可能被截掉,导致它在 2WikiMultiHop 上略低于 HippoRAG 2(88.0% vs 90.4%)。
基准测试结果
使用 bge-large-en-v1.5 嵌入模型,qwen3.6-flash 作为 LLM:
Recall@5 对比
| 数据集 | SAG | HippoRAG 2 | 差距 |
|---|---|---|---|
| MuSiQue | 80.0% | 65.1% | +14.9pp |
| HotpotQA | 96.5% | 94.4% | +2.1pp |
| 2WikiMultiHop | 88.0% | 90.4% | -2.4pp |
| 平均 Recall@2 | 79.30% | 68.14% | +11.16pp |
换用更强的 NV-Embed-v2 嵌入,MuSiQue Recall@5 达到 81.71%,说明提升来自结构设计本身,不只是嵌入质量。
消融实验:
| 配置 | MuSiQue R@5 |
|---|---|
| 完整 SAG | 80.0% |
| 去掉多跳扩展(H=0) | 69.4% |
| 去掉结构路径 | 56.2% |
| 用轻量重排序器替换 LLM 重排序 | 62.2% |
结论:多跳扩展贡献了约 10.6pp 的提升,LLM 重排序不可跳过(差距 17.8pp)。
快速开始
bash
git clone https://github.com/Zleap-AI/SAG.git
cd SAG
# 配置环境变量
cp .env.example .env
# 编辑 .env:填入 LLM API Key、Embedding API Key
# 启动 PostgreSQL(带 pgvector)
docker compose up -d
# 安装和初始化
npm install
npm run db:setup
# 启动开发服务器
npm run dev
# WebUI: http://localhost:5173
# API: http://localhost:4173
.env 关键配置:
env
# LLM(OpenAI 兼容 API)
LLM_BASE_URL=https://api.openai.com/v1
LLM_API_KEY=your_key
LLM_MODEL=qwen3.6-flash # 或 gpt-4o-mini
# Embedding
EMBEDDING_BASE_URL=https://api.openai.com/v1
EMBEDDING_API_KEY=your_key
EMBEDDING_MODEL=text-embedding-3-large
# 可选:Rerank 模型
RERANK_MODEL=qwen3-rerank
# 搜索模式
DEFAULT_SEARCH_MODE=fast # fast / standard
INGEST_CONCURRENCY=5 # 并行摄取并发数
两种搜索模式
Fast 模式(不调用 LLM 解析查询):
sql
查询 → BM25 全文实体匹配 → SQL 多跳扩展 → 重排序
优点:延迟低,无 LLM 查询解析成本
适合:实体名明确的查询
Standard 模式(LLM 提取查询实体):
sql
查询 → LLM 提取实体 → 向量+SQL 多跳扩展 → LLM 重排序
优点:对模糊和自然语言问题更鲁棒
适合:需要复杂推理的查询
MCP 集成
每个 SAG 项目自动暴露一个 MCP 服务器端点,可以直接给 Claude Code 等 Agent 用:
json
{
"mcpServers": {
"sag": {
"command": "npm",
"args": ["run", "mcp"],
"env": {
"SAG_MCP_SOURCE_ID": "your_project_id"
}
}
}
}
可用 MCP 工具:
| 工具 | 功能 |
|---|---|
sag_search |
搜索项目知识库 |
sag_ingest_document |
上传新文档 |
sag_explain_search |
解释特定问题的检索路径 |
sag_get_event |
获取特定事件的详细信息 |
Web UI 功能
- 知识图谱浏览器:可视化事件-实体节点,支持拖拽、缩放、展开
- 搜索链路追踪:实时显示每次检索的内部步骤和各阶段延迟
- 原始模型日志:查看完整的 LLM / Embedding / Rerank 请求和响应
- 项目隔离:每个项目有独立的文档库、对话历史、图谱和 MCP 配置
项目地址与资源
- 🌟 GitHub : Zleap-AI/SAG
- 📄 arXiv : 2606.15971
- 🔬 基准测试 : Zleap-AI/SAG-Benchmark
- 🌐 官网 : zleap.com
总结
SAG 的核心贡献在于找到了一种轻量的多跳检索实现:不构建全局知识图谱,不依赖 PageRank 评分,用关系型数据库的 JOIN 操作在查询时动态扩展图结构。这个选择有两个连带优势:文档更新是追加写入不需要重建,SQL JOIN 的可达性不受跳数影响不存在衰减。
"事件"作为语义单元(而非三元组)的设计也是值得关注的:每个 chunk 的完整语义被保留在一个 event 节点里,实体只作为连接点使用。这比三元组方案在 MuSiQue 上高出近 3 个百分点,验证了这个设计决策。
短板是透明的:固定裁剪预算在 2WikiMultiHop 上略逊于 HippoRAG 2,在需要探索低频桥接实体的场景会漏掉一些关键路径。知道短板在哪里,才能判断它是否适合你的场景。
探索 PrimeSkills ------ 精选 AI Agent 与技能的市场,每一个都经过真实企业工作流验证,去掉浮夸,留下真正有用的。
欢迎访问我的个人主页,发现更多有价值的见解和有趣的产品。