ARTICLE · INTELLIGENCE

战地情报 · 详情页

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

拆解六款开源RAG后,我这样设计自研RAG架构

拆解六款开源RAG后,我这样设计自研RAG架构

1. 为什么要对开源RAG下手:自研前最值回票价的一步

如果手头要搭一套正经的RAG系统,第一反应大概率不是自己从零写,而是先去翻开源项目。但拿开源项目直接部署和把开源项目拆开看门道,是完全两码事。我团队当初做自研RAG选型时,并没有急着画架构图,而是花了两周多时间把市面上主流的几款开源RAG产品逐个拉下来部署、调参、读源码,甚至刻意制造极端问答场景去逼它们的短板。

这里先交代一个背景:所谓的“RAG瓶颈”,真正卡壳的地方通常不是嵌入模型本身,而是你在召回链路里做的那些工程决策。很多人误以为RAG难在模型幻觉,其实更多时候难在上下文窗口塞进去的内容不对。一篇文章有三千个token,真正能回答用户问题的就一段话——你有没有能力把这段话找出来、找得准、排得靠前,决定了问答是“惊艳”还是“人工智障”。开源RAG项目最大的价值,就是把这些工程决策以产品形态摆在你面前:它们怎么切分、怎么嵌入、怎么召回、怎么重排、怎么交给大模型。拆完六款之后,我心里对自研蓝图基本就有数了。

这篇文章不是要给某个开源项目做广告,也不是要列一份“RAG框架排行榜”。我想要梳理的是做了大量源码级分析与真实部署测试之后,关于自研RAG架构的启发汇总。全文围绕六个核心产品展开,逐步拆出检索增强生成的共性骨架与差异化关键点,最后落成一套可以直接拿来落地参考的自研蓝图。如果你正处在“想自研RAG但没完全想清楚架构”或者“被开源项目选择困难症困扰”的阶段,这篇文章应该值得你花二十分钟读完。

2. 研究样本的选取逻辑:为什么是这六款产品

2.1 选取原则:覆盖架构风格的全部谱系

先说样本池的构成。既然目的是“逆向工程一套可复用的自研蓝图”,那么选取的开源产品必须覆盖不同的技术路线,不能只盯着一类。我最终圈定的六款分别是:Dify、RAGFlow、LangChain、LlamaIndex、MaxKB、QAnything。选它们的理由各有侧重:

  • Dify:工作流编排型RAG应用的代表,重点看它如何把检索、重排、模型调用组织成一个可配置的流水线。
  • RAGFlow:深度文档解析路线的代表,重点看它在PDF、表格、版面还原上砸了多少功夫。
  • LangChain:抽象框架型代表,看它的抽象层设计对复杂业务是助力还是负累。
  • LlamaIndex:索引-centric范式代表,看它如何把数据索引与问答解耦。
  • MaxKB:轻量化KB(知识库)型代表,看极简场景下RAG的必要组件能压缩到什么程度。
  • QAnything:端到端全链路型代表,看它两段式检索加融合重排的实际收益。

这个组合基本覆盖了当前开源RAG的技术光谱:有重文档解析的、有重流程编排的、有重抽象层的、有重终端效果的、有重轻量部署的。同一批测试数据挨个跑一遍,差异会比想象中大得多。

我这里提前声明一下:所有结论都基于当时测试的版本,开源项目迭代很快,后续版本可能有变化。但我关注的不是某个具体接口怎么调,而是架构层面的”为什么这么设计“和”这样设计的代价是什么“——这部分即使版本升级,思路也大概率延续。

2.2 测试基准:没有评测标准就没有发言权

我给自己定的评测维度有四块,这也是自研RAG时必须面对的四道关:

  • 召回准确性:给定明确引用来源的问题,召回内容在前五条内的命中率。
  • 切分质量:固定上下文窗口下,核心信息被完整保留的比例。
  • 配置成本:从零到跑通一条完整问答链路需要配置多少参数。
  • 可扩展性:换成自己的私有文档、自定义嵌入模型时,改动成本有多高。

测试语料用了一组混合类型的中英文文档,包含规范类PDF、技术博客HTML、表格型CSV和纯文本Markdown。四类文档对切分策略的考验完全不同,正好能把每款产品在解析层的真实水平逼出来。

