← 返回文章列表

Agent 与 AI / 2026-10-29

深入 RAG:知识接入、索引、召回、重排、上下文组装与效果评测

2026-10-29约 22 分钟阅读

证据生命周期、版本与权限、混合检索、重排、Token 预算、分层指标和诊断实验。

日期:2026-10-29 · 主题:检索系统、RAG、评估与可靠性

用户问:“按今年的企业版合同,欧洲客户提前解约,是否需要支付剩余合同金额?”

系统检索到一份高度相关的合同条款,模型流畅地回答“需要”。但那份条款属于去年的北美标准合同;今年欧洲企业版合同中,另有一条特殊退出条款。

这个错误可能发生在知识接入、版本过滤、候选召回、重排,也可能发生在上下文截断或模型推理阶段。只看最终答案,无法确定该换 Embedding、调 Top-K,还是修改生成提示词。

RAG 的深度,体现在能否追踪一条证据从原始来源进入系统,到最终支持某个结论的全过程,并通过实验判断错误发生在哪一层。

本文以虚构的企业合同知识助手为主线。合同问题只用于系统设计,不构成法律意见;所有参数与数字示例都是教学设定,未宣称生产收益或完整模型基准测试。文中将公开文档能力与自定义系统设计分开说明。

1. 为 RAG 定义一个更严格的目标

RAG 通常被概括为“先检索,再生成”。这个描述解释了调用顺序,但没有定义正确性。

对于企业知识助手,更严格的目标是:

在用户有权访问、适用版本和时间范围内,找到回答问题所需的证据,在有限预算下交给模型,并生成可以被这些证据支持的回答。

这个目标包含四个经常被忽略的条件:权限、时效、证据完整性、结论可支持性。

OpenAI 的 Retrieval 文档说明,语义检索可以找到关键词重合较少但语义相近的内容;File Search 则提供托管的文件检索能力。它们解决了部分检索执行问题,但应用仍需定义自己的业务适用条件与评测标准。Retrieval;File search

将系统写成两个阶段:

离线或增量阶段:
源数据 → 解析 → 规范化 → 切块 → 表征 → 索引 → 发布

在线阶段:
问题与身份 → 约束解析 → 候选召回 → 融合 → 重排
         → 证据组装 → 生成 → 引用与支持性检查

系统还需要一条旁路:监控、评估、删除与权限变更传播。缺少这条旁路,一个今天答对的系统,明天可能因为源文档更新而稳定答错。

结构图
flowchart LR
    A[源文档与权限] --> B[解析与版本化]
    B --> C[结构化切块]
    C --> D[稀疏与稠密索引]
    Q[问题与可信身份] --> F[范围与权限约束]
    D --> R[候选召回]
    F --> R
    R --> S[融合与重排]
    S --> P[预算内证据组装]
    P --> G[生成与主张检查]
    G --> E[结果与分层评估]
    E -. 定位失败与回归验证 .-> B
    E -. 定位失败与回归验证 .-> R
    E -. 定位失败与回归验证 .-> P

2. 知识接入首先是数据工程问题

假设源文件是一份扫描合同。页面包含正文、表格、脚注和盖章。简单 OCR 把表格的行列关系打乱,将“适用地区:欧洲”与另一列中的“标准版”拼成一句话。

后续 Embedding、ANN 和 Reranker 再强,也只能处理已经被破坏的内容。

接入需要分别保存三种东西:

层 保存内容 作用
原始层 文件、原始 API 响应、来源 ID、内容哈希 重放、审计与重新解析
规范层 章节、段落、表格、页码、字符或坐标定位 保留结构和出处
索引层 检索文本、Embedding、过滤字段、索引版本 支持高效查询

三层不一定要部署成三个独立服务,但不能丢掉它们之间的映射。只保存向量和一段文本,会使后续纠错、重建和引用验证非常困难。

对 PDF,文本提取和页面视觉分析可能提供不同信息。OpenAI 的相关 Cookbook 展示了两种处理路径;该示例已归档,本文仅引用其文档解析思路,不采用其中旧模型和 API 代码。How to parse PDF docs for RAG

