RAG 召回差,别先换 Embedding:从维度错误到重建索引的完整排错法
RAG 系统最让人困惑的故障,往往不是服务彻底报错,而是“看起来每一层都成功,答案却不对”。文档已经导入,向量库能返回结果,生成模型也正常输出,但最应该被召回的段落没有出现;或者更换 Embedding 模型后写入突然失败,查询结果从稳定相关变成似是而非;再或者 Dify 的知识库测试能搜到内容,放进工作流后却答非所问。
遇到这些现象,很多团队会直接换更大的 Embedding 模型、调高 TopK,甚至把责任归到生成模型上。这样做的问题是:RAG 是一条多阶段链路,任何一层都可能让相关证据消失。没有先确定故障在哪一层,调参只会让变量越来越多,最终无法解释“为什么这次变好了、下次又变差”。
本文围绕三个实际问题展开:Embedding 维度不一致为什么会导致写入或检索失败;更换 Embedding 模型后什么情况下必须重建索引;RAG 召回差时怎样区分切分、Embedding、过滤条件、近似检索和排序问题。
先给结论:维度相同不等于向量空间兼容
先记住四个结论。
第一,同一个向量字段或同一个向量集合通常要求固定维度。库里定义的是 768 维,写入 1024 维向量,系统没有足够信息自动“补齐”或“截断”,因为任何擅自修改都会改变向量的语义。因此,维度不一致首先表现为结构错误。
第二,维度一致只能说明两个数组长度相同,不能说明它们可以比较。两个不同 Embedding 模型都输出 768 维,也可能把同一句话映射到完全不同的坐标系。把模型 A 生成的文档向量与模型 B 生成的查询向量计算余弦相似度,数学上能算,语义上却未必有意义。
第三,只要文档向量的生成模型、模型版本、输出维度、归一化方式或前处理规则发生实质变化,就应把它当成一次索引迁移,而不是普通配置修改。最稳妥的做法是保留旧索引,新建版本化索引,用新配置重新生成全部文档向量,再用同一批标注问题做对照测试。
第四,RAG 召回差不能只看最终回答。必须先看解析后的原文,再看 chunk,再看候选召回,再看过滤后的结果,再看重排结果,最后才看生成回答。
一、把 RAG 拆成七层,先定位再优化
一个最小 RAG 链路可以拆成七层:
- 文档解析:从 PDF、Word、网页或数据库中提取文本和结构。
- 清洗与切分:去掉噪声,按标题、段落、表格或固定长度形成 chunk。
- 文档向量化:用指定 Embedding 模型为每个 chunk 生成向量。
- 存储与索引:保存向量、原文、文档 ID、章节路径和过滤字段。
- 查询与召回:用同一套 Embedding 配置生成查询向量,执行向量、全文或混合检索。
- 过滤与重排:应用租户、权限、时间、分类等过滤条件,再用 Rerank 调整候选顺序。
- 生成回答:把最终证据交给对话模型,要求它基于证据回答。
这七层中,前三层决定“可检索材料长什么样”,中间三层决定“哪些材料能到达模型”,最后一层决定“模型怎样使用材料”。
因此,向量库返回结果但回答仍错误,至少有三种不同的可能:
- 召回结果本身不相关;
- 召回结果相关,但被重排或上下文裁剪挤掉;
- 证据正确进入提示词,但生成模型忽略、误读或超出证据作答。
排错时应为每一层保留可观察证据。文档解析层要能查看抽取文本;切分层要能查看 chunk 边界与元数据;向量层要记录模型标识、维度和配置版本;检索层要记录查询、TopK、分数、过滤条件与候选 ID;重排层要保留重排前后顺序;生成层要记录实际送入模型的证据 ID,而不是只保存最终答案。
这里的“记录”不等于把所有原文和凭据写入日志。生产环境可以记录文档 ID、chunk ID、模型配置版本、分数、耗时和请求 ID。涉及敏感正文时,应根据业务要求做摘要、哈希或受控采样。API Key 只能使用YOUR_API_KEY或环境变量,不能出现在截图和公开日志里。
二、Embedding 维度不一致为什么会失败
Embedding 的输出可以理解为固定长度的数值数组。向量数据库需要知道数组长度,才能分配存储、建立索引并执行距离计算。
Qdrant 官方文档说明,同一 collection 中的向量必须具有相同维度,并使用对应的距离度量;pgvector 可以用vector(3)这种类型明确约束三维向量;Elasticsearch 的dense_vector字段也有dims参数,用于定义向量维度。
这意味着“维度”不是一条备注,而是向量字段的结构定义。假设索引期望四维向量:
document = [0.12, -0.08, 0.31, 0.44] query = [0.07, -0.02, 0.28, 0.39]两个数组可以按对应位置计算点积、余弦距离或欧氏距离。如果查询变成五维:
query = [0.07, -0.02, 0.28, 0.39, 0.15]第五个值没有对应的文档坐标。数据库拒绝请求,比静默截断更安全。静默截断虽然能让请求继续,但会改变模型原本定义的表示,召回结果无法解释。
常见触发方式
维度错误通常不是向量库凭空出现问题,而是配置发生了漂移。
- 导入文档时使用模型 A,查询时环境变量切到了模型 B。
- 开发环境和生产环境的模型别名相同,实际解析到不同版本。
- 调用方启用了可选的输出维度参数,索引创建脚本仍使用默认维度。
- 一部分任务走新配置,一部分后台重试仍走旧配置。
- 数据迁移脚本把向量序列化、截断或解析错了。
- 多个知识库共用一个物理表,却没有按模型与维度隔离索引。
最小检查不要猜维度
不要从模型名称推测维度,也不要从某篇教程复制数字。应直接对当前运行配置发起一次最小 Embedding 调用,读取返回数组的实际长度,再与目标索引的 schema 对照。
应用层可以在写入前增加硬校验:
functionassertVectorShape(vector,expectedDimensions,label){if(!Array.isArray(vector)){thrownewTypeError(`${label}不是数组`);}if(vector.length!==expectedDimensions){thrownewError(`${label}维度不一致:expected=${expectedDimensions}, actual=${vector.length}`);}if(vector.some((value)=>!Number.isFinite(value))){thrownewError(`${label}包含非有限数值`);}}constschema={embeddingModelConfig:"embedding-config-v2",dimensions:768,distance:"cosine"};constvectorFromProvider=newArray(768).fill(0);assertVectorShape(vectorFromProvider,schema.dimensions,"document_embedding");这里的768只是演示校验逻辑的示例值,不代表任何具体服务或模型。生产代码应从受版本控制的索引配置读取预期值,并把实际模型配置标识与索引版本一起记录。
能写入但检索差,仍可能是空间不一致
最危险的情况不是直接报错,而是两个模型输出维度刚好相同。数据库接受查询,距离函数也能返回数字,但这些数字不能证明语义相似。
可以把它类比为两张不同城市的坐标图:两张图都有横纵坐标,不代表同一组坐标指向同一个地点。
所以,排查维度之后还要核对:
- 文档与查询是否使用同一模型配置;
- 前处理是否一致;
- 是否采用同一种归一化约定;
- 距离函数是否符合模型用法;
- 查询端是否增加了不同的指令或前缀;
- 是否把不同模型的向量写进同一个未隔离字段。
三、更换 Embedding 模型后,什么时候必须重建索引
判断是否重建,不应只问“维度变没变”,而要问“文档向量与查询向量是否仍由同一个可比较的映射生成”。
必须重建或重新向量化的情况
出现以下任意一种变化,都应重新生成文档向量,并建立新的索引版本:
- 更换了 Embedding 模型或供应方;
- 模型版本发生变化,且供应方没有明确保证向量兼容;
- 输出维度发生变化;
- 文档向量与查询向量使用的指令模板发生变化;
- 归一化、池化、量化或前处理方式发生变化;
- chunk 文本发生变化,例如重新解析、重新切分或加入标题路径;
- 距离度量发生变化,但没有完成适配验证。
即使新旧模型维度完全相同,也应默认不兼容,除非模型提供方给出明确的兼容承诺,并且你用自己的标注集验证通过。不要用“请求没报错”替代兼容性证明。
不一定需要全量重建的情况
如果模型、维度、前处理、chunk 文本与距离度量均未改变,只是修改了 TopK、分数阈值、过滤条件、Rerank 配置或生成提示词,通常不需要重新生成文档向量。
如果只是文档新增或少量更新,可以只为发生变化的 chunk 重新计算向量。前提是能够用稳定的内容哈希识别变化,并且索引中保存了文档版本、chunk 版本和 Embedding 配置版本。
没有版本信息时,很难证明某个向量由哪套配置生成,最终往往只能全量重建。
推荐用蓝绿索引迁移
生产系统不应直接覆盖旧索引。更稳妥的流程是:
- 冻结并记录旧配置,包括模型标识、维度、距离函数、切分规则和过滤字段。
- 创建新索引,例如
kb_2026_07_embedding_v2,不要复用旧索引名称。 - 从原始文档重新解析或读取可信 chunk,为每个 chunk 生成新向量。
- 校验文档数、chunk 数、向量数、维度、空向量和失败任务。
- 使用固定标注集分别查询新旧索引,比较 Recall@K、MRR 或 nDCG。
- 让少量真实流量同时执行影子查询,只记录结果差异,不立即影响用户答案。
- 确认质量与稳定性达标后,把读流量切到新索引。
- 保留回滚窗口,确认没有旧消费者后再清理旧索引。
Qdrant 官方文档给出了一种命名向量迁移思路:为新模型增加新的 named vector,在后台重新为数据生成新向量,完成填充与验证后再移除旧向量。
不同数据库的具体操作不同,但原则相同:新旧空间隔离、全量补齐、可对照、可回滚。
迁移期间不要把一部分文档更新为新向量,却继续让所有查询只走一个无版本字段。这样会形成“混合空间”,同一个问题可能因为后台任务刚执行完而突然改变,难以复现。
四、RAG 召回差,按层排查而不是一起调参
第 1 层:原始内容真的被解析出来了吗
先在解析结果中搜索答案原文。PDF 的扫描页、双栏排版、页眉页脚、表格、脚注和图片文字都可能导致抽取失败。页面上肉眼可见,不代表解析器得到同样内容。
最小验证方法是选一个明确事实,然后完成三次搜索:
- 在原文件中搜索;
- 在解析后的纯文本中搜索;
- 在 chunk 列表中搜索。
如果第二步就找不到,Embedding 和向量库都不可能把它召回。
表格尤其需要检查。很多解析器会把列顺序打乱,把表头与数据行分离。此时 Embedding 得到的不是完整事实,而是失去字段关系的数值片段。
解决方向可能是保留 Markdown 表格、把表头复制到每个数据块、按行生成结构化文本,或为表格使用专门解析流程。
第 2 层:chunk 是否包含一个可回答的最小单元
chunk 太大时,一个向量要同时代表多个主题,查询信号会被稀释;chunk 太小时,答案所需的条件、主语、单位和例外条款会被拆开。
Dify 官方文档把 delimiter、最大长度与 overlap 列为核心切分参数,并提供父子分段模式:小的 child chunk 用于精确匹配,较大的 parent chunk 用于返回完整上下文。
检查 chunk 时不要只看平均长度,而要抽查边界:
- 标题是否与正文分离;
- 代码块是否被拆成两半;
- 问答对是否被拆开;
- 列表前的限定条件是否丢失;
- 表格表头是否仍在;
- 跨段落指代是否还能理解。
一个实用标准是:把 chunk 单独交给不知上下文的人,他能否说出这段在讲什么、对象是谁、结论有哪些限制。如果不能,检索即使命中也难以支持回答。
Overlap 不是越大越好。适量重叠能保留跨边界语义,过大则会产生大量相似 chunk,挤占 TopK,使检索结果看似很多,实际都来自同一段。
第 3 层:查询和文档是否使用同一 Embedding 配置
对一个已知 chunk 做最小测试:
- 直接读取它的文档向量;
- 用与生产完全相同的查询配置生成查询向量;
- 记录两者维度、模型配置版本和距离;
- 执行不带业务过滤的精确搜索。
如果已知相关 chunk 在精确搜索中仍排得很低,问题更可能在 Embedding、查询表达或 chunk 内容。如果精确搜索表现好,近似搜索表现差,则应检查 ANN 参数、索引构建状态和候选规模。
还要测试用户真实表达,而不只是把原文句子复制成查询。真实问题可能包含缩写、口语、错别字、产品别名和跨语言表达。只用“原文查原文”测试,会高估召回质量。
第 4 层:过滤条件是否把正确答案排除了
过滤错误经常被误判为 Embedding 问题。常见情况包括:
- 租户 ID 类型不一致;
- 文档状态仍是
draft; - 时间范围使用错误时区;
- 分类字段大小写不同;
- 权限标签缺失;
- 数组字段按字符串比较;
- 默认过滤器仍保留旧项目 ID。
排查时先执行一次“无过滤查询”,再逐项添加过滤条件。每加一项都记录候选数量。
如果无过滤时能命中,加上某个条件后消失,问题已经定位,不需要换模型。
在多租户系统中,不能为了排错长期关闭权限过滤。正确做法是在隔离的测试数据集或受控管理员工具中比较,并记录谁执行了无过滤测试。
第 5 层:近似索引是否牺牲了过多召回
HNSW、IVFFlat 等近似索引用速度换取召回率。参数过于保守、索引未完成构建、数据量太小、过滤在近似候选之后执行,都可能让结果数量不足。
pgvector 官方说明,带过滤条件的近似索引查询可能因为过滤在索引扫描后应用而返回更少结果,并提供 iterative index scans 等机制继续扫描候选。
这个事实提醒我们:看到“TopK 设置为 10,却只返回 3 条”时,不要立刻断言库里只有 3 条相关数据,要同时检查过滤、候选列表、空向量、删除记录和索引参数。
验证 ANN 的办法是对一个可控子集同时执行精确搜索与近似搜索。精确结果作为基线,统计近似搜索是否找回基线前 K 条。若差距明显,再调索引参数;如果两者都差,继续回到 chunk 与 Embedding。
第 6 层:TopK、阈值与 Rerank 是否互相冲突
提高 TopK 会增加候选覆盖,但也会把更多噪声送入后续步骤;提高分数阈值会减少噪声,却可能把低分但关键的长尾答案过滤掉。
不同模型、不同距离函数的分数尺度可能不同,不能把某个知识库的阈值原样复制到另一个知识库。
Dify 文档区分向量检索、全文检索和混合检索,并允许在候选上使用 Rerank。
遇到产品编号、错误码、配置键、专有名词时,全文检索可能保留精确词面;遇到同义表达和自然语言问题时,向量检索更有优势;混合检索则可以同时获取两类候选,再统一排序。
调试时一次只改一个变量。先固定 chunk 与 Embedding,比较向量、全文和混合检索;再固定检索方式,比较 TopK;最后测试 Rerank。否则无法知道改善来自哪一项。
第 7 层:召回正确,为什么答案仍然错误
如果正确 chunk 已进入最终上下文,问题就不再是“召回差”。需要检查:
- 证据是否在上下文裁剪时被删除;
- 提示词是否明确要求只依据证据;
- 多个证据是否互相冲突;
- 模型是否误读表格;
- 回答是否要求知识库没有提供的计算或推理;
- 系统提示与用户提示是否覆盖了引用规则。
建议在调试界面同时显示“最终送入模型的证据”和“模型回答”。
如果正确证据不在输入里,回到检索与上下文组装;如果证据在但答案错,再处理生成提示、模型能力和答案校验。不要用换 Embedding 修复生成层问题。
五、用小型标注集评估,不再凭单次问答感觉
只拿一个问题测试,很容易得到偶然结论。最低限度应建立一个小型标注集,先从 20 到 50 个真实问题开始。
问题要覆盖不同类型:
- 精确编号;
- 自然语言同义问法;
- 跨段落条件;
- 表格数据;
- 否定条件;
- 时间范围;
- 长尾术语;
- 无答案问题。
每个问题至少记录:
- 问题文本;
- 预期命中的文档 ID 或 chunk ID;
- 可接受的替代证据;
- 是否有唯一答案;
- 业务重要级别;
- 无答案时系统应该怎样响应。
不要只保存一段标准答案,因为检索评估关心的是“证据有没有被找到”。
Recall@K
Recall@K 适合回答“前 K 个结果里,相关证据有没有出现”。
如果一个问题有两个相关 chunk,Top5 找到其中一个,召回并不等于完全成功。具体计算方式要根据标注粒度定义,并在团队内保持一致。
MRR
MRR 关注第一个相关结果出现的位置。相关结果排第 1 与排第 10,对后续上下文质量影响很大。
用户问题通常希望最关键证据尽早出现,因此 MRR 能补充 Recall@K 只看“出现没出现”的不足。
nDCG
当相关性不是简单的相关或不相关,而是存在“核心证据、辅助证据、弱相关”多个等级时,可以使用 nDCG 评价排序质量。
BEIR 论文使用多种任务与数据集说明,检索模型在不同领域和任务上的表现并不一致。因此不能只看公开排行榜,必须用自己的语料和查询测试。
分开评价检索和回答
微软的 RAG 评估文档把文档检索与最终回答评价分开:检索层比较候选与标注证据,系统层再看 groundedness、relevance 和 completeness。
这个分层很重要。检索正确但回答错误,与检索失败却被模型常识“猜对”,是两种完全不同的风险。
建议每次实验至少输出两张表:
- 每个问题的 TopK chunk、分数与是否命中;
- 最终回答是否基于证据、是否完整、是否出现证据外内容。
这样才能判断下一轮应改检索还是改生成。
六、Dify 知识库的可复现排错步骤
使用 Dify 时,可以按下面顺序排查,避免在多个页面来回改参数。
第一步,打开文档的 chunk 预览,搜索答案原文。确认标题、表格、问答对和限制条件没有被错误拆开。若原文不在 chunk 中,先修解析与切分。
第二步,记录当前知识库的切分模式、delimiter、最大长度、overlap、索引方式和 Embedding 配置。不要只截图模型显示名称,最好同时记录知识库 ID、配置修改时间与测试问题。
第三步,使用知识库的检索测试功能,不经过工作流和生成模型,直接查看候选 chunk。先测试原文关键词,再测试用户真实问法。
第四步,暂时使用最少过滤条件验证基线,然后逐项恢复元数据过滤。每次只添加一个条件,观察正确 chunk 在哪一步消失。
第五步,固定其他参数,分别测试向量检索、全文检索与混合检索。包含错误码、函数名、字段名的查询要特别关注全文召回;口语化、同义表达和跨语言查询要关注语义召回。
第六步,调整 TopK 与阈值时使用同一批问题。不要只看某个问题是否“好像更准”,要统计整体 Recall@K 和首个相关结果位置。
第七步,启用 Rerank 前保存原始候选顺序,启用后保存重排顺序。如果正确 chunk 在召回阶段排名靠前、重排后反而下降,问题在 Rerank 或其输入,不应继续调 Embedding。
第八步,回到应用或工作流,查看最终传给模型的上下文。确认知识检索节点输出没有被后续模板、条件分支、变量映射或上下文长度限制截断。
如果更换 Embedding 配置,不要假设旧知识库会自动获得新向量。应查看产品当前文档和界面提示,确认是需要重建知识库、重新处理文档,还是支持版本化迁移。
无法确认时,创建新的测试知识库进行对照,比直接覆盖生产知识库更安全。
七、自建 RAG 的配置契约:让错误尽早暴露
自建系统应把 Embedding 配置写成索引契约,而不是散落在环境变量和代码默认值里。
{"index_version":"kb-support-v3","embedding_config_id":"embed-config-2026-07","dimensions":768,"distance":"cosine","chunking_version":"manual-parent-child-v2","document_schema_version":4,"created_at":"2026-07-01T00:00:00Z"}应用启动时应读取这份契约,并与查询端实际配置比较。不一致时直接拒绝启动或拒绝查询,不要让不兼容向量进入线上结果。
每个向量记录还应保存:
document_idchunk_idcontent_hashembedding_config_idchunking_version
这样才能支持增量重建和故障追踪。
对于生成回答阶段,如果使用 OpenAI 兼容的 Chat Completions 客户端,需要明确区分三个地址层级:
- 服务根地址:
https://api.vectorengine.cn - 兼容接口前缀:
https://api.vectorengine.cn/v1 - 完整对话接口:
https://api.vectorengine.cn/v1/chat/completions
这些地址只用于说明生成层的请求路径,不能据此推断存在某个 Embedding 模型、向量接口、模型数量、价格、并发或 SLA。
一个最小生成请求可以写成:
curlhttps://api.vectorengine.cn/v1/chat/completions\-H"Authorization: Bearer$VECTORENGINE_API_KEY"\-H"Content-Type: application/json"\-d'{ "model": "YOUR_MODEL_ID", "messages": [ { "role": "system", "content": "仅根据提供的检索证据回答;证据不足时明确说明。" }, { "role": "user", "content": "问题:如何处理维度不一致?\n证据:同一向量索引要求固定维度。" } ] }'YOUR_MODEL_ID必须替换为已经确认的真实模型标识,不能根据界面名称猜测。
示例没有声称该服务提供 Embedding 能力;检索层使用什么 Embedding 服务,应单独核验、单独配置、单独记录版本。
八、常见症状与定位表
| 症状 | 优先检查 | 不要先做什么 |
|---|---|---|
| 写入时报维度不匹配 | 实际向量长度、索引 schema、文档端模型配置 | 不要手工补零或截断 |
| 查询时报维度不匹配 | 查询端模型、环境变量、索引版本路由 | 不要只修改数据库维度 |
| 换模型后不报错但召回骤降 | 新旧模型是否同空间、是否混用旧文档向量 | 不要因维度相同就认为兼容 |
| 原文存在但检索不到 | 解析文本、chunk 边界、查询改写、Embedding | 不要直接调高 TopK |
| 无过滤能命中,加过滤后消失 | 租户、权限、时间、分类字段与数据类型 | 不要关闭生产权限过滤 |
| 精确检索好,近似检索差 | ANN 参数、索引构建状态、候选规模 | 不要继续换 Embedding |
| TopK 设为 10 只返回少量结果 | 过滤、近似扫描、空向量、删除记录 | 不要假设库里没有更多数据 |
| 召回正确但回答错误 | 最终上下文、证据顺序、提示词、生成模型 | 不要把生成问题归到向量库 |
| Dify 测试能命中,工作流不命中 | 节点变量、过滤条件、模板和上下文裁剪 | 不要同时改切分和模型 |
| 重排前正确、重排后错误 | Rerank 模型、候选文本与语言适配 | 不要丢失原始候选顺序 |
九、上线前检查清单
在把新知识库或新 Embedding 配置切到生产前,至少完成以下检查:
- 原始文档、解析文本、chunk 数量和向量数量可以相互核对。
- 随机抽查的 chunk 保留标题、主语、单位、条件和来源路径。
- 文档端与查询端使用同一 Embedding 配置标识。
- 向量实际长度与索引 schema 一致,没有空数组、NaN 或无限值。
- 距离函数、归一化和查询前处理有明确记录。
- 过滤字段的数据类型、默认值和权限规则通过测试。
- 精确检索基线与近似检索结果完成对照。
- 至少有 20 到 50 个真实问题及相关证据标注。
- 新旧索引在同一标注集上比较 Recall@K、MRR 或 nDCG。
- Rerank 前后的候选顺序可查看、可追踪。
- 最终送入生成模型的 chunk ID 可以记录。
- 无答案问题不会被系统强行回答。
- 新索引有独立版本,切换后可以回滚。
- 旧索引在确认无消费者前不会提前删除。
- 日志不包含真实 API Key、完整 Authorization 值或未经批准的敏感正文。
十、常见问题
1. Embedding 维度越高,召回一定越好吗?
不一定。维度影响表示容量、存储和计算成本,但检索质量还取决于模型训练目标、语料领域、查询类型、切分质量和距离函数。
不能只用维度大小判断模型优劣,应在自己的标注集上测试。
2. 两个模型都是 768 维,可以共用索引吗?
默认不可以。维度相同只代表数组长度相同,不代表坐标含义一致。
除非模型提供方明确保证兼容,并且业务标注集验证通过,否则应使用不同字段或不同索引。
3. 只把查询端换成新模型,文档向量不变可以吗?
通常不可以。查询向量和文档向量必须来自可比较的表示空间。
只换查询端是最常见的静默质量故障之一,因为请求可能成功,却返回没有语义保证的排序。
4. 更换生成模型需要重建向量索引吗?
如果只更换最终回答用的生成模型,文档 Embedding、查询 Embedding、切分和索引均未变化,通常不需要重建向量索引。
但应重新评价 groundedness、完整性、引用行为和上下文使用方式。
5. 修改 chunk 大小需要重建吗?
需要重新处理受影响文档。chunk 文本和边界已经变化,原向量对应的是旧文本,不能仅修改一个配置值就继续使用旧向量。
6. TopK 越大越好吗?
不是。更大的 TopK 可能提高候选召回,也会增加重复和噪声,挤占生成模型上下文。
应结合 Recall@K、首个相关结果位置、Rerank 能力和上下文预算测试。
7. 为什么相似度分数不能跨模型直接比较?
不同模型、归一化方式和距离函数会产生不同分布。
旧模型阈值 0.7 有效,不代表新模型也应使用 0.7。迁移时应重新统计正负样本分数,并按业务目标选择阈值。
8. RAG 返回了相关文档,为什么仍然会编造?
相关文档出现只是必要条件,不是充分条件。
还要确认正确片段实际进入最终上下文、没有被裁剪,提示词要求基于证据,模型能正确理解材料,并对证据不足的情况允许拒答。
9. 小型标注集会不会太少?
它不能替代长期评估,但比凭单个示例调参可靠得多。
可以先覆盖高频与高风险问题,再逐步加入线上失败样本。关键是固定问题、固定标注和固定计算方法,使每次修改可比较。
10. 是否应该直接删除旧索引节省空间?
不应在切换后立即删除。
应先确认新索引数据完整、质量达标、所有消费者完成切换,并保留足够的回滚窗口。删除时间应由业务恢复要求和存储策略决定,而不是使用未经验证的固定天数。
十一、把线上失败样本变成检索单元测试
RAG 质量不会在一次上线后永久稳定。文档会更新,解析器会升级,模型配置会变化,过滤字段也可能新增。
最有效的长期办法,是把每次真实失败转成可重复执行的检索测试。
当用户反馈答案错误时,先判断这是无答案问题、召回失败、排序失败还是生成失败。若属于检索问题,就保存经过脱敏的问题文本、预期证据 ID、当时返回的候选 ID、索引版本和配置版本。
修复后把它加入回归集,要求后续版本持续命中。
回归测试不应只判断最终答案字符串是否完全一致。生成文本可能因模型变化而改写,而检索层更适合检查稳定条件:
- 预期证据是否出现在 TopK;
- 第一个相关结果是否进入目标名次;
- 权限过滤是否只返回允许的数据;
- 无答案问题是否没有错误命中;
- 重排是否把核心证据保留在上下文预算内。
每次发布新切分规则、Embedding 配置、索引参数或 Rerank 模型时,先在固定回归集上运行,再比较变更前后的逐题差异。
整体平均分上升不代表可以上线:如果高风险问题从命中变成漏召回,仍然需要阻止发布。可以为合同条款、权限规则、故障处理和安全要求等关键问题设置更高权重。
同时保留实验配置快照。一个结果至少要能追溯到语料版本、解析版本、切分版本、Embedding 配置、索引版本、过滤规则、检索方式、TopK、阈值和 Rerank 配置。
只有这样,团队才能复现某次结果,而不是依赖“我记得当时效果很好”的主观印象。
总结
RAG 排错最重要的不是记住某个“最佳 chunk 大小”或“万能阈值”,而是建立证据链。
维度不一致是结构错误,必须让文档向量、查询向量和索引 schema 对齐;维度相同也不能证明不同 Embedding 模型兼容,模型变化应按索引迁移处理;召回差则要沿着解析、切分、向量化、过滤、近似检索、重排和生成逐层验证。
当你能回答“原始答案在哪里、它被切成哪个 chunk、由哪套配置生成向量、无过滤时排第几、加哪条过滤后消失、重排前后如何变化、最终有没有进入模型上下文”时,RAG 就从不可解释的黑盒变成可调试的工程系统。
延伸阅读:https://178.nz/dn
参考资料
- Qdrant,Collections:https://qdrant.tech/documentation/manage-data/collections/
- pgvector 官方仓库:https://github.com/pgvector/pgvector
- Elasticsearch,Dense vector field type:https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/dense-vector
- Dify,Configure the Chunk Settings:https://docs.dify.ai/en/cloud/use-dify/knowledge/create-knowledge/chunking-and-cleaning-text
- Dify,Specify the Index Method and Retrieval Settings:https://docs.dify.ai/en/cloud/use-dify/knowledge/create-knowledge/setting-indexing-methods
- Microsoft Foundry,RAG Evaluators:https://learn.microsoft.com/en-us/azure/foundry/concepts/evaluation-evaluators/rag-evaluators
- BEIR:https://arxiv.org/abs/2104.08663