3. 六款产品逐个拆:它们各自解决了什么问题,又留下了什么遗憾

3.1 Dify:工作流编排才是RAG工程的护城河

Dify在开源RAG圈子里热度一直很高,它的定位不只是RAG,而是完整的LLM应用开发平台。但正因为有工作流编排这套底座,它的RAG模块(Dify内部叫“知识库”)在实际使用中表现出一种难得的“组装感”。

拆Dify源码时给我最大触动的是它的召回策略不是写死的。知识库检索后端支持全文搜索、向量检索和混合检索三种模式,混合检索模式下可以通过权重参数调节关键词匹配和语义匹配的占比。这是非常实战化的设计——向量检索擅长语义相关,但对精确术语、编号、型号这类信息经常失灵,而全文检索恰恰擅长处理这种硬匹配。实际部署Dify做客服知识库问答时,我甚至把全文检索的权重拉到过0.4,效果好得出奇,专有名词命中率陡增。

Dify的重排(Rerank)也是可插拔的。它的设计思路很朴素——先用低成本检索拉取候选集,再用重排模型对候选集做精细排序。不要小看这个设计,很多人做RAG只做到“检索召回Top-K”就交差了,但召回结果的前几条往往掺杂着语义相似但并非答案的内容。重排模块的作用相当于加了一道精筛:宁可把Top-K从10缩到5,把精度从50%拉到80%,也不要让大模型在一个充满噪声的上下文里做“大海捞针”。

不过Dify留给自研者的遗憾也很明显——它的知识库处理对文档结构感知偏弱。面对纯文本Markdown还好,一旦丢进去一份带复杂表格和三栏布局的PDF,Dify的默认解析策略就会接近“无脑按长度切”,表格被拦腰截断、段落错位的情况时有发生。这说明一个问题:Dify的核心优势在应用层的流程组装,而文档理解层并不是它的主场。想在Dify基础上自研升级,重点投入方向应该在解析切分这一层。

3.2 RAGFlow:文档解析的偏执狂,但重得让人喘不过气

RAGFlow在开源RAG产品里走的是一条差异化路线——把深度文档理解做成了核心竞争力。它内置的DeepDoc工具链能识别文档版面结构、标题层级、表格关系,甚至能对图片里的文字做OCR识别。用它的理由很直接:如果你的语料以PDF和扫描件为主,RAGFlow的解析效果会比通用切分好一个量级。

我自己的测试数据也印证了这一点。一份十六页的PDF产品手册,里面有三个跨页表格和两处图文混排区块,用常规切分策略处理,表格信息直接被切得稀碎;而RAGFlow能先识别出这是一个表格结构,把它作为完整单元进行抽取,保留行列表头对应的语义关系。这在真实业务场景里太关键了——企业文档中大量年报、规格书、操作手册都是这种复杂版面。

RAGFlow的架构设计里有个很值得学习的概念:RAG引擎内置的模板化Chat和引文溯源。用户提问后,回答底部会附带引用来源的页码和段落预览,这个能力在企业内部知识库场景几乎是刚需,因为业务方默认要求“回答必须有出处”。自研RAG如果连基础的引用溯源都不做,上线时很难说服业务方信任输出。

但RAGFlow的代价是重量级。完整部署依赖Elasticsearch、MySQL、Redis、MinIO等外部组件,入门门槛完全不“开源友好”。为了得到文档解析的高质量,你也得接受系统复杂度和资源开销上去了。自研借鉴时不必照着全套搬,真正值得拿走的芯片是它的解析层思路:先版面还原,再语义分块,最后向量化。

3.3 LangChain:抽象层什么都有,但失控得也很快

LangChain严格来说不算RAG产品,而是LLM应用开发框架,但它确实是最多人用来搭RAG的“乐高积木”。它的价值在于把RAG拆成了可替换的组件:文档加载器(Loader)、文本分割器(Splitter)、向量存储(VectorStore)、检索器(Retriever)、大模型封装(ModelWrapper),这些组件都有丰富的第三方实现,组装起来的套路官方文档里给了几十种模板。

