
半年多前我接手了一个自研RAG的项目。第一次给业务方演示时效果非常尴尬用户问2024年Q3的营收数据系统从一份两百多页的财报里召回的却是风险提示章节的原文生成答案自然也跑偏了。那次翻车之后我做了一个决定——把市面上能跑起来的开源RAG产品全部拉下来逐一做逆向工程看它们到底用什么手段把检索生成这件事做到可用。我挑了六款最有代表性的产品RAGFlow、QAnything、Dify、FastGPT、LangChain、LlamaIndex。它们一个比一个卷架构理念各有侧重但拆完之后我发现真正决定RAG效果上限的翻来覆去就是那么几个环节。这篇文章是我那次逆向工程的完整记录也会直接给出提炼后的自研RAG蓝图适合正在搭建RAG知识库、被召回率折磨、或者准备从零自研RAG框架的团队参考。1. 为什么我要对开源RAG做逆向工程——自研的起点不是写代码而是拆产品1.1 自研RAG的第一次翻车我先说那次翻车的细节。当时团队选型时图省事直接用了向量数据库通用Embedding模型大模型的三件套组合。文档处理也很粗糙就是把PDF按页切块每块512个字符直接灌进向量库。演示那天业务方问了一个很正常的经营分析问题结果系统从财报的附录里捞出一段关于法律风险的原文。检索出来的内容跟问题在语义上不能说完全无关但就属于答非所问级别的错位。复盘时我们列了几个怀疑点是Embedding模型不够强是切块大小不对还是向量检索本身就有天花板后来我发现这些问题单独拎出来都不是根因根因是整个流水线缺了太多环节——没有版面分析、没有标题层级利用、没有混合检索、没有重排。说白了我们做的不是RAG只是一个带检索外壳的文本拼接器。那次之后我给自己定了一条规则别一上来就写代码先把成熟的开源产品拆明白。RAG这个领域已经卷了好几年像RAGFlow、Dify这些项目几千个Star不是白来的它们背后的架构决策全是拿真实业务数据试错试出来的。逆向工程开源产品本质上就是花别人的时间成本把别人踩过的坑提前踩一遍。1.2 逆向工程的方法论拆什么怎么拆我当时的拆解思路分四步。第一步是跑通官方Demo用它们的默认配置跑同一份测试文档记录效果基线。这一步很关键默认配置本身就是产品作者认为最不容易出错的参数组合相当于是白送的调参经验。第二步是读架构文档和代码目录结构确认每个模块的职责边界。我关心的不是某个函数怎么写而是数据流在哪几个环节发生了形态变化——原始文档变成文本块、文本块变成向量、向量召回结果如何拼装成Prompt。第三步是改参数做对照实验。比如把分块大小从256改到1024看召回结果怎么变把TopK从3改成10看生成质量是否崩。这一步能很快暴露产品的容错边界也能反推出它内部用了什么兜底机制。第四步是看Issue区和社区讨论。很多产品都把已知瓶颈写在Issue里比如表格解析效果差长文档丢失段落顺序这些信息比源码本身还值钱因为它们是作者亲口承认的短板。这套方法我建议自研团队直接抄核心原则就一条不要试图理解每一行代码要理解每一个设计决策背后的取舍。2. 六款开源产品的横向解剖架构共性里的答案2.1 RAGFlow和QAnything文档解析层是RAG的地基RAGFlow最让我惊艳的不是它的问答效果而是它的DeepDoc解析引擎。它把PDF当成一张版面图来处理先做版面分析区分标题、正文、表格、图片、页眉页脚然后再对每个区域走不同的处理管线。表格会走专门的表格结构识别图片会走OCR标题会保留在切块元数据里。这看起来像笨办法但对知识库问答的提升是决定性的。我做过一组对照实验同一份含表格的财报PDF用普通PDF文本抽取表格数据会乱成一团按行切出来全是碎片用RAGFlow式的版面分析表格先被识别为结构化数据再进行语义切分用户问华东区营收时能精准定位到表格对应行列。这就是解析层带来的差距。很多人觉得RAG瓶颈在模型其实模型能做的早做了真正卡住效果的往往是文档进来第一步就没处理好。QAnything的亮点则在本地部署和混合检索。它在默认配置里同时跑了向量检索和BM25关键词检索再用Rerank模型把两路结果合并排序。我当时很疑惑为什么一个本地优先的产品要搞得这么复杂后来想明白了向量检索擅长语义相似但精确的词面匹配还得靠BM25。产品型号、工单编号、合同条款号这类内容向量很容易在语义空间里跑偏关键词检索反而是最稳的。2.2 Dify和FastGPT应用编排与知识库运营Dify给我最大的启发是RAG流水线可视化。它把文档导入、切分、向量化、检索、重排、模型调用统统做成了可视化节点每个节点都能单独调参数、单独看日志。自研时最痛苦的事情就是排查链路问题——用户说答案不对到底是检索错了还是生成错了Dify这种设计相当于强迫开发者把每个环节的输入输出都暴露出来排查效率直接提升一个量级。FastGPT则是把知识库当成一个运营产品来做。它有很完整的知识库→应用→反馈闭环每一次问答都能标记点赞或点踩点踩的样本可以回流到知识库里做人工修正比如补充同义问题、修正切块边界。这个设计看起来不起眼但对长线效果维护特别重要因为RAG冷启动容易越跑越准很难。我当时还专门研究过Dify的检索逻辑。它在检索节点里同时支持向量检索、全文检索、混合检索三种模式混合模式下还能配权重。这个设计解决了一个很现实的问题不同知识库对检索方式的需求不一样。合同库需要精确词面匹配FAQ库需要语义泛化单一检索策略根本覆盖不了。2.3 LangChain和LlamaIndex框架思维的价值与陷阱LangChain和LlamaIndex可以放在一起说。它们不是完整可用的RAG产品而是RAG开发框架价值在于提前定义了RAG的通用抽象层。LangChain把检索器文档加载器输出解析器这些概念标准化LlamaIndex更极端直接围绕索引做了大量优化比如树状索引、关键词索引、文档关系图索引。逆向这两个框架最大的收获是RAG的架构设计必须有清晰的层次划分。数据加载、切分、索引、检索、合成这五层必须解耦每一层都能单独替换实现。很多自研项目把代码写成一坨切分逻辑和检索逻辑搅在一起后面想优化一个点就得动全身。框架类产品用抽象层换来了极大的灵活性代价是学习成本和性能损耗这个取舍自研时要想清楚。但我也要说直接用LangChain搭RAG会有明显的天花板。框架给的是积木不是房子效果好坏完全取决于使用者的工程能力。而且框架为了通用性默认实现普遍偏薄比如它的默认切分器就是按字符数硬切不会主动保留语义边界。这也是为什么我最终建议自研团队参考框架的分层思想但具体实现要往RAGFlow这些产品的水准靠。2.4 共性架构从六款产品里抽出的八层最小组件拆完六款产品之后我发现它们的核心流水线惊人地一致都是解析→切分→向量化→索引→检索→重排→生成区别只在于每个环节的工程深度。我整理了一张共性架构表这也是自研RAG的最小必要组件清单层核心组件常见实现关键参数数据接入文档加载器PDF/Word/HTML/Markdown 解析格式覆盖范围文档解析版面分析OCRDeepDoc、PaddleOCR、Tesseract是否保留标题层级/表格结构文本切分Chunker固定大小、递归字符、语义切分chunk_size、overlap向量化Embedding模型BGE、M3E、OpenAI Embedding维度、batch_size索引向量库倒排索引Milvus、Qdrant、Elasticsearch、pgvector索引类型、距离度量检索混合检索向量BM25/稀疏检索top_k、权重配比重排Rerank模型BGE-Reranker、Cohere Rerank候选集大小生成LLM调用各类大模型prompt模板、max_tokens、温度这八层就是RAG的最小必要组件。自研时每一层都可以先用简单实现跑通再逐步替换成更重的方案。我见过太多团队一上来就上重型组件结果链路太复杂排查问题时根本不知道从哪里下手。3. 从逆向工程到自研蓝图一套可复用的RAG架构3.1 数据接入与文档解析别在第一步偷懒自研RAG的第一个决策点是数据接入层。很多团队直接拿现成的PDF文本抽取库干活遇到扫描件就抓瞎遇到复杂表格就乱码。我在拆完RAGFlow之后把数据接入拆成了三个子步骤格式识别、版面分析、内容提取。格式识别决定走哪条处理管线。文字型PDF直接走文本抽取扫描型PDF走OCRWord和Markdown走结构化解析HTML要先去标签。版面分析是把页面拆成标题、正文、表格、图片、页脚几个区域这一步决定了后续切块是否保留结构信息。内容提取是真正的分水岭。表格要用表格结构识别提取后转为Markdown表格或HTML表格再入库图片里的文字要走OCR多列排版的文档要先还原阅读顺序。我实测下来不做版面分析的RAG表格类问题的召回准确率会掉30%以上这不是危言耸听是拿真实数据跑出来的结果。实操上我建议自研团队第一版就直接集成PaddleOCR或类似工具成本不高但能覆盖扫描件和图片型PDF。RAGFlow的DeepDoc不开源核心权重但它的版面分析思路完全可以复刻——把页面图像输入目标检测模型输出各区域的坐标和类别再按坐标顺序重组文本流。这一步做扎实后面所有环节都会轻松很多。3.2 分块策略chunking是一道数学题也是一道语义题分块是RAG里讨论最多、玄学味最重的一个环节。我拆完六款产品后发现大家的分块策略本质上都是在解决同一个矛盾块太大向量化后语义被稀释检索精度下降块太小上下文不完整生成阶段信息不足。我先给一个可用的起点参数chunk_size设为300到500个汉字overlap设为50到80个字符。为什么是这个区间因为中文Embedding模型大多数按token计算一个汉字大概对应1到1.5个token300到500字对应400到700个token在主流Embedding模型的512token上限附近既不会截断又能保留完整语义。但固定大小切块只是基线真正的提升来自语义切分。我推荐一种工程上很实用的做法先把文档按标题层级拆成章节再把每个章节按段落边界切块最后把过长的段落按句子边界二次切分。这样切出来的块天然带有章节归属信息检索时可以把章节标题作为元数据一起存入向量库召回后还能用标题做上下文补全。这里有个细节很多人忽略overlap不是越大越好。overlap的作用是防止切块正好截断一个完整语义单元但过大的overlap会让相邻块高度相似检索时大量返回重复内容。我实测下来overlap控制在chunk_size的10%到20%之间最稳。另外要记得切块时保留原始文档的页码和章节路径这两个字段在生成引用溯源时是救命稻草。3.3 混合检索与重排召回率提升的两个隐藏杠杆召回率不高很多团队第一反应是换更强的Embedding模型。但拆完QAnything和Dify之后我得说句实话换模型带来的提升远不如把检索策略从单路向量改成混合检索重排来得明显。混合检索就是同时跑向量检索和BM25关键词检索然后把两路结果合并。向量检索擅长语义泛化BM25擅长精确匹配两者互补。工程上最简做法是两路各取TopKK设置为最终期望候选数的2到3倍然后合并去重交给重排模型打分。如果向量库用的是Elasticsearch或OpenSearch本身自带BM25能力实现成本很低。重排是整个流水线里性价比最高的组件。Rerank模型会把候选集逐条与用户问题做深度语义匹配打出一个精确的相关性分数。我强烈建议自研团队把这个环节加上BGE-Reranker这类开源模型已经足够好用。我实测的一个案例只加重排不加其他优化Top1命中率从62%提升到81%这个提升幅度远超换一个更大的Embedding模型。重排还有一个额外好处它可以天然解决向量召回结果排序不合理的问题。向量检索返回的分数是语义空间里的余弦相似度这个分数在不同查询下的分布不稳定直接按它排序经常出错。重排模型把候选集重新排序后取TopN传给生成层整个链路的稳定性会好很多。3.4 KG知识库与Ontology RAG结构化知识怎么融入拆完开源产品之后有一个问题始终绕不开纯向量的RAG知识库处理不了结构化知识和复杂关系推理。比如问A供应商和B供应商之间是什么股权关系这种多跳查询靠向量检索几乎不可能答好。解决思路就是引入知识图谱也就是热词里常说的KG知识库。先厘清概念。RAG知识库向量知识库存的是非结构化文本块适合语义相似类查询比如这个产品的售后政策是什么。结构化知识库存的是数据库表、Excel、JSON这类规整数据适合精确查询和聚合计算。KG知识库存的是实体和关系适合多跳推理和关系链查询比如哪些员工同时参与了这两个项目。这三种知识库不是互斥的而是可以分层的。我在自研蓝图里建议的做法是把非结构化文档作为主知识库抽取其中的实体和关系构建KG辅助层结构化数据继续留在关系数据库里通过一个工具调用层接入RAG流程。查询进来时先做意图分类判断该走向量检索、走KG查询还是走数据库查询或者组合使用。Ontology RAG是KG知识库的进阶形态。普通知识图谱只有实体和关系Ontology则额外定义了概念层级和属性约束相当于给知识图谱加了一层Schema。在垂直领域里Ontology能大幅提升图谱构建的准确率。比如医疗领域预先定义疾病、症状、药物、不良反应这些概念及关系类型抽取时就不会把发热既当成症状又当成疾病。工程实现上可以用LLM配合Schema约束做三元组抽取再用Neo4j或NebulaGraph存图谱。3.5 生成与溯源答案不是终点引用才是检索做得再漂亮最后生成环节如果处理不好前面的努力全部白费。我拆完六款产品后发现成熟产品在生成层的设计上有两个共性一是Prompt模板里明确要求模型只能基于检索内容回答二是强制输出引用来源。Prompt模板的写法有讲究。我推荐的结构是先给系统角色定义再放检索到的参考内容带编号然后放用户问题最后加一条硬性约束——如果参考内容不足以回答问题请直接说明信息不足不要编造。这条约束是防幻觉的最后一道防线实测能显著减少无中生有的回答。引用溯源是决定RAG系统能否落地的关键。业务方用RAG不是要一个看起来对的答案而是要能追溯到原始文档的答案。我的做法是检索时保留每个切块的文档路径、页码和章节标题生成时要求模型在答案末尾标注引用编号后端再把编号映射成具体来源。这一步让RAG从聊胜于无的玩具变成了敢给业务方用的生产工具。4. 自研RAG的常见瓶颈与排查技巧实录4.1 召回质量差的排查路线图RAG效果不好90%的情况是召回环节出了问题不是模型不够强。我整理了一条排查路线按顺序走一遍基本能定位根因。第一步检查解析结果。随机抽几份文档看原始文本有没有乱码、表格有没有错位、扫描件有没有漏字。这一步的问题最隐蔽也最致命因为解析是静默失败的你很难发现某页内容其实根本没进知识库。第二步检查召回测试。用同一套Query分别测向量检索、关键词检索、重排后的结果。如果向量检索结果差大概率是Embedding模型与领域不匹配如果关键词检索结果差大概率是文档分词有问题如果两路都好但重排后变差那就是重排模型或候选集大小没调对。第三步检查切块策略。单独抽一个召回失败的案例人工看切出来的块是否语义完整。如果块里混入了表格残留、页眉页脚或者把一个完整的案例拆成了两半问题就出在切分策略上。第四步检查Prompt。召回没问题但答案差那就是生成环节的问题。把Prompt里塞入的检索内容打印出来人工读一遍如果内容本身是对的那就是Prompt约束不够或者模型对引用格式理解有误。4.2 分块参数怎么调一份可抄作业的配置表我在拆解产品时记录了一批可用参数整理成表格供直接参考。注意这是起点值不是最优值实际场景要在基线值上做对照实验调整。场景chunk_sizeoverlap切分策略检索方式财报/研报等长文档500字80字按标题层级段落混合检索重排合同/法务条款300字50字按条款编号切分关键词优先重排FAQ问答对整条不切0按问答对独立存储向量检索产品手册400字60字按章节表格保留混合检索重排代码文档200行20行按函数/类切分关键词优先调参的通用经验是先固定其他变量只调一个参数用一组标准Query跑两遍对比效果。我习惯准备20到30条真实业务Query其中包含精确匹配、语义泛化、多跳推理三种类型作为回归测试集。每次改完参数都跑一遍测试集记录Top1命中率和答案人工评分比凭感觉调靠谱得多。4.3 在Mac上本地搭建RAG知识库的注意事项不少读者问过我在Mac上怎么搭建RAG知识库。我踩过一轮坑说几个重点。硬件上Apple Silicon Mac建议内存16G起步8G能跑但很勉强。模型方面推荐用Ollama跑本地Embedding和LLMEmbedding用BGE-M3的中文优化版本生成模型用Qwen2.5系列70亿参数版效果和资源占用比较平衡。向量库的选择上轻量场景直接上ChromaDB或LanceDBPython装个包就能跑零运维成本。数据量超过十万级再考虑Docker跑Qdrant或Milvus。很多教程一上来就让装Docker Desktop其实小规模验证完全没必要纯Python方案够用且省内存。Mac上最麻烦的是PDF解析。macOS自带的文本抽取对扫描件无能为力建议直接集成PaddleOCR。注意PaddlePaddle在Mac上的安装依赖较多建议单独建虚拟环境。还有一个坑是中文路径问题文档名和目录别用中文和空格部分库在解析中文路径时会有编码问题排查起来非常费劲。4.4 多模态问题RAG知识库能存储图片吗这是被问得很多的问题答案是能但要看你要哪种能。目前的实现思路分三种。第一种是图片先转文本OCR提取图片里的文字图像描述模型比如CogVLM、Qwen-VL生成图片内容描述再把文本描述作为切块存入向量库。这种方案最简单适用于截图、扫描件、含文字的产品图。第二种是图像Embedding用CLIP类的多模态模型把图片本身编码成向量检索时用户文本也编码进同一语义空间。这种适合按语义找图的场景比如找一张疑似设备故障的现场照片但实际效果对中文场景仍不稳定需要挑选合适的多模态模型。第三种是混合方案图片既存原始文件路径又存OCR和描述文本同时用多模态模型做一个Image Embedding存进单独索引。检索时两路同时查用Rerank统一排序。RAGFlow就是这么做的它把图片抽取后单独建了图片索引。自研时建议按需选择纯文档场景用第一种就够了涉及大量工程图纸或产品图时再上多模态向量。最后再说一个我踩过的坑图片存储别把二进制直接塞进向量数据库大部分向量库对BLOB支持很差。正确做法是图片存在对象存储或本地文件系统向量库里只存文件路径和Embedding向量。我个人在拆解这六款产品之后最大的体会是RAG的难度不在任何一个单点技术上而在把所有环节咬合起来的系统工程能力。文档解析、切分、检索、重排、生成每一层都有现成的开源方案但把它们拼成一个稳定可用的系统需要的是对细节的敬畏和对数据流的清晰认知。如果你正在自研RAG我建议按这篇文章的八层架构先把最小闭环跑通再一层一层做深。别一上来就追求炫技先把地基打牢效果自然会上来。