解析后的质量检查应该覆盖:

  • 章节标题与段落是否保持从属关系。
  • 表格单元格是否仍能关联列名、行名、单位和脚注。
  • 页眉页脚是否被重复当成正文。
  • OCR 的数字、否定词和小数点是否存在高风险误识别。
  • 附件和主合同之间的引用关系是否保留。
  • 无法解析的页面是否明确标记,而不是静默跳过。

对于重要文档,可以把低质量页面送入补充解析或人工复核。不要让解析器的“调用成功”成为内容正确的代理指标。

3. 文档身份、版本和时间必须成为一等公民

一个可用的 chunk 记录可以包含:

{
  "chunk_id": "contract_42:v8:section_12:part_2",
  "document_id": "contract_42",
  "source_version": "v8",
  "content_hash": "sha256:example",
  "tenant_id": "tenant_a",
  "acl_policy_id": "policy_17",
  "effective_from": "2026-01-01",
  "effective_to": null,
  "region": "EU",
  "product": "enterprise",
  "section_path": ["Termination", "Early exit"],
  "source_locator": {"page": 18, "paragraph": 3},
  "parser_version": "parser_v3",
  "chunker_version": "chunker_v2",
  "embedding_version": "embedding_profile_v1"
}

这是自定义数据模型示意,不是某个托管检索接口的完整 schema。

document_id 表示逻辑文档,source_version 表示其版本,chunk_id 表示某一版本中的检索单元。不要把源文件名当成唯一身份:文件可以重命名,两个文件也可能同名。

时间还需要区分:

  • 业务有效时间:条款什么时候适用。
  • 系统获知时间:该版本什么时候被接入。

“现在适用什么规定”和“去年决策时系统知道什么”不是同一个查询。需要历史回溯时,必须保留这两个维度,并明确查询语义。

更新与删除的正确顺序

可以为新版本构建独立索引 generation,待解析、向量和关键词索引就绪后,再通过发布清单或别名切换到新 generation。这降低了查询同时看到新正文和旧向量的风险。

如果稀疏索引和向量索引分属不同系统,通常没有跨系统原子切换。需要在候选返回时校验 generation,或在服务层绑定发布清单。不能仅因为两个任务都启动了,就声称更新已原子完成。

删除要传播到向量索引、关键词索引、父文档存储、上下文缓存和答案缓存。权限撤销同样需要传播。对不能接受传播延迟的系统,应在返回证据或生成前进行权威状态复核;具体一致性等级必须写进系统契约。

4. 切块的目标是保存可回答性

把每 500 个 Token 切成一块,很容易实现,却可能把一般规则和例外拆开:

第 12 条:提前解约需支付剩余合同金额。

第 12.1 条:若发生特定服务违约,欧洲企业版客户可以免付该费用。

问题是,第一块单独看非常像完整答案。

切块时需要区分三个粒度:

粒度 例子 设计目标
检索单元 条款片段或段落 足够聚焦,便于匹配
生成证据单元 条款及其适用条件、例外 足够完整,便于判断
引用单元 页码、条款号、原文范围 足够精确,便于核对

三者可以不同。可以检索短 chunk,再按结构扩展到父章节或相邻条款。但扩展必须计入 Token 预算,也要保留权限、版本和来源校验。

切块策略的常见取舍包括:

  • 小块更聚焦,但可能缺少前提和指代对象。
  • 大块信息完整,但相似度可能被无关内容稀释。
  • Overlap 减少边界断裂,也会制造近重复候选并占用预算。
  • 标题与路径能补充语义,但不能代替缺失正文。
  • 表格需要保留字段关系,不能按普通段落随意截断。

用模型生成“检索摘要”或背景补充时,应把它标为派生字段,并保留原文。摘要用于帮助找到证据,不应在未经核对时成为合同事实的权威来源。

选择 chunk 大小时,应比较不同问题类型下的证据覆盖、上下文占用和最终答案,而不是只看向量检索分数。

5. 索引:Embedding 质量与搜索近似误差是两件事

稠密检索通常把问题和文档映射到向量,再计算相似度:

score(q, d) = dot(normalize(embed_query(q)),
                  normalize(embed_document(d)))

这里使用归一化后的点积,对应余弦相似度。实际系统必须遵守模型要求,包括查询与文档的编码方式、前缀、归一化和距离度量,不能任意混搭。