用LangChain搭一个Demo级别的RAG确实快,文档加载一条链,分块一条链,向量化一条链,问答一条链,基本是十分钟的事。这是我觉得它最大的贡献——降低了RAG的上手门槛,让很多人第一次意识到“RAG原来是这样拼出来的”。

但LangChain的缺陷在我的实测中被放大得比较厉害。一个是过度抽象导致的调试困难。当链路里任何一个环节出问题时,你很难快速定位是加载器的问题、分块参数的问题、还是向量存储的相似度算法问题。另一个是隐性默认配置的坑。某些文本分割器会默认按字符数硬切,导致语义断句错乱,中文场景尤其严重。这是LangChain最典型的坑——它给了你极强的自由,但没有告诉你这套默认参数在中文语境下极易水土不服。

从自研视角来看,LangChain给我们的启发恰恰是反向的:抽象层可以借鉴,但需要可控、可观测。自研RAG里链路的每一步都应该有日志、有中间产物、有可视化检查点,而不是一个打了激素的黑盒。这也是为什么后来我们自己设计蓝图时,坚持每一步存储中间产物,确保任何一个环节出了问题都能单步排查。

3.4 LlamaIndex:把索引当核心资产,查询层反而简单

LlamaIndex(原GPT Index)是另一派RAG思想的代表——索引-centric。它的核心不是工作流,而是“怎么把一个文档集变成可查询的结构化索引”。LlamaIndex引入了多种索引类型:列表索引、树索引、关键词索引、向量索引,还可以叠元数据和关系。查询引擎在这些索引之上提供统一的问答入口。

这个思路对我后续设计影响很深。它让我意识到:RAG的检索质量并不只由“切分+embedding”决定,元数据的构建与管理同样重要。如果文档切分后能挂上章节归属、文档来源、时间戳、业务线标签等元数据,那么召回阶段就可以做前置过滤,大幅压缩候选集的无效检索。

LlamaIndex在查询层有一个设计值得单独拎出来写——NodeParser与Response Synthesizer的职责分离。前者负责把原始文档解析成节点(Node),后者负责把检索结果合成最终回答。自研蓝图里应该保留这个分离:解析层管结构,合成层管语言。把两者耦合在一起,时间一长必然是一锅粥。这也是LlamaIndex代码可读性优于LangChain的重要原因——职责边界足够清晰。

但LlamaIndex的短板在于它默认把自己定位为“数据框架”而非“可用产品”,所以配套的API调用、前端交互、知识库管理界面几乎都是空白。拿它做实验没问题,直接交付给业务方用还有一段距离。这也就说明了另一层道理:RAG框架与RAG产品之间,还需要一层“产品化”去填充。

3.5 MaxKB:极简场景足够,但天花板肉眼可见

MaxKB是一个国内团队开源的知识库问答项目,定位是“最简单的企业级知识库”。它的设计哲学很鲜明——能省则省。默认使用向量检索,提供开箱即用的Web UI,也内置了一个不错的文档召回策略。你在官网下载后跟着文档走,半小时内就能跑起一个带知识库的问答机器人。对很多中小企业来说,这就够了。

MaxKB的启发价值反而很大,因为它定义了一条RAG的“底线”——最小可用配置是什么样。我拆它的代码时,最关注的不是它用了什么高级算法,而是它“不做什么”:不做复杂重排、不做多路召回、不做深度文档理解、不做知识图谱。这些高级组件全都砍掉后,产品依然可用,说明RAG有一个轻量基线存在。自研时如果资源有限,先守住这个基线,把高级增强放到迭代清单里,这个顺序不能乱。

但MaxKB的沉闷也很明显:检索只有粗粒度的Top-K召回,准确率在复杂问答上会露怯。比如多跳问题“A部门负责人在B项目的汇报中提到的第三季度预算是多少”,这种带条件链的问题,它的召回效果就会迅速下滑。这个现象验证了另一个判断——开源RAG产品的天花板,往往不在表面功能多丰富,而在检索策略的单薄程度。

3.6 QAnything:两段式检索加融合,全链路工程化的样板

QAnything是网易有道开源的RAG方案,主打“任何格式、任何来源的文档都能问答”。它的架构有两个核心创新值得深度研究:两段式检索和融合重排(Fusion Rerank)。

