news 2026/7/13 15:51:56

向量召回有结果为什么仍答错:Dify 父子分块、元数据过滤与引用校验实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
向量召回有结果为什么仍答错:Dify 父子分块、元数据过滤与引用校验实战

RAG 最容易制造一种假象:调试页面里明明返回了多个片段,相似度也不低,最终答案却仍然答错。

团队看到“召回成功”,通常会先调大 TopK、降低阈值、更换 Embedding,或者换一个能力更强的生成模型。参数改了一圈,当前问题可能碰巧答对,但换一种问法又会出错。

原因在于:召回列表中出现了正确关键词,不等于模型拿到了可用证据。PDF 解析可能打乱原文顺序,子块可能丢失标题和适用范围,向量引擎可能混入其他产品,TopK 还可能把彼此冲突的条款一起送给模型。此时,相似度越高,只能说明系统更自信地找到了“看起来像答案”的文本。

本文用一组产品手册完成一次控制变量实验。每轮只调整一个因素,依次检查 PDF 解析、父子分块、标题继承、Embedding、collection 或 namespace、metadata filter、TopK、重排、Dify 检索链路及引用校验。

最终验收标准不是“检索返回了几个片段”,而是:

答案中的每个关键断言,能否回指到当前有效、语义完整、位置可定位的原文证据。

一、先复现一个“命中了却答错”的样本

测试资料由三份虚构但结构接近真实业务材料的 PDF 组成:

  • 《星云存储专业版服务说明 2026》
  • 《星云存储基础版服务说明 2026》
  • 《星云存储历史费率附录 2024》

专业版文档第 8 页采用双栏排版。左栏写着:

同一区域内恢复不收取数据传输费。

右栏写着:

跨区域恢复按恢复数据量计费。

页面底部还有一条脚注:

灾备演练产生的临时副本存储费另计。

基础版文档中则有另一条规则:

基础版所有恢复任务均计收操作费。

实验问题是:

专业版用户在同一区域执行灾备恢复,是否收取数据传输费?临时副本是否完全免费?

正确答案应包含两个断言:

  1. 同一区域恢复不收取数据传输费;
  2. 临时副本存储费另计,不能概括为“完全免费”。

第一次把三份 PDF 导入 Dify 后,检索结果中确实出现了“同一区域”“不收取”“恢复”等关键词,生成模型却回答:

专业版用户在同一区域恢复时完全免费,不会产生任何附加费用。

如果只看最终答案,很容易把问题归因于模型幻觉。继续打开检索片段,才能发现真正的异常:

chunk_031: 跨区域恢复按恢复数据量计费 灾备演练产生的临时 chunk_032: 同一区域内恢复不收取数据传输费 副本存储费另计 chunk_087: 基础版所有恢复任务均计收操作费

解析器先读取了右栏,随后跳回左栏;脚注被拆到两个分块中。chunk_032同时包含正确结论和残缺脚注,却没有“专业版”标题。第三个片段又来自基础版。

向量引擎并没有停止工作。它忠实地返回了语义相近的文本。真正失效的是证据形成过程。

为了避免凭感觉调参,我建立了一个包含 30 个问题的小型离线评测集,并记录四项指标:

轮次单一改动正确文档命中率证据完整率引用可定位率最终答案正确率
A0原始导入90%47%20%53%
A1修复 PDF 解析93%70%67%70%
A2父子分块与标题继承97%87%83%83%
A3元数据过滤97%90%87%90%
A4TopK 与重排100%93%90%93%
A5引用校验与拒答100%97%100%97%

这些数据只是本次实验结果,不代表任何平台或模型的通用性能。

真正值得观察的是指标间的差距:A0 的正确文档命中率已经达到 90%,最终答案正确率却只有 53%。如果系统只统计 Recall@K 或“是否召回到正确文件”,团队很可能误以为检索质量已经过关。

二、第一轮只修 PDF:先恢复文本的阅读顺序

RAG 真正接收的输入不是 PDF 页面,而是解析器输出的字符序列。文档在人眼看来排版正常,不代表提取后的 token 顺序仍然正确。

多栏、表格、脚注和扫描页是最危险的四类结构。

我暂时不修改分块和模型,只替换解析结果。检查方法很朴素:从每份文档中抽取首页、目录页、多栏页、表格页、跨页内容和脚注页,把解析文本与页面视觉顺序逐段比对。

