如果你最近在折腾知识库问答,一定对 RAG 这个名字不陌生。我上个月刚把一个跑了大半年的 RAG 本地问答系统翻了个底朝天,换成了 SAG(Search-Augmented Generation)思路,底层引擎也换成了开源的 OpenViking。这篇文章就是这次改造的全过程记录,包括我为什么觉得传统 RAG 思路到头了、SAG 和 OpenViking 到底做了什么不一样的事、以及你在复现时最容易踩的五个坑。
先说结论:纯 RAG 的问题是结构性的,不是调个 prompt、换个大模型就能解决。SAG 也不是 RAG 的替代品,它把“检索”这个动作升级成了“搜索”这个完整任务,多了一整套意图识别、实体链接、多路召回、记忆调和的机制。OpenViking 恰好是这套思路的一个开源落地实现,我把它接进自己的本地知识库,跑了三周,效果比原来那个“向量检索 + 拼接生成”的老方案提升了一个档次。
1. 从 RAG 到 SAG:解题思路完全变了
1.1 为什么 RAG 不够用了
先说我的原始项目。它是个客服知识库问答系统,输入是一堆产品手册、售后政策、历史工单记录和聊天记录导出文件。最早用的标准 RAG 流程:文档切块、向量化、TopK 召回、拼进 prompt、大模型生成。一开始还觉得挺像那么回事,但跑了一个月,问题越来越明显。
第一个痛点是召回粒度。我把一份 2000 多页的产品手册按固定长度切块,问“非质量问题的退货申请怎么处理”,系统只回了一句“非质量问题概不退换”。但手册里其实还有后半句:“特殊情况可通过人工审核通道申请”。这两个信息被切进了不同 chunk,模型只拿到前半段,自然答不对。这不是模型问题,是切块策略破坏了语义边界。固定 500 token 的切法对段落、条款、表格这类结构信息完全无感,你需要的是“段落感知切分”,不是“字数切分”。
第二个痛点是知识快照是静态的。老 RAG 每次问答都是“从零开始检索”,没有记忆。我今天往知识库里新增了 200 条退货规则,如果不重新把所有文档向量化一遍,新的内容永远搜不到。全量重建一次大概要跑二十分钟,磁盘 IO 还特别重。当时的客服团队每天都在更新规则,我总不能让他们一天等我二十分钟。
第三个痛点更致命:多跳和跨实体推理能力弱。用户问“五月份那次促销活动的退货率触发赔付条款了吗”,这个问题需要同时关联促销活动表、退货工单、赔付政策。传统 RAG 只会召回三个“看起来相似”的文本片段,然后让大模型瞎猜它们之间的关系。没有实体链接,没有路径检索,大模型只能靠自己的先验知识脑补,那幻觉率可想而知。这些就是我理解里的 RAG 瓶颈:切分粒度、静态快照、多跳推理,三个问题全是结构性的。
1.2 SAG 到底改了什么
SAG 全称是 Search-Augmented Generation,直译是搜索增强生成,核心思路是把“检索”升级为“搜索”。传统 RAG 的流程是“理解 query → 相似度匹配 → 拼接 → 生成”,而 SAG 把中间这一步拆成了一整套检索任务:先识别用户意图,判断这个问题需要走哪几路召回,然后并行去向量库、倒排索引、知识图谱、多模态通道里搜,再做融合打分和重排,最后才交给大模型生成。
为了说清楚差异,我整理过一个对比表:
| 维度 | 传统 RAG | SAG |
|---|---|---|
| 输入理解 | 直接向量化 query | 意图识别 + 实体链接 + 子问题拆解 |
| 索引类型 | 向量索引为主 | 向量 + 倒排 + 图谱 + 多模态混合索引 |
| 召回逻辑 | 单路相似度 TopK | 多路并行召回,按任务类型分配权重 |
| 上下文组装 | TopK 文本直接拼接 | 按实体路径、文档层级、时间顺序组装 |
| 记忆能力 | 无状态 | 短时会话记忆 + 长期图谱记忆调和 |
| 生成与溯源 | 模型自由发挥 | 引用 chunk ID、来源文件、段落号 |
这六个维度的差别其实归结为一句话:RAG 把问答当成“找相似文本”的任务,SAG 把问答当成“找正确答案证据”的任务。打个比方,去图书馆找资料,RAG 像是你只报一个作者名,然后自己去书架层一本本翻;SAG 是先有管理员帮你确认你要的是哪类书、哪一年的版本、要不要看上次借阅记录,然后多路查询同时开跑,最后把相关度最高的几份材料按逻辑摆在你桌上。
正是这个结构差异,让 SAG 能同时解决我前面说的三个痛点。多跳推理靠的是知识图谱里的实体路径,不再是文本相似度;增量更新靠的是记忆调和机制,把新实体并进旧图谱,而不是全库重向量化;跨模态检索靠的是多路召回里有一条专门的视觉通道。这些都是传统 RAG 框架里没有的东西。
1.3 OpenViking 和 SAG 是什么关系
OpenViking 是一个开源的“记忆增强 AGI 应用服务端”项目,它把整体能力拆成了记忆、感知、认知、行动几个大模块,模块之间通过 HTTP 接口通信。最核心的是它的记忆系统和 search 模块,对外提供的检索代理就叫 SAG Agent。换句话说,OpenViking 不是 SAG 论文里的概念验证,而是一套能直接部署的工程实现,而且它是离线的、可本地跑的,不依赖任何云服务。
我为什么选它而不是自己写?因为如果从头实现 SAG,我需要自己搞定联邦检索、知识图谱构建、记忆调和、多模态索引,这一整套没有三个月下不来。而 OpenViking 已经把这些组件打包好,我只需要关注自己的业务数据怎么接进去、效果怎么调优。它的组件结构大概是这样的:
- memory:向量记忆 + 图谱记忆 + 会话记忆,负责所有“状态”的存取
- perception:感知输入,包括文本、图片、OCR、结构化数据
- search:SAG Agent 本体,负责意图识别、子查询划分、多路召回、融合排序
- learn:记忆调和与增量学习,处理新数据怎么并进旧知识
- actor:行动模块,把检索结果和生成结果输出给上层应用
我当时看中的就是这五个模块刚好对应 SAG 的关键链路。search 管“搜得全”,memory 管“记得住”,learn 管“更新得了”,perception 管“读得进多模态”,actor 管“答得出”。这比我之前的“一个向量库 + 一个模型调用”复杂了不少,但也正是因为每个环节都有独立模块,调优空间才大。
2. 动手搭一套 SAG:OpenViking 核心模块拆解
2.1 先搞清 OpenViking 里谁在干“检索增强”的活
如果只看名字,你可能会以为 SAG Agent 全部逻辑都在 search 模块里,实际不是。一次完整的 SAG 问答,是 perception、search、memory、actor 四个模块接力完成的。我把这个工作流画成过伪代码,看起来像这样:
def sag_query(raw_query): # 1. 感知层:统一入口,处理文本、图片、语音等原始输入 query = perception.parse(raw_query) # 2. 认知层:意图识别 + 实体链接 intent = search.intent_classifier(query) # 判断是事实型/多跳型/闲聊型 entities = search.entity_linker(query) # 把"五月份的促销"链接到图谱节点 # 3. 记忆辅助:查会话记忆,带上对话历史与用户偏好 session = memory.session.get(session_id) context = memory.longterm.query(entities, top_k=20) # 4. 多路并行召回 vector_hits = search.vector_recall(query, top_k=15) # 语义通道 keyword_hits = search.keyword_recall(query, top_k=15) # 倒排索引通道 graph_hits = search.graph_recall(entities, top_k=15) # 图谱路径通道 # 5. 融合排序 fused = search.rrf_merge([vector_hits, keyword_hits, graph_hits]) final_docs = search.rerank(fused, query) # 6. 生成 answer = actor.generate(query, final_docs, session_prompt=context) # 7. 写回记忆:本次问答沉淀为短期记忆,供后续 session 使用 memory.session.update(session_id, query, answer) return answer这个顺序不是随便定的。意图识别必须在最前面,因为后续所有召回通道的权重都要依赖它。比如“这个破产品怎么又坏了”这种抱怨型 query,没必要走图谱多跳,走向量召回就够了;而“退货率触发赔付了吗”这种任务,如果不走图谱路径,答案大概率是编的。实体链接也很关键,它把文本里的“五月份”这样的口语化表达,映射到图谱里的一个时间节点和促销活动节点,后面才能做路径检索。
这里有个细节值得注意:多路召回的结果不能简单地按分数取平均合并。向量召回和关键词召回的分数分布范围完全不同,直接平均等于让高分通道一票否决其它通道。OpenViking 默认用 RRF 之类的融合策略,把不同召回源的排序位置转换成统一分数,再合并。我后来也试过给它加一个 cross-encoder 重排,效果还能再往上提几个点,但延迟会变高,后面会说到这个权衡。
2.2 记忆系统:SAG 和 RAG 最本质的分水岭
老 RAG 最让我难受的就是无状态。用户前两天问过“你们产品能不能退”,今天再问“那我那个订单呢”,系统完全不记得昨天聊过什么。OpenViking 的记忆系统把这个补上了,它分两层:短期会话记忆和长期知识记忆。
短期会话记忆就是 session buffer,每次问答之后把 query 和 answer 写进去,下次问答自动带上。这个直接用向量查找近期对话片段就能实现,实现成本低,但体验提升非常明显。客服场景下,用户经常会说“我刚才说的那个型号”,没有会话记忆这句话根本没法理解。
长期知识记忆更有意思,它是对知识库图谱节点的持续维护。新文档进来以后,learn 模块会抽取其中的实体和关系,再去和旧图谱比对,能合并的就合并,能拼到已有实体上的就拼上去,有冲突的则先标记为“待确认”,不急着覆盖。这个过程 OpenViking 管它叫记忆调和。
一个配置示例可以说明 session 和长期记忆是怎么分开管理的:
memory: session: enable: true store: milvus_shortterm ttl_minutes: 30 longterm: enable: true store: milvus_longterm reconcile_schedule: "*/10 * * * *" entity_link_threshold: 0.82这段配置里,session 记忆只保留 30 分钟,适合客服会话;长期知识记忆每 10 分钟做一次调和,实体链接置信度低于 0.82 的不入库。这样设计的好处是,客服人员上午更新了一批产品规格,下午用户来问就查得到,而不需要重新向量化整个知识库。
2.3 多模态与图片入库:SAG 给了新答案
很多人问过“RAG 知识库能存储图片吗”,这个热搜词背后的真实诉求其实是:我的知识库里有大量图片,怎么让它们参与到问答里来。传统 RAG 的默认做法是忽略图片,或者先 OCR 成文本再入库。第一种方案等于扔掉一半信息,第二种方案只留下了文字,图片里的排版、形状、颜色、关系全都丢了。
SAG 的路线不一样。OpenViking 的 perception 模块对图片有三个处理通道并行:OCR 提取文字、版面分析识别标题和区域、视觉模型生成图片语义向量。这三个通道的结果会单独建索引,同时还会把图片和知识图谱里的实体关联起来,形成一个“图片节点”。
我在实际项目里遇到过一个典型案例。产品手册里有一个故障示意图,图片里红灯在闪烁,旁边画着传感器位置。售后记录里有一段聊天记录描述“红灯闪两下然后熄灭”。传统 RAG 里这两份资料完全无法关联,因为一个是图片像素,一个是聊天文本。但 SAG 把图片节点做出来后,图谱里有了“红灯闪烁”和“传感器故障”的关联路径,查询“这张图里的红灯闪烁代表什么”就能沿着这条路径把故障说明和售后政策一起拉出来。这个体验,老 RAG 是给不了的。
3. 从零搭建 SAG 知识库的实操记录
3.1 环境准备与数据入口
我的搭建环境不算豪华,没有一整台 A100 给我折腾。实际用的是一台 32G 内存的工作站,一块 8G 显存的消费级显卡。生成端用的是量化过的 7B 本地模型,向量库用开源中间件,OpenViking 跑在不同端口上。整体架构是:OpenViking 负责 SAG 链路,向量库和关系库做存储,本地模型做生成,所有组件全部本地部署。
| 组件 | 最小要求 | 我的配置 | 备注 |
|---|---|---|---|
| 操作系统 | Linux / Windows | Ubuntu 22.04 | WSL 也能跑但别碰 Docker 网络转发 |
| Python | 3.9+ | 3.10 | 3.11 也没问题 |
| 内存 | 16G | 32G | 知识库越大越吃内存 |
| 显卡 | 纯 CPU 可跑 | 8G 显存 | CPU 能跑但延迟感人 |
| 向量库 | 内存模式即可 | 独立容器 | 越大越需要独立部署 |
| OpenViking | 最新 release | v0.9.x | 直接用源码跑也行 |
数据入口这块,我处理的原始材料非常杂:微信导出的 HTML 群聊记录、几十张产品实拍图、PDF 版手册、Excel 退货记录表。每种格式踩过一遍坑之后,我总结出来的预处理原则只有一条:不要让原始格式的复杂性传到后面处理阶段。HTML 先抽正文并保留段落层级标签,PDF 先做版面分析再按页切,Excel 先转成带表头结构的 Markdown 表格,图片先过 OCR 存一份文本副本同时保留图片路径。这步花的时间占整个改造的三分之一,但后面所有检索质量的提升都建立在它上面。
3.2 结构化拆解:别再傻切 500 token
现在热搜里还有个词叫“本地 RAG 文本拆解工具”,可见大家都被切分问题折腾过。我的建议是,如果你的文本有明确结构,就不要用纯字符切分。我知道很多教程让你固定 500 token 带 50 token 重叠,因为实现起来最简单。但就像我之前说的,这会把条款、表格、段落关系全部切开,给后续所有环节挖坑。
我的拆解策略是按文档类型差异化处理:
| 文档类型 | 拆解规则 | 产出 chunk 的元数据 |
|---|---|---|
| PDF 手册 | 按版面分析得到的标题层级切 | source_file, page, section, heading |
| HTML 聊天记录 | 按对话消息分组,保留 sender 和 time | source_file, message_id, sender, timestamp |
| Excel 表格 | 整表转 Markdown,按主题行分块 | source_file, sheet, header_row |
| 图片 | OCR 结果存文本,图片路径单独建节点 | source_file, image_path, ocr_text |
切完以后,每个 chunk 都要带元数据。这一点是我在整个项目中收获最大的经验。早期我的 chunk 只有纯文本,无法做引用溯源,用户问“这个政策出自哪里”系统答不上来。给 chunk 加上 source_file、page、heading、entity_ids 这些元数据之后,SAG 的引用溯源、实体链接、路径检索才有了基础。别嫌这一步繁琐,它决定你后续能走多远。
实体抽取我也放在这一步做,不是在检索时临场做。我用 OpenViking 的 learn 模块在文档入库时顺便抽一遍实体和关系,抽完存入库。这样查询阶段实体链接可以走缓存,延迟会低很多。
3.3 混合索引与多路召回
索引阶段我同时建了三种索引:向量索引负责语义相似,倒排索引负责关键词精确匹配,知识图谱负责实体关系路径。听起来工程量很大,但 OpenViking 已经把这三类索引的写入和查询封装好了,我要做的只是配好数据源和索引字段。
查询阶段的核心是多路召回。一次 query 进来后,向量检索、关键词检索、图谱路径检索并行跑,每路各回 15 条左右,然后做融合。我参考的是经典的 RRF(Reciprocal Rank Fusion)策略,它的思路特别朴素:每个召回结果先看它在各路的排序位置,位置越靠前综合得分越高,排序位置转换成1 / (k + rank),k 通常取 60。这个策略最大的好处是不依赖各路分数尺度一致,所以不用做复杂的分数归一化。
def rrf_merge(rank_lists, k=60): scores = {} for rank_list in rank_lists: for rank, doc_id in enumerate(rank_list): scores[doc_id] = scores.get(doc_id, 0) + 1.0 / (k + rank + 1) return sorted(scores.items(), key=lambda x: x[1], reverse=True)融合之后再过一次重排。重排我用的轻量 cross-encoder,一次只能处理几对 query 和文档,但它看的是完整的语义匹配关系,准确度比双塔向量高不少。只重排前 20 条的话,耗时能控制在几十毫秒内,完全值得。
3.4 组装生成与引用溯源
生成阶段的 prompt 设计,我踩过最深的坑是:如果你不约束大模型引用来源,它真的会自由发挥。老 RAG 时代我经常看到模型一本正经地回答“按公司政策,您可获得三倍赔偿”,实际上政策文件里根本没有这条。模型不是坏,是没被约束。
SAG 的做法是把检索结果的 chunk ID、来源文件、段落号直接作为可引用的证据传给模型,并明确要求答案必须标注出处。我的 prompt 模板大致长这样:
你是客服知识库问答助手。请严格依据下方【参考证据】作答,并在答案末尾列出引用来源。 引用格式:[证据 ID 列表],例如:本项目知识点: 来源1: 售后政策.pdf 第3节 非质量问题退货 来源2: chat_log_202405.html 消息ID 10234 参考证据: {evidence} 用户问题: {query} 作答要求: 1. 若参考证据不足,请直接回复"资料库中暂无相关信息"。 2. 不得超出参考证据内容自行发挥。 3. 必须给出引用来源。加上这个约束以后,两个提升特别明显。一是幻觉率肉眼可见地下降了,因为模型不再需要靠“记忆”补全政策细节,每句话都能对应到证据。二是系统具备了解释能力,用户说“凭什么是这个结果”,我直接把引用来源渲染成超链接,他点进去就能看到原文出处。这一点在客服场景里太重要了,用户要的是“能查证”,不是“像那么回事”。
4. 三个跑通 SAG 的实战场景
4.1 多跳问题:一只智能客服的 17 连问
我用 300 条由政策、订单记录、客服日志混合构成的社区问答做了个测试集,专门测多跳问题。老 RAG 基线在这个测试集上的答对率只有 0.42,SAG 做到了 0.58。提升看起来不算夸张,但样本里大多数是多跳任务,单跳简单问题的正确率本来就差不多。
一个很典型的例子:用户问“五月份促销活动买的智能门锁,如果触发退货,退款走什么流程”。老 RAG 召回了三份内容,一份促销活动介绍,一份门锁退换条款,一份售后政策,但模型无法在它们之间建立关系,于是答出了一个分不清适用范围的方案。SAG 走的是另一条路:先把“五月份促销活动”链接到图谱节点,再通过该节点找到关联商品“智能门锁”,再找到对应的退换条款和支付方式说明,最后把这三份证据按逻辑顺序组织起来。模型最终输出的是完整的“活动商品适用标准退款流程”,并标注了每步的来源。
这个场景跑通之后我才真正意识到,SAG 不是说模型更强了,而是它让模型拿到的不再是“三块孤立的拼图”,而是一串能串成线的证据链。
4.2 图文混合档案:图片终于能进检索链路
前面提到的红灯闪烁故障图,是我觉得最值的一个案例。这张图存在于产品手册 PDF 里,客服聊天记录里有一段对应的文字描述,售后政策里也有传感器故障处理条款。老 RAG 时代,这三份资料互相之间没有任何链接,图片更是完全不参与问答。
SAG 改造后,perception 模块把图片做了 OCR 提取、版面识别和语义向量化,learn 模块把“红灯闪烁”这个OCR结果跟“传感器故障”这个实体连了起来,再把图片节点挂到产品节点之下。当我问“红灯闪两下代表什么,配件坏了能修吗”,召回链路是:图谱路径“图片节点 → 传感器故障 → 配件维修政策” + 向量召回“历史聊天记录” + 关键词召回“红灯闪烁”。最终答案不但解释了故障含义,还带了维修政策和聊天记录里客服给出的操作建议。每条结论都能点出对应的图片或原文,这在传统知识库里几乎是不可能的。
4.3 持续增量:知识库不再是“一次性快照”
我还专门做了一个压力实验:连续一周每天往知识库里增量导入 100 条客服聊天记录和更新后的产品政策。老 RAG 的做法是每天全量重建索引,一次大概跑 22 分钟,期间检索接口基本不可用,而且因为全量重建的窗口期太长,往往没跑完新数据就又来了。
SAG 的记忆调和机制是增量处理:每天晚上定时任务识别新增数据里的实体和关系,合并到图谱中,同时把新增的文本 chunk 增量写进向量库。实测下来,一天 100 条记录的增量调和只要 5 分钟,期间检索服务完全在线。对比一下:
| 更新方式 | 100 条记录耗时 | 服务可用性 | 图谱一致性 |
|---|---|---|---|
| 全量重建(RAG 型) | 约 22 分钟 | 期间不可用 | 一次性替换,旧数据丢失 |
| 增量调和(SAG 型) | 约 5 分钟 | 全程可用 | 合并新增,保留历史 |
当然,增量调和不是银弹。跑了一周之后我发现,图谱里的实体关系会慢慢膨胀,偶尔出现两个重复节点这时候得手动跑一次全量校准。我的策略是每周日凌晨跑一次全量对齐,把这一周的增量结果合并到基准图上。代价是固定的,但换来了平时更新的低延迟。
5. 常见问题与避坑清单(含实测数据)
5.1 性能:SAG 会不会比 RAG 慢
会,这是肯定的。我实测单条问答延迟,老 RAG 平均 0.9 秒,SAG 平均 1.8 秒。多出来的时间主要花在三路并行召回和后面的一层重排上。但 1.8 秒对客服问答场景完全可接受,用户感知差异很小。
如果延迟敏感,有三个优化手段我试过都有效。第一是意图分流,不是所有问题都需要走图谱多跳,让意图分类器把简单的实体查询直接走向量通道,绕过图谱路径,大概能省 0.3 秒。第二是结果缓存,高频问题直接命中缓存,完全不用走检索链路,这个能把平均延迟直接打下来。第三是给重排环节加阈值,前两条召回结果已经高度一致的时候就跳过 cross-encoder,减少无谓计算。优化后我自己的环境下延迟回到 1.2 秒左右,与老 RAG 的差距基本可以忽略。
5.2 模型与硬件:本地最小配置参考
很多朋友问我没有好显卡能不能玩。我拿一台纯 CPU 的旧服务器也跑通过,只是延迟要到 5 秒以上,只能当个人实验用。配置参考如下:
| 环境 | 组件 | 配置 | 可用性 |
|---|---|---|---|
| 最小可跑 | 生成模型 | 4bit 量化 7B,纯 CPU | 能跑,单答约 5-8 秒 |
| 最小可跑 | 向量库 | 内存模式 | 数据量 10 万条以内没问题 |
| 舒适配置 | 生成模型 | 8G 显存跑 7B/13B | 单答约 1-2 秒 |
| 舒适配置 | 重排模型 | cross-encoder 小模型 | 显存占用约 1G |
我强烈建议把检索链路和生成模型分开部署,哪怕都在同一台机器上,也要用独立的进程或容器。这样当生成模型负载高时,向量检索和图谱查询不受影响,反过来也一样。
5.3 我自己踩过的坑(务必看)
这套项目做下来,坑真的不少,我挑五个最典型的说说。
第一个坑是文档级约束失效。我一开始把 PDF 里的免责声明切进了两个不同 chunk,模型只看到一半,就开始乱发挥。后来给每个 chunk 都加了“所在文档”和“所在条款”两个元数据字段,检索时如果召回结果来自同一条款,就把整个条款段落完整带上,而不是只带被切碎的那一段。这个问题不解决,后面所有优化都白搭。
第二个坑是多路召回结果直接全量拼接。三路各回 15 条,45 条证据全部塞给大模型,上下文窗口直接爆掉,模型开始长篇大论胡说。后来我改成融合后只取 top 5 到 top 8 条,再按证据与问题的相关性排序,生成质量明显提升。记住,上下文不是越多越好,而是越准越好。
第三个坑是实体链接乱链。刚开始我把置信度阈值设得很低,导致图谱里产生了大量错误关系。比如把“红色”和“红灯闪烁”当成同一个实体,导致查询“红色外壳的型号”时错误地召回了故障文档。后来我把实体链接阈值提到 0.82 以上,宁可少链一个,也不要链错一个。图谱污染比图谱稀疏更可怕,因为它会带偏所有后续路径检索。
第四个坑是增量调和不能替代全量校准。跑了一周增量之后我发现两个重复的“售后政策”节点同时存在,导致某些查询的召回结果分裂。后来定了每周日全量校准的计划任务,把这一周的增量结果合并到基准图谱上,这个问题才算解决。
第五个坑是只调生成器不调检索链路。我最早花了大量时间调 prompt 和温度参数,正确率始终上不去。后来把精力转向检索侧:改切分策略、调融合权重、加实体链接,正确率提升幅度远超调 prompt。生成端能救的只是“拿到好证据后怎么组织好”,它救不了“证据本身是错的”。
还有一个算半坑半经验的东西:OpenViking 的配置项非常多,一开始别急着全开。我只开了 search 和 memory,其它模块保持默认,等跑通一个最小闭环以后,再逐个打开 learn、perception 的高级功能。一次全开的结果就是配置写了几百行,出了问题根本不知道从哪排查。
这套 SAG 实验做下来,我最大的体会是:传统 RAG 不是不够好,是它的假设太省事了——以为向量相似度足够代表语义,以为无状态检索够用,以为文本块之间不需要关系。而 SAG 至少把这些问题都摆到了台面上,并且给了工程上的解决路径。OpenViking 的价值在于,它把这套复杂链路做成了可以启动的模块化系统,让我这种单兵作战的开发者也能在两周内把完整方案跑起来。
如果你也在维护知识库问答,我建议别急着推翻现有系统,先用一天时间,拿自己的数据集,按我上面说的方案搭一个 SAG 最小闭环,再拿同一批刁钻问题来对比。结果很可能会让你重新审视“检索增强”这四个字到底意味着什么。我自己的下一步计划,是把多模态通道换成更便宜的 OCR 加版面识别组合,然后继续跑增量更新压力测试。这类实验目前还远没到天花板,值得持续投入。