所谓两段式检索,通俗地说就是第一轮先用BM25和向量检索分别做召回,各自得到一批候选结果,然后第二轮用一个专门的排序模型(通常是交叉编码器cross-encoder)对两批结果做精细重排,最后输出融合后的Top-K。这个设计的精妙之处在于它兼顾了关键词的精确匹配和语义的泛化匹配,同时又用重排模型把精度做了二次拉升。

我在自己的测试集上跑过对比,同样的切分策略和向量模型,QAnything的召回精度比单纯向量检索高出约一成半左右。代价是两段式检索的计算量更大,延迟更高。但如果是企业问答场景对准确率要求高,这个代价是值得付的。自研蓝图里的关键借鉴点是:不要只做单路召回,至少保留关键词+向量双路召回的设计冗余。

QAnything的另一个亮点是它的文档解析对中文做了特调。中文文本不像英文天然按空格分词,所以切分和embedding都需要考虑中文语言特性。QAnything内置的parser对中文段落、中文表格的处理针对性很强,这一点在自研时很容易被忽略,很多人直接照搬英文RAG方案到中文场景,效果打对折。

4. 逆向工程的收获:RAG自研蓝图的七个核心要素

4.1 文档解析与切分:RAG质量的起跑线

六款产品全部复测之后,发现一个共性规律:文档解析与切分是RAG质量版图的起点,也是绝大多数开源项目投入差异最大的环节。RAGFlow之所以在复杂文档上表现突出,就是因为这一层做得深;LangChain和MaxKB之所以在复杂版面翻车,也是因为这一层做得浅。

自研蓝图里,文档解析器必须做到“感知版面、保留结构、核心内容不外流”。具体落地上我建议走两级流水线:第一级做物理解析,识别标题、段落、表格、图片等版面元素;第二级做逻辑重组,把物理元素按语义关系组合成一个个带元数据的块(Chunk)。一级解决“切得对不对”,二级解决“切得懂不懂”,两级之间要有清晰的置信度标注,方便人工干预修正。

切分策略本身也有讲究。固定长度切分虽然稳妥但质量平庸,语义切分需要模型的配合和计算开销。业界比较常见的做法是混合切分:先按文档结构落位,再对过长文本按语义边界二次切分。切分完成后,还需要对每个Chunk做元数据补全——来源文档ID、标题路径、章节序号、内容类型、时间戳。后续检索阶段这些元数据会成为过滤的有力武器。

4.2 向量化与Embedding:不要让单一模型决定上限

开源产品在embedding这一步很少做文章,多数直接接入现成API或本地模型。自研时实测发现,embedding模型的选型对检索效果的影响,经常比调参还大。同样是中文场景,通用模型和垂直领域微调模型在同一份文档上的召回率可能差距超过百分之二十。

更值得警惕的是embedding模型的“长文本短板”。很多向量模型的输入上限是512个token,而自研知识库中的长文档切分块很容易超过这个长度。解决方式有两种:一种是把超长文本强制截断,另一种是切分时动态限制块长。后者更优雅,但需要切分器和向量输入端之间的参数联动——这就是为什么要让切分器和向量化模块深度协同,而不是各自独立运作。

另外,除非业务场景极其单一,不建议只用一个模型做所有向量化。可以按文档类型分别走不同的embedding模型,比如代码走代码专用向量模型、中文文本走中文优化模型,最后向量化后统一落到同一个向量库。这样检索入口统一,向量质量分层提升。

4.3 检索召回:混合检索不是可选项,而是默认项

从六款产品的对比来看,检索召回做得好的产品没有一个是单路召回。最简单的混合策略就是BM25关键词召回加向量语义召回,两路候选集合并后做去重。BM25补充了向量检索的精确匹配盲区,向量检索又补上了BM25的语义泛化弱项,两者天然互补。

自研时可以把混合检索设计成可配置策略,而不是写死的逻辑。很多企业知识库里的问题高度专业化,比如设备型号、订单编号、零件代码,这种场景向量检索表现非常不稳定,但BM25几乎可以命中百分百。反过来,“类似竞品有哪些”“推荐一套安全方案”这种语义型问题,又是向量检索的主场。给不同场景配不同权重,是自研RAG“可复用”的关键。

