SAG
基于事项-实体图结构的 RAG 检索工作台,支持多跳问答和 MCP 工具接入
加载项目详情…
本应用为开源项目,仅供学习研究,请遵守其开源协议。
基于事项-实体图结构的 RAG 检索工作台,支持多跳问答和 MCP 工具接入
加载项目详情…
本应用为开源项目,仅供学习研究,请遵守其开源协议。
你可能有过这样的经历:往 RAG 系统里塞了几百份项目文档,满心期待它能像资深顾问一样回答各种刁钻问题,结果一测试,发现它要么找错章节,要么在多跳推理时「失忆」——明明答案就在文档 A 和文档 B 的交叉处,系统就是串不起来。这类问题的根源不在模型不够强,而在于传统 RAG 把文档切成碎片后,丢失了文档内部深层的语义关联。
SAG(State-Action-Graph) 尝试从根本上解决这个问题。它不靠给模型喂更多 chunks,而是用一套更轻量的结构重新组织文档知识——把每个 chunk 提取为一个完整事项(Event)和若干实体(Entity),事项之间通过实体相连,构成一张可检索的语义图谱。这套方法的论文复现代码为 Zleap-AI/SAG-Benchmark,在 HotpotQA、2WikiMultiHop、MuSiQue 三个多跳问答数据集上,对比 HippoRAG 2 实现了平均 Recall@2 从 68.14% 提升到 79.30% 的显著改进,相对提升约 16.4%。
本文档是 Zleap-AI 团队基于 SAG 论文构建的开箱即用本地工作台,目标用户是希望快速验证 RAG 原型、构建项目知识库或接入 MCP Agent 的开发者。
假设你维护着一个上百页的系统设计文档,涵盖架构选型、API 规范、部署流程和常见故障排查。某天同事问:「我们这套系统在网络抖动时,API 网关和消息队列的重试策略是怎么配合的?」
用传统向量 RAG 回答这个问题,你需要:先让 embedding 模型把问题转成向量,然后在文档碎片库里做相似度搜索,祈祷找到两段相关的内容,最后靠 LLM 自己推理关联。这个过程中,向量相似度搜索擅长找「词汇相近」的片段,但「网络抖动 → API 网关 → 消息队列重试」这条链路跨越了文档的多个章节,而 embedding 模型很难把这种跨章节的隐含关系捕获出来。
SAG 的做法不同:文档入库时,每个 chunk 会被「拆解」为两部分——一个完整的事项(Event)和若干实体(Entity)。事项保留了 chunk 的完整语义和上下文,实体负责构建索引和关系扩展。当用户提出上述问题时,系统先抽取问题中的实体(「网络抖动」「API 网关」「重试策略」),然后以这些实体为起点,在事项图中做多跳扩展,把「网络抖动相关事项」和「消息队列重试相关事项」同时召回,最终把跨越多个章节的完整证据链交给 LLM 生成答案。
SAG 的核心思想可以用三个变换来概括:
chunk → event # 每个 chunk 提取为一个完整事项
chunk → entities # 每个 chunk 提取多个实体
event ↔ entities # 事项通过共同实体相互关联
这套设计规避了传统知识图谱 RAG 的两大痛点:一是全局图谱构建成本高,每次文档更新都要重新计算全量图;二是单纯依赖实体节点检索,忽略了 chunk 本身的完整语义。SAG 把 chunk 本身也作为一等公民(Event)保留下来,同时通过共享实体把多个 Event 连接成图——这样检索时可以双向工作:既可以从实体出发找到相关事项,也可以从事项出发扩展到更多实体。
在 benchmark 中,使用相同配置(Embedding = bge-large-en-v1.5,LLM = qwen3.6-flash),SAG 在 MuSiQue Recall@5 上从 HippoRAG 2 的 65.13% 提升到 80.04%。换用更强的 NV-Embed-v2 后进一步达到 81.71%,说明收益主要来自 SAG 的结构设计本身,而不是更强的 embedding 模型。
在「文档」页可以一次性上传多个 Markdown / TXT 文档。系统会实时展示每个文档的处理阶段(上传 → 切片 → 事项提取 → 实体提取 → 向量化),处理完成后可以逐条查看每个切片的内容、提取出的事项、实体和对应的 Embedding 向量。文档列表支持标题关键字搜索和分页浏览,方便在大量文档中快速定位。
SAG 提供极速模式和标准模式两种检索链路。极速模式直接用 query 在实体库做全文 / BM25 匹配,结合 SAG 多跳扩展,最后用 qwen3-rerank 选择 top-k,不需要 LLM 参与 query 实体抽取,速度更快,适合快速迭代调试。标准模式则先用 LLM 抽取 query 中的实体,再走 SAG 多路召回和 LLM 精排,适合追求更高精度的正式场景。两种模式都基于 event/entity 索引,而非普通向量搜索。
在对话页的右侧面板,每次检索都会实时展示 SAG 内部的检索链路和耗时数据:实体抽取花了多少毫秒、多跳扩展召回多少候选事项、各候选事项的 rerank 得分是多少。这些信息对于调试检索策略、对比不同 embedding/rerank 模型效果非常有价值,同时也让整个 RAG 链路变得透明可解释。
在「图谱」页可以将项目内所有事项和实体以交互式图谱的方式可视化呈现。节点可以拖动、缩放、展开,点击节点查看详情,双击直接跳转到对应的文档切片。这不仅是检索工具,也是理解文档结构的有效手段——特别适合用于审计大型文档库的内部关联。
每个项目都可以配置自己的 MCP Source ID,对外暴露四个工具:sag_ingest_document(文档摄入)、sag_search(检索)、sag_explain_search(带链路的检索)和 sag_get_event(按 ID 查询事项)。外部 Agent(如 Claude Desktop)通过 MCP 协议直接调用这些工具,实现「Agent 主动查询知识库」的场景,而不仅仅是「用户提问后被动检索」。这也是 SAG 定位为「面向 Agent 的 RAG」的核心体现。
SAG 采用 TypeScript 贯穿前后端的 monorepo 结构,源码按职责分为以下几层:
API 层(src/api/server.ts,约 20KB):基于 Fastify 5 构建,暴露 20+ 条 REST 路由,涵盖项目管理、文档操作、图谱查询、上传任务等核心接口。Fastify 的高性能和良好的 TypeScript 支持是选型关键——文档处理场景需要高并发的 upload job 路由和流式检索响应,Fastify 对这两者都有良好支持。
服务层(src/services/*.ts):包含核心业务逻辑:
ingestion-service.ts(约 15KB):文档摄入流水线,协调 markdown 切片 → 事项/实体提取 → embedding 生成 → 数据库写入全流程。search-service.ts(约 24KB):检索引擎核心,实现极速模式(BM25 + 多跳扩展 + rerank)和标准模式(LLM 实体抽取 + 多跳 + rerank)两种策略,是代码量最大的服务模块。mcp-agent-service.ts(约 29KB):MCP 会话管理和 Agent 运行时封装,处理流式事件(tool_start / tool_end / search_progress 等)。webui-service.ts(约 11KB):Web UI 专用查询接口,封装对话检索的便捷调用。ai-settings-service.ts(约 5KB):AI 运行时配置管理。数据访问层(src/db/repositories.ts,约 49KB):最大模块,包含所有数据库操作。核心表结构:sources(文档来源)、source_chunks(切片)、events(事项)、entities(实体)、entity_entities(实体关系)、mcp_sessions / mcp_messages / mcp_tool_calls(MCP 会话记录)。使用 pgvector 的 vector(1024) 类型存储 embedding 向量,支持向量相似度检索。
AI 层(src/ai/):
llm-client.ts(约 24KB):通用 LLM 调用封装,核心特性包括 JSON 模式(无需 prompt engineering 就能约束输出格式)、函数调用支持、以及可配置的 baseURL(默认指向 302ai 兼容接口,支持 OpenAI-compatible 任何后端)。embedding-client.ts(约 5KB):Embedding 生成,含确定性 embedding 工具函数 deterministicEmbedding。rerank-client.ts(约 9KB):Rerank 模型调用,默认使用 qwen3-rerank。MCP 协议层(src/mcp/server.ts,约 6KB):基于 @modelcontextprotocol/sdk 构建标准 stdio MCP 服务器,暴露四个工具,进程独立运行,通过环境变量 SAG_MCP_SOURCE_ID 关联项目上下文。
PostgreSQL + pgvector 是 SAG 的数据层基础。选择 pgvector 而非专用向量数据库(如 Milvus、Pinecone)有几方面考量:一是减少基础设施复杂度,项目目标是「开箱即用的本地工作台」,直接复用已有的 PostgreSQL 即可;二是 pgvector 在 100 万级向量规模下性能足够,配合全文检索(tsvector/tsquery)可以做到单库多路检索;三是 1024 维的固定维度设计(EMBEDDING_DIMENSIONS=1024)简化了向量列的类型定义,存储和索引都更紧凑。
SAG 的多跳查询通过 SQL 实现:先根据 query 实体找到相关实体,再 JOIN events 表通过共享实体做扩展,最后用 rerank 模型对候选结果打分排序。这套链路不依赖图数据库,用纯 SQL 实现了图遍历的核心能力。
src/observability/ 目录下有两个模块:
logger.ts:基于 pino 的结构化日志,默认 info 级别,支持 LOG_LEVEL 环境变量配置。model-call-log.ts:记录每次 LLM / Embedding / Rerank 调用的原始请求和返回数据,存储在 ai_provider_settings 表中,开发者可在浏览器端查看完整的模型交互日志,用于排查检索质量问题和调优 prompt。SAG 的部署友好度在同类开源 RAG 项目中属于上游水平。docker-compose.yml 提供了完整的 PostgreSQL + pgvector 环境,用户只需四步即可启动:克隆项目、复制 .env 配置文件、启动数据库、完成数据库初始化。没有 Kubernetes 没有 Helm Chart,对于个人开发者和小团队来说,这是合理的复杂度边界。
主要的部署前提是:
硬件需求方面,由于所有 AI 推理都走 API 调用,本地只需要跑一个 Node.js 服务和 PostgreSQL,CPU 和内存要求都很低(实测 4GB RAM 即可流畅运行),无需 GPU。这使得 SAG 可以轻松部署在树莓派、轻量云服务器乃至无头服务器上。
需要注意的是,虽然 docker-compose 解决了数据库问题,但 .env 配置中必须填入有效的 API Key——SAG 本身不包含任何模型推理能力,是一个纯调用的中间层。
SAG 的设计目标明确,但这个目标之外的场景不应勉强使用。首先,SAG 不做模型推理,所有 AI 能力依赖外部 API,这意味着它不适合需要完全离线部署(无外网)的场景——虽然可以用本地 Ollama 等服务替换 baseURL,但那样需要自行处理 embedding 和 rerank 的本地服务部署,官方并不提供开箱即用的支持。
其次,目前仅支持 Markdown 和 TXT 两种文档格式,PDF、DOCX、HTML 等常见办公文档需要先转换再摄入。对于已有大量非结构化文档库的用户,这个预处理的转换成本不可忽视。
第三,在 4600 个项目的知识库规模下,pgvector 的查询性能是否还能保持流畅需要实际验证——pgvector 在超过百万向量时会出现明显的 ANN 召回率下降,如果 SAG 的目标用户需要处理千万级向量的大规模知识库,可能需要迁移到 Qdrant 或 Milvus 等专用向量数据库。
SAG 代表的「事件图谱 RAG」路线,正在成为继朴素向量 RAG 和传统知识图谱 RAG 之后的第三条路。它的核心价值在于:用比知识图谱更低的构建成本,实现了比朴素向量 RAG 更好的多跳推理能力。这个平衡点对于 Agent 场景尤为重要——Agent 需要在不确定的环境中自主探索知识,而不是等待人类把所有知识预先结构化。
从项目本身的发展来看,Zleap-AI 团队同时维护了 SAG 论文复现代码(SAG-Benchmark)和 SAG 工作台两个仓库,形成了从学术验证到工程落地的完整闭环。这对于推动 SAG 技术被更广泛地采用是一个正向信号——用户可以在 Benchmark 上复现论文结果以建立信任,再在工作台上直接体验工程实现。
在 RAG 技术逐渐从「能用」走向「好用」的2025年,SAG 作为新一代 RAG 架构的工程化代表值得关注。它不仅是一个可用的工具,也是理解 RAG 演进方向的一扇窗口——当 Agent 成为人机交互的主流范式,「面向 Agent 的检索」这一细分领域将会涌现更多类似 SAG 的专门优化。