ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

深入浅出RAG:从基础检索到Agentic与GraphRAG的进阶实践

深入浅出RAG:从基础检索到Agentic与GraphRAG的进阶实践

最近有同行跟我吐槽,他们的RAG问答系统上线两个月,检索命中率(Hit Rate)曲线非常漂亮,用户却天天反馈答非所问。这种反差我见得太多了——基础RAG做得再顺手,一到生产环境就会撞见知识割裂、全局语义丢失、复杂多跳问题答不上来。这篇是“深入浅出RAG”系列第14章,专门聊高级RAG技术:不是再堆一个向量库完事,而是把检索策略、知识结构、评测闭环放到同一张桌上重新设计。适合已经跑通过基础RAG、正被线上效果和知识库规模困扰的团队阅读,看完就能对着自己的pipeline逐级做诊断和升级。

1. 基础RAG的边界在哪:先把高级技术要解决的问题摆上桌

很多团队对RAG的理解停在一张图里:文档切块、向量化、存进向量库、查询时Top-K召回、拼进Prompt、丢给LLM生成。这个流程本身没错,但它隐含了一个前提——每个问题都能被“一小段相似文本”回答。一旦问题跨出这个前提,基础RAG就会以各种姿势翻车。

先看几个我实际遇到过的失败场景。第一个是全局性问题,比如“我们公司这套员工手册整体强调什么价值导向”,向量检索会把所有跟“价值”沾边的片段都拉回来,模型被迫从一堆互不相关的碎片里“脑补”总结,结果自然挂。第二个是多跳问题,比如“上面那条报销制度的附件里提到的申请单模板放在哪个目录”,答案要经过两次跳转才能拿到,中间任何一跳断了,生成端只能给出一个看似合理但完全编造的路径。第三个是知识冲突,文档A说“请假提前1天申请”,文档B说“请假提前3天申请”,基础RAG可能把两段同时拼进上下文,模型没有足够信息判断哪个是最新版本,就开始了自由发挥。

这些失败的共同点在于:基础RAG把“检索”理解成了一次性的相似度匹配,而不是一个有策略的信息获取过程。高级RAG技术想解决的,本质上就是三件事:让检索过程具备决策能力(Agentic RAG)、让索引结构保留知识之间的关联(GraphRAG和本体RAG)、让每一步都有办法被测量和优化(评测体系)。

1.1 基础RAG的典型失败链路

可以画一条最简链路来定位问题:Query → 向量化 → 向量检索 → Top-K → 拼接上下文 → LLM生成。这条链路里至少有五个拐点会出问题。

第一是Query本身质量不过关。用户随口问“那啥时候给钱”,如果知识库里全是正式说法“款项支付周期”,embedding模型又不够强,召回结果可能完全不相关。这不是高级技巧能兜底的,但确实属于常见病。第二是分块策略太粗。纯按固定字符切,把一段完整表格逻辑拦腰截断,或者把一个标题跟正文拆到两个chunk里,后续无论怎么检索都有先天缺陷。第三是向量模型和语料领域不匹配。通用embedding模型在垂直领域的表现下降非常明显,尤其是代码、医疗、法律这种术语密集的场景。第四是Top-K数量拍脑袋。K=3,有时候够用,有时候压根不够,但没有评测支撑的话,没有人说得清该调成几。第五是上下文组织方式粗糙。所有召回片段一股脑塞进Prompt,顺序随意,重复信息一堆,LLM抓不住重点。

在高级RAG语境下,这些节点都不再是“调参就完事”的琐碎问题,而是会被系统地对待:每一条都要有可观测的指标,每一次改动都要有对比实验。

1.2 “知识割裂”到底怎么发生的

热词里反复出现“知识割裂”,这个词其实是基础RAG最本质的短板。向量检索把每个chunk当成孤立的点,查询时用余弦相似度去捞“长得像”的点,但文档之间的层级关系、引用关系、时间关系、因果关系全部丢了。就像一个图书馆把所有书拆成单页扔进水池,你去捞“关于某个人物的完整履历”,结果捞上来的可能是不同书里各自独立的一句话,它们之间毫无逻辑衔接。