更换 Embedding 模型,即使维度相同,也不意味着新旧向量位于兼容空间。通常应重建文档向量,并绑定查询编码配置。

生产语料大时,常用近似最近邻搜索 ANN 降低查询开销。需要区分两种召回:

ANN Recall@K:
近似检索找回了多少“同一向量空间下精确搜索的 Top-K”。

任务证据 Recall@K:
检索找回了多少“回答当前问题真正需要的证据”。

ANN Recall 很高,业务证据召回仍可能很差,因为 Embedding 把错误条款排在了前面。反过来,Embedding 在精确搜索中表现很好,但 ANN 配置过于激进,也会漏掉本来可找到的证据。

定位时可以先在可控样本上做精确向量搜索:如果精确搜索也找不到,优先检查数据、切块和表征;如果精确搜索能找到而 ANN 找不到,再调整近似搜索预算、索引结构和过滤策略。

对于非常小的语料,精确搜索可能已经足够。不要默认系统必须使用复杂的向量数据库。

6. 权限过滤会改变你的检索空间

假设全库最相似的前 100 个候选都属于另一个租户。先取 Top-10 再过滤,最后可能什么也不剩,即使用户有权访问的集合里存在答案。

正确的检索目标应该是:

在 Authorized(user) ∩ Applicable(time, region, product)
定义的集合内,找到相关证据。

这与“全库检索之后把不允许看的候选删掉”不是等价操作。

可以使用预过滤、租户分区或支持过滤的 ANN 方案,但过滤本身可能影响 ANN 质量和性能。应按选择性、租户规模和数据分布测试,而不是只在全库无过滤条件下测一次。

权限条件必须从可信身份和策略服务派生,不能相信模型传来的 tenant_id。允许模型帮助理解“欧洲企业版”这样的业务条件,不代表允许它自行扩大访问范围。

托管检索服务可能提供属性过滤,OpenAI Retrieval 文档也描述了相关能力;但复杂 ACL 的同步、撤销与最终授权复核仍需应用明确设计。Retrieval:Attributes 与 filtering

更不能把未授权候选先送给 Reranker 或生成模型,再只过滤最终答案。此时内容已经越过访问边界。日志、调试页面和缓存也属于可能泄露证据的路径。

7. 混合召回:语义相关与精确匹配各有盲区

用户可能问:

“ERR_AUTH_042 到底是什么意思?”

这类问题很依赖精确标识符。另一个用户问:

“客户不想继续合作,什么情况下能免掉后面的费用?”

这又依赖语义匹配,关键词可能与合同原文完全不同。

因此,稀疏检索与稠密检索可以互补:

  • 关键词检索适合产品名、错误码、条款编号、稀有术语。
  • 稠密检索适合语义改写、表达差异和跨词面匹配。

以 BM25 这类关键词排序为例,稀有词通常具有更高区分度,词频增加的收益逐渐饱和,并通过文档长度归一化避免长文档仅凭重复出现占优。这也解释了为什么专有标识符可能很有效,以及为什么把大量无关正文塞进一个 chunk 会改变排序。中文分词、大小写、符号保留和代码标识符切分都需要针对语料检查。

召回应扩大有价值的候选集合,再由后续阶段精筛。直接把 BM25 分数与向量分数相加通常没有清楚含义,因为两种分数的尺度不同。

一种简单融合方法是 Reciprocal Rank Fusion:

RRF(d) = Σ_i weight_i / (k0 + rank_i(d))

rank 从 1 开始。
某通道没有返回 d,则该通道贡献为 0。
k0 为平滑参数,不是候选数量 K。

它依据名次而非原始分数融合,减少尺度不一致的问题,但也丢弃了原始分差。k0、权重和候选数都应通过任务数据验证。

下面是一个仅依赖 Python 标准库、可以本地运行的教学实现。输入必须已完成授权过滤;chunk ID 必须在参与融合的通道中具有一致身份。

from collections import defaultdict


def rrf(rankings, k0=60):
    if k0 <= 0:
        raise ValueError("k0 must be positive")
    scores = defaultdict(float)
    for ranking in rankings:
        # 同一通道的重复条目不能重复投票。
        unique = list(dict.fromkeys(ranking))
        for rank, chunk_id in enumerate(unique, start=1):
            scores[chunk_id] += 1.0 / (k0 + rank)
    return sorted(scores.items(), key=lambda item: (-item[1], item[0]))