召回数量同样需要校准,而不是拍脑袋定Top-K。我实测的经验是:初始召回阶段可以Top-K拉到20到30条,经过重排后压缩到5条左右再交给大模型。因为检索模型的召回率在前20到30个位置才有保障,而大模型的上下文窗口又非常宝贵,重排就是为了平衡这两个矛盾。

4.4 重排与精排:决定最终输出质量的二次筛选门

重排(Rerank)是很多自研RAG容易忽略的环节。我把开源项目的代码翻了一遍后发现,真正做成生产级水准的产品都有重排层。重排模型使用交叉编码器,可以把“用户问题+候选文档”拼在一起做深度语义交互,打分结果比双塔式的向量相似度更准。

自研蓝图里重排模块的定位应当是:小而关键的精筛通道,而非可选的优化项。部署重排模型的计算开销不小,如果语料规模可控,可以考虑只对召回Top-K段落做重排,这样可以把性能开销控制住。实际测试中,接入重排后回答质量提升非常显著,很多答案从“沾边”变得“正中靶心”。

重排之后还需要一个去重与合并层。多路召回合并后会有信息重叠,如果不去重,最终喂给模型的上下文会被重复信息占满,挤掉真正有价值的内容。简单的做法是基于内容哈希和相似度阈值双重判断,重叠度高于某个值的段落只保留得分最高的一条。

4.5 Prompt与生成策略:给你的大模型配一套导航系统

检索做好之后,最后一个关键环节是Prompt设计。开源产品在Prompt上的用心程度差异非常大——有些产品直接“把检索段落拼起来丢给模型”,效果就是大模型在读一堆堆砌的碎片后强行生成答案,时而漏掉关键点,时而凭空编造。

好的Prompt生成策略至少需要三件事:角色设定、指令约束、引用规范。角色设定明确模型应该以“知识库助手”身份作答;指令约束限定它只基于给定上下文回答;引用规范要求回答中标注引用来源。这套Prompt模板看似简单,实际对输出质量的影响极其显著。

同时,Prompt需要设计成可组装的形态。这里指的是把“检索结果处理逻辑”与“用户问题”解耦,在Prompt模板里预留不同的槽位,这样检索层和生成层的迭代互不干扰。这也是自研相比拿来即用的开源方案的优势所在——Prompt模板可以做成热更新的配置表,运营人员调Prompt时不用改代码。

4.6 知识库管理与更新机制:RAG的长期运营生命力

文档新增或修改后,知识库如何同步更新,是自研时必须面对的现实问题。开源RAG产品大多提供了重新同步接口,但对增量更新、局部更新的细化程度普遍不足。实际业务中文档总是不断更新迭代的,每次全量重建索引会带来巨大的计算浪费。

自研蓝图中需要实现细粒度的变更检测与索引更新:新增文档走增量索引流程,修改文档先删除旧Chunk再写入新Chunk,删除文档则按元数据来源定位并清理对应向量。还可以加入一个定时校验机制,定期比对文档版本号和内容哈希,自动触发失效索引下线。

文档权限控制也是知识库运营绕不开的坎。在真实企业环境里,不是所有人都能检索到全部文档,权限不隔离的RAG系统基本不具备上线条件。自研方案里比较务实的做法是把权限规则前置到检索层:在查询入口带上用户标识,检索时依据元数据中的权限维度和用户所属角色做强制过滤,避免信息越权。这一步必须在检索链路完成,不能指望大模型自己判断哪些文档“可看不可看”。

4.7 评估体系:没有评测就没有迭代依据

从六款开源项目的代码观察下来,没有一款在发布时标配完整的评估体系,多数只提供在线调试工具。这其实是开源RAG最大的短板之一——使用者很难量化地知道“新版切分策略比旧版提升了多少”。自研时必须补上这一课,否则后续优化全部靠感觉。

我的做法是把评估嵌入RAG流水线中,构建三层指标:检索层指标(召回率、命中率)、生成层指标(答案相关度、忠实度、完整性)、运营层指标(端到端延迟、用户反馈留存、未知问题率)。这三层对应同一个RAG链路的三个环节,每个环节调优时都有对应指标可看。