更麻烦的是,向量相似度本身是一个“局部语义近似”的度量,它既不懂实体是不是同一个,也不管两个实体之间到底什么关系。“张三的报销单”和“张三的考勤记录”在向量空间可能距离很远,因为它们讨论的主题不同,但对用户来说,这两份文档都属于“张三”这个实体,应该能被一个关于张三的查询同时命中。高级RAG里走GraphRAG和本体RAG的路线,就是要把这层“实体—关系”结构显式地建出来,让检索从“捞相似文本”升级成“按图索骥”。

2. Agentic RAG:把检索从一次查询变成一场有策略的对话

Agentic RAG用一句话概括,就是让模型在回答之前先决定“该怎么检索”。它不再是被动接收Top-K结果,而是拥有一个循环:分析问题、选择工具、执行检索、观察结果、判断够不够、不够再查、够了再回答。

为什么需要这个循环?因为真实用户的问题很少是单跳的。拿一个我做过的人力知识库项目举例,用户问:“我入职两个月,想请五天年假,按制度我需要提前多久申请?跟出差报销的时间要求一样吗?”这个问题拆开至少有三跳:第一跳要找到年假制度;第二跳要检索请假申请时限;第三跳要找到出差报销制度并比较时间要求。如果一次性只用原始query去检索,向量召回的结果会夹杂大量不相关内容,模型想答好都没有原料。Agentic RAG的做法是先把问题拆成子问题,逐个检索,最后汇总。

2.1 Agentic RAG的四个核心技能

我自己在落地时习惯把Agentic RAG拆成四个能力,缺一个都觉得不完整。

意图识别与路由。系统先判断这个问题是否需要检索:问“你好”就不检索,直接聊天;问“公司邮箱怎么配”就走IT知识库;问“上个月考勤异常怎么处理”可能既要查考勤系统又要查制度文档。路由的粒度越细,后续检索就越轻松,这一步通常用一个轻量分类模型或LLM本身就能做。

子问题拆解与查询改写。把复杂问题拆成多个可独立检索的子问题,或者把模糊的口语化表达改写成与文档更匹配的规范术语。拆解不是简单切句子,而是根据文档结构反推:文档里有哪些概念模块,问题涉及哪几个,需要什么顺序去查。Query改写讲究“怎么改才能召回率高”,不是把句子变得更长更复杂,而是补充实体信息、去掉多余修饰。

工具调用与多步检索。这是Agentic RAG和普通RAG最关键的区别。模型可以调用多个工具:向量检索、图数据库查询、SQL查询、Web搜索、特定文件的读取接口。每一步检索的结果会作为新的上下文参与下一步决策。比如先查出“报销制度版本V3.2”,下一步就精确去检索“V3.2里的差旅部分”,而不是重新做一次全库模糊匹配。

答案验证与反思。生成答案之后再加一道校验:答案里的关键论断是否都能在已召回的片段中找到依据?有没有关键信息缺口?最近有一批做Agentic RAG的框架在这一步引入了“自我批评”机制,模型先答一版,再追问自己“我要证明这个结论还缺什么证据”,缺口存在就继续检索。很多RAG项目把忠实度指标从70分拉到90分,靠的就是这一步。

2.2 一个看得见摸得着的落地参考

理论上讲得再多,不如给一个最简单的代码形态。我见过很多团队一上来就套重型Agent框架,结果连工具调用的JSON解析都没调明白。其实一个最小可用的Agentic RAG循环,用原生的模型工具调用就能搭:

import json def run_agent(query, retriever, llm): tools = [{ "name": "retrieve", "description": "从企业知识库中检索与问题相关的内容片段", "parameters": { "type": "object", "properties": { "query": {"type": "string", "description": "用于检索的问题或关键词"} }, "required": ["query"] } }] messages = [ {"role": "system", "content": "你是一个检索助手。如果需要更多资料,调用retrieve工具。" "调用后请根据返回内容判断信息是否充分,不充分就继续检索,直到可以作答。"}, {"role": "user", "content": query} ] for _ in range(5): response = llm.chat(messages=messages, tools=tools) if response.get("tool_calls"): for call in response["tool_calls"]: args = json.loads(call["function"]["arguments"]) chunks = retriever.search(args["query"], top_k=5) messages.append({ "role": "tool", "name": "retrieve", "content": "\n\n".join(chunks) }) else: return response["content"] return "抱歉,检索轮数已达上限,信息可能不完整。"

注意几个关键点:循环次数必须设上限,否则模型可能陷入无限检索;“tool”消息要带回完整的检索结果,让模型在下一轮自己判断够不够;系统提示词里必须明确“不充分就继续检索”的指令,否则模型倾向于拿到一次结果就直接编答案。

我在生产项目里通常不会直接裸写这个循环,而是用LangChain的create_agent或LlamaIndex的ReActAgent封装一层,理由不是它们更智能,而是它们在消息格式、工具注册、错误恢复上已经处理了大量边界情况。自己写状态机也不是不行,但连工具报错、超时、重试这些事情都要自己处理,开发成本一下就上去了。

2.3 哪种场景不建议上Agentic RAG

虽然Agentic RAG是高级RAG里最热门的方向,但我不建议所有人都追。先算两笔账:一笔是延迟,一次Agentic查询至少两三轮LLM调用,每轮还有检索开销,体验上通常从“1秒出答案”变成“5到10秒出答案”;一笔是成本,模型调用次数翻倍,大模型的token消耗也翻倍,如果业务本身对成本敏感,这套方案需要额外评估。

还有一个更要紧的前提:基础RAG的失败样本要先统计,再决定要不要上Agent。我见过一个项目,用户问的问题其实都是单跳FAQ,但回答质量差是因为embedding模型没选对,结果团队花了两周把Agent框架搭好,问题依旧。正确的姿势是先跑一段时间的标准pipeline,把用户问但答错的日志捞出来,逐条归类。如果大量失败源自“需要拆解、需要多步检索、需要对比多个文档”,Agentic RAG就是对症下药;如果失败主要源自检索本身召回差,先回去修分块和向量模型。

3. 从GraphRAG到本体RAG:用结构化知识治“知识割裂”

很多人第一次听到GraphRAG,以为是某个新检索模型,实际上它是一整套把知识图谱引入RAG的工程路线。核心思想不复杂:既然向量索引丢了关系,那就把关系显式地建回来。

当年的GraphRAG论文里最打动我的一个数字,是它在“全局性问题”上相比朴素RAG的显著提升。所谓全局性问题,就是“这个语料库整体上讲了什么”“这些项目之间的依赖关系怎么样”这类问题。这类问题在普通RAG里几乎无解,因为没有一个chunk包含全局答案;但放到图里,节点和边天然就是全局结构的一部分,检索路径可以沿着关系在图上走。

3.1 GraphRAG到底改了什么:局部检索与全局摘要两条路

GraphRAG的建图过程通常是:先把语料拆成小块,用LLM抽取出实体(比如“张三”“报销制度”“考勤系统”)和关系(“张三提交了报销单”“报销制度引用考勤数据”),再把它们写入图数据库。接下来关键一步是社区检测,把紧密关联的实体簇划分出来,针对每个社区生成一段摘要文本,存在索引里。到了查询阶段就有两条路。

局部查询走实体定位:先把问题里的实体识别出来,比如“张三”和“报销单”,然后在图里找到这些实体所在的子图,把直接相关的实体和关系作为上下文返回。这条路适合“特定实体之间的关系是什么”这类问题。全局查询则走社区摘要:把匹配到的社区摘要逐层汇总,作为回答“整个体系在讲什么”的依据。这两条路径和向量检索不冲突,实践中通常是混着用:先用向量召回的chunk兜底,再用图路径补充关系和全局信息。