if __name__ == "__main__":
    result = rrf([["A", "B", "C"], ["B", "D", "A"]])
    assert result[0][0] == "B"
    assert rrf([["A", "A", "B"]]) == rrf([["A", "B"]])
    assert rrf([]) == []
    print([chunk_id for chunk_id, _ in result])

OpenAI Retrieval 文档也提供了混合检索相关配置,包括稠密与文本通道的融合权重;不同托管接口的可调范围应以对应文档为准,不要把这个自定义实现当成其内部源码。Retrieval:Ranking

查询改写何时有帮助?

“它可以提前结束吗?”需要结合会话还原“它”指哪份合同。但改写也可能把用户没说过的条件加进去。

一个稳妥起点是保留原始查询,与改写查询并行召回,再记录改写来源。型号、否定词、金额、日期和条款号尤其需要检查。

多跳问题可以拆解。例如先确定客户适用的合同,再检索对应退出条款。每次检索都应有明确的信息缺口、预算和停止条件,否则 Agentic RAG 容易变成无限搜索。

8. 重排:判断候选是否真正回答了问题

召回器关注“别漏掉重要证据”,重排器关注“在这批候选里,把更能回答问题的排到前面”。

Bi-encoder 通常分别编码问题和文档,便于预计算与检索;Cross-encoder 则联合处理问题和候选,可以更细地判断两者关系,但逐候选计算的成本更高。

常见设计是先取较大的候选集合,再对有限候选重排。OpenAI Cookbook 展示了这种两阶段思路;其旧模型与旧接口示例已归档,不能直接当成当前接入指南。Search reranking with cross-encoders

重排器输入不应只有孤立正文,还可以包括标题、结构路径、版本和适用条件。但硬性权限和适用性约束应先执行,不能寄希望于重排分数把非法候选降下去。

一个关键上限是:重排器只能调整已召回的集合,不能凭空补回从未进入候选的证据。 如果例外条款在召回阶段就丢了,换更强 Reranker 可能毫无帮助。

还要注意:

  • 重排输入长度限制可能截掉真正关键的后半段。
  • 模型分数未必是“答案正确概率”,跨模型阈值通常不能直接复用。
  • Listwise LLM 重排可能受候选顺序影响,可用换序测试检查。
  • 重排很高的十个片段可能全是同一条款的重叠切块。
  • 对多跳问题,最重要的下一条证据不一定单独最像问题。

因此排序目标可以加入覆盖与去重。需要保留“版本不同且内容冲突”的证据,不能把它们误当成重复文本删除。

如果重排服务故障,可以降级到融合排序,但必须记录降级状态,并根据业务风险决定能否继续生成。降级是一种质量与可用性的取舍,不是无损替换。

9. 上下文组装:Top-K 不是最终答案

假设召回 100 条、重排后保留 20 条,其中 15 条来自同一份合同的重叠段落。直接把前 10 条塞进模型,很可能遗漏唯一的例外条款。

上下文组装应考虑证据集合的价值,而不只是逐条分数。

可以用一个简化优化目标表达:

在 Σ token_cost(d) ≤ B 的约束下,选择集合 S:

maximize 相关性(S)
       + 必要事实覆盖(S)
       - 重复度(S)
       - 未解决冲突带来的风险(S)

这里是设计视角,不是宣称存在一个通用、可直接计算的完美目标函数。冲突也不能靠扣分后简单删掉;重要冲突应被明确呈现,并根据来源与版本规则解决,无法解决时应说明不确定。

预算 B 需要扣除指令、问题、工具 schema、必要历史、预留输出与所用模型需要的其他空间。计数应使用对应模型的实际 Token 工具,不能用字符数替代精确预算。

一个实用流程是:

  1. 过滤失效、无权访问和不适用的证据。
  2. 合并同版本的近重复片段,保留多个来源定位。
  3. 根据结构补齐前提、定义、例外和必要相邻段落。
  4. 选择覆盖不同必需事实的证据集合。
  5. 对打包后的最终输入重新计数,而非只统计正文。
  6. 生成稳定的引用 ID,并保留原文映射。

给模型的证据可以采用这样的结构:

