拆解六大开源RAG项目,提炼自研企业知识库问答系统架构蓝图
发布时间:2026/10/8 11:01:15 锦皓数字建站

1. 为什么我决定拆开源项目而不是直接抄一个先说个背景。去年我所在的小组要自研企业知识库问答系统大家都清楚现在RAG方案看着热闹但落到自己的业务数据上经常是一问就答非所问、一查就召回一堆无关片段。当时我们面临一个决策是直接套用某个开源框架还是花钱买商业产品又或者硬着头皮从零写。最终我提了个方案——把市面上口碑不错的几款开源RAG项目一个个拆开看它们的代码结构、看它们的文档设计、看它们在GitHub issue里被抱怨最多的地方然后把这些观察沉淀成一张自研蓝图。这篇文章就是那段时间工作的整理。为什么选这条路线因为RAG的瓶颈从来不是调用一个大模型API而是工程系统中每个环节的误差叠加问题。文档解析漏了表格、切分把一个完整段落拦腰截断、向量召回时Top-K里塞了太多语义相似但无关的片段、重排模型又把时间信息搞乱了——这些问题在你写出一版demo之前几乎感知不到只有当你站在一个已经跑起来的系统上回头看才知道每一层的坑长什么样。开源项目恰恰是最好的教材它把决策过程和权衡结果都摆在那里代码就是答案issue区就是避坑手册。我给自己定的拆解范围是六款RAGFlow、QAnything、Kotaemon、Dify、LlamaIndex和LangChain。选它们的逻辑是RAGFlow把重心压在文档解析上QAnything在检索优化上做得很重Kotaemon注重交互与引用溯源Dify把知识库封装进应用编排LlamaIndex是开发者向的索引抽象LangChain则提供了完整的生态集成样板。六款产品覆盖了解析、索引、检索、重排、生成、交互这条完整链路拆完它们相当于把RAG各个派别的典型解法都见了一遍。逆向工程的方法上我有一条核心原则不要去逐行读源码而是去读每个项目的决策痕迹。所谓决策痕迹包括三类架构文档和README里反复强调的定位、代码目录结构反映的分层逻辑、以及issues里用户反复吐槽的点。这三样东西拼起来你看到的不是一个项目的代码而是它背后团队对什么重要、什么可以放弃的一系列判断。写自研系统最缺的就是这种判断因为任何模块都有十种实现方式你要知道的是在什么约束下选哪一种开源项目恰好把答案展示在你面前了。2. 六款产品逆向拆解它们各自的护城河与放弃接下来我按产品逐个说不面面俱到只挑对自研有直接借鉴价值的部分讲。2.1 RAGFlow文档解析才是RAG的第一道坎很多人做RAG第一个想到的是向量数据库和embedding模型RAGFlow却把重心砸在了文档解析上这个定位本身就很有启发。它用DeepDoc做版面分析把PDF、Word、PPT先还原成版面结构——标题、段落、表格、图片、页眉页脚——再做后续处理。默认的切分逻辑也是配合版面来的而不是简单按字符数吃这和我见过的很多自研方案有本质区别。从逆向角度RAGFlow让我看到三件事。第一解析层如果做得糙后面所有环节都会被污染。一个PDF里面带三栏排版、页眉里有公司名、表格跨页这些不做版面语义还原的话解析出来的文本就是乱的切分出来的chunk自然也是乱的。第二RAGFlow引入了模板化配置不同文档类型可以用不同解析模板它没指望一套解析器通吃所有文档这本质上是一种务实的工程妥协。第三它对文档还原的优先级设置得很高宁可检索慢一点也要让喂给模型的内容是结构完整的。自研借鉴点在建设自己的RAG系统时最先要确认的不是选哪个embedding模型而是你的文档集里到底有多少种版式每种版式需要什么级别的解析能力。如果文档以扫描件为主OCR流程就要前置如果表格密集就必须考虑表格结构还原否则检索到了也读不懂。2.2 QAnything两阶段检索的工程模板QAnything是网易有道开源的它的slogan很直接Anything to Answer任何你能问的都能回答但真正让我感兴趣的是它把检索做成了清晰的流水线。它默认使用BCE的embedding模型做第一轮召回再接一个cross-encoder做精排这种粗召回加精排的两阶段结构在搜索领域是老套路但在RAG项目里真正做到工程可复用、并且把default配置给好的QAnything算一个。拆它的时候我在意的是几个细节。第一每个知识库是隔离的文件入库时自动做向量化和倒排索引这避免了全局混合检索时互相污染的问题。第二QAnything对query的处理不是直接丢给检索而是有一套query改写逻辑它会把用户口语化的问题转成更适合检索的表达。这个点在自研时很容易被忽略但实际上用户问今年Q3的销售额怎么样和2024年7月到9月的销售数据在向量空间里的表达差异很大不做改写就会召回不一致。第三是它的排序策略。QAnything没有把Top-K直接交给LLM而是先靠BM25和向量召回各取一批候选合并后由cross-encoder精排再截断到最终K条。这个思路我在自研时完整抄了原因是单路向量检索在长尾query上掉得厉害加上BM25做互补就相当于用关键词保证覆盖率、用向量保证语义相关性再靠精排模型干粗活。这么一套下来召回质量稳定很多也特别好调试。2.3 Kotaemon交互层面决定了用户信不信你的RAGKotaemon是CLeanUI组织下的开源项目做的是一个带界面、带文件管理、带对话历史的RAG问答系统。它的算法不算激进真正做得讲究的是让用户能追查答案来源这件事。每个回答都会带上引用来源用户点开引用就能看到原始文档中的对应片段来源还能在文档里高亮。这个功能看似轻描淡写实则是RAG系统能不能在真实业务里落地的关键——因为LLM的答案一旦没有出处用户就无法判断它是胡编的还是依据文档说的信任感直接崩塌。拆Kotaemon还有个收获是它的对话组织方式。它把单轮问答升级成多轮对话每一轮都会继承之前的检索历史而且它没有把历史记录全部塞给模型而是只保留和当前问题最相关的历史片段。这个决策值得细品因为很多自研系统图省事把最近几轮对话直接拼接进prompt很快就把上下文窗口吃光了反而导致当前问题的召回被历史噪声干扰。Kotaemon的启发不在于代码而在于产品意识RAG系统的输出不只是一个答案还应该包含依据、过程、可验证性。自研时哪怕界面再朴素引用溯源这个能力无论如何都要做进去因为它不仅解决用户体验问题还能当评测工具用——当回答出错时你翻引用就知道是检索错了还是生成错了。2.4 Dify知识库在AI应用里只是中间件Dify其实是做AI应用编排的知识库只是它的功能模块之一但正是这个定位让我对知识库在整个应用中的位置有了重新认识。Dify里的知识库是作为工具和上下文被工作流引用的用户可以定义在什么条件下检索知识库、检索后如何拼装prompt、最终交给哪个模型生成。它把RAG从文档进、答案出的单一问答变成了一个可编排的数据处理中间件。拆Dify最大的收获是它的分段模式设计。它支持自定义分段标识符、最大分段长度、重叠长度这些参数全都可以在界面上调甚至允许按自定义规则做结构化分段。这个灵活度意味着同一个知识库可以根据不同业务场景调整切分策略不用每次改代码重新跑入库。Dify还提供了召回模式的选择——向量召回、全文召回、混合召回——让用户根据内容类型决定用哪种。这些能力本身不稀奇但它把调参这件事变成了产品能力这给自研系统的提示是RAG的配置化程度决定了它能不能被业务人员自己调优而不是每次都要开发介入。2.5 LlamaIndex索引抽象是面向开发者的核心LlamaIndex给我的感觉是没替你决定任何事但帮你把一切整理好了。它不是一个开箱即用的产品而是一套构建RAG的组件库。它最大的贡献是把索引这个概念抽象得很清晰——VectorStoreIndex、SummaryIndex、KeywordTableIndex、KnowledgeGraphIndex每种索引对应一种数据组织和检索方式你可以按需组合。而且它把数据连接器LlamaHub做成了插拔生态各种格式的数据源都可以通过连接器灌进来。从逆向角度看LlamaIndex让我把注意力放回到数据接入这一层。它专门设计了文档节点Node这个中间结构原始文档进来先被解析成节点节点上可以挂元数据再决定用哪种索引和检索方式。这个结构对自研的意义在于你的系统里其实应该有一个统一的数据中间表达不能让文档格式直接影响索引结构。先做解析、再转成标准节点、再决定索引方式这个数据流一旦固定后面加新文档格式就只是写一个新的解析器而不是动检索链路。2.6 LangChain生态集成力胜过算法创新LangChain我不需要多介绍它已经成了AI应用开发的事实标准之一。但拆它的时候我想明白了一件事LangChain的护城河不在算法而在集成生态。它把几十种向量库、几十种模型、几十种工具全部抽象成统一接口让开发者可以像搭积木一样组合方案。自研RAG不太可能复刻这种生态但可以借鉴它的抽象思路——你的Retriever接口、Embedding接口、LLM接口都应该做成可替换的这样以后升级模型、换向量库才不需要重写整个系统。LangChain还有一个值得抄的设计是它的Chain结构。它不把检索加生成写死成一个函数而是拆成RetrievalQAChain、StuffDocumentsChain等一系列可组合的Chain。这个设计的好处是方便做中间结果观测——每一环的输入输出都有明确的类型和记录点出了问题可以单独测试某一环。自研时哪怕不引入这个框架也应该把手写逻辑拆成同样清晰的模块边界而不是几百行代码揉在一起。3. 从六款产品里抽象出的自研RAG架构蓝图拆完六款产品我把它们的共性部分抽出来合并成一张自研可复用的蓝图。这张蓝图不绑定具体技术栈而是描述RAG系统应该有的分层结构、每层的核心职责、以及层与层之间的数据接口。在动手写代码之前把这张图画清楚后面会少走很多弯路。3.1 预处理与解析层决定上限的一层这一层是所有产品里最容易被低估的但六款项目里做得用心的一款产品几乎都把解析当成头等大事。蓝图上这一层的内容拆成四段流水线格式识别判断是PDF还是Word还是扫描件、内容解析版面分析、表格还原、OCR、清洗规整去页眉页脚、去重复空行、合并断行、结构标注识别标题层级、段落边界、列表项。四段完成之后原始文档才被改造成干净的半结构化文本。这一层输出的不是纯文本而是带元数据的节点列表。每个节点至少包含文本内容、文档来源、页码或位置信息、文档标题、章节路径。这些元数据在后续检索和引用溯源时都是必需品。切分策略同样属于这层我推荐的默认策略是结构优先、长度兜底优先按文档本身的标题和段落边界切如果段落太长再按最大长度截断同时设置少量重叠来避免上下文断裂。3.2 索引与存储层多路索引并行把切好的节点入库时蓝图建议不要只做一种索引而是构建三类并存的结构向量索引对应语义检索、倒排索引对应关键词检索、元数据索引对应结构化过滤。向量索引负责语义相似度倒排索引负责精确匹配术语元数据索引负责按时间、作者、文档类型做预筛选。这三类索引共享同一个节点存储也就是同一个节点ID可以同时存在于三种索引中只是索引键不同。选择embedding模型时我的建议是优先看中文场景的通用表现BGE系列和M3E系列在中文上默认效果都还可以但最终要以自己的数据集评测为准。向量数据库现在选择很多Milvus适合大规模、Elasticsearch适合已有ES技术栈、Qdrant和Weaviate部署更轻。蓝图里我建议先用一套最简单的方案跑通链路因为瓶颈往往不在向量库本身而在数据质量等数据规模上来再迁移也不迟。3.3 检索与排序层混合召回加精排这层的设计我直接参照了QAnything的两阶段方案。第一阶段是召回并行执行三路向量召回Top-K默认K可以设大一点比如50、BM25召回Top-K、元数据过滤后的范围内召回。合并后做去重和分数归一化进入第二阶段。第二阶段是精排用cross-encoder模型对候选集逐一打分输出最终Top-K。我在实际使用中会把第一阶段的K值设得偏大宁可多召回一些可能相关的内容让精排去判断也不要在一开始就把正确答案漏掉。精排模型的选择上bge-reranker-base在速度和效果之间比较均衡。另外提醒一句精排之后不要直接把Top-K一股脑塞给LLM建议做相关性阈值检查低于阈值的宁可少给避免噪声段落干扰生成如果召回结果整体分数都很低可以让系统明确告诉用户知识库中没有找到足够相关的资料而不是硬凑答案。3.4 生成与交互层prompt、引用和流式输出生成层的核心不只是把Top-K拼给LLM而是模板化地组织上下文。我的prompt模板大致分四块系统指令说明你是基于知识库回答的助手禁止编造、知识库上下文按相关性排序拼接每条前面标记引用ID、用户问题、输出要求如果知识库没有相关内容直接说明不确定。引用溯源这一环节把精排结果中每条文本对应的节点ID映射成一个可点击的引用标记回答中每出现一个引用标记界面上就能展示对应的原始片段。流式输出我建议必做。RAG场景下用户等待时间本来就比普通对话长流式输出能显著改善体感。另外对话历史这块不要简单拼接最好对历史做轻量压缩或只取与当前问题相关的部分Kotaemon的做法值得抄——它把历史对话也做了相关性判断只保留有用部分。4. 蓝图落地时的取舍你的场景决定你该砍掉什么蓝图是理想态真上手时不可能全都要取舍依据是你的数据规模、实时性要求、和团队维护能力。我根据拆解经验把常见场景分了三档你可以对号入座。4.1 轻量版文档千篇以内团队两人以内这档适合个人知识库、小团队内部文档问答。技术栈可以极简一个开源向量库比如Chroma或Qdrant的本地模式、一个embedding模型、一个rerank模型、一个LLM API。解析层不用做太复杂的版面分析用成熟的文本抽取库先顶着切分策略直接按结构优先的默认方式。蓝图中的元数据索引可以简化只保留来源和页码。轻量版的精排我仍然建议留但可以用更轻量的模型。遇到的问题是recall不够还是precision不够再决定是否升级解析或加更多路召回。最重要的是评估先行——先准备50到100条真实业务问题当评测集每次改动前后都跑一遍不然你根本不知道改完是变好了还是变坏了。4.2 均衡版面向企业知识库需要权限控制和多租户这档对应大多数做企业知识库的场景。解析层要上版面分析能力因为企业文档里PDF和Word比例高表格和扫描件都不少。索引层建议向量、倒排、元数据三路全上元数据里要记录部门、权限级别、文档类型等字段检索前先做权限过滤这一步不是在提示词里加一句只返回有权限的内容——元数据过滤是硬规则在进入检索之前就完成不能依赖模型自觉。均衡版还建议引入工作流编排能力把知识库问答嵌入到具体的业务流程里。比如客服场景需要先检索FAQ再查产品文档先做意图判断再决定检索策略这些用Dify式的工作流设计可以灵活配置。模型层面embedding和rerank都要选效果好一些的版本因为企业场景对准确率的容忍度比个人场景低。4.3 重型版多源数据、大规模知识库、高并发重型版的关注点开始转向系统性能和数据治理。解析层要支持异步流水线因为文档量大时入库是持续性的任务不能全塞在同步流程里。索引层要考虑分片和扩容Milvus这类分布式向量库是合理选择同时要注意embedding模型的批处理吞吐。检索层为了扛高并发粗召回可以预先缓存热门query的结果精排模型要支持GPU部署必要时还要加一层规则缓存来降低重复问句的算力消耗。重型版还有一个容易忽略的点是数据更新的时效性。文档改了之后索引要不要立即可见如果允许延迟可以走定时增量更新如果要求实时就需要监听数据源变更事件触发对应文档的重新解析和索引更新。这块复杂度不低我建议先明确业务到底需要多实时再决定投入多少成本。5. 真正动工前先想清楚这三件事蓝图有了取舍也定了但如果这三件事没想清楚代码写一半大概率会返工。5.1 评测集没有评测集一切调优都是玄学这是最重要的一条。动手第一天就要从真实业务场景里攒一批有代表性的问题最好包含简单事实问答、复杂推理问答、跨文档组合问答、知识库外问题四类每类问题预先标注期望答案和它应该在哪些文档里。评测集不用大50到100条足够启动但要持续扩充。每次改动解析规则、切分参数、检索策略、prompt模板都跑一遍评测集用召回率和答案准确率两个指标看变化。没有评测集你只会在感觉变好了和感觉变差了之间反复横跳最后浪费大量时间。5.2 渐进式落地先用最小闭环跑通再逐步加厚不要一开始就把蓝图里所有模块都实现完。第一版系统可以用最简方案跑通——一个文档解析器、一个embedding、一个向量库、一个Top-K直接拼给LLM。跑通之后拿评测集跑一遍记录基线效果然后逐个加模块先加BM25双路召回看提升再加rerank看提升再加元数据过滤看提升。每一步都用量化效果验证这个模块到底值不值得加这样你能清楚知道每个环节的边际收益而不是凭感觉堆技术。5.3 可观测性每一个中间结果都要能看见RAG系统调试最大的痛点是不透明——用户问一个问题你很难知道是解析错了、召回错了还是生成错了。所以从第一版开始就要把每个环节的中间结果记录成结构化日志原始query、改写后的query、每路召回的候选及其分数、精排后的Top-K列表、最终拼进prompt的上下文、LLM的原始输出。这些日志既是排查问题的抓手也是后续评估数据的重要来源。我见过太多团队把这一步拖到上线之后结果一出问题就只能靠猜。别省这个功夫它会让你在调优路上快很多。几款开源项目拆下来我最大的体感是RAG不是一个装上就能用的技术而是一个每个环节都要亲自校准的系统工程。开源项目的价值不在于让你少写代码而在于给你提供了经过验证的判断框架——什么重要、什么可以放弃、哪里是坑。把这张蓝图变成适合你自己的版本然后拿着你自己的文档和问题集一段一段地调一趟一趟地验证最终那个系统才会真正长在你的业务上。
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。