3.2 本体RAG:老概念在LLM时代重新翻红

本体RAG是GraphRAG的近亲,但有一个关键区别:GraphRAG倾向于让LLM自由抽取实体和关系,本体RAG则先定义一个显式的Schema,强制抽取按照Schema执行。这个Schema就是本体——概念、属性、关系类型的明确定义。

打个比方,GraphRAG像是让实习生去整理档案,看到什么记什么;本体RAG是先给实习生一张表头:“你必须记录人名、部门、职级、入职日期、汇报对象”,漏填或填错就要重来。好处非常明显:抽取结果一致性高,没有概念漂移;同一个实体在不同文档里的别名能被统一到标准节点上;查询时能顺着Schema做引导式推理,因为系统知道“人—部门—职级”可以串联。

我自己的经验是,本体RAG特别适合领域关系明确、术语规范的知识库。比如IT运维知识库,Schema里定义“服务—依赖—中间件”“告警—关联—变更单”,抽取和查询都围绕这套模型转。如果领域比较开放,或者语料非常杂,硬套Schema反而会让抽取无从下手,这时候自由抽取的GraphRAG更合适。

3.3 落地成本与选型判断:什么时候值得上图谱

图谱不是银弹,它有明显的成本和门槛。抽取阶段要拿LLM跑全量语料的批处理,可能抽出一大堆噪音三元组;存储和查询要引入图数据库,团队需要学习Cypher或Gremlin;本体设计要业务专家参与,Schema随时可能变动。所以我的建议是:先判断你的知识库是不是“关系密集型”。如果文档里有大量人员、项目、系统、制度之间的引用和归属关系,而且用户问题经常跨文档、需要追溯完整关系链,图谱就值得上。如果知识库本质上是FAQ合集,每个问题独立成篇,关系密度很低,那老老实实把向量检索做好就行,不要硬上图谱。

折中的方案我也用过不少:先保留向量检索做主要召回,再叠一层轻量三元组索引。不需要完整图数据库,用CSV或者SQLite存“实体—关系—实体”即可,查询时解析出实体后直接查关联节点,把相关实体对应的原始chunk补充进上下文。这种方式成本低,但对付一般的跨文档关联问题已经够用。

4. 高级评测:别被Hit Rate骗了,把pipeline一层层拆开看

热词里有一个“rag hit rate”,它在技术群里出现频率极高。Hit Rate在RAG语境里通常指:检索返回的Top-K结果中,是否包含人工标注的相关文档。这个指标本身没错,但它只回答“检没检到”,完全不回答“答没答对”。如果你的评测只跑Hit Rate,得到的结果再高也没法说明系统在生产环境里好不好用。

我见过最典型的自欺欺人案例:团队拿文档标题改写生成了一批测试query,语料库就几百个chunk,Hit Rate一测0.95,上线后用户满意度不到一半。原因就两条:测试query和语料库重合度太高,几乎等于开卷考试;测试问题全是单一事实查询,没有比较、多跳和全局问题。所以评测集的设计质量,有时候比评测指标本身更容易决定项目的成败。

4.1 先搞懂不同指标在回答什么问题

不同指标对应pipeline的不同环节,选错了指标等于用错了尺子。我习惯把评测分成检索侧和生成侧两组来看,下面这个表是我常用的指标参考:

环节指标它回答的问题常见用法
检索Hit Rate@K正确答案是否进了前K快速判断检索是否有硬伤
检索MRR正确答案排名有多靠前判断排序质量,K里第1名和第5名差别很大
检索Recall@K所有正确答案召回了多少多答案问题必需,只看一个答案命中会漏
生成Faithfulness答案是否忠实于上下文防幻觉的关键指标
生成Answer Correctness答案本身与标准答案是否一致需要标注标准答案,成本较高
生成Context Relevance返回的上下文与问题相关度判断是不是检索结果本身就跑偏了