需要重点排查以下异常:

  • 双栏内容是否被横向穿插读取;
  • 页眉和页脚是否在每页重复进入正文;
  • 表格是否丢失列名,导致数值与产品错位;
  • 合并单元格中的适用条件是否只保留在第一行;
  • 跨页表格的续表是否继承表头;
  • 脚注编号与脚注正文是否被分离;
  • OCR 是否误识别小数点、百分号、负号和数字;
  • 目录点线及页码是否产生大量无意义片段;
  • 页码是否与解析后的文本块保持对应关系。

对于多栏页面,不能简单按照字符坐标从上到下排序。更可靠的做法是先识别页面区域,再按照“页面—栏—段落”的顺序组合文本。

表格也不应直接压成一串字符。实验中,我将其转换为带有表名、列名和行语义的结构:

[表名] 灾备恢复计费规则 [列] 产品版本 | 恢复范围 | 数据传输费 | 备注 [行] 专业版 | 同一区域 | 0 | 临时副本存储费另计 [行] 专业版 | 跨区域 | 按恢复数据量 | 以账单周期汇总

这种表达的价值不只是方便阅读。每一行都保留了列语义,Embedding、重排和引用校验都能知道“0”指的是数据传输费,而不是临时副本费。

如果解析结果只有:

专业版 同一区域 0 临时副本存储费另计

向量模型可能仍能理解大致含义,但系统很难验证数值究竟属于哪一列。

完成版面解析后,还要建立页面定位。每个正文块至少记录:

{"document_id":"nebula-pro-2026","page":8,"block_id":"p8-b12","layout_type":"table_row","bbox":[102,438,911,512],"text":"专业版 | 同一区域 | 0 | 临时副本存储费另计"}

并非所有系统都必须保存bbox,但页码和稳定的block_id非常重要。没有这些定位信息,答案即使显示了文件名,用户也很难回到原文核查。

只修复解析后,A1 的答案正确率由 53% 上升到 70%。这说明相当一部分所谓的模型幻觉,实际是上游把证据顺序拼坏了。

三、第二轮改父子分块:小块负责查找,大块负责阅读

解析正常后仍会遇到另一个矛盾:

  • 分块太大时,一个向量同时包含多个主题,查询语义容易被稀释;
  • 分块太小时,虽然容易命中,却会丢失标题、适用条件、例外和脚注。

父子分块把两个目标拆开:

  • 子块较短,负责与用户问题匹配;
  • 父块较完整,负责向生成模型提供上下文。

以前面的条款为例,父块可以覆盖一个完整小节:

父块 P-8-3 标题路径:服务说明 > 灾备恢复 > 费用规则 适用产品:专业版 同一区域内恢复不收取数据传输费。 跨区域恢复按恢复数据量计费。 灾备演练产生的临时副本存储费另计。

子块则分别保存原子事实:

C-8-3-1:同一区域内恢复不收取数据传输费。 C-8-3-2:跨区域恢复按恢复数据量计费。 C-8-3-3:灾备演练产生的临时副本存储费另计。

问题“同一区域恢复是否收费”更容易命中C-8-3-1,但最终送给模型的是P-8-3。模型因此能同时看到主要结论和例外条件,不会把“不收取数据传输费”扩写成“不产生任何费用”。

父块也不能无限扩大。如果一个父块跨越多个一级章节,里面同时出现基础版、专业版和企业版,冲突证据会重新进入上下文。

本次实验采用以下边界规则:

  1. 优先根据标题层级形成父块;
  2. 一个父块只表达一个主要业务主题;
  3. 表格标题、列名、数据行和相关脚注尽量放在同一父块;
  4. 父块超过上下文预算时,按照二级标题或语义段落继续拆分;
  5. 子块不能跨越父块边界;
  6. 每个子块必须保存明确的parent_id
  7. 不使用文本相似度临时反查父块;
  8. 相邻父块之间可以保留少量重叠,但不能重复整个章节。

在 Dify 中启用父子分块后,不能只看字符数设置,还要检查实际分块预览。我会随机抽取至少 20 个子块,分别回答三个问题:

  • 子块是否能独立表达一个可检索事实?
  • 父块是否包含回答所需的限定条件和例外?
  • 父块中是否混入了会改变答案的其他产品、地区或版本?

