ARTICLE · INTELLIGENCE

战地情报 · 详情页

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

自研RAG服务前,逆向开源产品后的架构蓝图

自研RAG服务前,逆向开源产品后的架构蓝图 聊一个我最近反复在做的功课当你已经决定不直接用开源RAG产品而是准备自研一套RAG服务时第一步应该做什么我的做法是先把当前一线开源RAG产品拆开看一遍。这个“拆”不是看热闹也不是为了改个皮肤抄代码而是带着问题去逆向工程它们的架构决策。我前后对比了Dify、FastGPT、RAGFlow、QAnything、Haystack、LlamaIndex六款产品每一款在RAG链路上都有自己的明确取舍。今天就把这份逆向工程结果整理成一套可以复用的自研RAG蓝图给正在自研知识库问答的团队省点弯路。这篇内容不打算讲“RAG是什么”这种基础概念重点放在产品之间到底在哪些地方花了大力气、哪些设计是公认的必选项、哪些花哨功能其实可以砍掉。如果你正处在“要不要自研”的犹豫期或者已经拍板自研但还没想清楚架构这篇文章应该能给你一张比较清晰的地图。1. 自研之前先看清开源格局1.1 六款产品可以分成三派市面上的开源RAG项目数量早就过百了但真正值得逆向研究的并不多。我选这六款不是因为它们名气大而是因为它们恰好覆盖了三种完全不同的实现路线。产品产品形态核心定位RAG链路里最值得看的设计DifyLLMOps应用平台偏向搭建AI应用和Agent工作流知识库配置化、检索模式可选、可视化编排FastGPT知识库问答平台开箱即用适合快速搭建问答应用索引与检索规则分离、流程节点可编排RAGFlow深度文档理解型RAG引擎面向真实业务文档强调解析质量版面感知解析、模板化提示词、Rerank链路QAnything端到端知识问答框架强调本地模型配合知识库问答从OCR到语义向量、重排一体多轮处理成熟Haystack开发者框架可组装的生产级管道索引管道和查询管道彻底解耦组件化极强LlamaIndex数据框架面向开发者的数据连接与检索抽象各类索引结构、查询引擎、可编程接口丰富第一派是应用平台代表是Dify和FastGPT。它们解决的核心问题是“让企业快速搭出一个带知识库的问答应用”所以把大量精力花在流程编排、知识库管理界面、API输出规范上。如果你自研的目标是给内部做一个可维护的知识库这一派的设计思路参考价值最大。第二派是RAG引擎代表是RAGFlow和QAnything。它们不搞花哨的应用搭建而是死磕“文档进来之后到检索之前这段路”。RAGFlow对PDF、图片、表格做了版面级解析QAnything在OCR和重排模型上做得非常扎实。如果你自研时遇到“资料一堆但检索效果就是不行”的困境问题大概率出在这一层。第三派是开发者框架代表是Haystack和LlamaIndex。它们不绑定任何具体实现而是提供抽象的管道、索引、检索器、查询引擎让你自己拼装。这一派的价值在于“设计模式”比如组件接口怎么定义、数据流怎么流转、插件怎么扩展。我自研时大量设计习惯其实是从Haystack的管道思想里借鉴来的。1.2 逆向工程的重心不是代码是决策很多人一听到“逆向工程”第一反应是把项目源代码拉下来读。但面对这些动辄几万行、十几万行的开源项目逐行读代码是最低效的做法。我做逆向工程时心里其实只带三个问题第一它为什么选择了这个方案比如RAGFlow为什么要自研文档解析模型而不是直接调现成的PDF解析库因为通用解析库在复杂版面、表格、扫描件上表现差而知识库场景里这些又是高频需求。第二它砍掉了什么对比一下会发现很多产品并没有一上来就上Agent、图谱这些概念而是把基础检索链路做得非常扎实。第三它有哪些共性六款产品虽然路线不同但都存在“文档解析层—切分索引层—召回层—重排层—生成层”这条主链路说明这是RAG绕不开的骨架。把这三个问题想清楚比记住某个开源项目的某个函数要重要得多。后面几节我会沿着这些决策点逐一展开。2. 解析与切分召回质量的第一道闸门2.1 文档解析的深度决定了产品水位我见过太多自研RAG翻车的案例最后排查来排查去问题都出在文档解析上而不是检索模型上。尤其是PDF表面上看起来是文本实际上内部结构复杂得离谱有双栏排版、有表格、有图片注释、有页眉页脚。如果用最简单的文本抽取方式去解析这些信息全会被打乱。RAGFlow让我印象最深的一点就是它把文档解析当成核心工程在做。它的DeepDoc模型能做版面分析识别出标题、正文、表格、图片区域再按阅读顺序重组内容。一个双栏PDF不做版面识别就按文本流硬切结果往往是左栏一行接右栏一行全被揉碎了。这种数据喂给再好的向量模型也没用因为源头的语义单元就是错的。QAnything在这块同样花了很大力气。它对扫描件、图片类PDF做OCR识别对表格做结构还原。我在实测中传过一些带复杂表格的技术规格书QAnything解析后能把表格转成相对规整的Markdown格式而某些直接抽文本的方案会把表格内容全部丢掉。所以自研蓝图里解析层不能只做个文件格式转换必须具备基础版面分析能力。早期可以接入现成的OCR服务或者文档解析服务但架构上必须为“解析算法可替换”留好接口。2.2 分块策略与块大小不是越大越好也不是越小越好解析完之后就是切分。最基础的做法是按固定字符数硬切省事但副作用明显可能把一句话从中间劈开也可能把一个完整的知识点切成两半。自研时我强烈建议至少用递归字符切分优先按段落、句子、标点这些自然边界来切而不是按字节数硬切。更进一步的方案是结构感知切分。如果文档本身带标题层级比如Markdown、HTML那就按标题把内容聚成树状区块子标题下的内容跟随父标题。这样切出来的块天然自带上下文检索命中一个子块时可以把父标题一起带上回答时模型就知道这个知识点属于哪个章节回答的条理性会好很多。再精细一点是语义切分比如检测文本中语义向量的突变点来决定边界。这种方案效果好但计算成本和延迟都高短时间内不适合作为自研第一版的选择。我见过不少团队一上来就上语义切分结果分块环节比整个检索环节还慢这是典型的过度设计。块大小方面业界常见的经验值是单块控制在256到512个token之间重叠区设置32到64个token。但这不是死的得看文档类型。法律条款类适合按条款编号切块一条一议产品手册类适合按功能模块聚合衔接上下文更重要。判断的标准很朴素把检索召回的那一段单独拿出来给一个人看他能不能不借助其他信息就理解这段在说什么。如果不行说明块切细了或者切碎了。2.3 元信息设计检索召回之后的最后一根稻草解析和切分之后每个块不能只存文本和向量必须带上丰富的元信息。我在自研时至少会给每个块保存文档ID、文档名称、章节路径、页码、块序号、字符数、token数。这些字段看着不起眼实际作用非常大。第一是引用溯源。回答里要标注“这段话来自《某某手册》第几节”没有元信息就只能靠文本里猜极不准确。第二是权限过滤。企业知识库经常有部门隔离需求可能一个文档只允许部分人检索到那就要在召回时按元信息里的权限标签做过滤。第三是召回后的上下文重组。检索到的多个块来自同一份文档时可以按页码和章节顺序重新排列而不是按相似度分数硬排这样组装出来的上下文更像一篇连贯的文章而不是一堆孤立的碎片。有个容易踩的坑切分时如果改了文本内容比如去掉了换行符、把多个空格压缩成一个那么块和原文之间的映射关系就断了后面要做引用精确到原文段落时非常痛苦。我建议每个块都保存它对应原文的起始偏移和结束偏移哪怕第一版用不上后面做高亮显示和精确定位时也会感谢这个设计。3. 混合检索与重排序六款产品共同的胜负手3.1 为什么单靠向量检索远远不够很多人以为RAG的核心就是Embedding加向量数据库但真做了会发现单纯向量检索在知识库场景里表现并不稳定。向量模型对语义相近的内容很敏感但对专有名词、型号编码、工单编号这类精确信息经常“视而不见”。举个实际例子文档里写着“型号XYZ-2000的设备需要定期校准”用户提问是“XYZ2000怎么校准”如果Embedding模型把型号编码当噪声处理了这个文档块可能根本不会被召回到。这时候用关键词匹配反而能一击命中。所以Dify的知识库提供向量检索、全文检索、混合检索三种模式FastGPT和QAnything的底层也都有类似的关键词检索通道。我的建议很直接自研RAG的第一版就要上混合检索不要省这一步。向量召回负责语义相近但用词不同的情况全文检索负责精确匹配和专有名词命中两者取并集再统一排序。全文检索可以直接用数据库自带的全文索引比如PostgreSQL的tsvector也可以单独挂一个Elasticsearch。小规模场景用数据库自带功能就够别为了关键词检索专门引入一套ES徒增运维成本。3.2 Rerank是投入产出比最高的一个环节召回阶段为了召回率通常会把候选块放宽到几十甚至上百个但真正塞进大模型上下文里的只能有三五个。如果不做重排只按向量相似度取TopK会出现一种常见病召回的块看着都相关但最该用的那个排在第十位前三个都是似是而非的内容。解决这个问题要靠Rerank模型。Rerank用交叉编码器把用户查询和候选块一起输入模型算出更精准的相关性分数。通俗点说向量检索是“先把可能相关的先捞上来”Rerank是“在捞上来的里面仔细打分排序”。QAnything和RAGFlow都把Rerank放在链路的核心位置FastGPT也把重排选项开放给了使用者这说明行业共识已经非常一致。Rerank模型的选择上中文场景优先考虑BGE系列的重排模型英文场景通用模型选择也很多。部署时注意Rerank的推理延迟实测下来单次推理在几十到几百毫秒不等需要控制并发或者做缓存。还有一个经验Rerank不要对全部候选块做先通过粗召回把候选压缩到50到100个再做精细排序。这能省下不少延迟效果差别不大。3.3 两路召回结果怎么合并才不打架混合检索听起来简单做起来最头疼的是合并排序。向量相似度分数和关键词匹配分数根本不在一个量纲上不能直接相加比较。我测试过两种主流合并方案都有各自的适用场景。第一种是加权线性融合。把向量分数和全文检索分数各自归一化到0到1区间再按权重相加。好处是可以精细调参坏处是权重对数据分布敏感换一批文档可能就要重新调。第二种是RRFReciprocal Rank Fusion不直接用分数而是按排名位置算倒数两路结果按排名加权融合。RRF的好处是无需归一化对异常值不敏感实现起来也简单实测在大多数场景下效果都很稳。我个人更倾向于第一版用RRF参数少、稳定等积累了足够的线上检索日志之后再根据实际效果换加权融合。切记不要在还没上线的时候花太多时间去调融合权重因为你没有真实数据调出来的参数大概率是自欺欺人。4. 从检索到生成上下文组装与链路协同4.1 上下文组装不是简单把结果拼在一起检索完成之后有了几个候选块很多人就直接把它们拼成一大段文本丢给大模型。这么做能用但效果通常不如预期。原因在于模型需要的不是“相关片段的大杂烩”而是“逻辑上连贯的素材”。我在自研时采纳了Haystack管道思想里的一个习惯组装上下文之前先对候选块做一轮清洗和排布。首先是去重同一个来源的块可能被两路检索各召回一次合并前必须按块ID去掉重复。其次是按文档逻辑重排如果候选块来自同一份文档尽量按原始章节顺序组织而不是按相似度分数降序组织。最后是精简如果检索到的块里混入了纯导航内容或页眉页脚要能识别并剔除。还有一类比较容易忽略的情况一个知识点可能跨越多个相邻块。比如某个方案说明在块7只讲了一半关键参数在块8里。如果组装上下文时只选了检索分最高的块7模型就缺了另一半信息回答自然不完整。所以组装策略里最好带一个“邻居扩展”能力当选中某个块时允许按原始顺序带上它前后相邻的一到两个块作为上下文补充。这个能力放在知识库问答场景下效果提升非常明显。4.2 引用溯源不是加分项是企业落地的底线我在帮团队做RAG自研时发现凡是面向真实业务使用的知识库引用溯源都是硬需求不是可选需求。用户问到的每一个回答系统必须能说清楚“这句话是从哪份文档、哪个章节里来的”。这里面有两个层面的问题要处理。第一是提示词约束。要在System Prompt里明确要求模型在作答时只参考给定材料并且回答中标注来源编号比如“根据材料[1]和材料[2]”。第二是结果后处理。模型输出的“材料[1]”要能映射回真实文档ID和块IDAPI返回结果里要有结构化的引用列表而不只是文本里的标注。这块容易踩的坑是模型经常自作主张把不存在的来源编出来。我的应对方式是在提示词里强制要求“如果答案无法从给定材料生成直接回复无法回答不要编造来源”并且在后处理时校验引用编号是否超出实际提供的材料范围。简单说就是把引用当结构化数据来校验而不是只当文本来看。4.3 从固定链路走向Agent化编排传统RAG的链路是“查一次、拼一段、答一次”简单直接。但真实问题往往没那么乖巧用户问“帮我对比一下A和B两款设备的参数差异”这种问题可能需要在多个知识库板块分别检索然后汇总比较。再比如多轮对话中用户说“那它的维护周期呢”这个“它”指代的是上一轮的某个设备孤立检索当前问题根本查不到。六款产品里QAnything在多轮对话上做了不少工作它对历史对话的压缩和指代消解做得比较成熟。Dify和FastGPT也都开始在流程编排上加入条件分支、工具调用本质上就是向Agent方向演进。我自研蓝图里特意把编排层设计成可插拔的第一版可以是一条简单的“检索—组装—生成”固定管道但接口上留出对话改写、意图识别、工具调用这些扩展点。这样后面要升级成Agent形态时不需要推翻重来只需要在管道里插入新的节点。切记不要在自研第一天就想着做Agent那是给自己挖坑先把固定链路跑通、业务验证有价值了再一步步扩展。5. 逆向工程后的自研蓝图服务边界、数据模型与演进路径5.1 模块划分与数据流设计综合六款产品的共性我整理了一套适合中小团队自研的服务划分方案总共六个模块接入与解析服务负责文件上传、格式识别、文档解析、版面分析、切分。向量化服务负责调用Embedding模型把文本块转成向量。存储层向量数据库加关系型数据库配合前者存向量后者存文档、块和任务状态。检索服务负责混合召回、Rerank、结果合并与过滤。编排服务负责对话管理、上下文组装、引用映射未来扩展Agent节点。API与应用层对外提供统一接口面向Web端、IM端或其他业务系统。数据流走的是单向管道文件进入接入解析服务后产出结构化的块数据块数据交给向量化服务生成向量解析结果和向量分别写入关系库和向量库用户提问时检索服务从两路存储中召回候选块经过Rerank后交给编排服务编排服务组装上下文调用大模型生成答案最后把答案和引用返回给应用层。这个设计里最忌讳的是模块之间互相调内部表。比如检索服务直接去查解析服务的临时表短期看着方便后面一改动就全乱套。哪怕第一版简单点也建议各服务之间只通过接口通信数据通过明确的DTO传递。5.2 核心表结构与状态管理存储层建议至少设计四张核心表数据集表、文档表、分块表、解析任务表。数据集表记录每个知识库的配置包括使用的Embedding模型、检索模式、Rerank开关等。文档表记录文件本身的信息包括文件路径、解析状态、切分参数、来源URL。分块表是核心字段上除了内容、向量、元信息之外建议加上唯一的块哈希用于识别内容变更。解析任务表是容易被忽略但非常重要的表因为文档解析不是瞬时完成的大文件可能耗时几十秒甚至几分钟异步任务的状态必须被记录。解析任务的状态机建议这样设计pending等待解析→ parsing解析中→ indexing向量化中→ ready完成或 failed失败。失败的任务要记录失败原因并且支持重试。这个状态机的价值在于当用户在界面上传一批几十个文件时系统可以展示每个文件到了哪一步哪一个失败了、为什么失败。没有这套治理机制批量导入基本就是噩梦。有一个经验值得强调解析和向量化要设计成可断点续跑的。假设你第一批导入了200个文档向量化跑到一半服务崩了重启后应该能从断点继续而不是全部重来。实现上很简单只要向量化任务也走状态表每次启动时捞取未完成的任务继续处理即可。5.3 模型选型与向量数据库的选择策略模型选型是自研RAG里最需要务实权衡的部分。嵌入式模型的路线中英双语场景首选开源的BGE-M3或BGE系列中文模型都能本地部署避免数据出域。如果预算充足、数据允许走云端API也可以选商业Embedding服务但自研知识库通常都有数据合规要求我见过的多数团队最后还是选择了本地模型。维度是一个关键参数。BGE-M3输出1024维向量相对早期的768维模型在细粒度语义上表现更好但占用存储空间也更大。一个小规模知识库可能几万条块记录感觉不到差别但到了百万级向量规模存储和检索延迟就会有明显压力。第一版不用过度纠结选择主流模型但要保证向量维度这个参数可配置后面换模型不用改代码。向量数据库的选择我按规模给三个档位的建议。十万级向量以下直接用PostgreSQL加pgvector插件省掉一个独立中间件十万到百万级可以考虑Milvus或者Qdrant支持标量过滤和混合检索百万级以上才会需要认真设计分片和集群。绝大多数企业内部知识库连十万级都到不了直接上独立向量数据库属于给自己加戏。5.4 MVP演进三阶段的路径规划蓝图给得再完整也不能第一天就全量落地。我建议按三个阶段推进。第一阶段目标是跑通闭环文件上传、解析切分、向量化、单路检索、生成答案、引用溯源。哪怕检索只用向量方式哪怕切分只用递归字符切分都没关系关键是先把端到端链路打通。这个阶段的产出是“能用的最小系统”。第二阶段目标是提升命中率加入全文检索通道实现混合召回加入Rerank节点把切分从固定长度升级为结构感知切分开始积累评测集。这个阶段会有比较明显的效果跃迁。第三阶段目标是精细化引入Agent化编排支持多轮改写、多路检索、工具调用建立完整的评测和监控体系线上检索日志回灌评测集。这个阶段系统才算真正达到生产可用标准。切记不要跳阶段。我在实际项目里见过一支团队第一版就上了Agent加知识图谱结果光排查链路问题就花了几周核心的检索质量反而没人关注。地基都没打牢楼层盖得再花哨也立不住。6. 避坑实录常见故障与我的实操心得6.1 常见故障速查表现象可能原因优先排查方法检索结果经常为空向量索引未创建、切分粒度太小、关键词完全缺失检查索引任务日志用单条文本直接查向量库确认是否有写入回答张冠李戴切分粒度过大导致块内混入不相关内容或Rerank候选范围太小查看命中的几个块原文判断是不是切分把不同主题揉在了一起知识更新后检索不到新内容增量任务失败或未触发向量库存在旧版本缓存检查解析任务状态表确认向量化节点是否处理了新文档PDF表格内容丢失或乱码解析方案对表格支持不足表格被当纯文本抽取换用带表格识别能力的解析方案或对表格类文档单独走OCR流程回答时快时慢、不稳定Rerank推理耗时高或大模型并发受限给Rerank加缓存和并发控制分开统计每段耗时定位瓶颈引用来源指向不准确块和原文偏移没有记录或提示词未约束来源后处理校验引用编号确保块ID和文档ID映射一致6.2 几条写在最后的实操心得第一个心得评测集必须在第一天就开始攒。不要等系统上线了才想“效果到底好不好”。我自己的做法是准备一份百条左右的人工标注评测集覆盖常见问题、模糊问题、专有名词问题、多轮问题每次改动模型或检索策略都拿这份评测集跑一遍用命中率和人工打分对比改动前后效果。没有评测集你做的任何优化都是盲人摸象。第二个心得多轮对话的检索一定要做问题改写。用户说“那它的维护周期呢”你要先把“它”还原成设备名再去做检索。实现方案可以用大模型做改写也可以用规则匹配上下文实体。第一版用规则就够后面再上模型改写成本低而且效果稳定。第三个心得不要试图一次把检索命中率做到100%。RAG系统的效果上限不仅取决于算法还取决于源文档的质量。如果源文档本身就没有那个信息任何检索和生成技巧都无法无中生有。所以与其死磕模型参数不如花时间整理知识库的源文档把过时的、重复的、残缺的内容清理掉。第四个心得生产环境一定要留检索日志。把每次用户提问、命中的块、排序分数、最终答案都记录下来。这些日志有三个用途一是排查线上问题二是积累真实数据优化Rerank和融合权重三是反哺评测集。我见过很多团队把精力全花在预处理和模型调优上却忽略了这份最便宜、最真实的资产。最后一个感受自研RAG最大的风险不是技术做不到而是低估了链路之长、问题之多。文档解析、切分、召回、重排、上下文组装、引用溯源、评测迭代每一环都需要持续打磨。开源产品的存在不代表你不需要自研但如果没有先把这些开源产品拆透、想清楚它们为什么这么做自研大概率会踩着它们已经填平的坑再走一遍。这份蓝图只是一个起点真正的功夫在后期的数据治理和评测循环里。
RELATED READING

延伸阅读

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