评估工具方面,RAGAS和TruLens是业内用得比较多的,LlamaIndex也内置了一套基于LLM-as-judge的评测模块。这些工具能减轻写评估代码的工作量,但我不建议完全无脑用,因为LLM打分本身有偏差,最好定期抽样人工复核。

4.2 我的评测集构造经验:正例负例与难度分配

评测集比指标更值得下功夫。我的做法是:第一,从真实用户日志里捞问题,而不是让实习生自己编,因为真实问题的表述随机性远大于考题;第二,按难度分层,至少分四类——单跳事实、多跳关联、否定和反例、跨文档比较。如果真实日志里没有某些类型,就要主动构造补充,否则上线后一遇到这种问题就会暴露短板。第三,每个问题都要人工标注“正确答案来自哪几个chunk”或“正确答案在哪个实体关系路径上”,这份标注是之后所有指标计算的基础,花费大但值得。

还要一个重要步骤:加负例。特意构造一批知识库里根本没有答案的问题,用它们测系统的“拒答率”。很多RAG系统的毛病不是答错,而是没有答案也硬编一个。负例比例通常配到15%到20%,目标是把强势幻觉压下去。

4.3 从评测结果倒推调优顺序

评测不是为了得一个好看分数,而是为了定位该调哪里。我会按下面这个顺序诊断:

第一,看Hit Rate和Recall。如果检索阶段就没把正确答案捞回来,后面全白搭。低的话先检查分块是否破坏语义、embedding模型和领域匹不匹配、Top-K是不是太吝啬。第二,看MRR。如果Hit Rate高但MRR低,说明正确答案排得太靠后,优先引入重排序,或者换更强的embedding。第三,看Context Relevance。如果召回内容跟问题关联度差,多半是查询改写不够,或者混合检索没配好。第四,看Faithfulness。上下文都对但答案写着写着就溜出去了,这时候重点调Prompt约束、输出结构,甚至换一个更服从指令的生成模型。第五,看Answer Correctness。上下文和答案都对不上标准答案,可能需要增加检索轮数、扩充上下文窗口、或者补充多轮追问。

调优最忌讳上来就动模型。分块参数、Top-K、重排序这些低成本改动没验证之前,先不要碰微调embedding,更不要换底模。

4.4 重排序(Re-rank)是性价比很高的升级

如果只能给读者一条调优建议,我选重排序。它的逻辑是:召回阶段尽量宽,精排阶段尽量准。向量模型负责快速筛出候选集,Re-rank模型再对每个候选和query算一遍精确相关度分数,重新排序。普通向量检索用的是双塔结构,query和文档分别向量化再算相似度,速度快但精度损失;Re-rank用的多为Cross-Encoder,query和候选文档拼在一起过模型,交互信息多,准确率高不少。

投入产出比非常可观。我做过一个线上项目,加了一层Re-rank之后,MRR大概涨了7到10个点,而改动量只是多调了一个模型接口。常用方案有bge-reranker系列,也能接Cohere Rerank这类云端API。没有GPU的环境跑大号Re-rank模型会吃力,可以用小规格模型或者退而求其次先用BM25做一次初筛再进向量检索。

5. 本地知识库的务实落地:Ollama、Easy RAG与文本拆解工具

聊完原理和评测,回到很多人真正关心的问题:怎么在本地把一套能用的RAG知识库搭起来。这两年“数据不出内网”的需求越来越多,不只是为了隐私,有时候纯粹是业务数据不能出企业边界。Ollama这类本地推理工具把模型部署门槛降到了很低,配合本地embedding模型和向量库,一个零基础也能复制的流程是存在的。

但“零基础可复制”不等于“随便跑跑就行”,我踩过的坑里,至少有三分之一发生在文档解析和切分环节,而不是模型选型。

5.1 文本拆解工具怎么选:先看文档再看工具

