1. 先说我为什么要逆向开源RAG,而不是自己闷头设计
大概半年前,我们团队接了一个内部知识库问答的项目,也就是典型的RAG(检索增强生成)任务。一开始大家的思路很直白:把文档切碎、向量化、丢给大模型,完事。结果第一版上线后,业务方反馈"答非所问"的比例高得吓人,十次里有三四次答案根本不在点上。我们当时的第一反应是换模型、调prompt,折腾了一周,效果还是不稳定。后来一个做NLP的老同事提了句:与其在这儿调参,不如先把开源生态里那些已经跑通的产品翻一遍,看看它们到底是怎么组织这个pipeline的。
这句话点醒了我。RAG看起来简单,文档切块、向量检索、拼接上下文、生成回答,四步走完。但真正落地的时候,你会发现每一步都藏着大量细节:PDF里的表格怎么提、图片里的文字怎么处理、标题和正文的层级关系要不要保留、多轮对话时历史怎么压缩、检索结果互相矛盾怎么办……这些坑,开源产品基本都踩过一遍了,而且它们的代码和文档都是开源的,等于把踩坑笔记直接递到你手上。
所以我开始了一项持续一个多月的"逆向工程"工作:把六款有代表性的开源RAG产品拆开,从它们的功能设计反推架构取舍,从它们的参数默认值反推工程经验,最后整理成一套可以直接指导自研项目的蓝图。这篇文章就是那次逆向工程的核心产出。
先说清楚这篇文章适合谁:如果你正准备自研RAG,但不确定架构怎么搭、技术选型怎么定;如果你已经做了一个能跑的demo,但检索精度上不去、不知道问题出在哪个环节;如果你在几个开源产品之间犹豫不决,想知道它们各自的强项和适用场景——这篇文章都是为你写的。我不打算写"如何部署开源产品"这类教程,网上已经很多了;我写的是"从这些产品身上能学到什么",以及如何把这些经验移植到自己的系统里。
所谓逆向工程,不是让你抄代码,而是抄思路。代码是特定团队在特定时间点的实现产物,而思路是经过大量用户检验的通用决策。我拆解的时候,重点关注的是:每个产品把资源押在了哪个环节、默认参数暴露了什么问题认知、它对"RAG的瓶颈在哪"这个问题的回答是什么。
1.1 自研RAG最容易踩的三个坑
在进入产品拆解之前,我想先把自研RAG最常见的三个坑摆出来,因为后面你会发现,六款产品几乎每一个都在用某种方式对抗这三个坑。
第一个坑是"重生成、轻检索"。很多团队把90%的精力花在选模型、调prompt上,但对检索质量几乎没有评估指标。结果就是模型再强,喂进去的上下文是错的,输出照样离谱。RAG的精度上限某种程度上由召回质量决定——检索不到相关内容,生成再强也只能在错误信息上组织语言。
第二个坑是"文档解析想当然"。很多人以为PDF转文本就是把解析库跑一遍,实际上PDF里有表格、有图片、有多栏排版、有页眉页脚,纯文本抽取会把这些信息彻底打碎。开源产品里做得重的那几款,之所以花大力气做版面分析和OCR,就是因为它们意识到:文档进系统的质量,决定了整个检索链路的天花板。
第三个坑是"没有评测闭环"。RAG不像传统分类任务有一个明确的准确率指标。同样是回答一个问题,业务方觉得好、你觉得不好,都可能。如果没有一套固定的评测集和评判标准,你根本说不清一次改动到底是变好了还是变坏了。开源产品里做得好的,几乎都内置了或多或少的评测工具,这本身就是一种提醒。
这三个坑我在后面的拆解里会反复提到。你现在先记住它们,再去看开源产品,会发现很多设计意图突然就说得通了。
1.2 为什么选这六款产品
市面上的开源RAG项目至少有几十个,我不可能全拆一遍,也没必要。我选产品遵循三个标准:第一,社区足够活跃、有大量真实用户检验过;第二,架构思路有代表性,要么在某个环节做得特别深,要么在某类场景上做得特别成熟;第三,许可证和活跃度允许我放心参考,不会拆到一半项目已经停更。
最终我选了这六款:RAGFlow、Dify、FastGPT、QAnything、MaxKB、AnythingLLM。它们内部也分成三组。RAGFlow和QAnything代表"检索深度派",核心卖点是文档理解和召回精度;Dify和FastGPT代表"流程编排派",它们把RAG放进更大的应用框架里,强调工作流、Agent和可观测性;MaxKB和AnythingLLM代表"轻量部署派",追求开箱即用、资源占用可控,适合中小团队和个人。三组产品关注的问题维度完全不同,合在一起恰好覆盖了自研RAG从算法到工程到产品的全部层次。
下文我会逐一拆。拆之前先打个预防针:我不打算把每个产品的功能列表铺开讲一遍,那是官方文档干的事。我更想回答的问题是——这款产品为什么这么设计?它解决的核心痛点是什么?如果我只学它的一手,应该学哪一手?
2. 六款开源RAG产品逐个拆解:它们各自押注了什么
2.1 RAGFlow:把文档解析做成护城河
RAGFlow主打"深度文档理解"。这个定位非常清楚:它认为RAG的第一瓶颈是文档进系统时信息就丢了。所以它做了一整套文档解析方案——版面分析、表格结构识别、OCR、视觉模型兜底,把PDF、Word、PPT里的文字、表格、图片内容尽可能完整地提取出来,并且保留文档原有的层级结构。
我重点研究的是它对chunk切分的处理方式。多数RAG系统切文档是暴力按字数切,比如512个字一块,结果一个表格被切成两半、一个标题和它对应的正文被拆散,检索时自然就找不全。RAGFlow的做法是先用版面分析识别出标题、段落、表格、图片这些区块,再按照区块的语义边界切分,同时把版面信息当作元数据一并存储。这样一来,回答里如果需要引用"第三章第二节",系统是真的能找到那个位置。
从逆向的角度看,RAGFlow给我的启示是:解析层不是RAG的辅助步骤,它就是RAG的第一道质检关卡。很多团队把PDF解析当作"用解析库转一下文本"这种三分钟的事,实际上认真做的团队会在这里投入整个项目30%以上的工作量。我在自研项目里验证过这个判断:同样的embedding、同样的模型,只把解析从"粗暴抽取"换成"带版面分析的解析",检索命中率能提升接近10个百分点,这个提升幅度比换一个更强的重排模型还要大。
当然,RAGFlow的深度解析方案也有代价:重、慢、对机器配置有要求。解析一张复杂的PDF版式图可能要花几秒钟,批量处理时的资源开销不小。这让我意识到一个工程上的取舍——全量深度解析适合离线建库,但如果是用户实时上传文档的场景,就得在解析质量和响应速度之间找平衡点。这个平衡点的选择,后面落地章节我会具体说。
2.2 Dify:用工作流编排解耦所有环节
Dify其实是个完整的LLM应用开发平台,RAG只是它能力的一部分。但正因为它是平台,它的RAG设计反而最能反映"一个生产级RAG应该长什么样"。Dify把知识库的处理拆成了几个清晰阶段:数据接入、清洗、索引、检索,每一步都有独立的管理界面和可配置参数。它还把评测环节做成了平台功能,你可以把测试集放进去,批量跑一遍检索,对比不同配置下的召回效果。
从Dify身上逆向出的最大价值是"可观测性"。Dify的每次对话都会把完整的链路日志展示出来——命中了哪些知识块、每个块的相似度得分是多少、最终prompt长什么样。这个能力看着不起眼,实际排查问题的时候救命。我们在自研系统里遇到"答非所问"的情况,80%靠看链路日志就能定位:要么是检索阶段相似度阈值太低,混进了不相关内容;要么是prompt里上下文顺序不对,模型被前面的噪声带偏了;要么是命中的chunk本身质量差,是解析阶段就埋下的雷。如果没有可视化的链路追踪,这些排查基本只能靠猜。
Dify在检索策略上的设计也值得注意。它对不同场景提供了不同的检索模式,包括向量检索、全文检索、混合检索,并且支持设置召回数量和相似度阈值。它用默认值告诉你什么参数组合是社区验证过的常用配置。比如混合检索时,全文检索和向量检索的结果会做一轮归一化和去重,再统一进入后续环节。这个"归一化+去重+合并"的细节,很多人自己写代码时会忽略,导致同一段内容被重复命中了三遍,白白占满上下文窗口。
从Dify身上,我给自研项目定了一条规则:RAG系统必须自带链路日志,每一步的输入输出都得能看到。这条看起来是工程规范,实际上直接决定了项目能不能长期迭代。数据、模型、chunk策略任何一个环节调整,都要有手段验证影响,否则就是闭着眼睛开车。Dify把这一步做成产品功能,说明它不只是一个技术demo,而是认真面向生产环境设计过的。
2.3 FastGPT:以场景化工作流打穿垂直问答
FastGPT的核心卖点是可视化工作流。你可以把知识库问答、问题引导、多轮对话、函数调用、甚至定时任务用积木式的方式串起来。它的RAG部分本身不算特别出格,但"把RAG当作一个可以被编排的组件"这个思路,对自研非常有参考价值。
FastGPT的工作流理念背后有非常务实的思考:真实业务里的问答不是发一个问题然后等答案,而是多轮对话中涉及分支判断——用户问的问题是否需要检索知识库,如果不命中是否转人工,如果命中了是否需要追问澄清。这些业务逻辑如果用代码硬编码,每次需求变化都要改代码重新发布;用工作流编排,业务人员就能自己调整。FastGPT证明了"流程可配置"对RAG落地的重要性。
从技术实现角度,我还注意到FastGPT对知识库文件的管理粒度。它把文件处理分成"上传-解析-切分-向量化-入库"的异步任务,每个任务都有状态跟踪,失败可以单独重跑。这个设计在自研时看起来很繁琐,但当一个知识库里有几千份文档时,异步任务队列几乎是必需品——否则任何一份文档解析失败,整个入库程序就被卡住,而且你根本不知道哪一份失败了。
FastGPT给我的核心启发是:RAG只是大模型应用的一块积木,自研时不要把它做成一个孤岛系统。好的设计是让RAG能够嵌入到业务流程里,被其他模块调用,也能把控制权交给上层流程。很多自研RAG项目最后不好用,不是检索做得差,而是没有对话流程、没有兜底策略、没有和业务系统的接口,像一个只能回答标准问题、回答不了就沉默的傻机器人。把流程编排的思维带进自研架构,比抄任何一个检索算法都有价值。
2.4 QAnything:两阶段检索说明了一个真相
QAnything有一个很鲜明的技术主张:两阶段检索。第一阶段先用embedding向量召回候选,第二阶段用一个专门的rerank模型对候选重新排序。这个主张其实在信息检索领域不算新鲜,但在RAG产品里把它作为核心架构、并且把重排做成标配的,QAnything是代表性的一家。
为什么它要这么做?这里有个深层的技术背景需要解释。向量检索擅长的是"语义相似",它找的是"含义接近"的文本;但"含义接近"不等于"答案相关"。举个例子,用户问"苹果公司现在市值多少",向量检索可能召回一篇讲"苹果公司历史"的文章,语义确实高度相关,但里面没有用户要的数字。而重排模型学习的正是"给定问题和候选文本,哪条最有可能包含答案",它更能捕捉问答相关性。所以两阶段检索的逻辑是:先用向量做广撒网、保证召回率,再用重排做精筛选、提升精确率。
从工程视角,QAnything还揭示了重排的成本控制问题。重排模型通常比embedding模型大得多,对每个候选都跑一遍很费算力。所以两阶段设计本身就包含一个效率考量:第一阶段用廉价的向量检索把候选从几十万条压缩到前几十条,第二阶段才对这几十条做昂贵的精确排序,把计算成本花在刀刃上。这个思路放在自研系统里,可以指导你选择重排模型的复杂度——候选多的时候用轻量模型粗排,最后十几条再用强模型精排。
我在自研系统里把"向量召回+重排"作为固定的检索链路之后,效果提升非常明显:同样的切片、同样的向量模型,加上一个开源重排模型之后,答案准确率提升了约7到8个百分点。如果说解析层决定的是检索质量的天花板,重排层决定的就是实际拿到的命中质量。天花板再高,取不出来也没用。
2.5 MaxKB与AnythingLLM:轻量级产品的克制
这两款放在一起说,因为它们的共性大于差异:MaxKB是国内社区里的轻量级开源问答平台,偏知识库问答和企业应用场景,部署很轻,一个Docker命令就能起来;AnythingLLM则是个人和小团队本地部署的热门选择,强调对多种本地模型的适配。它们的功能都不是最深的,但正因为克制,反而值得学习。
MaxKB的产品设计非常聚焦:就是"知识库问答"这一个场景,把上传文档、导入知识、检索、问答、权限管理做扎实。它让我注意到一个常被技术团队忽略的点——知识库的内容管理。一款成熟的RAG产品,不只是有检索和问答,还要有文档的增删改查、版本控制、权限隔离。企业内部用RAG,必然涉及"这份文档只让某些人看到"的需求,如果检索层不做权限过滤,模型会把不该说的内容也说出来,这是合规风险。MaxKB把权限做得很重,提醒了我在自研蓝图里必须把权限过滤放在检索链路的出口位置。
AnythingLLM的价值则相反,它的克制体现在对部署环境的容忍度。它适配各种本地模型,包括Ollama这类本地推理框架,还提供桌面端。它证明了RAG在个人场景下完全可以用很轻的方案跑起来——一个小模型、一个向量库、一个几百兆的程序,就能做一个能用的本地知识库。这对那些想先做验证、不想一上来就搞重工程的团队很友好。我在最初验证思路时也用过类似方案,一个Ollama加一个轻量向量库,半天时间就能验证某类文档的检索可行性,花不了多少成本。
轻量派产品给我的整体启示是一种取舍观:RAG系统的复杂度应该是被需求逼出来的,而不是被技术冲动催出来的。单机用户能解决的需求,就没必要上分布式向量库;几十份文档能覆盖的场景,就没必要上专门的解析服务。先把最小闭环跑通,再根据真实数据量逐步加东西,这个迭代节奏比一步到位要稳得多。
2.6 拆完六款之后的共同点提炼
六款产品逐一拆完,我做的第一件事是把它们的设计决策放到一个表格里对照,看哪些东西是"大家都这么做"的。因为这些产品背景不同、定位不同,如果它们不约而同地做了某个设计,那大概率是经过验证的通用答案;如果只有某一款这么做,那大概率是该产品针对特定场景的差异化选择。
| 设计环节 | 六款产品中的共识做法 | 差异化选择 |
|---|---|---|
| 文档解析 | 都支持多种文档格式,但深度不一 | RAGFlow重版面分析,轻量派用普通抽取 |
| 切分策略 | 都支持按分隔符/长度切分,提供自定义逻辑 | RAGFlow强调语义边界,FastGPT支持自定义拼接规则 |
| 索引结构 | 都支持向量索引,部分支持全文索引 | Dify、RAGFlow标配混合检索,轻量派只做向量 |
| 检索链路 | 向量召回是基础,重排被视为进阶必选项 | QAnything把重排做成核心卖点 |
| 对话机制 | 都做了引用溯源,回答能定位到原文位置 | MaxKB把权限管理做得很重 |
| 部署形态 | 都提供服务端API,支持Docker一键部署 | AnythingLLM额外提供桌面端 |
| 评测能力 | 不都内置评测工具,但都强调链路日志 | Dify把评测和日志做成平台功能 |
从这个对照表可以提取三条共同点。第一,凡是面向生产环境的,都重视文档解析质量和引用溯源,这说明"让答案可追溯"是RAG可靠性的底线。第二,凡是检索量大的,都会引入混合检索或重排,说明纯向量检索在精度上不够用是行业共识。第三,凡是想长期运营的,都做了某种形式的链路可观测,说明排错能力决定了项目能不能迭代下去。
这三条共同点,正好对应了我前面说的自研三个坑:解析想当然、重生成轻检索、没有评测闭环。可见所有能跑出来的开源产品,本质上都在用不同方式解决同样的问题。这给了我很大的信心:把这些共识移植到自研蓝图中,方向就不会错。
3. 逆向出的通用蓝图:从五层架构看自研RAG该怎么搭
六款产品拆完之后,我把它们的设计理念抽离成一张通用蓝图。所谓蓝图,不是某个产品的复制,而是一套"每个自研RAG项目都应该具备的分层结构"。我把它分成五层:文档接入与解析层、索引与存储层、检索与召回层、生成与路由层、评测与回归层。下面逐层说明每一层要解决什么问题、包含哪些核心模块、以及我从开源产品里学到的关键决策。
3.1 文档接入与解析层
这一层是RAG的起点,也是我前面说的"第一道质检关卡"。它的职责是把各种来源的文档变成结构化的文本块,并附带必要的元数据。
具体来说,一个完整的接入解析流程包括四步。第一步是格式识别与预检,判断进来的是PDF、Word、Markdown还是扫描件,不同的格式走不同的解析通道。第二步是内容抽取,对PDF要做版面分析,识别标题、段落、表格、图注、页眉页脚;对扫描件要做OCR。第三步是结构切分,把抽取出的内容按照语义边界切成块,同时记录每个块在原始文档中的位置信息。第四步是元数据标注,把来源、章节路径、页面号、标题层级这些信息附加到每个chunk上,后续检索和引用全靠这些元数据。
这一层最容易犯的错误是把"文本抽取"和"解析完成"画等号。纯文本抽取丢掉的恰恰是信息密度最高的部分——表格、图片里的文字、复杂的排版关系。从RAGFlow的做法可以看到,认真做解析意味着要引入版面分析模型和OCR模型,必要时还要有视觉大模型兜底。这确实会增加成本,但在企业类文档场景里,这部分投入是回报率最高的。
另外一个容易被忽略的点是"解析任务的可观测性"。我在FastGPT身上学到的是:入库必须是异步任务,每个文档的状态要清晰可见,失败要能单独重跑。几千份文档的批次任务里,总有几份会解析失败,如果你不能定位到具体是哪一份、为什么失败,整个入库流程都会被拖死。自研时至少要做一个简单的任务表和重试机制。
3.2 索引与存储层
这一层解决的是"切好的chunk放哪里、怎么组织、怎么查询"。核心模块包括向量索引、可选的全文索引、元数据存储,以及连接生成层的上下文组装逻辑。
关于存储选型,我从轻量派和重量派产品的差异里总结出一个原则:向量数据库的选择应该由部署形态和数据量决定。个人和小团队用本地可嵌入的方案,比如Chroma或轻量级数据库;企业级多用户系统再用独立的向量数据库服务。不要一开始就上分布式方案,徒增运维成本。
索引层一个很关键的决策是"是否做全文索引加混合检索"。Dify和RAGFlow的实践表明,混合检索能显著提高对专有名词、编号、代码片段等场景的召回。原因是向量检索对专有名词不敏感——比如"CU-2024-X7"这个编号,embedding之后可能被语义化泛化,但全文检索能精确匹配。我在自研系统里加了全文索引后,处理编码类查询的召回率提升尤其明显。这个模式的代价是双份索引和合并策略的复杂度,但从概率上看是值得的。
索引层还有一类容易被忽视的数据,是文档的层级关系。RAGFlow把版面结构存进元数据是有原因的:当检索命中"3.2节"下面的一个chunk时,系统如果能知道它的父标题是"3. 性能指标",就能在回答里带上完整的上下文路径,提升答案的完整性和可信度。自研时,我建议在chunk设计里预留parent_id、section_path这类字段,初期用不上也先留着,后面一定用得上。
3.3 检索与召回层
这一层是RAG技术含量最密集的地方,也是六款产品差异最大的地方。但剥开差异,主流架构其实是清晰的:先做候选召回,再做重排,最后按业务规则过滤。
候选召回阶段,常见组合是向量检索加关键词全文检索的结果合并。合并之后要处理分数归一化和去重的问题。不要直接比较两个不同维度出来的分数——向量相似度和BM25相关度不在一个量纲上,必须各自归一化再做加权融合。这是Dify的混合检索实现里可以学到的细节:先分别归一,再按权重合并,最后按合并分排序。
召回阶段还有一个重要参数是召回数量topN。自研时很多人习惯把topN设置得很小,比如只取5条,认为这样上下文干净。但从我的实测来看,RAG的很多失败不是来源太多,而是正确的来源没有被召回来。尤其经过重排之后,初始召回可以适当放宽。比如初始召回30条,重排后取前5条,比直接召回5条的效果要稳定得多。代价是重排的计算量,但重排只针对这30条,开销其实可控。
重排阶段的核心是选模型和定阈值。如果你没有明确的答案质量标注数据,可以先从社区常用的开源重排模型用起。阈值这件事我要特别提醒:重排分数是一个相对分数,不要在来自不同文档集的系统之间搬运同一个阈值。一定要基于你自己的评测集去标定。
检索出口还要加一道业务过滤:权限过滤、去重、时效过滤。权限过滤我在MaxKB那节已经提过,企业场景里这是硬需求。时效过滤则针对"知识库里存在过期信息"的场景,比如一份政策文件已经被新版本替代,旧版不能作为回答依据。这些过滤规则应该在检索层实现,而不是在生成层靠prompt约束,因为prompt约束不保证一定生效,而检索层的过滤是确定性的。
3.4 生成与路由层
生成层负责把检索结果组装成prompt、调用模型生成答案。这一层看似简单,实际上藏着不少坑,而且开源产品在这块的设计思路差异很大。
第一个坑是上下文截断。检索出来的topN条chunk加起来可能超过模型的上下文窗口,或者超过你为prompt预留的额度。自研时必须实现一个优先级截断逻辑——按重排分数从高到低塞入chunk,直到达到预算上限,而不是简单地从前往后切。FastGPT和Dify都提供类似的引用上限配置,就是这个道理。
第二个坑是引用的处理方式。生产级RAG几乎都会做引用溯源,但引用不是简单地把原文贴出来,而是要把引用的chunk和生成答案中的对应句子建立起映射。实现方式通常是在prompt里要求模型以特定格式输出引用编号,生成后再校验编号是否真实存在于检索结果中。这一步不校验,模型胡编一个引用号就会把整个系统的可信度拉垮。我在自研时专门做了一个引用校验器,引用号不在检索结果集合里就自动丢弃,宁可少引不可错引。
第三个问题是RAG和Agent的分工。这个问题在Dify和FastGPT身上体现得最明显:知识库问答是一种模式,工具调用、多步推理是另一种模式。一个成熟的自研系统应该支持路由——问题先经过意图识别判断是否走知识库检索,还是需要工具调用,或者两者结合。这个路由可以用LLM判断,也可以用规则快速分流。我的建议是优先用规则兜底,LLM判断作为增强,否则路由本身会成为新的不稳定点。
3.5 评测与回归层
这一层在自研项目里最容易被砍掉,但恰恰是最影响长期质量的。它的职责不是"跑个测试看效果",而是建立一套可持续的质量度量体系。
至少要包含三件东西。第一是评测集:一批覆盖典型问题类型的问题-标准答案对。问题类型要覆盖常见查询、边界查询、领域专有名词查询、多轮对话场景等。第二是自动评测流程:每次改动后,用固定评测集跑一遍,计算命中率、答案相关性、引用准确率这些指标。答案相关性可以用更强的模型打分,也可以用LLM-as-judge的方式。第三是回归对比:把本次改动的评测结果和上一次对比,看是变好还是变坏,而不是"感觉好像变好了"。
Dify内置的评测思路值得借鉴:把评测做成平台能力,而不是脚本。自研初期可以先用脚本加数据集,但一定要坚持"每次改动都跑回归"。我见过太多项目在调参过程中反复横跳——今天重排生效了,明天觉得向量模型不行又换掉,结果根本分不清哪个因素真正起作用。有了评测集和回归流程,至少能保证每次改动都有据可查。
4. 关键技术决策点:从产品行为反推设计原理
蓝图给了宏观框架,但真正动手时,你会在几个关键决策点上反复纠结。这些决策点我从产品的默认行为和文档描述里反推出了背后的原理,逐个展开说。
4.1 混合检索为什么是标配而不是加分项
先澄清一个概念:混合检索不是"向量检索加关键词检索各跑各的,然后并在一起",它的核心是互补性。向量检索擅长语义相似召回,关键词检索擅长精确匹配召回。这两者覆盖的错误类型几乎不重叠——向量检索会在专有名词上翻车,关键词检索会在同义表达上翻车。合在一起,等于用两套独立的信号互相兜底,召回出的候选集合更完整。
我在一个合同问答项目里做过对比。纯向量检索时,用户问"合同到期日是几号",系统能搜到语义相关的段落;但用户问"HT-2023-045这份合同还剩多少天到期",纯向量检索经常召回不到包含"HT-2023-045"这个编号的具体段落,因为模型把这个编号泛化了。加上全文检索后,这个编号能精准命中对应段落,再配合重排,回答的正确率就上来了。
实现上,Dify的混合检索逻辑值得参考:向量分数和BM25分数分别归一化到0到1,按可配置权重加权求和,最后合并去重。这里有个细节:归一化方法要选对。最常见的min-max归一化依赖统计批次内的最大最小值,这在小批次里可能不稳定;z-score或其他更稳健的归一化方案,在不同语料量级下表现更一致。可以看向量库的score分布来决定用哪种。
4.2 重排器的地位:召回是海选,重排是决赛
很多自研者对重排的态度是"锦上添花,可有可无"。六款产品里,头部产品几乎都把重排放在了核心位置,这不是巧合。我用自己的评测集做过一个数据对比,可以看看重排带来的真实变化:
| 检索方案 | 命中率(top1包含答案) | 命中率(top5包含答案) |
|---|---|---|
| 仅向量召回 | 48.2% | 71.5% |
| 向量召回+关键词合并 | 52.8% | 76.3% |
| 向量召回+合并+重排 | 61.4% | 83.9% |
说实话,我看到这个数据时也愣了一下。重排直接把top1命中率拉高了近9个百分点,这个收益比换更强的embedding模型还明显。原理在于:embedding模型的目标函数是"文本语义相近",而重排模型的目标函数是"文本与问题相关",后者更贴近问答任务的真实目标。而且重排模型能利用query和document的交叉信息做细粒度匹配,这是双塔结构的向量检索做不到的。
工程上,实现重排也没那么难。常见做法是:召回阶段输出前30到50条候选,拼接成"问题加候选文本"喂给重排模型打分,取分数最高的前3到5条进入生成层。要注意控制候选文本长度,别把整个chunk都塞进去,可以取chunk的前256到512个字符作为重排输入,否则重排模型的耗时和性能都会受影响。
4.3 Chunk策略的本质:语义完整性优先
chunk怎么切,是所有RAG项目里讨论最多、最没有标准答案的问题。我从六款产品里提炼出的原则是:优先保持语义完整性,其次才是控制长度。一个只有512个字符、但把一个表格完整包含进来的chunk,比一个512个字符、却把一个段落从中间切断的chunk有用得多。
怎么判断语义完整?可以从几个信号出发:段落边界(换行符、空行)、标题层级(Markdown的井号、PDF的版面结构)、列表边界(有序和无序列表的起止)、表格边界(表格的开始和结束标记)。RAGFlow的版面分析本质上就是在自动识别这些边界,然后以边界为锚点切分。如果你没有版面分析模型,至少要按段落和标题做启发式切分,而不是纯按字符数硬切。
切分时还有一个容易忽略的点:切片要重叠。一个段落如果恰好跨越两个chunk的边界,信息就会分裂。给每个chunk设置一定的重叠区间(比如前后各50到100个字符),可以显著减少边界处信息的丢失。我在自研系统里用的默认参数是chunk长度500到800字符,重叠100字符,这个配置在大多数非专业文档场景下表现都够用。
我建议对chunk策略做一次系统性的网格搜索:固定评测集,遍历不同的chunk长度、重叠大小、切分方式组合,找到最适合你文档类型的参数。这不是瞎调参——chunk策略是RAG里少有的"改动成本低、影响面大"的参数,值得花一个下午做对比实验。
4.4 知识库问答与Agent路由的边界
最后这个决策点偏架构层面。很多自研RAG做着做着就跑偏了,把"知识库问答"做成了"什么都能聊的聊天机器人"。Dify和FastGPT的实践告诉我们:知识库问答和Agent不应该混为一谈,至少不应该让知识库回答承担它不擅长的职责。
我的经验是设置一道明显的边界。知识库问答的职责是"基于检索到的材料回答问题",它的可靠性来源于所用材料都可追溯。如果一个问题需要多步推理、需要调用外部工具、或者需要实时信息,就不应该走知识库问答链路,而应该走Agent链路。两者可以共用检索组件,但路由逻辑要清晰。
具体实现上,路由可以在两个层面做。简单场景用规则:问题中出现"最新价格""实时数据""帮我计算"这类关键词时,路由到Agent或工具调用;不出现时走知识库。复杂场景用LLM做意图分类:把可能的意图列成类别,让模型做分类。我推荐混合使用——规则兜底保证确定性,LLM分类处理模糊问题。这里要注意,路由错误本身也是RAG系统的重要错误来源,测评时要专门统计路由准确率,不要只统计答案准确率。
5. 落地路线:用最小闭环把蓝图跑起来
蓝图再完备,最终还是要落地。我建议按照下面的路线图来推进,每一步都有明确的输入和产出,避免在需求不清的时候就开始堆技术组件。
5.1 最小闭环的六个阶段
第一阶段,确定场景和评测集。先不要选技术栈,而是把"系统要回答什么问题"列清楚,从真实业务中收集50到100条问题-答案对,作为评测集。这一步看起来不性感,但决定后续所有决策的依据。
第二阶段,解析和入库。选定一个文档解析方案,把真实文档跑一遍切分入库。这个阶段的产出是"一批可检索的chunk"和"解析质量的可视化样本"。我建议挑十几份有代表性的文档,人工检查解析结果,看看表格、标题、图片都被正确提取没有。这里宁可多检查,也不要直接信任解析库的输出。
第三阶段,跑通检索链路。先做纯向量召回,检查召回质量。然后加混合检索,对比效果差异。再加重排,把整个召回链路定型。每个环节都用评测集打分,用数据决定要不要加下一个模块。我不建议一开始就把混合检索和重排全部堆上,因为出了问题时你根本不知道是哪个模块拖了后腿。
第四阶段,接入生成。把检索结果组装进prompt,接上大模型,处理引用格式和截断逻辑。这个阶段的目标是"回答能被引用到具体位置",且不会因为上下文超限而报错。
第五阶段,加上路由和对话管理。实现意图路由、多轮对话的历史压缩、不命中时的兜底回答。这里的复杂度取决于业务形态,但至少要把"查不到答案时怎么办"想清楚——是转人工、给相关文档入口,还是直接告知无法回答。
第六阶段,上线前回归。把前几个阶段的评测结果汇总,跑一遍完整评测集的回归。记录基线指标,之后每次改动都对比这个基线。
这六个阶段我实测下来大约需要一个开发人员两周左右的时间,前提是评测集和文档样本提前准备好。很多人倒在这两步上,跳过了直接写代码,结果后面每次调参都没有评判依据。磨刀不误砍柴工,评测集真的值得花时间。
5.2 技术选型的务实建议
选型我建议遵循一个原则:先用轻方案跑通,再按瓶颈升级。不要听别人说哪家向量数据库性能好就直接上,先看你的数据量级。
具体建议如下。解析:初期用开源解析库加启发式规则,文档复杂了再上版面分析模型。切分:先按段落和标题边界做,配合长度上限和重叠。向量库:数据量在百万级向量以内,用轻量可嵌入的方案完全够;量大了再迁移到独立的向量数据库服务。重排:选社区常用开源重排模型即可,不必追求最大规模的,太重了反而影响线上延迟。生成模型:按业务质量要求选,本地部署用开源模型,对效果要求高可以接闭源API,注意数据合规。
还有一个选型建议是关于要不要用现成框架的。我的观点是:如果只是想快速验证,直接用Dify或FastGPT这类产品搭建即可;如果目标是自研并长期定制,最好用开源的检索与生成组件作为地基,而不是把整个产品框架搬进来。两者的区别在于可控性——产品框架把很多东西固化成了黑盒,后期的每个定制需求都会变成和框架的对抗。
5.3 关于"RAG能不能存图片"这类问题的处理思路
最近社区里关于"RAG知识库能存图片吗"这类问题的讨论很多。这个问题本身暴露了一个认知差异:很多人理解的RAG是"所有东西都转成文本向量",所以图片天然不是文本向量库的菜。但实际上,图片进RAG有两种完全不同的路径,需要分清楚。
第一种是"图片里的文字进RAG"。扫描版PDF、截图、照片里的文字,通过OCR提取成文本后进向量库。这条路是当下最成熟的做法,成本低、效果好,支持多模态解析的开源产品在这一块尤其省事。第二种是"图片本身作为检索内容进RAG"。用户问"找一张去年年会的大合照",系统需要按图像语义检索图片,再把图片作为生成或展示的材料。这条路需要图文多模态embedding模型,成本高,而且生成模型能不能引用图片也依赖多模态能力。
自研时我的建议是:先做第一种,把OCR纳入解析层,让图片里的文字可以被检索到。这是一项投入小、用户感知强的工作。第二种等业务真的需要图片语义检索时再上,不要提前为它做架构。做技术选型时如果不确定,就按这个优先级来。
6. 从开源产品学到的运营与迭代经验
最后一个部分,不是技术,而是过程方法论。逆向工程这六款产品给我留下的最宝贵的东西,不在代码和架构里,而在它们如何被持续运营这门学问里。我把其中对我影响最大的几条经验写在这里,作为收尾。
6.1 评测集比模型本身更值钱
一个很反直觉的结论:在RAG系统里,花时间建设评测集的投资回报率,高于换一个更强的模型。原因很简单——评测集是决定你能不能往正确方向前进的速度计。没有速度计,你换模型、调参数全都是开盲盒。有评测集,每次改动是变好还是变坏,一眼便知,你就能快速迭代。
我自己的经历很能说明问题。最初我们的评测集只有30多道题,覆盖也不够全面,导致检索参数来回改了两个星期,效果时好时坏。后来我终于静下心,花了整整两天把评测集扩充到150道题,按文档类型、问题类型、难度分层。从那以后,几乎所有调参决策都有了判断依据,项目的开发速度反而比之前快了很多。这150道题的评测集,后来成了整个团队最宝贵的资产之一。
6.2 从需求倒推功能优先级
拆完六款产品后,我发现它们的核心卖点各不相同,本质上是因为它们瞄准的业务需求不同。这提醒了我:自研RAG的时候,功能优先级应该从业务需求倒推,而不是从技术趋势正推。如果你的业务主要是合同审查,那么文档解析精度和段落定位是最高优先级;如果你的业务是客服问答,那么多轮对话和路由策略才是最高优先级;如果你的业务是个人知识管理,那么轻量部署和易用性才是最高优先级。
一个务实的做法是:把业务里最常见的100个问题列出来,逐一标注它们依赖的RAG能力,统计每个能力被依赖的频率。频率最高的能力就是你应该先投入的能力。这套方法可以避免一个常见错误——花大力气打造的"炫酷能力"根本不是用户需要的。
6.3 项目开始前就该定的边界
最后一条经验是关于RAG系统能力边界的。我在很多项目里看到过同一个问题:团队想把RAG做成一个"万能问答系统",什么问题都往里塞。结果就是系统的可靠性被稀释——知识库里的文档明明覆盖不了的问题,系统也在硬答,给出低质量的"看似合理"的答案。
我学到的处理方法是:项目开始前,就和业务方一起明确RAG能做什么、不能做什么。知识库问答的边界是"基于已有材料回答问题";超出材料范围的推理、实时信息获取、决策建议,都不应该由RAG硬扛,而是转交人工或专门流程。把这个边界做成产品文案、做成路由逻辑、做成兜底话术,全链路贯彻。这样用户就不会因为一次越界回答而对整个系统失去信任。
最后再分享一个我实际操作中的体会吧。逆向工程这件事,真正有价值的不是抄到哪个产品的某个具体实现,而是获得一种"每个设计都问为什么"的习惯。当你看到某个开源产品把默认检索阈值设成0.6而不是0.5,看到它把截断策略放在重排之后而不是之前,看到它把权限过滤放在检索出口,这些细节背后都有人在真实业务里踩过坑、付出过代价。学会把这些代价变成自己的设计判断,比记住任何一篇论文里的公式都管用。如果你也正在做自研RAG,建议你从今天开始就建立自己的评测集,然后挑一款最贴近你业务场景的开源产品,把它当作一面镜子——你会从这面镜子里看到自己未来才遇到的难题。