[E3]
来源:合同 contract_42,版本 v8,第 18 页,第 12.1 条
适用范围:欧洲 / 企业版 / 2026-01-01 起
原文:……

用途:与提前退出费用相关的例外条件。
说明:“用途”是检索系统附加信息,不是合同原文。

不要让生成的“用途”混入原文后再被引用。不要用相似度分数证明法律或业务结论。不要以为加了 XML 标签,就能保证模型不受其中恶意指令影响。

如果上下文过长,可以做抽取式裁剪或摘要,但要检查摘要是否保留否定、适用条件、数字和例外。证据压缩后的正确性需要单独评估。

10. 从证据到回答:有引用不等于被支持

下面的回答看起来很专业:

“欧洲企业版客户可以无条件提前退出。[E3]”

如果 E3 只允许在特定服务违约时免除费用,这个引用是真实存在的,结论却过度扩张了。

应把生成结果拆成可检查的主张:

主张 1:提前解约通常需要支付剩余金额。
支持:E2 的一般条款。

主张 2:特定服务违约情况下存在例外。
支持:E3 的例外条款。

主张 3:本客户满足例外条件。
支持:当前缺失;需要客户事实或业务查询。

这里必须区分文档规则与当前业务事实。RAG 找到“什么条件下允许退款或退出”,不代表已经证明“这个用户满足条件”。实时状态、余额、权限等应通过可信业务工具获取和校验。

对证据不足的情况,系统至少有三种行为:补充检索、向用户澄清、明确无法确认。选择哪一种取决于缺失信息来自语料、用户还是外部系统。

判断证据充分性时,可以检查必需字段与来源、相关性模型分数和模型辅助判断,但不能只依赖模型自己报出的置信度。拒答过多同样是失败,需要与错误作答一起评估。

11. 效果评测必须分层,否则指标无法指导改进

如果只有一个“回答满意度”,发现退化后很难定位。应该为每一层定义可观察指标。

层 关键问题 可观察指标
接入 重要内容是否成功进入系统 解析覆盖、结构正确率、更新延迟
索引 当前可访问语料是否可搜 索引就绪率、删除传播延迟、ANN Recall
召回 必需证据是否进入候选 Evidence Recall@K、完整证据集命中率
重排 重要证据是否排到可用位置 MRR、nDCG、固定预算下的证据覆盖
组装 证据是否真的进入最终输入 Packed Evidence Recall、重复 Token 比例
生成 结论是否正确并被支持 正确性、支持性、引用准确与完整性
产品 系统是否帮助用户完成任务 任务成功、拒答质量、延迟、成本

OpenAI 的评估建议强调任务相关数据集、持续评估以及用人类判断校准自动评分。文档里的数值目标只是示例,不能直接作为所有企业 RAG 系统的上线标准。Evaluation best practices

检索指标的分母必须说清楚

对一个问题 q,设人工标注的相关证据集合为 R_q,返回前 K 条为 C_q:

Recall@K    = |R_q ∩ C_q| / |R_q|
Precision@K = |R_q ∩ C_q| / |C_q|

RR(q) = 1 / 第一个相关结果的名次
MRR   = 对所有问题的 RR 取平均;无相关结果时 RR = 0。

DCG@K  = Σ_(i=1..K) (2^rel_i - 1) / log2(i + 1)
nDCG@K = DCG@K / 该问题理想排序的 DCG@K

无可用相关证据的问题需要单独定义计分,避免除以零;不应随意把它混进可回答问题的召回平均值。

这些公式还隐含一个条件:你知道相关集合。真实语料很难穷尽标注,因此可以通过多种检索器的候选池加专家补充来构建 judged pool,但必须承认未标注相关证据可能使指标有偏差。

切块策略变化时,原有 chunk ID 标注会失效。更稳妥的是以原始文档版本中的证据范围或事实单元标注,再映射到各版本 chunk。否则“召回率提高”可能只因为把同一段切成了更多块。

多跳问题需要完整证据集指标

合同问题至少需要一般条款、例外条款,以及当前客户适用版本。

只找回其中一个,Hit@K 已经可以为 1;MRR 也可能很好,但系统仍无法可靠作答。

因此可以标注若干最小充分证据集 G1、G2……,只要候选包含其中任意一套,就记为完整命中。这样能允许不同来源支持同一答案,又不把“找到一条相关内容”误当成“证据齐全”。