文本拆解是本地RAG最容易忽视又最影响效果的一环。很多人的第一反应是拿LangChain的RecursiveCharacterTextSplitter按字符硬切,用起来确实方便,但它有两个毛病:不理解文档结构,标题、列表、表格经常被切开;固定长度切分会在句子中间断掉,语义变残。

我的建议是先看你的文档长什么样。PDF、Word里如果带有清晰的标题层级和表格结构,优先用Unstructured这类解析工具,它能提取标题、表格、列表项,输出带结构的markdown或json。如果文档还想转成更规整的文本,微软的MarkItDown和Docling也都不错。拿到结构之后再做语义切分,按段落语义边界断,而不是按硬编码字符断。LlamaIndex的SentenceSplitter和LangChain的SemanticChunker都属于这一类。

切分策略上,我常用的一个组合是“父子分块”:保留大块作为检索单元,小块作为生成单元。检索命中了父块后,把父块切成更细的子块喂给模型,这样既能保证召回时不丢上下文,又能保证生成上下文足够精炼。chunk_size一般128到512 token之间调,具体要看你的文档风格和embedding模型的上下文长度,没有万能参数。

5.2 本地推理的模型选型与硬件参考

模型选型是另一个容易踩坑的地方。Ollama里能跑的模型很多,但要注意区分:聊天模型用Qwen2.5 7B或Llama 3.1 8B这类主流开源模型就够了,embedding模型选bge-m3这类中文效果稳定的,Re-rank本地方案选bge-reranker-v2-m3。显存参考可以按量化等级算,Q4_K_M量化下7B模型大约需要5到6GB显存,14B模型需要9到10GB,如果还要同时跑embedding和Re-rank,建议再留2GB余量。

用途推荐模型说明
本地LLMqwen2.5:7b / llama3.1:8b日常知识问答足够,中文建议qwen
本地LLM增强qwen2.5:14b显存够就上14B,推理质量提升明显
Embeddingbge-m3多语言语义效果好,适合中文文档
重排序bge-reranker-v2-m3Cross-Encoder,精排效果好
轻量Embeddingbge-small-zh资源不足时的降级方案

跑本地模型,CPU内存和磁盘IO也可能成为瓶颈,尤其是加载embedding模型做批量索引的时候,建议先把文档切分好了再一次性批量向量化,别一条条循环处理。

5.3 用LangChain4j Easy RAG快速搭一个能跑的Demo

很多Java技术栈的团队被Python生态的RAG方案劝退过,其实Java这边也有不错的接入方式,LangChain4j的Easy RAG模块就是朝着“简化上手”设计的。它把文档加载、切分、向量化、检索、生成串成了一套高层API,本地模型跑在Ollama上,整个过程不需要写太多胶水代码。

// 示意代码,实际API以官方最新版本为准 OllamaChatModel chatModel = OllamaChatModel.builder() .baseUrl("http://localhost:11434") .modelName("qwen2.5:7b") .build(); EmbeddingModel embeddingModel = OllamaEmbeddingModel.builder() .baseUrl("http://localhost:11434") .modelName("bge-m3") .build(); EasyRag easyRag = EasyRag.builder() .chatModel(chatModel) .embeddingModel(embeddingModel) .build(); easyRag.ingest(Path.of("docs/员工手册.pdf")); String answer = easyRag.chat("出差报销需要提前多久申请?"); System.out.println(answer);

这段代码的骨架和大多数Easy RAG示例是一致的。对于Java团队来说,最大的价值是不用为了一个内部知识库专门起一套Python服务,直接在现有Spring应用里嵌入问答能力。不过目前Easy RAG相关的文档和社区案例还没有Python生态那么丰富,遇到细节问题要多看源码和GitHub Issue。

5.4 图片和表格真的塞不进去吗:多模态RAG的方向

