ARTICLE DETAIL

资讯详情

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

RAG项目成败的关键:先搞懂你的数据类型

RAG项目成败的关键:先搞懂你的数据类型 做RAG项目的人最常犯的错不是embedding选得不对也不是没用Rerank而是根本没搞明白自己手里的数据到底该往哪个方向上处理。我接手过几个知识库项目第一轮联调时表现不错一到真实文档集就塌了最后排查下来基本都是数据类型识别出了问题——有人把一堆扫描版PDF直接当成纯文本切块有人把几十列的业务表硬塞进向量库还有人拿重复度很高的监控日志去建索引结果召回的全是同一条信息。这篇文章就是想从“数据类型”这个最底层但最容易被忽视的视角把RAG的设计决策重新捋一遍。不管你是刚开始接触RAG还是已经在调优线上系统弄清楚数据类型与切块、索引、检索、生成之间的关系很多问题根本不用费力排查。1. 为什么数据类型决定了RAG的天花板1.1 一次失败的RAG项目复盘之前有个内部文档问答需求原始材料是几十份产品说明书PDF格式每份大概二三十页。当时图省事直接用PyPDF2抽文本按固定512字符切块塞进向量库完事。首轮测试看起来不错能回答“设备最大功率是多少”这类问题。但换成一问“第三章节中关于故障排除的步骤是什么”答案就开始乱来经常把不同产品的故障码混在一起。复盘时发现问题出在两个地方一是PDF里大量文字其实是表格和流程图标注PyPDF2抽出来的文本顺序是乱的二是固定长度切块把章节标题和小节内容拆散了导致召回时丢失上下文边界。这两个问题本质上是数据类型没有在切块之前被识别和处理。如果当时先把文档分类——纯文本段落、表格、图片注释分开处理结果会完全不同。1.2 数据类型的四个核心维度很多人听到“数据类型”第一反应是int、string、json这些编程概念但RAG场景里看的是另外四个维度。第一是结构化程度。结构化数据数据库表、CSV有明确字段和关系半结构化数据JSON、HTML、XML有嵌套结构但不完全规整非结构化数据PDF文本、邮件正文、聊天记录没有固定模式。结构化程度直接决定了你是做向量检索、关键词检索、还是text-to-SQL。第二是文本长度。一句话和一篇1万字的合同切块逻辑完全不同。短文本可能不需要切长文本必须考虑边界。问题往往在长文本因为切片切得不好语义就碎了。第三是语义密度。一段技术文档和一段营销文案同样1000字前者每个句子都可能承载多个关键概念后者可能几句话才有一个实体。语义密度越高对切块和embedding的粒度要求就越高。第四是模态复杂度。同一份材料里可能混着文字、图片、表格、音视频处理器差异很大。比如图片里的文字不做OCR就相当于这种信息凭空消失。这四个维度组合起来基本能决定你的RAG系统是“简单向量检索LLM”就行还是必须上混合检索、重排、多路召回这一整套工程化方案。1.3 从数据类型推导出“检索方式”和“生成策略”我自己习惯先画一个决策判断表来定RAG的基础形态数据类型特征推荐的检索方式生成策略结构化表格、字段明确关键词过滤 text-to-SQLLLM直接生成SQL或直接查库半结构化JSON/HTML结构解析 向量检索/图检索按结构字段召回后组装上下文非结构化长文本向量召回 BM25关键词召回 Rerank切片重新排序后送入LLM多模态图文混排OCR 多模态embedding文本视觉特征拼接代码仓库/日志语法解析 标识符分块按函数/类级别召回这张表不是一个严格的规则但能把很多项目方案从一开始就拉到正确方向。比如数据是标准的关系型业务表格你却非要把它转成自然语言文档再去做向量检索效果肯定差。反过来如果数据是几十万字的技术手册你只靠BM25关键词匹配很多同义改写就匹配不到。2. 打底常见数据类型图谱与RAG应对方案2.1 非结构化长文本PDF/Word/网页——切块是核心难点非结构化长文本是RAG最普遍的输入也是翻车重灾区。难点主要在解析和切块两件事上。先讲解析。PDF分两种数字版PDF能直接复制文字扫描版PDF本质是图片。很多项目在第一步就把扫描版当成普通PDF处理抽出来全是乱码或空文本。正确做法是先用OCR比如PaddleOCR、Tesseract识别然后再进文本流程。Word文档还要注意页眉页脚、文本框、批注里的内容这些如果不过滤会污染向量索引。再讲切块。固定长度切块最简单但经常破坏语义边界。更可靠的做法是“结构感知切块”按标题层级、段落、句子边界来切。举例来说对于Markdown源文档可以把H1/H2/H3作为切分点对于PDF先抽取出文档大纲再按“标题-内容块”切。切完后建议保留一个结构元数据字段比如“source_section 3.2”这样后来做引用溯源和权限过滤都容易。切块大小怎么定我的经验是通用领域300到500个字比较稳技术文档可以放大到600到800个字代码或结构化字段应该更小甚至一条函数一个块。不过这个参数不是拍脑袋定的要结合你的embedding模型的最大输入长度。如果用OpenAI的text-embedding-3-small单次输入token限制是8191但超过512或1024个token后语义精度会下降。所以一般控制在300到800字之间。另一个常见问题是“重叠overlap”。如果两个相邻块完全无重叠查询“介绍系统架构和它如何实现高可用”时可能“高可用”这个词落在了前一块末尾“系统架构”落在了后一块开头结果两块都召不全。设重叠为10%到20%可以在一定程度上缓解。重叠不是越多越好太多会放大重复内容导致召回结果冗余。2.2 结构化表格数据——不能硬塞向量库表格类的结构化数据最常见的错误是把它序列化成“第一行是...第二行是...”然后交给向量检索。表格的价值在于行列关系直接用embedding会把这种关系打平查询“上季度华东区销售额最高的产品”时向量检索只能靠字面相似度去猜极容易混淆行列。对表格数据主流方案有三类。第一种是text-to-SQL让LLM根据用户问题生成SQL然后在真实数据库上执行。这种方案依赖schema描述和示例需要对表结构做一层语义层映射。第二种是表格转JSON或自然语言描述后做成混合索引先用关键词匹配找到相关表再用LLM做字段对齐。第三种是把表格按“行表头”或“列列名”切块作为辅助召回信息再配合SQL查询结果一起送入LLM。真实项目里第三种通常效果不错因为它既保留了结构化查询的精确性又能给LLM足够上下文。当然也有纯表格组成的知识库。处理时我会先把表头、单位、枚举值这些信息抽取出来做成元数据然后在检索阶段先用元数据过滤掉无关表再在候选表内做细节匹配。比如一张“2024年销售明细表”和一张“员工考勤表”如果问题跟销售相关第一步就应该通过元数据把考勤表过滤掉而不是让embedding去大海捞针。2.3 半结构化JSON/知识图谱——GraphRAG和Hybrid RAG半结构化数据最常见形态是JSON、HTML、XML也包括带层级关系的知识库节点。这类数据的处理思路是保留结构而不是强行拍平。比如一个JSON对象“用户”和“订单”之间存在明确的嵌套关系用普通文本切块会失去这层语义。更合理的做法是先把JSON解析成树状结构每个节点一个块同时在元数据里记录父节点路径。这样查询“这个用户最近的订单状态”时可以先定位用户节点再按父路径召回订单节点。如果数据本身实体关系复杂比如多个业务对象互相引用GraphRAG会更合适。构建知识图谱时先把实体和关系抽取出来存到图数据库再在检索时用“先向量召回实体、再图遍历扩展”的方式。GraphRAG特别适合“关系查询”和“多跳推理”比如“A部门的项目涉及哪些供应商这些供应商有没有被处罚记录”。但GraphRAG的项目门槛不低实体抽取质量直接影响下游建议只有在数据实体关系确实复杂、且普通向量检索明显不够用时才上。Hybrid RAG是另一种实用组合本质是“向量检索关键词检索”的多路召回融合。向量检索擅长语义相似关键词检索擅长精确匹配比如产品型号、错误码、编号这类信息向量模型经常会把它们当成噪声但BM25能精准命中。实际项目中我会把两种结果做加权融合RRF或简单加权再统一交给Rerank。这样既能找回同义改写又能保证精确词不丢失。2.4 图片与多模态数据——OCR多模态embedding现在很多知识库不仅包含文字还包含图片、截图、流程图、扫描件。如果图片里的文字是关键信息必须先做OCR。OCR完成后的文本可以走普通RAG流程但要注意坐标信息——有时需要把识别出的文本块和它在图片中的位置对应起来否则如果用户问“这张图的标题是什么”我们无法回答。如果图片内容本身是重要的视觉信息比如产品设计图、趋势折线图则必须用多模态embedding模型比如CLIP、SigLIP把图片编码成向量和文字向量对齐。查询时用户问“图片里库房布局是怎样的”通过视觉embedding能找到图片但回答时LLM并不能直接“看”图除非用多模态LLM如GPT-4o、Gemini、Qwen-VL。所以多模态RAG的完整链路是图片先经过视觉编码进索引检索出图片候选后再把图片本身传给支持视觉输入的生成模型。做这个方向时容易忽略一个细节图片切块。一张很大的架构图只切一块embedding可能只捕捉到全局风格细节就丢了。实践上可以结合目标检测把图片中关键区域截出来每个区域单独做向量同时保留原图引用。这样用户问“图中负载均衡器后面有几个后端服务”时能定位到具体区域而不是整张图。2.5 代码与日志——特殊解析器和召回策略代码和日志在RAG场景里经常出现比如企业内部研发助手、运维知识库。这类数据的类型特征和自然语言差异很大。代码不能简单按字符数切块否则很容易把一个函数切成两半。正确做法是按语法树切块以函数、类、代码块为最小单元同时保留文件路径和注释信息。如果项目用了LangChain可以组合使用特定语言的解析器或者调用tree-sitter这类语法分析库。日志数据的难点在于高重复度和高噪声。同一台服务器的报错信息可能一天出现几万次直接建向量索引会让召回结果高度集中。处理时先做聚合去重把相同pattern的日志聚合成一条样本并保留出现时间、频次等作为过滤条件。召回策略上也建议先用时间范围关键词过滤再进入向量检索否则用户问“最近一次OOM是什么时候”向量检索并不擅长按时间排序这类操作。3. 从数据类型推导RAG系统架构3.1 单路召回什么时候够用很多科普RAG的文章一上来就推一套完整的“切块-向量化-向量检索-重排-生成”链路对很多小项目来说是杀鸡用牛刀。单路向量召回在什么场景够用我总结了几个条件数据总量不大比如百万字以下、问题语义比较丰富不是精确词匹配、文档结构相对规整没有太多表格和图片、权限控制不复杂。满足这些条件直接单路向量召回也能跑得不错。但单路召回最怕的是“双关词”和“同义词歧义”。比如“苹果”既可能是水果也可能是品牌向量模型会把两种含义搅在一起。如果业务场景里这种词特别多至少应该加上词典限制或关键词纠偏。3.2 多路召回向量召回BM25为什么是标配虽然单路召回能用但生产级RAG我强烈建议上多路召回。原因很简单embedding模型不是万能的。准确编号、专有名词、型号、缩写这些对向量模型不友好比如“CU-3042”和“CU-3043”在向量空间里距离非常近但它们是两个完全不同的部件。BM25对这类精确token匹配却非常准。多路召回常见结构是两个或更多检索通道并行比如“向量召回Top20 BM25召回Top20”然后用RRF算法合并。RRF公式不复杂score 1/(60rank)把每个通道的排名映射成分数再相加。这么做的好处是让精确匹配通道里排名靠前的结果有更高的权重同时保留语义通道的结果作为补充。多路召回还包含一种特殊场景元数据过滤。如果权限卡控是强需求比如不同部门只能看本部门文档那么先通过元数据过滤缩小候选集再在多路召回里选结果比召回后再硬过滤效果稳定得多。后面章节会单独讲权限卡控。3.3 Rerank的必要性与触发条件Rerank重排是一个让人又爱又恨的组件。它的作用是把召回回来的几十个候选片段用更精细的模型如bge-reranker、Cohere Rerank重新打分排序把真正和问题相关的文本提到前面。它对长文本、强指代、多意图问题特别有效。但Rerank会引入额外延迟和成本不是所有项目都必须要上。我判断是否要上Rerank主要看三个条件数据文档是否很长长文档中相关性分布在局部容易漏排问题是否包含大量限定词如“2024年第四季度华东区的销售额”以及召回结果是否经常出现“相关但顺序不对”的现象。如果都中就上。如果只是简单FAQTop1基本正确Rerank意义不大。Rerank还有个容易被忽略的作用它可以把“上下文窗口”用得更高效。比如向量召回Top20每段500字全塞给LLM可能超长。用Rerank先选Top5再拼装上下文能省很多token也能减少噪声。我在政务类知识库里常这么干因为文档长、查询点多直接送Top20效果反而不稳。3.4 权限卡控元数据过滤与检索层隔离RAG的权限卡控有时比检索效果还重要尤其在企业内部或政务场景。最基本方案是在文档入库时给每份文档打上权限标签比如部门编码、密级、可见范围。检索时先从用户身份中解析出权限集合然后用权限过滤条件或安全查询层从向量库中预过滤。实现上有几种选择。简单场景下在向量数据库的过滤条件里写上权限字段召回前先减掉无权访问的文档复杂场景下把文档和用户权限分开在两个系统里RAG服务先向权限服务查询用户可见的文档ID列表再把这些ID作为过滤项传给向量库。后者更稳但每次请求要多一次权限查询需要缓存。我踩过的坑是权限标签打错粒度。如果只把权限打到文档级而文档内包含跨部门内容那么检索结果仍然可能泄露片段信息。更稳妥的做法是权限打到“章节或块”粒度但运维成本会很高。折中方案是文档级权限过滤 生成阶段用提示约束“只回答当前用户有权访问的内容”并按需对生成结果做二次校验。3.5 查询改写从“用户说的”到“数据表达的”数据类型不同用户问法可能和数据表达方式完全不同。比如用户问“最近有没有设备报警”数据里存的是“告警记录”或“AlarmEvent”。如果不做查询改写向量检索可能匹配不上。查询改写常见做法是让LLM先对用户问题进行扩展、去指代或转换成语料中更可能出现的形式。具体操作时我会先定义一个改写模板比如“把用户问题转成更适合检索的查询语句保留专有名词和编号必要时拆分成多个子查询”。子查询用于多路召回中每条子查询分别跑检索再合并结果。这个过程很依赖提示词设计建议在冷启动阶段人工标注30到50条改写前后的例子再用Few-shot方式让LLM模仿。查询改写要注意不要过度改写。用户问“系统卡顿怎么办”你改成“系统卡顿原因分析解决方案”语义没变但可能引入更多无关候选。改写的目标不是变得更长而是更接近文档里的表达习惯。4. 实操一套可复用的RAG构建流程4.1 第一步数据摸底与分类不要一上来就写代码。先花半天时间把数据源梳理清楚建一个数据清单列表至少包含文件名称、数据格式、大概大小、是否有表格/图片、是否有扫描页、更新频率、是否需要权限控制、语言类型。这个清单就是后续一切方案的基础。举个例子我做过一个政务知识库项目输入是政策文件、办事指南和咨询工单。只看表面都是PDF和Word实际上政策文件是长文本办事指南是半结构化流程咨询工单是短文本带高重复度。如果统一用一种切块方式效果必定参差不齐。我们最后把数据分成三种类型分别用不同parser再合并进统一索引才把效果提上来。4.2 第二步分块参数怎么定分块策略不是全局统一的但也不是每个文档一套参数尽量控制在3到5套模板以内。比如“长文本模板分隔符一标题或段落块大小600字重叠10%”“短文本模板每条FAQ或每条聊天记录单独一块”“表格模板按行表头切块不做上下文重叠”。实操时先用测试集验证。选20到50个典型问题把分块后的文档建立索引跑一轮检索看Top3召回是否包含正确答案。如果答案总是被切散就增加块大小或让分隔符优先级更高如果召回结果包含大量无关片段就缩小块大小。这个实验要多跑几轮直到找到在你的数据和模型下的平衡点。4.3 第三步embedding模型选择与向量化embedding选择要考虑语言、领域和硬件。中文场景下bge-series、m3e、text-embedding都是常见选择如果追求更强领域效果可以用领域微调的模型。硬件上如果并发不高、模型量不大普通CPU就能跑但延迟会高建议还是上GPU或直接调用API。向量化时要特别注意batch大小和文本长度。很多模型在超过512或1024 token时精度明显下降因此切块大小不应超过模型上限。我还习惯在向量化之前做文本清洗比如去除重复空行、标准查引号、过滤HTML标签这些脏数据不会让模型崩溃但会稀释向量质量。清洗前后我对Top5召回准确率做过对比普遍能提升3到8个百分点。4.4 第四步索引设计与召回调试索引设计不是简单建个collection就完事。以Milvus或Qdrant为例我会按业务维度设计Fieldid、text、metadata、permission、source_type、created_at。其中permission和source_type要设置为可过滤字段。召回调用时先加过滤条件比如“permission IN [user_permissions] AND source_type IN [policy,guide]”再做向量检索。调试召回时不要只看准确率还要看“召回结果多样性”。有时Top10全是同一篇文档的不同片段说明切块重叠太高或embedding区分度不够。这时可以试试MMRMaximum Marginal Relevance算法在后处理时让结果在相关性和多样性之间平衡。MMR参数lambda通常取0.7到0.9lambda越大越偏相关性越小越偏多样性。4.5 第五步生成策略与提示词模板RAG的生成阶段不是简单把召回片段拼起来就完事。提示词里至少要包含三个要素用户原始问题、召回片段、输出约束。输出约束包括“如果召回内容中没有明确答案请回答不知道”“只使用给定内容回答不要编造”“回答问题前先总结相关片段”。我一般还会在提示词中加入“引用来源”要求让LLM按照“内容参考文献文件名/章节”格式回答。这样既方便用户判断答案可信度也方便后续做答案审计。实践中这对降低幻觉率帮助很大因为模型知道要标注来源时会更倾向于引用已有内容而不是自由发挥。4.6 第六步评估指标与回归测试RAG评测比一般模型评测麻烦因为检索和生成都要测。建议至少建立两组评估一组是“检索评估”用人工标注问题到标准答案片段关联指标可以看RecallK另一组是“生成评估”看答案是否准确、完整、有来源。生成评估最好让领域专家打分而不是只用自动指标因为很多表面答案正确关键细节却错了。每次修改分块参数、召回策略或提示词后都要跑一遍回归测试。我习惯把历史所有测试问题的结果记录下来做对比。如果新方案在某个问题上效果变差了要能快速定位是检索环节还是生成环节的问题避免“改了一个参数别的环节崩了”这种连锁反应。5. 常见问题与排查实录5.1 问题速查表问题现象可能原因排查建议答案张冠李戴切块切碎章节边界召回内容跨章节改用结构感知切块按标题层级分割精确编号查不到向量模型对短编码不敏感加BM25关键词召回或做编号增强表格数据答错列表格被打平成自然语言用text-to-SQL或按“行表头”切块图片文字无法检索扫描件未OCR接入OCR流程再把识别文本入索引召回结果全是同篇文档片段重叠比例过高或embedding区分度不足降低重叠使用MMR提高多样性用户有权限但搜不到内容权限过滤字段没正确设置检查过滤条件是否与权限标签一致长文档答案漏细节块太小或Rerank没上增大块大小增加Rerank模型日志问题答案重复日志重复度太高先聚合去重再建索引这张表不是万能的但绝大多数RAG性能问题最后都能倒推到“数据形态”和“检索策略”的匹配度上。5.2 咬文嚼字为什么召回结果总是乱序很多人遇到过这个问题向量检索Top5看起来都对但排第一的不是最相关的。原因通常是embedding模型的匹配粒度比较粗用户问题短向量空间里“语义相近”和“问题可回答”并不完全等价。比如问“设备坏了怎么修”文档里一段完整维修步骤可能和另一个案例里的“设备坏了”字样更相似反而比正版维修手册片段排名更靠前。这个问题的解法不是只调embedding而是组合方案。一是查询改写让问题更接近文档表达。二是引入Rerank让精排模型在粗排结果中重新打一遍分数。三是把召回数量从Top5调到Top20这样即使粗排顺序不对候选里也包含正确答案靠Rerank再提上来。顺序不对并不可怕怕的是正确答案压根没进候选集。5.3 实战中的小技巧分享几个我实际用下来很简单但有效的小技巧。第一个是给每个块打上唯一ID和来源路径。所有后续的权限过滤、引用溯源、结果展示都依赖这两个字段一定要提前设计好。第二个是embedding之前把文本里的超长字符串或连续数字替换成占位符。比如把所有长串数字替换成“NUM”可以减少噪声但在替换前要把原始值存到元数据里否则答案里没法保留真实编号。第三个是测试集不只准备标准问法还要准备口语化问法。比如文档里写“注销登记”用户可能会问“取消注册”RAG系统是不是能把两者关联上测试一次就知道。第四个是定期下线无效文档。数据源会更新旧版本和重复内容如果不清理会不断污染检索结果。我做了一个简单的文档指纹去重SimHash每周跑一次效果立竿见影。结尾最后说点个人体会。RAG项目的复杂度不在模型而在数据。很多时候把数据分类、清洗、切块、索引这四件事做扎实了模型用最普通的也有不错表现。反过来如果数据类型没认清堆再多Rerank和查询改写都是在修补一个错误的底座。我每次启动新项目都会先逼自己回答一个问题“这批数据到底是结构化、半结构化、还是非结构化里面的表格和图该单独处理吗”回答清楚了后面每一步都有据可依。这个习惯救了我很多次也希望对你有用。
返回列表