如果第三个问题经常得到肯定答案,说明父块边界仍然太宽。

四、标题继承不是装饰,而是检索语义的一部分

许多产品手册的正文只写:

支持自动恢复。 不收取传输费。 费用另计。 默认保留三十天。

真正的主语和适用范围藏在章节标题中。脱离标题后,这些句子几乎无法判断属于什么产品、什么功能或什么地区。

因此,我没有只保存正文,而是把标题路径作为独立字段,并生成专门用于 Embedding 的检索文本:

服务说明 / 专业版 / 灾备恢复 / 费用规则: 同一区域内恢复不收取数据传输费。

数据结构可以拆成四个字段:

{"raw_text":"同一区域内恢复不收取数据传输费。","embedding_text":"专业版 / 灾备恢复 / 费用规则:同一区域内恢复不收取数据传输费。","display_text":"同一区域内恢复不收取数据传输费。","heading_path":["专业版","灾备恢复","费用规则"]}

这样做有几个好处:

  • raw_text保留原始证据;
  • embedding_text补充检索所需的主语和语境;
  • display_text控制引用展示;
  • heading_path用于过滤、调试和定位。

并非所有 metadata 都适合拼入embedding_text。租户编号、同步时间戳、内部任务 ID 等信息没有自然语义,加入后可能产生噪声。

适合参与 Embedding 的通常包括:

  • 产品名称;
  • 功能模块;
  • 章节标题;
  • 术语别名;
  • 必要的上下文主语。

更适合作为 metadata filter 的包括:

  • tenant_id;
  • permission_group;
  • document_status;
  • document_version;
  • region_code;
  • knowledge_base_id。

标题继承也需要控制长度。如果每个片段都以十几级目录开头,大量相同前缀会削弱正文的区分度。实际处理中可以只保留最近两到三级有效标题,并去掉“文档中心”“产品手册”等信息量很低的根节点。

完成父子分块和标题继承后,A2 的证据完整率升至 87%。提升并不是因为召回数量增加,而是命中的事实终于带上了正确主语、适用范围和例外条件。

五、Embedding 与向量引擎:保证写入和查询处于同一语义空间

Embedding 调优经常被简化成“换一个更强的模型”,实际项目中更常见的问题是写入侧与查询侧不一致。

至少需要固定以下配置:

  • Embedding 模型标识;
  • 输出向量维度;
  • 文本预处理规则;
  • 是否拼接标题;
  • 是否归一化;
  • 距离度量方式;
  • 索引版本;
  • 空白、大小写和特殊符号处理规则。

如果文档向量由模型 E1 生成,查询向量却改用 E2,即使两个模型输出维度相同,也不代表向量处于可比较的语义空间。

更换 Embedding 模型时,应建立新索引或完成全量重算,不能只替换查询 API。

向量引擎中的 collection、namespace、partition 等概念,在不同产品中的名称和能力不完全一致。本文统一把它们理解为“候选数据的逻辑边界”。

一种比较清晰的组织方式是:

collection: rag_chunks namespace: tenant_acme metadata: knowledge_base_id: kb_storage_manual product: nebula_storage edition: pro region: cn language: zh-CN doc_status: active document_id: nebula-pro-2026 document_version: 2026.1

不能把所有业务文本都写入一个没有边界的集合,再指望 TopK 自动找到正确范围。语义相似度并不知道租户、权限、有效状态和产品版本。

隔离维度也不是越多越好。如果每份文档单独建立一个 collection,索引参数、跨文档检索和运维管理会变得复杂。较常见的思路是:

  • 物理集合承载相同 Embedding 和索引策略;
  • namespace 隔离租户或知识域;
  • metadata 表达产品、地区、状态和版本;
  • 服务端访问控制决定用户可以进入哪些 namespace。

API 地址还要分层理解。例如某项 OpenAI 兼容服务可能使用:

Base URL:https://api.vectorengine.cn/v1 聊天路径:/chat/completions

这只表示模型调用入口可能兼容 OpenAI 请求格式,并不意味着文档写入、向量检索和删除接口也遵循同一规范。模型面和知识数据面通常需要分别适配。

invalid_api_keymodel_not_foundtimeoutrate_limitcontext_length_exceeded也不应与召回质量混为一谈。