12. 一个数字例子:如何定位证据丢失?

下面使用一组刻意构造的数据,不代表真实系统表现:

回答必需证据:{一般条款 A,例外条款 B,适用版本 C}

召回 Top-20:包含 A、B、C。
重排 Top-5:包含 A、B、C。
最终上下文:只包含 A、C;B 因预算被裁掉。
模型回答:必须支付剩余金额。

此时:

  • 候选证据 Recall 是 3/3。
  • 重排保留的必需证据仍是 3/3。
  • 打包后的证据 Recall 是 2/3。
  • 最终回答错误。

调整向量模型无法直接修复这个例子,因为错误发生在上下文组装。

如果 B 从一开始就没有进入候选,则应该先检查接入、过滤和召回;如果 B 完整进入最终输入,模型却忽略它,则应检查生成模型、提示、证据布局和推理。

可以定义一个严格的、证据驱动成功事件:

I:权威语料中存在可访问且已正确接入的充分证据
R:候选检索保留充分证据
K:重排与上下文打包保留充分证据
G:生成正确且可支持的答案

P(I ∩ R ∩ K ∩ G)
= P(I) × P(R | I) × P(K | I,R) × P(G | I,R,K)

这是概率链式法则,不要求各阶段独立。它表达的是这条证据路径上的联合成功率,不等于所有可能回答正确的概率:模型可能凭参数知识猜对,另一条工具路径也可能得到答案。

工程上的意义是,某一层质量下降可能限制整体收益。盲目增强生成模型,不一定能弥补接入遗漏和证据截断。

13. 用 Oracle 实验区分检索失败和生成失败

只比较“换模型前后准确率”很难解释原因。更有帮助的实验是固定其他环节,替换一个中间产物。

实验 替换什么 帮助回答什么
Gold evidence → Generator 直接给人工确认的充分证据 模型在证据齐全时能否正确作答
Gold candidates → Reranker 候选中人工加入必需证据 重排能否保留正确证据
Gold ranking → Packer 按理想顺序提供证据 预算与扩展策略是否仍丢关键信息
Exact search → ANN 在同一向量空间比较 近似搜索损失有多大
Current index → Fresh snapshot 使用完整最新快照 错误是否来自更新与版本滞后

Oracle 不是无条件上界。例如人工证据过于干净,与实际上下文分布不同;不同错误还可能存在交互。因此这些实验用于诊断,不应被当成绝对因果证明。

随后做消融:稀疏、稠密、混合、加重排、加结构扩展分别测试。比较时同时控制最终上下文预算、候选规模和成本,避免一个方案只是因为读了更多文本而看起来更好。

14. 测试集应该覆盖系统真正会遇到的困难

一个有用的样本不只包含问题和标准答案,还应该包含:

问题、用户角色、查询时间、地区与产品条件
语料快照和版本
可回答性标签
必要事实与可替代充分证据集
预期引用范围
需要澄清或拒答的条件
风险类别

测试集至少包括:

  • 直接查事实的问题。
  • 表达与原文差异很大的问题。
  • 产品编号、条款号、错误码等精确匹配问题。
  • 需要多个条款共同判断的问题。
  • 新旧版本冲突和历史时点问题。
  • 文档确实没有答案的问题。
  • 用户没有权限访问答案的问题。
  • 表格、OCR、脚注和跨页内容问题。
  • 恶意内容试图指挥模型越权的问题。
  • 撤销权限、删除来源后再次查询的问题。

合成问题可以扩充覆盖,但如果全部由 chunk 自动反向生成,问题往往会贴近文档措辞,从而高估召回质量。应加入真实、脱敏的用户问题和专家构造的困难案例。

训练、调参和测试划分不能只随机拆问题。来自同一模板、同一文档或同一合同家族的近重复问题,可能造成泄漏。可以按文档家族、时间或业务实体分组划分,并保留冻结测试集。

15. 自动评审需要校准,线上实验需要结果指标

LLM-as-a-judge 可以评估主张是否被证据支持,但也可能偏好更长的回答,被格式影响,或重复生成模型的错误。

应提供明确评分标准、必要证据和原子主张;用专家标注集校准自动评分。做成对比较时随机交换答案顺序,检查位置偏差。对高风险问题保留人工复核。

