ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

六款开源RAG产品逆向工程:自研RAG架构蓝图与落地实践

六款开源RAG产品逆向工程:自研RAG架构蓝图与落地实践 2023年年中开始RAG这个词就从论文里跳进了工程圈。到了2024年大家都默认一件事模型只解决“生成”的问题企业真正缺的是把私有知识送进模型的那条管道。于是开源社区冒出大量RAG项目有的做文档解析有的做应用编排有的干脆把整个检索链路都包装成开箱即用的知识库问答系统。我自己的团队当时也面临同样的选择直接上开源平台还是自己造一套折腾了几个月之后我选择了一条中间路线——先把六款主流的开源RAG产品逐层拆开做一次系统性逆向工程再基于拆解结果设计自研RAG的架构蓝图。这篇文章就是这次逆向工程的完整记录内容包括选型思路、架构拆解、通用模式提炼以及最终落到自研代码里的关键参数和踩坑经验。适合正在做RAG选型、或者准备自研RAG管道的工程师参考。1. 为什么要拆开源RAG而不是直接用1.1 选型样本六款产品怎么挑出来的开源的RAG项目少说也有几十个我不能全部看一遍也没有必要。这次逆向工程选了六款标准是“覆盖足够多的设计形态”RAGFlow、Dify、FastGPT、LangChain、LlamaIndex、Haystack。这六个项目的定位差异很大恰好对应了RAG落地的几种典型路径。项目定位核心设计形态适合观察什么RAGFlow深度文档解析RAG文档结构化 与 引用溯源解析质量对RAG上限的影响DifyLLM应用开发平台可视化编排 知识库集成应用层如何包裹RAG能力FastGPT知识库问答系统工作流引擎 在线问答知识库产品化的交互细节LangChain开发框架抽象链式组装框架抽象的得与失LlamaIndex索引中心框架文档节点化 索引器索引与检索的分层设计Haystack流水线框架DAG组件管道组件复用与运行时解耦这六款合在一起基本覆盖了RAG的四个关键层面文档处理、索引构建、检索策略、生成编排。单独看任何一个项目都会以为RAG只是“向量化加拼接提示词”放在一起对比之后才能真正看到哪些设计是所有项目反复出现的哪些设计只是某个特定场景的偏执。1.2 逆向工程的核心目标有人会问直接读源码不行吗当然行但源码是“结果”不是“决策”。看代码只能看到某款产品在某个版本里做了什么却看不到它为什么这么做。比如RAGFlow里那一堆版面分析代码单独读起来只觉得复杂只有把它和FastGPT“直接整个文档塞进向量库”的做法摆在一起对比才能理解文档解析在RAG链路里的权重到底有多高。所以这次逆向工程的目标不是“抄代码”而是回答三个问题。第一这些产品把RAG分成了哪几个环节每个环节的边界长什么样。第二它们为了解决同一个问题比如召回不准分别做了什么共性的做法是什么。第三哪些设计是我们自研时可以低成本复制的哪些设计高度依赖特定场景复制了反而是负担。带着这三个问题看代码就能从“这代码真多”里抽出真正可复用的架构思想。1.3 拆解的三个层次我给自己定了拆解规则按三个层次逐层推进避免一上来就扎进参数里出不来。第一层是架构层只看组件拓扑。每个项目有哪些模块模块之间怎么通信数据以什么形态流动。这一层解决“大框架长什么样”的问题。第二层是流程层看典型RAG请求的完整生命周期。一个用户提问进来之后依次经过哪些处理步骤哪些步骤是并行执行的哪些步骤存在前后依赖。这一层解决“一条链路怎么串起来”的问题。第三层是参数层看具体配置。分块大小、重叠区间、top_k取值、重排策略、相似度阈值这些都是能直接影响效果的细节。第三层信息量最大但只有在前两层框架清晰的前提下才有意义。六款产品拆完以后最直观的感受是RAG看似解法无数核心骨架高度相似差别主要来自对某个环节的增强深度和产品化程度。2. 六款产品的架构解剖2.1 RAGFlow文档解析是重武器RAGFlow给我的第一印象是“重”。它把大量工程资源投在了文档解析上包括版面分析、表格结构识别、阅读顺序还原甚至把解析结果组织成带有层级关系的文档结构。它走的不是“切一段文字就算一个chunk”的路线而是先理解文档的物理结构和逻辑结构再按结构去切分。这个设计背后是一个很务实的判断RAG的上限由召回质量决定而召回质量受分块质量影响极大。文本先被版面分析划出标题、段落、表格、图片区域然后在每个区域内按语义边界切分这样的chunk比“按固定字数硬切”出来的文本在语义上完整得多。表格被单独识别并转成结构化描述提问涉及表格数据时召回效果会显著好于把表格拍平成一大段纯文本。从逆向工程的角度看RAGFlow最有价值的贡献是把“文档解析结构化切分”这条路线做成了可参考的工程实现。它把PDF、Word、扫描件这些复杂格式的处理流程模块化每个阶段都有独立的处理组件。如果你的业务里面有大量PDF和扫描文档RAGFlow的解析思路几乎就是必抄作业。2.2 DifyRAG不是孤立的检索组件Dify本质上是一个LLM应用开发平台RAG只是它的一个知识库能力。但恰恰因为RAG在Dify里是“被嵌入到应用链路”的反而让我看到了RAG产品化的关键问题检索结果不是直接拼进提示词就算完事。Dify的典型RAG流程里用户输入进来之后会先做会话上下文的整合再把问题做一次prompt改写然后才进入知识库检索。检索到的文档块会和聊天历史、用户当前问题一起组合成最终上下文。这种编排方式说明RAG在真实产品中不是一个“查一下拼一下”的简单管道而是需要对问题本身做预处理、对结果做后处理并对整个对话状态保持感知。它还暴露了一个容易被忽略的问题知识库的召回结果需要一套管理界面去调试。召回阈值、引用来源、相似度分数这些信息如果不可视化产品上线之后基本没法排查用户反馈的问题。Dify把这些做成了平台能力说明这个环节在真实业务里的必要性远高于大部分教程里描述的。2.3 FastGPT知识库问答的产品化样板FastGPT把RAG包装成了“知识库问答SaaS”它的嵌套工作流可以编排检索、对话、工具调用并且内置了一整套知识库管理的交互。这套交互恰恰是自研时最少被讨论、却最容易决定上线体验的部分。FastGPT的检索策略默认走向量检索但用户可以针对知识库设置不同的检索模型和相似度阈值。它的应用层把“检索参考了多少条”“哪些片段参与了回答”“用户对该回答的反馈”做成可见可查的信息这在产品运营层面是必要的。知识库问答一旦上线知识维护者需要不断根据实际问答效果调整切片、补充文档、修正阈值如果没有这些反馈闭环知识库只会越用越偏。我在FastGPT里学到的最有价值的设计是“知识库的可见性”。它让使用者能感知到每一次回答背后检索到了什么、没检索到什么这直接决定了一个RAG系统能不能被持续维护下去。自研RAG时如果只做后端管道而忽略反馈界面基本就放弃了优化能力。2.4 LangChain抽象太多也是债LangChain早期给我带来的困惑远多于帮助它把RAG建模成了“链式组件”提供大量的回调、模块、集成器看起来功能全面但实际调通一个简单问答链路的时间成本经常高得离谱。它的组件抽象层过于灵活导致阅读代码时很难判断一个自定义逻辑应该插在哪个环节。不过这次逆向工程里LangChain还是有参考价值的。它把RAG流程拆成loader、splitter、embedding、vectorstore、retriever、prompt、llm这些模块这个“模块边界”定义得非常清晰。团队自研时完全可以借鉴这套边界把组件接口定位在同样的粒度上。真正的教训是“不要过度抽象”。LangChain为了兼容各种模型、各种存储、各种链路付出的代价是调试复杂度急剧上升。自研RAG时与其做一套无所不包的抽象框架不如把五六个关键环节做成稳定接口其他逻辑尽量平铺直叙。工程复杂度是递增的每多一个抽象层排查问题时就要多跳一层。2.5 LlamaIndex一切围绕索引转动LlamaIndex现在改名LlamaCloud周边生态后仍然沿用一个核心思路把RAG的核心抽象定义为索引。它把文档解析成带有关系的文档节点节点再进入不同的索引结构检索器从索引中取回节点最终由合成器把节点组织成回答上下文。这套设计的妙处在于“节点化”。文档不仅仅被切分成文本片段它还是一个可以携带元数据、关联关系、位置信息的实体。切分后形成的节点天然适合做引用溯源也适合在后来做更精细的过滤、融合、重排操作。自研RAG时如果只把切分结果当成“字符串数组”后面想加元数据过滤、结构化检索会非常痛苦。LlamaIndex还提供了多种索引类型向量索引只是其中一种还有树索引、关键词索引、知识图谱索引。这提醒我RAG的检索层其实不只向量一条路向量擅长语义相似关键词擅长精确匹配图谱索引擅长多跳关系。真正成熟的RAG架构应该允许不同索引协作而不是默认只用一种。2.6 Haystack流水线老司机的组件化智慧Haystack在中文社区的声量不如LangChain它的架构却是我拆解过程中觉得最舒服的。它以流水线为核心组件之间通过明确定义的输入输出连接支持运行时参数传递。调试时能看到每个节点的输入输出定位问题清晰很多。Haystack把RAG拆成retriever、reader、websearch、classifier等一堆细粒度组件组件间可以灵活组装成不同类型的问答系统。它对检索器的抽象做得很克制Retriever只管从文档库中找出候选真正的答案生成交给reader或生成模型。这种单一职责的设计在工程落地时优势很明显出现问题可以立刻锁定是检索端还是生成端。拆完Haystack我对自研RAG的模块划分有了更清晰的判断组件粒度宁可细一点每个组件只做一件事接口稳定改动成本低。复杂的编排逻辑放到流水线层而不是写在组件内部。3. 逆向出来的通用自研蓝图3.1 所有产品共有的五段式流水线把六款产品的架构放在一起对照后共性很快浮现出来。无论哪款产品RAG链路都被划分成五个阶段接入解析、索引构建、查询处理、检索排序、上下文生成。你可以在LangChain里找到这五段也可以在FastGPT和RAGFlow里找到映射。这五段式可以看作自研RAG的基准骨架。第一段是接入与解析。把各种格式的文档转成可处理的文本或者结构化数据这一段的产出质量直接决定后续所有环节的天花板。第二段是索引构建。对解析后的内容做切分、向量化、建立倒排索引或图谱索引索引的目的不是“存起来”而是“为了检索效率而做预处理”。第三段是查询处理。对用户输入做改写、扩展、分解让查询更贴近文档的表达方式。很多自研RAG忽略这一段直接拿原始问题做向量检索是召回率上不去的常见原因。第四段是检索与排序。先粗召回候选再用重排模型精排最后按阈值截断。这一段是RAG里迭代最频繁的部分。第五段是上下文生成。把筛选后的文档块组织成带顺序、带引用的提示词上下文交给模型生成答案。这一段看着简单实际涉及上下文压缩、引用标注、历史对话拼接多个细节。这五段里的每一段六款产品的实现细节各不相同但这种切分方式几乎是公约数。自研时沿着这个骨架展开就不容易遗漏关键环节。3.2 检索链路上的六个高杠杆设计逆向过程中我特别关注了那些能显著改变效果的设计点最后整理出六个几乎所有成熟产品都会覆盖的高杠杆设计。第一个是混合检索。向量检索负责语义相似关键词检索负责精确匹配两者结合取并集再过重排是召回稳定性的基本保证。RAGFlow和FastGPT都对混合检索有不同程度的支持纯向量方案在专有名词、编号、型号查询上会明显偏弱。第二个是重排。先粗召回50到100条再用重排模型精排取前5条在效果和成本之间找一个平衡点。没有重排环节的RAG系统召回结果受embedding模型能力限制很大基本等于裸奔。第三个是查询改写。用户的问题经常是口语化、指代不明的直接检索效果差。Dify和FastGPT都在不同位置加了查询改写能力把“这个功能怎么配置”改写成“在系统配置项中开启功能的操作步骤”召回效果完全不在一个量级。第四个是多路召回。通过多路查询词扩展或多角度搜索然后用融合算法合并结果。RAG-Fusion这类思路已经相当普及在答案覆盖度要求高的场景里几乎是必须的。第五个是元数据过滤。在检索前按文件类型、业务线、时间范围做过滤用结构化的约束缩小向量检索的搜索空间。这个设计在知识库内容多、业务分类明确时效果立竿见影LlamaIndex的节点元数据机制让这件事做起来很自然。第六个是引用溯源。每一段被送入上下文的文档块都要记录原始文件名、页码或段落标识。这不仅是产品体验问题也是排查“幻觉来自哪里”的关键线索。RAGFlow在这方面的设计值得直接借鉴。这六个设计点并非要求一次性全部铺开但自研的时候接口上一定要留好位置否则后续每加一个都是重构级别的改动。3.3 向量知识库、知识图谱库和结构知识库怎么区分逆向过程中绕不开一个老问题什么场景用向量知识库什么场景用知识图谱知识库什么场景直接上结构化查询。六款产品中有一些项目内部已经引入了图谱相关能力搜索引擎热词里也大量出现“ontology rag”和“rag 知识库 结构知识库 应用场景”说明这个区分已经成为行业共识级别的困惑。简单总结我的判断向量知识库适合“语义模糊但表达多样”的场景比如客服问答、文档问答用户问法和文档写法差异大靠语义匹配找相关内容。知识图谱知识库适合“实体关系复杂、需要多跳推理”的场景比如查“某供应商名下的所有未结订单涉及哪些产品线”要沿着关系边走得越远越有价值。结构化知识库适合“精确且可计算”的场景比如直接查数据库表按字段、条件做聚合和筛选。这三个不是互斥选项。实际业务里更常见的是混合架构用向量检索做初筛结合图谱做关系遍历再对特定槽位做结构化查询最后把各来源的结果统一组织进上下文。RAGFlow里对表格的结构化处理本质上就是在为结构化查询铺路。自研时不要过早选边站把“结果来源”设计成可扩展的接口比锁死一种知识组织形式更重要。3.4 一套可落地的技术选型参考结合拆解结果我整理了一份自研RAG初版技术选型表这套选型的出发点是在有限人力下保证效果上限。环节推荐方向备选说明文档解析开源解析器自研二次开发优先RAGFlow风格的结构化解析路线切分策略语义切分 定长兜底先按标题/段落边界切落不了再按长度强制切Embedding模型中文业务首选bge-m3或同级别模型领域垂直模型需要基于评测集测试后决定向量库早期用开源的milvus或qdrant数据量低于百万级也可先上轻量方案关键词检索Elasticsearch / OpenSearch和向量检索做结果融合检索融合RRFReciprocal Rank Fusion简单稳定效果好于线性加权重排模型bge-reranker 或同等精排模型精排阶段不要省是效果稳定关键查询改写单独的小模型或LLM调用用流式或批处理降本生成模型根据业务合规选择建议上下文窗口留足余量评测反馈维护评测集 记录全链路日志无评测闭环的自研RAG没法持续优化这套选型的核心原则是“稳定优先别追新”。每一项都选经过大量项目验证过的开源方案自研的增量主要放在业务流程适配和评测闭环上而不是重新发明嵌入模型或者向量数据库。4. 自研落地的核心实操环节4.1 文档解析与切片参数实战解析环节最容易被低估我见过太多项目花大量时间调检索阈值最后发现问题是表格内容根本没被正确处理。实战中我建议按这个顺序处理先做布局分析识别标题层级和段落边界再对表格和图片做单独处理表格转结构化描述或者Markdown最后才进入文本切分。文本切分不能只按字符数硬切。常规做法是设置一个目标块大小比如350到500字辅以重叠区间重叠比例建议控制在10%到20%。过大的chunk容易稀释语义区分度过小的chunk又会导致检索时上下文碎片化。但固定长度只是兜底优先策略应该是按语义边界切分在检测到标题、段落、句号、换行这些自然边界时做断点再检查切出的块长度是否落在合理区间内。表格和图片在解析阶段一定要走独立逻辑。按RAGFlow的思路表格要还原成行列结构再转成描述性文本比如“某行某列对应的数值是什么”。如果把表格直接拍平成连续文本检索效果会明显下降。图片则要看业务是否需要多模态能力如果只需要OCR解析时就抽文本如果需要视觉理解那向量库需要能存图像向量整个链路会复杂不少建议先想清楚场景再决定是否引入。切片完成后一定要做抽样检查。随机挑几十个chunk打印出来看语义是否完整、是否有段落被切碎、是否有表格内容被错误拼接。这一步成本很低但对后续检索质量的影响极大。我见过一个项目因为解析器把反反复复出现的页眉页脚切进了大量chunk导致检索结果被噪声污染抽查一轮就发现了。4.2 索引库选型与混合检索配置索引库的选型和数据规模、部署环境强相关。数据量在百万级以内很多轻量方案都能扛住数据量再往上建议直接上milvus或qdrant这类专门为向量检索设计的引擎。选库的标准不是“哪个火”而是三个问题是否支持混合检索接口、是否方便做元数据过滤、导入导出维护是否顺手。混合检索的具体配置建议这样落地对每个chunk同时写入向量索引和关键词倒排索引。查询时向量检索取top K1关键词检索取top K2再用RRF做结果融合融合后的候选集交给重排模型精排。RRF的公式很简单对每个文档在多个列表中的排名取倒数加权求和不需要调复杂权重就能取得不错的效果我实际用下来比手动调线性加权稳定得多。元数据过滤要提前设计。每条chunk在入库时就带上来源文件名、文档类型、业务标签、更新时间等字段。检索时先按用户请求的过滤条件缩小范围再执行向量和关键词检索。这个问题如果上线以后才补会面临全量重建索引的成本非常痛苦。4.3 重排与上下文压缩怎么配重排模型的效果提升是实打实的。我测试过的场景里同样的召回候选集加了重排模型之后答案准确率普遍能提升十个百分点以上。重排阶段建议把候选集控制在50条以内重排模型输出打分后再取前5条或前10条作为最终上下文。超过这个数量重排延迟会变得明显对交互式问答不划算。上下文压缩是很多人忽略的一步。把重排后的chunk原封不动地拼进提示词经常会出现信息冗余、前后矛盾的问题。压缩通常做两件事一是去掉与问题明显无关的句子二是对过长段落做摘要。压缩可以由一个小模型批量处理也可以在构造提示词时让生成模型只关注与问题匹配度高的片段。拼接上下文也有讲究。不要简单地把所有chunk按顺序堆在一起建议在上下文里明确标注每一段的来源标题和页码让生成模型知道哪段话是哪份文档里的内容。这一步对引用功能的实现很有帮助。同时要控制上下文总长度预留出用户问题和历史对话的位置避免提示词过长导致模型截断。4.4 引用溯源与评测闭环引用溯源这件事六款产品里做得最好的几个都遵循同一个原则从检索到生成的每一跳都不丢来源信息。chunk从解析、索引到检索的每一层都保留文档标识和位置标识拼接最终上下文时按chunk分组标注来源。用户端展示引用来源时直接使用这个标识去反查原文。评测闭环是自研RAG里我最强调的部分。没有评测集的RAG优化就是玄学别跟我说“感觉回答变准了”要拿出对比数据来。评测集至少包含三类数据标准问答对、带错误干扰的问答对、跨文档的复合问题。每次改完解析规则、检索参数或提示词都在评测集上跑一遍记录检索召回率、答案准确率、引用正确率三个指标。日志系统至少要记录用户原始问题、改写后的问题、召回chunk的ID和分数、重排后的分数、最终选中的chunk、生成模型返回的答案和引用列表。有了这些日志用户投诉“回答错了”的时候能快速定位是召回阶段就没找到对的内容还是召回对了但生成阶段理解错了。4.5 成本与性能的平衡RAG的主要成本集中在三个地方Embedding调用、重排调用、生成模型的token消耗。Embedding可以在入库时一次性算好查询时的Embedding可以加一个简单缓存相同或相似问题直接命中缓存省掉重复计算。重排模型如果自部署建议用较小的精排模型精度差距在业务场景里通常可以接受延迟却低很多。生成模型token消耗的控制最有效的手段是上下文压缩和严格限制检索块数量。检索chunk数量不是越多越好我从实践中得到的感觉是大多数业务问题5到8个高质量chunk带来的答案质量已经达到平台期再多只会增加成本和回答的噪声。可以用评测集验证自己业务场景下的最优chunk数量。还有一个容易爆的地方是文档入库的并发处理。批量导入几百份PDF时如果解析进程和Embedding进程不加控制内存很容易被打满。建议把文档解析、Embedding入库按照队列解耦分批处理每批数量根据机器配置压测确定。宁可入库慢一点不要让服务在导入文档时把线上问答拖垮。5. 常见问题与排障实录5.1 召回质量差的排查顺序当RAG回答效果变差时我通常按固定的顺序排查避免东一榔头西一棒子。第一步看日志里的召回chunk确认检索阶段返回的内容到底是不是跟问题相关。如果召回结果本身就不相关问题出在检索链路和生成模型无关。第二步检查切分结果。打开几个典型问题对应的chunk看内容是否完整、是否被噪声污染。很多“检索不到”的案例根源是chunk里混入了页眉页脚或者表格断裂关键词被冲散。第三步检查查询改写结果。日志里如果显示改写后的问题变成了一堆废话或者偏离了原意那就是改写环节的问题可以考虑换模型或者干脆去掉改写。第四步看重排环节。粗召回的相关文档是否被重排模型错误地排到了后面。重排模型的领域适配问题比较常见如果业务文档风格特殊最好在少量标注数据上验证重排模型的表现。第五步才轮到检查生成环节确认是否为幻觉、上下文溢出或指令冲突。5.2 三个被低估的瓶颈点第一个被低估的瓶颈是文档解析的不稳定性。同一个解析器在不同PDF上的表现可能天差地别有的PDF提取出来是完美段落有的却全是乱码。解决方案是给解析环节加上质量分数自动识别解析异常的文档进入人工复核队列。这件事没有银弹只能靠质量门禁兜底。第二个被低估的瓶颈是评测环节的缺失。不少团队上线RAG之后优化依靠“感觉”和“用户投诉”没有统一的评测集。结果就是改来改去不知道是变好了还是变坏了。我建议无论项目多紧都花一到两周先把评测集建起来这是所有优化的前提。第三个被低估的瓶颈是查询改写对非技术用户的重要性。企业内部的提问方式五花八门有人问“这个单子啥时候能到”有人问“物流进度查询”直接按原问题检索效果极不稳定。凡是上线后用户反馈“AI回答不了”的案例有一半左右能在查询改写环节找到改善空间。5.3 自研RAG的避坑清单最后整理一份我实际踩过坑之后总结的清单每一条都是真金白银换来的经验。一是不要重复发明轮子。文档解析、向量库、重排模型都有成熟开源方案自研的重心应该放在业务编排和评测闭环上不在底层组件上硬造。二是不要把流程写死在代码里。解析、检索、重排、生成的边界要清晰最好支持配置化调整否则每次调参都要改代码部署迭代速度会很慢。三是不要只做向量检索。纯向量方案在专有名词和企业内部术语上表现很差混合检索是稳定性的底线这个环节省不得。四是不要忽略引用溯源。自研初期就把chunk来源信息保留好后面做引用展示和查错都会省很多力气。五是要设计“回答不出来的能力”。RAG不是万能的当知识库里确实没有相关内容时系统要有兜底话术而不是强行编造。这个逻辑写起来很简单但能显著减少上线后的幻觉投诉。六是不要追求一次到位。自研RAG的第一版尽量做“简单但完整”的链路把评测集和日志先跑起来再根据数据做迭代增强。一开始就规划一堆复杂功能往往连基础链路都调不透。我在实际做这次逆向工程之前对自研RAG的信心其实不太足。把六款开源产品拆完、把共性模式和差异点整理成表格以后思路反而清晰了自研不等于从零开始而是站在开源方案的肩膀上把通用骨架确定好把业务特有的评测和数据做好。如果你也在犹豫要不要自研RAG我建议先花一两周把几款主流开源项目跑一遍重点看它们如何处理解析、检索和评测这三个环节再回来画自己的架构图思路会清楚很多。
返回列表