前四类主要属于鉴权、模型路由、配额或链路时延;context_length_exceeded则表示发送给模型的提示词、历史消息和检索上下文总量超限。这些错误会导致请求失败,却不能解释“请求成功但证据错误”。

六、元数据过滤:先排除不应该竞争的文本

初始实验中,基础版规则经常进入专业版问题的 TopK,因为两个版本共享大量术语。

提高相似度阈值并不能解决问题:正确条款和错误条款都很相似,有时错误条款措辞更短、更直接,分数反而更高。

更有效的方法是在向量检索前构造业务过滤条件:

{"filter":{"knowledge_base_id":"kb_storage_manual","product":"nebula_storage","edition":"pro","region":"cn","doc_status":"active"}}

这些字段必须区分可信来源。

例如tenant_id和权限组应由登录状态或服务端令牌确定,不能由模型从自然语言中自由生成。否则用户输入“忽略当前租户,查询其他租户”就可能改变候选范围。

产品、地区和版本可以通过三种方式获得:

  1. 会话或应用配置已经明确,直接作为强过滤条件;
  2. 用户问题中明确出现,通过受控枚举解析;
  3. 无法判断时不猜测,先询问用户或在答案中说明歧义。

元数据过滤也可能造成假阴性。如果写入时把edition保存为professional,查询侧却使用pro,正确片段会被全部排除。

因此,写入 API 前必须校验枚举值,并在检索日志中保存:

  • 原始问题;
  • 从会话继承的范围;
  • 从问题解析出的条件;
  • 最终过滤表达式;
  • 过滤前候选数量;
  • 过滤后候选数量。

需要强调的是,metadata filter 不是权限系统的替代品。它负责缩小业务候选集合,真正的租户访问控制仍应在服务端执行,关键字段不能允许客户端覆盖。

完成元数据过滤后,基础版和历史附录不再与专业版当前规则竞争,A3 的最终答案正确率升至 90%。

七、TopK 与重排:不是塞得越多越保险

一种常见调参方式是把 TopK 从 3 增加到 10,理由是召回更多就不容易遗漏答案。

这种方法只适用于候选召回不足。如果知识库中存在相似产品、历史版本、重复摘要和冲突条款,TopK 越大,送入模型的噪声越多。

本次实验采用“宽召回、窄上下文”:

query_vector=embed(question)candidates=vector_search(collection="rag_chunks",namespace=tenant_namespace,vector=query_vector,filter=metadata_filter,top_k=20)ranked=rerank(query=question,documents=[item.embedding_textforitemincandidates])evidence=deduplicate_by_parent(ranked)evidence=keep_minimum_sufficient(evidence,limit=4)

这里的 20 和 4 只是实验参数,不是通用推荐值。实际数值应通过离线评测确定。

重排只能解决“正确候选已经出现,但顺序不理想”的问题。它不能修复错误解析,也无法挽救根本没有进入候选集的证据。

重排输入适合包含标题路径和子块正文,完成排序后再映射到父块。如果直接用多个巨大父块进行重排,延迟与成本都会明显增加。

去重也不能只依靠文本哈希。相同规则可能同时出现在正文、摘要和附录中,文字略有差异但证据含义相同。可以按照以下字段组合去重:

  • parent_id;
  • 规则编号;
  • 规范化标题路径;
  • document_id 和版本;
  • 业务主题标识。

同时优先保留当前有效、定位字段完整的来源。

“最小充分证据”可以根据问题槽位判断。实验问题同时询问数据传输费和临时副本费,因此证据集合必须覆盖两个费用项。

如果只找到第一项,系统应该继续检索,或者明确说明第二项没有找到。不能把“数据传输费为零”概括成“完全免费”。

八、拆开观察 Dify 检索链路

在 Dify 应用中,不能只查看最终回答。至少应分开观察以下阶段:

用户问题 → 问题规范化或参数提取 → metadata 构造 → 知识检索 → 子块命中与父块展开 → 重排、阈值和去重 → 上下文组装 → 模型生成 → 引用展示与校验

调试时每轮只改变一个节点。如果同时更换 Embedding、修改分块、调整 TopK 和改写提示词,即使结果变好,也无法确定究竟是哪项配置产生作用。

建议为每次请求建立统一的trace_id