至少分开报告:

  • 正确性:答案是否符合权威事实。
  • 支持性:给出的证据是否足以推出答案。
  • 引用准确性:引用的片段是否支持对应主张。
  • 引用完整性:需要支持的主张是否都有证据。
  • 拒答表现:该答时是否答、不能答时是否克制。

一个回答可能正确但无证据,也可能忠实复述了一份过期文档而在业务上错误。单一分数会掩盖这些差异。

比较版本时,应在同一问题集上做配对评估,记录分项差值和不确定区间。若样本按文档或客户聚集,统计方法也应考虑这种相关性,不能把所有问题当成完全独立样本。

线上实验则需要关注任务完成、用户纠正、升级人工、延迟、成本和安全事件。点击引用不一定意味着回答正确,用户可能只是因为怀疑才点击;满意度也可能偏好自信但错误的回答。

缓存必须包含与正确性相关的条件,例如租户、权限版本、语料 generation、时间范围与模型配置。只按查询文本缓存答案,可能把一个用户的答案返回给另一个用户,或在政策更新后继续提供旧结论。

16. 端到端运行链路应该如何记录?

推荐为一次请求生成可追踪记录:

request_id / principal_scope / policy_version
raw_query / resolved_constraints / rewritten_queries
corpus_generation / parser_chunker_embedding_versions
每个召回通道的候选 ID、名次和耗时
融合与重排后的候选 ID、分数和截断情况
最终打包的证据 ID、来源、Token 数和丢弃原因
生成模型与提示版本
答案、主张与引用映射
各阶段延迟、费用、降级与错误分类

这些记录可以帮助回答:例外条款究竟从未接入、没有召回、被重排压下去,还是最后被预算裁掉?

日志本身可能包含敏感查询、合同信息和权限上下文,需要最小化记录、脱敏、访问控制与合理保留期。诊断便利不能成为复制整个私有知识库到普通日志的理由。

17. 一个可以执行的优化顺序

面对“RAG 回答不好”,建议按下面的顺序排查:

  1. 证明答案存在:在正确版本和授权范围内,是否确实有充分证据?
  2. 检查接入:原文、表格和例外是否被正确解析?
  3. 检查候选:稀疏、稠密和过滤分别在哪一步丢掉证据?
  4. 检查排名:候选中有证据,但能否进入最终预算范围?
  5. 检查打包:扩展、去重和裁剪是否损坏完整含义?
  6. 检查生成:有充分证据时,模型是否仍然犯同样错误?
  7. 验证收益:在冻结测试集和线上指标中,改善是否成立?

这套顺序不会保证每次都找到唯一原因,但能避免把所有问题都归结为“模型不够强”或“向量库不好”。

18. 真正理解 RAG,需要能回答哪些追问?

  • 为什么 ANN Recall 很高,任务证据召回仍然很差?
  • 为什么先取 Top-K 再做权限过滤会损害可回答性?
  • 为什么换 Embedding 模型通常需要重建向量,而不仅是修改查询端?
  • 为什么命中一般条款不能证明已经拥有充分证据?
  • 为什么重排器无法解决候选阶段的遗漏?
  • 为什么 chunk 级 Recall 不能直接横向比较不同切块策略?
  • 如何证明错误来自上下文组装,而不是模型推理?
  • 如何在权限撤销、文档删除和缓存命中之间保持约束?
  • 如何验证提升来自算法,而不是更大的 Token 预算?
  • 如何分别衡量答案正确、引用真实、证据足以支持结论?

RAG 的核心工程能力,是把来源、版本、权限、检索、上下文和答案连成一条可以检查的证据链。系统每增加一个优化,都应该说明它改善了哪一环、引入了什么代价,以及通过什么实验判断有效。

参考资料

延伸阅读:理解 Agent Loop;AI Coding 中的 Skill 最佳实践。

KEEP READING

继续阅读.

Agent 与 AI · 2026-10-30Multi-Agent 原理与应用:从协作模式到生产系统设计系统设计 · 2026-10-30DDD 开发实战:从业务语言到领域模型,如何设计一个可靠的退款系统研发实践 · 2026-10-29应用 SDD 研发模式:让需求、AI 实现和交付结果保持一致