更关键的是要沉淀一套可复现的高质量评测集。评测集里不能只用标准问答,还要包含边缘Case:带数字约束的问题、带引用来源要求的问题、模糊语义问题、信息稀缺问题。定期用固定评测集回归测试,任何改动上线之前先跑一遍回归,这是自研RAG持续良性迭代的基础。

5. 从逆向到正向:落地一套可复用的自研RAG架构

5.1 架构总览:模块化、插件化、可观测

综合六款开源产品的优势与短板,我最终沉淀的自研RAG蓝图可以用四个关键词概括:模块化、插件化、可观测、可配置。

  • 模块化:文档解析、切分、向量化、检索、重排、生成各占独立模块,模块间只通过标准化的数据接口通信。
  • 插件化:embedding模型、检索算法、重排模型、Prompt模板都是可插拔的,换一套逻辑不改主流程。
  • 可观测:全链路埋点日志,每个环节产出固定格式的中间结果,可以任意回放定位问题。
  • 可配置:参数集中管理,业务方可以在配置中心调整召回数量、和检索权重,不用动代码。

5.2 数据流设计:每一层的输入输出都要结构可控

RAG的数据流设计听起来很抽象,但直接决定后续调试的体验。我建议把数据流设计成明确的阶段产物:

  1. 原始文档经过解析器输出结构化文档对象,包含文本块、表格块、图片引用和版面坐标。
  2. 结构化文档经过切分器输出Chunk对象,每个Chunk带唯一ID、元数据、文本内容和token数量。
  3. Chunk经过向量化模块输出VectorChunk,绑定向量和Chunk ID。
  4. 查询请求经过检索模块输出候选段落列表,含来源、得分、路径信息。
  5. 候选段落经过重排模块输出精排段落列表。
  6. 精排结果进入生成模块,拼装Prompt并返回最终答案和引用列表。

这六道数据流每一步都有明确产物和格式约束,任何一个环节想优化,只要对着对应数据格式改就行,不会波及其他模块。团队协作时,数据流格式文档就是接口契约,前后端并行开发也不会有歧义。

5.3 可复用扩展:预留RAG向智能体演进的接口

RAG不会永远停留在“知识库问答”这个单点形态,自研架构必须预留向智能体(Agent)演进的扩展位。一个最重要的改造点是:把RAG的输出从“一篇回答”扩展为“一组行动”。包括给用户展示引用、调用外部工具、发起多轮追问、流转到人工客服等。这些动作在架构上应该被建模为“结果类型”的枚举值,而不是在生成逻辑里写if-else硬编码。

另一个演进方向是支持多文档联合问答与跨库检索。实际业务场景中,回答一个问题经常需要同时参考技术手册、报价单、合同模板等多个知识库。在设计索引容器时就要允许多库Association,检索时按需跨库召回,而不是每个库隔离成一堆孤岛。这个设计从原型阶段就要留好接口,不然等到业务方提出需求再拆,成本会高好几倍。

6. 实操过程中踩过的坑:自研RAG最容易翻车的五个环节

6.1 切分噪声过大,导致上下文污染

自研初期我犯过一个典型错误——切分时为了追求“语义完整”,把Chunk长度上限拉得很大,结果embedding的短文本匹配优势完全发挥不出来,导致检索结果里混入大量低相关内容。后来做了一轮切分粒度对照实验:同样一万字的文档,切分为512 token比切分为1024 token的检索命中率高出近十个百分点。结论很简单:如果段落没有明显的结构边界,优先用小粒度切分,靠重排层去粗取精。

6.2 元数据缺失,权限与过滤无从谈起

一开始我只在Chunk里存了文本内容和文档ID,没考虑挂接业务层面的元数据。后来要加文档权限过滤时才发现,没有元数据根本不知道怎么区分“可全员见的制度文件”和“仅管理层可见的战略材料”。只能回头把文档重新解析一遍,补上权限维度的元数据,白白浪费了一整轮索引构建时间。这个教训就是:元数据在切分阶段一次性补齐,没有元数据的Chunk不如不建。

6.3 多路召回结果合并,信息重复爆炸