{"trace_id":"rag-20260713-00042","question":"专业版同区域灾备恢复是否完全免费?","filter":{"edition":"pro","doc_status":"active"},"candidate_chunk_ids":["c31","c44","c18"],"candidate_scores":[0.82,0.79,0.74],"rerank_order":["c44","c31","c18"],"selected_parent_ids":["p8-3"],"prompt_evidence_ids":["p8-3"],"answer_claims":["claim-1","claim-2"],"citation_ids":["cite-1","cite-2"],"latency_ms":{"embedding":86,"retrieval":41,"rerank":173,"generation":912}}

企业统一接入时,日志审计还要兼顾可诊断性与数据保护:

  • API Key 只能记录哈希或末尾少量字符;
  • 问题与检索片段可能包含敏感信息;
  • 日志需要按照数据等级决定脱敏、采样和保留期限;
  • 成本统计应拆分 Embedding、向量查询、重排和模型生成;
  • timeout 和 rate_limit 等接口失败应与证据质量错误分开统计。

如果团队使用 Cursor 编写入库脚本,或者使用 Chatbox、Cherry Studio 测试 OpenAI 兼容接口,也应固定同一组问题、证据和模型参数。否则客户端自带的系统提示词、历史消息或 Base URL 差异会污染对照实验。

我的配置备忘会把接口排错与第三方体验记录分开,其中曾参考过 https://178.nz/dn 的页面组织方式;真正进入生产环境的 Base URL、模型名称和 API Key 权限,仍应以团队自己的最小请求测试、供应商文档和审计策略为准。

九、文档 API 必须保留引用血缘

RAG API 最容易遗漏的是来源字段。如果写入时只保存文本和向量,检索时就只能返回相似片段,无法可靠回答“这句话来自哪一页”。

下面使用通用伪代码说明数据结构,不代表 Dify 或某个向量引擎的固定接口:

POST /rag/documents Authorization: Bearer ${RAG_API_KEY} Content-Type: application/json { "document_id": "nebula-pro-2026", "version": "2026.1", "knowledge_base_id": "kb_storage_manual", "source": { "file_name": "星云存储专业版服务说明2026.pdf", "content_hash": "sha256:...", "parser": "layout-parser-a" }, "chunks": [ { "chunk_id": "c-8-3-1", "parent_id": "p-8-3", "page": 8, "block_id": "p8-b12", "heading_path": ["专业版", "灾备恢复", "费用规则"], "raw_text": "同一区域内恢复不收取数据传输费。", "embedding_text": "专业版 / 灾备恢复 / 费用规则:同一区域内恢复不收取数据传输费。", "metadata": { "edition": "pro", "region": "cn", "doc_status": "active" } } ] }

检索响应至少应返回:

{"trace_id":"rag-20260713-00042","matches":[{"document_id":"nebula-pro-2026","document_version":"2026.1","chunk_id":"c-8-3-1","parent_id":"p-8-3","page":8,"block_id":"p8-b12","score":0.82,"rerank_score":0.91,"text":"同一区域内恢复不收取数据传输费。","source_name":"星云存储专业版服务说明2026.pdf"}]}

删除接口不能只删除文档表中的一行。它还要清理:

  • 文档对应的子块;
  • 父块;
  • 向量记录;
  • 引用映射;
  • 缓存中的候选结果。

同时需要保证重复调用安全:

DELETE /rag/documents/nebula-pro-2026?version=2026.1 Idempotency-Key: delete-nebula-pro-2026-2026.1

版本替换时,可以先写入并验证新版本,再切换doc_status或可见指针,最后清理旧候选。这里的版本管理只是为了避免新旧冲突条款同时参与检索,并不把增量更新或回滚作为本文主线。

文档 API 的验收重点包括:

  • document_id、chunk_id 是否稳定且唯一;
  • 父子关系能否追踪;
  • 页面和块定位是否存在;
  • metadata 枚举是否经过校验;
  • Embedding 模型与索引版本是否记录;
  • 重复写入是否产生重复向量;
  • 删除后是否仍能检索到孤儿块;
  • 新版本可见后,旧版本是否退出默认候选;
  • 检索响应是否把来源字段完整传递给生成节点。

十、引用校验:把“看起来有依据”变成可以复核

只在答案末尾显示[1],不代表引用正确。

常见的伪引用包括:

  1. 引用片段与答案主题相关,但不支持具体结论;
  2. 引用来自正确文件,却属于错误版本或错误产品;
  3. 引用只支持半句话,模型却扩大了适用范围;
  4. 文件名正确,但页码和片段无法定位;
  5. 模型生成了并不存在的引用编号。