“RAG知识库能存图片吗”这个问题我问过自己很多次,答案是可以,但要分情况。文档里如果只是插图配文,最简单的做法是把图片转成描述文本再入库。让多模态模型看一眼图片,生成一两句话描述,存入文本chunk;查询时用户问“那幅架构图里有哪些组件”,系统检索到的是那段描述文本,效果已经够用。还有更稳重的做法,用表格结构识别工具把表格转成markdown或JSON文本,而不是直接embedding一张图片。

如果知识库里图片量很大,比如大量产品截图、设计稿,那就要考虑真正的多模态RAG:图向量与文本向量用CLIP类模型做统一表征,查询文本也能匹配到图片本身。或者是先检索到关联图片再整体传给多模态LLM处理。这个方向工程复杂度高,一般团队先不用碰。我的建议是:优先把图片里的文字内容“榨”出来存文本,图表用结构转换,实在需要原图理解再上多模态模型,一步一步来。

6. 知识库产品化的几个长期问题:Wiki对比、知识更新与系统边界

做到这一步,RAG已经不只是“写个Demo”了,它开始成为团队的知识基础设施。这个时候会出现一些跟技术关系不大但决定成败的问题,比如和现有Wiki的关系、知识更新的节奏、权限边界。

6.1 RAG知识库和Wiki不是二选一

经常有人问“我们已经有Wiki了,还需要RAG知识库吗”。我的看法是,两者解决的完全不是一类问题。Wiki解决的是“知识怎么整理、协作、沉淀”,强调编辑体验、版本历史、权限控制;RAG解决的是“知识怎么被自然语言访问”,强调召不召得回、答不答得对。成熟的做法是让Wiki当知识源头,RAG当问答入口。Wiki里的页面经过清理和结构优化后同步给RAG做索引,用户在对话里得到答案,答案下方附上来源链接,点击就跳回Wiki查看完整内容。反过来,Wiki的编辑者可以通过问答日志发现哪些内容频繁被问但没有好答案,反过来改进文档。这样知识在“编辑—索引—问答—反馈—再编辑”的闭环里滚动起来,RAG就成了Wiki的放大器,而不是替代品。

6.2 增量更新、权限与治理:早晚要面对的三件事

第一个是增量更新。知识库每天都有新文档、修订、删除,如果每次全量重建索引,文档一多就扛不住。常见做法是记录每个文档的变更时间,每次增量拉取变更文件做切分和向量化,删除的文档对应清理向量数据。向量库的upsert接口要注意幂等性,同一个chunk反复写入会在检索结果里造成重复,建议切分时给每个chunk分配稳定的内容hash,写库前先查重。

第二个是权限。如果知识库会被多个角色共同使用,比如HR制度库同时面向HR和普通员工,不同角色能看到的内容不同,那么权限控制必须下推到检索层,而不是在生成之后做文本过滤。实现上可以给每个chunk打上可见性标签,检索时按当前用户身份过滤后再做相似度排序。这个问题很多团队上线半年后才想起来,改动成本翻倍。

第三个是治理闭环。RAG系统上线只是开始,问答日志、失败case、评测集要持续积累。我自己的习惯是每周抽一批badcase做归类,标记是检索失败、理解失败还是生成失败,然后回填到评测集里,确保下轮改动不会让旧问题复发。这个循环跑起来之后,系统才会真正越用越稳。

另外说一句掏心窝的话,做到这个阶段,其实会发现高级RAG并没有某一个“银弹组件”。Agentic RAG、GraphRAG、本体RAG、Re-rank、评测闭环,每个工具都有自己的适用边界,而真正让系统变好用的,是把检索、知识结构和评估当成一个整体持续打磨的过程。如果只记住一句话,那就是:先建立评测,再谈高级技巧,永远别让没有测量的优化变成玄学。

最后分享一个我能想到的最朴素但最有效的建议:无论你选了多少高级组件,请把每一个检索结果都配上可追溯的来源。用户不一定在乎答案本身,但他一定在乎这个答案从哪来、可不可信。这一点做好,RAG项目在业务侧的口碑会直接上一个台阶。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进