混合检索第一版上线时,我发现召回结果里BM25和向量检索经常召回同一份文档的不同片段,如果把两路结果直接合并,重复信息占了一半以上。后来加了一个合并去重模块才解决。具体做法是计算候选段落两两之间的相似度,相似度过高则保留重排得分更高的那一条。这一步看似简单,但可以显著提升喂给模型的上下文信息密度。

6.4 中文标点切分边界,语义被拦腰斩断

中文切分遇到引号、破折号、顿号这些标点时,如果按英文的标点规则处理,很容易把原本连贯的语义拆散。比如“根据《网络安全法》相关规定”这句话,按英文句号切分逻辑可能被拆成两段,造成语义滑移。我换了好几个切分器,最后是采用中文专用标点边界规则加长度兜底的双重策略解决的。自研时如果做的是中文场景,切分器一定要针对中文标点和句式做过测试再上。

6.5 端到端延迟失控,重排层成为瓶颈

重排模型交叉编码器效果好,但计算开销是真大。最开始我没做并发控制和缓存,几十个请求同时进来重排层直接被打挂。后来加了三个优化:重排结果按query和文档内容双维度做缓存、重排请求排队限流、大批量召回时分批重排再合并。这样优化之后,P95延迟从四秒多降到了一秒以内,效果不变。重排这层一定要提前考虑容量规划和缓存策略,否则性能测试那一关就过不去。

7. 常见问题速查表:自研RAG避坑指南

常见问题现象描述排查思路推荐解法
检索召回不相关回答内容与问题明显偏离先查切分粒度,再看embedding模型是否匹配场景按语义边界切分,替换或微调embedding模型
回答存在事实幻觉模型编造了知识库不存在的细节检查Prompt是否强制约束引用来源,重排是否过滤了低置信内容增加引用规范约束,收紧重排阈值
专有名词命中差型号、编号、人名总是匹配不到单靠向量检索硬匹配效果差加入BM25关键词召回,必要时加词典辅助
长文档信息丢失文档越长,关键信息越答不出来切分边界把关键段落切散了结构感知切分,优先按标题、段落边界切分
更新索引不及时文档改了,答案还是旧的增量索引逻辑缺失或触发机制不完善实现文档级变更检测,删除旧Chunk并写入新Chunk
端到端延迟高问答响应超过三秒大概率是重排计算或向量检索大数据量扫描加缓存、限流、分批重排,必要时上GPU推理
权限越权访问用户检索到无权限文档检索层未做权限前置过滤元数据携带权限信息,检索时按用户角色强制过滤

这张表不是“万能药”,但覆盖了自研RAG从开发到上线最常撞墙的环节。每一条背后都有我真实踩坑的痕迹,希望你能少走几步弯路。

8. 最后再分享一个小技巧:保存中间产物,让RAG链路可回放

我在整个逆向分析和自研开发过程中,坚持做了一件事,觉得受益最大——把RAG链路的中间产物全部持久化保存。文档解析结果、Chunk列表、向量化后的ID映射、每次查询的召回列表和重排得分,单独存成调试日志。表面上这占存储,实际上这是排查问题的利器。

想象一个场景:用户反馈某个问题回答得不对,如果你只有最终答案和输入文档,排查基本靠猜。但如果可以把那次请求的召回列表回放出来,看一眼当时Top-K里到底是什么内容,问题出在检索层还是生成层立刻一目了然。所以自研RAG一定要有记录中间结果的机制,最好能按请求ID全链路追踪。

这个习惯的副产品是——你攒下来的中间产物本身就是一份高质量的评估数据。你可以拿这些真实请求做回归测试集,迭代切分器、换模型时,回放历史请求看指标变化,比手工构造测试题客观得多。

开了这个头之后,后续还可以往多模态方向扩展。很多人问“RAG知识库能存储图片吗”,我的回答是能,但要用对方案,不要用文本向量去硬匹配。图片和图表这类非结构化内容,要么使用多模态embedding模型做图文联合向量化,要么在RAG链路里单独增加图片理解模型环节,先把图像内容抽取成结构化描述再进入检索流程。这套演进思路也是一样:先有可观测的数据流,再做能力扩展才不会乱套。

RELATED READING

延伸阅读

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