因此,我把生成结果约束为“断言—引用”结构:

{"claims":[{"text":"专业版同一区域恢复不收取数据传输费。","citation_ids":["cite-1"]},{"text":"灾备演练产生的临时副本存储费另计。","citation_ids":["cite-2"]}]}

随后进行四级校验。

1. 存在性校验

引用 ID 必须来自本次实际送入模型的证据集合,不能由模型自由编造。

2. 血缘校验

引用必须具有有效的 document_id、version、chunk_id、page 或 block_id,并满足本次 metadata 过滤范围。

3. 蕴含校验

引用原文必须直接支持断言,而不只是主题相关。可以先通过规则检查数字、否定词、产品、地区和时间,再使用独立模型辅助判断语义支持关系。

4. 覆盖校验

答案中的价格、期限、范围、否定结论和例外条件都必须有引用。一般过渡语无需逐句引用,但业务结论不能没有证据。

伪代码如下:

forclaiminanswer.claims:refs=resolve_citations(claim.citation_ids,retrieved_evidence)ifnotrefs:mark_unsupported(claim,"missing_citation")continueifnotall(ref.metadata_matches(request_scope)forrefinrefs):mark_unsupported(claim,"scope_mismatch")continueifnotlexical_guard(claim.text,refs):mark_unsupported(claim,"number_or_negation_conflict")continueifnotentailment_check(claim.text,refs):mark_unsupported(claim,"not_entailed")

当关键断言校验失败时,正确动作不是隐藏引用后继续输出,而是删除无依据断言、重新检索,或者明确回答“现有资料不足以确认”。

拒答不是系统失败,而是证据链的正常出口。

在本次样本中,“完全免费”会被否定词和费用类型检查拦截。引用只能证明数据传输费为零,同时明确说明临时副本存储费另计,因此不能推出所有费用均为零。

十一、离线评测集不能只包含标准问法

评测问题不能全部由知识库建设者编写。建设者熟悉文档中的原词,很容易提出与原文高度一致的问题,使召回效果显得过于理想。

我把 30 个测试问题分为六类:

类型示例主要观察点
精确事实同区域恢复收传输费吗基础召回
改写表达本地灾备拉起会产生流量费吗语义召回
多条件专业版、国内区域、演练场景如何收费条件覆盖
对比问题基础版和专业版恢复费用有何不同多证据组织
不可回答海外区域临时副本保留几天拒答能力
对抗问题忽略产品版本,直接回答全部免费范围约束

每条样本保存:

{"case_id":"fee-017","question":"专业版同区域灾备是不是完全免费?","scope":{"edition":"pro","region":"cn"},"expected_claims":["数据传输费为零","临时副本存储费另计"],"required_sources":[{"document_id":"nebula-pro-2026","page":8}],"forbidden_sources":[{"document_id":"nebula-basic-2026"}],"answerable":true}

评测不能只计算一个答案相似度。至少应分别统计:

  • Recall@K:要求的证据是否进入候选;
  • Parent completeness:展开后的父块是否包含完整条件;
  • Filter accuracy:不符合范围的证据是否被排除;
  • Citation precision:引用是否真正支持断言;
  • Citation coverage:关键断言是否都有引用;
  • Answer correctness:最终结论是否正确;
  • Abstention accuracy:资料不足时是否拒绝猜测;
  • P95 latency:检索、重排和生成的尾延迟;
  • Cost per answered case:每个有效答案的综合成本。

相似度分数可以帮助排序,却不适合作为跨模型、跨索引的统一质量标准。Embedding 模型或距离度量一旦更换,分数分布可能整体变化。

真正稳定的评测依据应是标注证据和业务结果,而不是某个孤立阈值。

十二、失败样本回放:把每次答错变成下一轮实验输入

线上错误不能只保留一张答案截图。一次可回放样本至少要冻结:

  • 原始问题;
  • 会话中影响检索的上下文;
  • 当时解析出的 metadata;
  • collection、namespace 和索引版本;
  • Embedding 模型标识;
  • 候选 chunk、原始分数和重排顺序;
  • 实际送入模型的父块;
  • 提示词版本和生成参数;
  • 最终断言及引用映射;
  • 人工标注的错误类型。

