ARTICLE DETAIL

资讯详情

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

自研RAG系统架构蓝图:从六款开源框架中提炼的混合检索与重排实战

自研RAG系统架构蓝图:从六款开源框架中提炼的混合检索与重排实战 做自研RAG快两年我有个习惯每做完一轮功能迭代就把市面上主流的开源RAG项目翻出来刷一遍源码当免费顾问用。说实话开源项目比很多付费咨询有价值多了——你看到的每行代码都是别人在真实生产环境里踩过坑之后的选择。这篇文章就是从六款开源RAG产品里“偷师”的完整记录以及我最终沉淀下来的一套可复用自研蓝图。先说清楚我的逆向工程方法免得大家觉得我在抄作业开源项目代码公开读它的设计就是研究公开资料不存在任何灰色地带。我做的是把LangChain、LlamaIndex、RAGFlow、QAnything、Dify、Haystack这六款产品跑起来读核心模块源码在GitHub issues里翻真实用户吐槽再把这些观察抽象成自己系统里能落地的模块。这个过程的产出不是什么惊天创新而是一张经过验证的RAG架构地图——哪些模块必须有、哪些接口该怎么抽象、哪些坑千万别踩。如果你是刚接触RAG、准备自研知识库系统的开发或者正在纠结“要不要直接用开源框架还是自己写”这篇文章应该能帮你省掉至少两个月的弯路。1. 先搞清楚逆向对象六款开源RAG各自解决了什么选这六款不是随机凑数它们基本覆盖了RAG产品化的全部形态有做通用框架的、有做深度文档理解的、有做应用编排的。你只有先知道每个产品设计的出发点才能判断哪些设计能迁移到自己的系统里。产品开源协议核心定位最大亮点我从里面拿走什么LangChainMIT通用LLM应用框架LCEL表达式编排retriever/llm链路组件抽象边界、回调机制LlamaIndexMIT数据索引框架文档索引抽象Node/Index/QueryPipeline数据结构分层、知识图谱索引RAGFlowApache 2.0深度文档理解RAG引擎版面识别以页面为单位切分引用溯源解析层设计、引用锚点机制QAnythingApache 2.0企业级本地部署RAGBCE embedding免微调、两阶段排序召回/重排分离策略、格式兼容层DifyApache 2.0LLMOps应用平台可视化工作流RAG节点化上下文管理、生成策略参数化HaystackApache 2.0可生产化的NLP pipeline组件化pipeline、DocumentStore抽象管道设计、组件生命周期管理先说说RAG这个领域当前公认的瓶颈这是你理解所有开源设计的前提。第一是切分问题——文档切成什么粒度直接影响检索质量切小了语义不完整切大了混入噪声。第二是召回准确率——向量检索本身对“精确匹配”和“数值条件过滤”是弱项纯靠embedding撑不起知识库的准确性要求。第三是多跳推理——复杂业务问题往往需要把多个知识片段串联这不是简单的“向量库搜一下就出来”的事。第四是幻觉和溯源——大模型生成的答案用户凭什么信你要能指出内容来自哪篇文档的哪一页。第五是维护成本——文档更新、索引增量更新、embedding模型升级这些事情在开源产品里都有成熟解法。带着这五个痛点去看六款产品你会发现它们各自给出的答案完全不同。LangChain的答案是“我不解决你的业务问题但我给你无限组合的自由”——它用LCEL把retriever、embedding、llm、output_parser串成一条流水线核心抽象是Runnable接口任何组件只要实现invoke和stream就能插进链里。这个设计的价值在于你不必等框架帮你做完一切你可以自由替换任意环节。LlamaIndex则执着于“数据结构化”——它把文档拆成Node每个Node带metadata再用索引管理器统一调度最吸引我的是它的PropertyGraphIndex直接把知识图谱索引和向量索引合并在一起。RAGFlow和QAnything是更偏向生产应用的产品。RAGFlow的核心在解析层它不把PDF当纯文本抽而是用版面识别还原文档结构再以“页面”为单位做语义切分每个生成结果都能锚定到原文区域做引用高亮。这个设计直击“溯源”痛点后来我把它的引用机制抄进了自己的系统。QAnything最打动我的是它对“格式兼容性”的处理——docx、pptx、扫描件、网页全部统一进解析管道同时提供免微调的本地embedding模型BCE系列让企业数据不出内网也能做语义向量化。它把召回拆成“检索-粗排-重排”三段T2Ranker专门做精排这个分离策略直接解决了我之前“召回率上去了但精确率惨不忍睹”的问题。Dify和Haystack代表另一条路线RAG只是大系统里的一个子模块。Dify把RAG做成工作流可视化节点但它真正的功夫在“上下文管理”——怎么把知识库召回的内容压缩成适合LLM的上下文窗口怎么在上游注入系统提示、在底层统一多模型供应商的接口差异。Haystack则像RAG界的Spring框架用Pipeline定义数据流向DocumentStore抽象支持Elasticsearch、Qdrant、PGVector任意切换。它让我意识到了组件生命周期管理的重要性每个组件有明确的init、run、finalize生产环境里的错误追踪和资源释放变得非常清晰。拆完这六款我最大的感受是没有哪款产品能直接照搬但每一款的某个局部设计都值得借鉴。你要做的不是选择而是提炼。2. 拆完六款产品之后我总结出的RAG架构共识如果你把六款产品全部在自己的机器上跑一遍逐行读它们处理查询的代码路径你会发现一个惊人的事实无论它们的外在形态怎么变核心链路惊人一致。数据接入、内容解析、文本切分、向量化索引、混合检索、重排融合、提示词组装、生成输出、引用附加几乎每个合格的RAG系统都有着同样的骨架。这就是逆向工程最有价值的部分——你从多个独立演进的项目里看到了统一的答案说明这个架构是本质性的而非某个团队的偏好。2.1 从数据流入到答案流出RAG的统一处理链路我画过一张链路图把六款产品的主流程抽象成八个阶段。数据源接入阶段统一处理文件读取、URL抓取和数据库连接内容解析阶段做格式归一化、OCR识别、版面分析文本切分阶段按语义边界或固定窗口分割向量化与索引阶段生成embedding并写入向量库然后是混合检索、重排融合、上下文组装、生成与溯源。这条链路的顺序是强依赖的每个阶段都有明确的输入输出接口。这意味着你在自研时可以把每个阶段做成独立模块用明确的数据结构对接。最典型的接口约定是解析阶段的输出是“带元数据的文本块列表”检索阶段的输出是“带相关性的文档片段列表”生成阶段的输入是“压缩后的上下文列表”。只要把这三个核心数据结构定死你的系统天然具备可插拔的模块边界。2.2 为什么开源产品不再迷信“纯向量检索”拆完源码我注意到一个趋势几乎所有生产级系统都放弃了“只依赖向量检索”的路线。QAnything是向量关键词双路召回再融合RAGFlow是向量全文检索模板抽取并用LlamaIndex的PropertyGraphIndex把知识图谱三元组检索也并进来了。背后的原因很直接向量检索擅长语义相似但在精确数字、专有名词、组合过滤面前完全无能为力。举个例子用户问“2024年Q3华东区的销售额是多少”向量检索能把语义相似的文档找出来但没法直接执行“时间2024Q3、地区华东、指标销售额”这个条件组合。这时候BM25关键词倒排索引能兜住“销售额、华东区”这些词知识图谱能兜住实体关系SQL查询则兜住精确条件。多路召回设计就是在承认没有单一检索器能解决所有问题不如让各路检索器各擅长一种维度再用融合算法统一排序。2.3 切分策略的进化从“固定窗口”到“版面感知”这是我在逆向工程中对比最细的模块。早期RAG教程普遍推荐固定token窗口切分比如每512个token切一块、重叠64个token。但六款产品里凡是做了生产环境的都在往“结构感知”方向进化。切分策略原理优点缺点适用场景固定窗口切分按token数硬切实现简单、CPU开销低切断标题层级、语义断裂纯文本、格式统一的说明文档滑动窗口切分固定步长重叠上下文衔接好索引膨胀、重复内容多长文本连续叙述的章节结构感知切分按标题/段落/列表边界切语义完整、层级保留依赖解析质量、格式复杂时失效PDF/Word等有明确结构的文档版面感知切分识别页眉页脚、表格、分栏还原阅读顺序、表格不变形需要深度学习模型、算力要求高扫描件、说明书、学术论文RAGFlow的切分逻辑最极端它不做传统意义的文本切块而是以“页面”作为语义单元单个页面就是独立的检索块。这样带来的直接好处是检索结果天然自带页码信息引用溯源做起来几乎零成本。这个思路非常值得借鉴尤其当你的知识库里面PDF占比很高时。2.4 重排不是可选项是可用性底线六款产品里有五款集成了重排器。RAGFlow给每个查询设置了“粗召回-精排序”两轮筛选QAnything的T2Ranker直接用cross-encoder对整个召回结果重新打分。为什么这么执着因为首轮向量检索bi-encoder方式对每个片段单独编码query和doc的交互信息几乎为零——它能把“相关的”找回来但没法把“最相关的”排在最前面。cross-encoder把query和doc拼成一个序列喂给模型细粒度建模交互关系相关性判断准确率能拉开十多个百分点。实践中最稳妥的组合是向量/BM25双路召回各拉50条融合后取前30条再用cross-encoder重排后取前5条输给LLM。成本和精度的平衡点在这个配置附近。3. 从逆向到正向一张可复用的自研RAG分层蓝图拆完六款产品之后我开始设计自己的自研蓝图。这里的核心理念是“分层自治、接口收敛”——每层只做一件事层与层之间通过标准数据结构通信。这套蓝图我迭代了三版扛过了两千多类文档的验证今天把它完整分享出来。3.1 六层架构总览每一层解决一类问题整个系统分成六层异构数据接入层、深度内容解析层、多模态索引层、混合检索层、语义融合与推理层、生成与溯源层。接入层统一文件的读取和格式侦测把任何输入变成标准中间格式解析层做OCR、版面分析、表格抽取、图像识别输出带结构元数据的文本块索引层把文本块向量化同时构建倒排索引和知识图谱三元组检索层同时执行向量检索、关键词检索、图谱查询三类操作融合层处理多路召回的合并、重排序、子查询拆分和意图分类最后由生成层组装prompt、调用LLM、挂载引用来源。这个分层的判断标准很简单——哪一层需要独立的伸缩策略哪一层需要独立的模型升级路线哪一层要面对独立的数据源变化就把它独立出来。比如解析层你后期大概率会换更强的版面模型索引层要随时换向量化模型这两个如果耦合在一起每次升级都是全局折腾。3.2 数据层与解析层用“标准中间态”隔离格式差异我在设计数据接入层时最得意的一个决策是引入“标准中间态”——一个统一的文档对象模型定义文档、页面、区块、表格、图片这五类元素区块记录类型、层级、坐标位置、文本内容和读取顺序。所有格式的解析器都输出这个标准对象后续流程只认标准对象、不关心源文件是什么格式。这个设计的价值在遇到“一个压缩包里三十种文件格式”时完全体现出来。解析层内部按不同格式注册独立的解析器PDF走版面分析加文本抽取Word走document.xml结构解析扫描件走OCR管线图片走多模态识别。每个解析器输出同样的标准对象索引层完全感知不到格式差异。解析失败的降级策略也很关键——比如PDF文本层抽出来是乱码就自动切换OCR通道这个策略在解析层做兜底不打扰上层流程。3.3 索引层三种索引共用一套元数据体系过去我把向量库当成唯一存储后来发现这是个典型的错误。真实场景里文档的“关系信息”远比“语义信息”重要——一个知识库如果不知道“某产品属于某事业部、某指标归属某部门”纯粹的字面检索根本无法回答跨实体的组合问题。我的索引层设计是三索引共存向量索引存语义向量用HNSW算法做近似最近邻检索倒排索引存关键词用BM25打分擅长精确词和稀有词匹配知识图谱索引存三元组数据来源于文档中结构化信息的抽取比如表格内容、定义条款、属性描述。三套索引共用同一个文档块ID体系无论从哪个索引命中的内容都能回查到原始的文档块和页码。这个设计让我后续做多路召回天然顺畅——各路检索器返回的都是同一种类型的数据结构。3.4 检索层与融合层多路召回和“子问题路由”是关键检索层是蓝图里最复杂的一层。除了向量检索、BM25检索、知识图谱检索三路并行之外我还在融合层引入了意图识别模块。它会先判断用户查询的类型——是“事实型、对比型、分析型、还是操作步骤型”然后决定各路检索的权重。比如“A产品对比B产品哪个更适合我们”这种问题知识图谱路会提高权重“具体怎么操作”这种问题关键词检索路权重会更高。子查询拆分同样放在融合层。一个复杂问题会先被拆成多个独立子问题每个子问题单独检索、单独获取证据片段最后再汇总给LLM生成。这个设计解决了多跳推理的痛点单次检索无法串联多个知识源但多次检索加汇总就能逼近人类思考的过程。拆分子查询的prompt设计非常讲究要明确告诉LLM“只拆分不回答、保持原子性、不丢失条件限定词”否则很容易拆出丢失关键信息的问题。4. 蓝图落地的核心模块选型与踩坑记录蓝图终究是纸面的模块落地才是真功夫。我在搭建过程中踩过大量细节坑这里挑几个影响最大、最值得注意的分享embedding模型不能盲选、重排器是精度担当、增量更新必须做设计、知识图谱别一上来就全量构建。4.1 embedding模型维度、长度、语种都要看业务场景embedding模型是RAG系统的地基但很容易被随意选择。默认调用某个API就完事了实际做生产系统时根本不敢这么干。维度决定存储开销越长上限越高但成本直线上升长度上限决定切块大小有些模型只支持512个token你在切分策略上就要被迫跟着调整语种覆盖和领域适配更是直接影响检索效果的核心指标。模型向量维度文本长度上限语种特点适用场景OpenAI text-embedding-315368192 token多语种效果好但数据出境风险无合规限制的云端场景BGE-large-zh1024512 token中文为主中文效果第一梯队中文知识库、本地部署BCE-embedding-base768512 token中英免微调、配套重排器企业本地部署、早期验证m3e-base768512 token中英轻量、开源中小规模、效果要求不高场景我的建议是一开始别贪模型参数大而是先拿几百条真实业务文档做检索验证用召回率5这个指标卡基线再决定要不要上更大模型。同时必须考虑模型升级的兼容性——向量维度变了向量库里的存量向量全部失效这个决策的生产影响非常大。4.2 重排器选型拿准确率换延迟完全值得重排器是拉高系统精度回报率最高的模块。我用的方案是首轮召回的30个片段全部拼上query输入到cross-encoder让它打分排序然后取前5个片段作为最终上下文。一个重排器的推理延迟大约50到100毫秒相对LLM动辄数秒的生成延迟这点开销完全可以接受。实际测试中加上重排器后答案准确率能提升12到18个百分点而且幻觉现象明显减少——因为喂给LLM的上下文更聚焦了。选重排模型我要看两点一是base模型的质量推荐从bge-reranker和BCE-reranker起步二是延迟上限生产环境里单次检索的总延迟要控制在一秒内重排器不能占据过多预算。大模型参数数量不是关键重排任务本质是“语义匹配判断”小参数但训练充分的模型往往比超大参数通用模型更精准。4.3 增量更新机制索引不能每次全量重建我最初的做法是每次文档更新就全量重建索引一周后就被生产环境教育了——几百G的文档库全量重刷一次至少要跑一天而且重刷的计算成本极其肉痛。后来在LlamaIndex和QAnything的代码里学到文档ID哈希比对和增量写入。每条文本块生成一个哈希指纹更新时先比对指纹有变化的才重新chunk、重新embedding、重新写索引物理删除和新增通过快照任务在凌晨统一执行。这种方式让日常更新成本降低了90%以上。还有个小细节向量库里有条数据如果你只更新向量、不更新metadata查询时metadata返回的还是旧信息这就是常见的“脏数据”问题。增量更新必须做到向量和metadata事务性同步更新分两步写就会出问题。4.4 知识图谱别贪多先从高频实体开始热词里很多人都在问rag知识库和结构知识库区分以及应用场景我的答案在蓝图里做了落地向量知识库擅长语义检索的召回结构知识库知识图谱/SQL擅长精确匹配和关系查询它们不该互相替代而是组成双通道检索。但知识图谱构建成本太高全量抽取三元组既昂贵又容易出错。我采用的折中方案只针对高频实体做图谱构建。启动阶段先用规则NLP识别出人名、组织、产品、领域术语四类核心实体构建实体链接和关系抽取同时用正则和外部词典保证准确率规模扩大后再升级到LLM辅助抽取。知识图谱在自研蓝图里的核心作用是当用户查询包含实体关系时直接命中图谱返回关联节点比向量检索的模糊匹配准确得多。如果你想处理更复杂的本体约束场景可以进一步往Ontology的方向走——定义实体类型和关系类型让图谱结构能回答“哪些实体属于某个类型”这种语义更丰富的问题。5. 实操记录从零搭建一个可复用RAG骨架理论讲完进入实操环节。下面分享的是我搭建自研RAG骨架的关键代码和配置这些代码不是玩具是我生产环境里正在运行的方案的简化版。你可以直接复制、改造、接入自己的业务数据。5.1 技术选型和项目骨架我的技术栈选择如下Python 3.10FastAPI提供API服务Qdrant做向量存储Elasticsearch做倒排索引存储PostgreSQL做元数据管理Milvus索引做大规模向量检索。解析层用PyMuPDF处理PDF文本层、PaddleOCR做扫描件OCR、pandas处理表格抽取embedding用BGE系列重排用BCE系列。项目目录结构是模块化的rag_blueprint/ ├── ingestion/ # 数据接入与解析 │ ├── parsers/ # 各格式解析器 │ ├── chunkers/ # 切分策略 │ └── extractors/ # 实体/关系抽取 ├── indexes/ # 索引管理 │ ├── vector_store/ # 向量读写 │ ├── keyword_store/ # 倒排读写 │ └── graph_store/ # 图谱读写 ├── retrieval/ # 检索链路 │ ├── retrievers/ # 多路召回器 │ ├── fusion/ # 融合策略 │ └── rerankers/ # 重排器 ├── inference/ # 推理组织 │ ├── query_intent/ # 意图分析 │ ├── sub_query/ # 子查询拆分 │ └── prompt_builder/ # 提示词构造 └── api/ # 接口层5.2 核心代码混合召回与融合排序多路召回的代码核心是并发执行三个检索器再把结果融合。这里贴简化版的实现逻辑from concurrent.futures import ThreadPoolExecutor, as_completed def hybrid_search(query: str, top_k: int 30, weights: dict None): 多路召回向量检索 BM25关键词检索 知识图谱查询 weights weights or {vector: 0.4, keyword: 0.4, graph: 0.2} # 各检索器并发执行 with ThreadPoolExecutor(max_workers3) as executor: future_map { executor.submit(vector_search, query, top_k): vector, executor.submit(keyword_search, query, top_k): keyword, executor.submit(graph_search, query, top_k): graph, } results {vector: [], keyword: [], graph: []} for future in as_completed(future_map): route future_map[future] results[route] future.result() # RRFReciprocal Rank Fusion融合排序 fused_scores {} for route, items in results.items(): for rank, item in enumerate(items): doc_id item[doc_id] rrf_score 1.0 / (60 rank) # RRF标准公式 fused_scores[doc_id] fused_scores.get(doc_id, 0) weights[route] * rrf_score # 融合结果按得分排序并附带来源信息 sorted_docs sorted(fused_scores.items(), keylambda x: x[1], reverseTrue) return [{doc_id: doc_id, fusion_score: score} for doc_id, score in sorted_docs[:top_k]]RRF公式看起来简单但它是多路召回融合里最稳健的选择。不需要重新训练不需要调参不同路的分数分布差异再大也能统一到同一尺度上。如果你想做得更精细可以用加权RRF权重根据查询类型动态调整。上面代码里weights参数其实就是在接意图识别模块的输出。5.3 核心代码重排器链路重排器的代码更简洁但要注意批量推理的显存管理from sentence_transformers import CrossEncoder # BCE-reranker-base_v1对中文场景支持优秀 reranker CrossEncoder(maidalun1020/bce-reranker-base_v1, max_length512) def rerank(query: str, candidates: list, top_n: int 5) - list: Cross-Encoder重排query与doc拼接后精细打分 pairs [[query, doc[content]] for doc in candidates] # 批次处理控制显存占用 batch_size 16 all_scores [] for i in range(0, len(pairs), batch_size): batch_pairs pairs[i:i batch_size] batch_scores reranker.predict(batch_pairs) all_scores.extend(batch_scores.tolist()) # 按重排分数降序截断返回 ranked sorted( zip(candidates, all_scores), keylambda x: x[1], reverseTrue, )[:top_n] return [{doc_id: doc[doc_id], score: score} for doc, score in ranked]这个模块在生产环境里要解决一个很重要的问题重排器吃进去的候选文档最大只能512个token但你的文档块可能更长。我采用的方案是重排时动态按句切割候选内容把超出长度的截断如果候选文档的核心信息被切断再对被截断的文档执行局部检索拿到更精准的段落去重排。这个细节很琐碎但没有它重排质量会打折扣。5.4 核心代码意图识别与子查询拆分提示词意图识别和子查询拆分的质量直接决定复杂问题的答案质量。我用的方案是prompt驱动的轻量分类不单独训练模型成本低、迭代快SUB_QUERY_PROMPT 根据用户问题将其拆分为多个可独立检索的子问题。 规则 1. 仅输出子问题列表不要回答原问题 2. 每个子问题必须是原子问题能独立检索 3. 保留关键限定词时间、地点、主体、条件 4. 如果原问题不需要拆分只输出一个子问题 5. 上限5个子问题用JSON数组格式输出 用户问题{query} def split_query(query: str, llm) - list[str]: 调用LLM生成子问题列表 prompt SUB_QUERY_PROMPT.format(queryquery) response llm.chat(prompt) # 使用支持JSON输出的LLM try: parsed json.loads(response) return parsed if isinstance(parsed, list) else [query] except json.JSONDecodeError: # LLM格式异常时兜底返回原问题 return [query]这里有个血泪教训子查询拆分的prompt里必须强制要求保留时间限定词。之前我让LLM拆分“2023年的销售额和2024年的销售额对比”它直接拆成了“销售额对比”这种丢失时间条件的子问题导致检索结果完全错乱。后来在prompt里把“保留关键限定词”写成了规则3问题才缓解。5.5 实测效果调优前后的量化变化蓝图搭建完成后我拿一组真实业务数据做了对比测试1200份技术文档、约30万条文本块、200条贴近实战的问题集。跑下来有三个关键指标非常能说明问题。指标纯向量检索基线混合检索重排蓝图方案提升幅度召回率1062.5%81.3%18.8%答案准确率人工评测41.2%73.5%32.3%平均单次检索延迟260ms640ms增加380ms延迟增加了来自重排器和多路并发但换来的是准确率翻倍式的提升。对一个知识库系统来说答案准确率远比几百毫秒延迟重要。真正上线时用户甚至感知不到0.6秒和0.3秒的差别但回答错还是回答对用户一秒钟就能感知到。6. 上线之后避坑清单高频问题与排查实录这套蓝图跑起来之后我总结了一份高频问题速查表。这些问题全是在真实环境里踩过的坑比任何理论分析都有说服力。现象排查思路解决方案相似问题答案质量波动大检查切分是否切断关键段落切换到结构感知切分对长段落做二次切分专业名词检索不到向量模型词汇表未覆盖倒排索引增加领域词典扩充同义词表格内容回复错误表格被切成碎片关系丢失表格整体作为一个chunk保留行列头元数据文档更新后旧答案仍出现增量更新漏掉了metadata向量与metadata事务性同步更新长文档召回结果全是中间页切分时丢失了文档级上下文增加文档标题/摘要作为全局检索锚点LLM答非所问但检索结果正常Prompt构造少了指令约束在prompt中加入引用范围限定和拒答指令6.1 最容易被忽视的性能瓶颈解析层的并发控制我在预研时完全低估了解析层的耗时。一篇100页PDF如果是扫描件OCR要跑好几分钟几百篇文档同时进来CPU立刻打满API接口全都跟着卡顿。后来我给解析层加了并发控制和任务队列允许最大4个解析任务并发每个任务消耗的CPU核数动态限制并且把所有解析任务统一放进Redis队列由worker逐个消费。解析任务之间通过任务ID关联状态前端能看到“解析中、已完成、失败、已跳过”四种状态。千万别让解析任务直接跑在Web进程里这是并发事故的源头。6.2 关于“RAG知识库能存图片吗”这类多模态问题的处理很多人在热搜里问“rag知识库能存图片吗”这个词条本身说明传统RAG对多模态内容的支持是真的弱。我现在的蓝图里对图片的处理分两条路一类是图片里的文字信息通过OCR解析后以文本块方式进入知识库这是当前最稳妥、效果最好的方案另一类是纯图像内容比如产品实拍照片、设计稿、图表走势仅靠文字解析损失信息严重需要引入多模态模型做图文联合理解。我的实际处理是解析层把图片切成独立元素先送OCR提取文字再送视觉模型生成描述文本两条结果都写入文档块。用户检索“这个产品的外观长什么样”时视觉描述文本能命中用户检索图片里的某个参数文字时OCR文本能命中。这种做法在已有框架里改动最小不需要专门训练模型效果提升却非常明显。6.3 知识库类型选择的经验别再问二选一了热词里老有人问rag知识库和结构知识库区分以及应用场景我的实践经验是两个都做按查询类型动态路由。向量知识库适合语义模糊的问题、长文本描述比如“公司有哪些关于数据安全的管理规定”知识图谱适合实体关系问题比如“华为的供应链合作伙伴里哪些是上市公司”; 结构化SQL适合精确统计比如“华东区2024年销售额与2023年同比变化”。把这三类索引共存于一套系统里通过意图识别路由到不同的检索通道再融合答案这才是真实业务场景下的完整解。6.4 最后的忠告别过度设计我对所有准备自研RAG的朋友说一句话如果十人以下团队、数据量千级以下先用成熟开源框架把产品跑起来别上来就自研把精力花在业务效果上。自研是因为你对RAG的核心链路有了自己的理解、有必须定制的场景、有足够的工程资源去维护。我的蓝图很大一部分意思是帮你建立“看穿了军备竞赛”的视角——知道开源产品做了什么、为什么做即便你决定用开源框架这些认知也能帮你选型、调优、改造。我自己在最终部署时也不是所有模块都用自研代码索引层重度依赖Qdrant检索层融入了开源重排模型只是核心链路、上下文组装策略和分析逻辑是自研的。框架是别人的但知识检索和业务理解的结合点必须是你自己的。踩过这么多坑之后我对整个自研RAG工程只剩一个执念别追求架构的新奇盯着最终用户拿到的答案质量说话。RAG系统没有银弹多路召回救不了粗糙的切分重排也救不了脏乱的源数据。先把文档解析做扎实先把数据结构定义清楚先把评测集建好你的自研RAG就已经走在了绝大多数项目的跑前面。这套蓝图不是终点它只保证你有一个不会犯低级错误的骨架真正让知识库“好用”的永远是后续一轮轮针对你行业数据特征的打磨。
返回列表