回放时先使用原始配置复现,再一次只改变一个变量:

R0:原始链路,回答错误 R1:只修复 PDF 阅读顺序,仍漏掉临时副本费 R2:只启用父子分块,两个费用项均进入上下文 R3:只增加 edition=pro 过滤,基础版冲突消失 R4:只增加引用覆盖校验,“完全免费”结论被拒绝

这种记录可以区分“修好了当前问题”和“改善了同类问题”。

如果某项修改只让一个样本变好,却让不可回答问题开始强行作答,就不能直接发布。

上线前,我使用以下清单验收:

  • 多栏、表格、脚注和扫描页完成抽样核对;
  • 子块适合匹配,父块包含限定条件和例外;
  • 标题路径进入检索语义,但没有过度重复;
  • 写入与查询使用相同的 Embedding 配置;
  • collection 或 namespace 边界清晰;
  • metadata 枚举统一;
  • 可信过滤字段不能由用户覆盖;
  • TopK、阈值和重排参数来自离线评测;
  • 每个候选都能追踪到文档、版本、页码和块;
  • 每个关键断言均绑定真实引用;
  • 数字、否定词、产品和地区经过一致性检查;
  • 没有充分证据时允许澄清或拒答;
  • 删除操作不会留下孤儿向量;
  • API Key、问题原文和日志按敏感等级脱敏;
  • timeout、rate_limit 等接口错误与质量错误分开统计;
  • 成本看板覆盖 Embedding、向量查询、重排和生成;
  • 失败样本能够通过 trace_id 完整回放。

这次实验最重要的变化,不是把某个相似度从 0.79 调到 0.86,而是改变了 RAG 的质量收口方式。

旧问题是:

向量引擎召回结果了吗?

新问题是:

答案中的每个关键断言,是否由当前范围内、语义完整、位置可定位的证据支持?

前一个问题只能证明链路正在运行,后一个问题才接近证明答案可以使用。

有召回结果不等于有证据,有引用标记也不等于引用正确。只有当 PDF 解析、父子分块、标题继承、Embedding、向量隔离、元数据过滤、重排和引用校验形成闭环,Dify 中那条看起来顺畅的 RAG 链路才真正具备可验证性。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/13 15:51:25

15_Python迭代器、生成器与推导式:写出更优雅的循环代码

Python迭代器、生成器与推导式:写出更优雅的循环代码 前面写循环时,你一直用 for i in range(100) 或 for item in my_list,没出过问题。但你有没想过——为什么 range(100) 和 my_list 都能放在 for 后面?range 不是列表&#xf…

作者头像 李华
网站建设 2026/7/13 15:47:47

Gammazero/Deque社区贡献指南:如何参与这个高性能队列项目的开发

Gammazero/Deque社区贡献指南:如何参与这个高性能队列项目的开发 【免费下载链接】deque Fast ring-buffer deque (double-ended queue) 项目地址: https://gitcode.com/gh_mirrors/deque3/deque Gammazero/Deque是一个高性能的环形缓冲区双端队列实现&#…

作者头像 李华
网站建设 2026/7/13 15:47:01

132、EDVR实战:基于可变形卷积的视频超分网络训练与优化

132、EDVR实战:基于可变形卷积的视频超分网络训练与优化 上个月调EDVR的时候,我差点把服务器搞崩了。原因是显存占用直接飙到了24G,而我的RTX 3090只有24G——你懂的,就差那么一丁点就OOM了。后来发现是DataLoader的batch size和帧数没算清楚,一个简单的配置失误让我浪费了…

作者头像 李华
网站建设 2026/7/13 15:42:36

Java笔记6-字符串

一、BigDecimal 精准浮点运算1. 问题double/float 二进制存储存在精度丢失double a1.06,b1.01; System.out.println(ab); //2.07000000000000032. 核心说明包:java.math.BigDecimal 推荐构造:new BigDecimal("数值"),避免 double 入…

作者头像 李华
网站建设 2026/7/13 15:41:30

如何快速集成GlassActionBar:3种主流ActionBar实现方案对比

如何快速集成GlassActionBar:3种主流ActionBar实现方案对比 【免费下载链接】GlassActionBar Android - a library that adds a glass-like effect to the action bar. 项目地址: https://gitcode.com/gh_mirrors/gl/GlassActionBar GlassActionBar是一款专为…

作者头像 李华