RAG 优化前先做评测:检索、引用与拒答的最小基线
RAG 回答不好时,团队常先改 chunk 大小、换 embedding 或增加 prompt。没有评测基线,这些变化只能带来更好看的几个 demo。最小评测应把系统拆成可观察阶段:相关证据是否被检索,回答是否只使用证据,引用是否指向支持句,证据不足时是否拒答。本文只构建自己的确定性数据集,不声称通用 benchmark。
从 40 个高价值问题开始
选取真实文档的典型任务,覆盖直接事实、跨段组合、相似术语、过期版本、权限隔离和无法回答。每个样例记录问题、允许文档集合、一个或多个证据 span、可接受答案要点,以及是否必须拒答。证据使用文档 ID、版本和字符/段落位置,不只保存容易变化的 URL。
数据集由领域专家审核。将开发集和保留集分开;优化只看开发集,最后一次才运行保留集。若每次失败都立即改 gold answer,评测会逐渐迎合当前系统。新增案例记录来源与原因,旧结果保持可重放。
from dataclasses import dataclass
@dataclass(frozen=True)
class RagCase:
case_id: str
question: str
evidence_ids: frozenset[str]
required_points: tuple[str, ...]
should_abstain: bool
Schema 保持小而清楚,原始私密文档不写进测试日志。CI 可用脱敏或合成片段,安全环境再运行受控真实集。
先独立测检索
对每个问题保存 top-k 文档与 chunk ID、分数和检索配置。计算 evidence recall@k:gold span 所在 chunk 是否出现;同时观察无关 chunk 比例和首个相关证据排名。只测最终答案会把“根本没找到”误判成生成问题。
比较 chunk 策略时固定 embedding 模型、索引版本和 query。更大 k 可能提高召回,也会增加上下文噪声和成本。混合检索、重排器和 metadata filter 分阶段添加,每次只改变一个变量。权限过滤必须在召回之前生效,不能依赖生成模型忽略无权文档。
引用需要精确验证
答案每个事实性声明应关联证据 ID。自动检查引用 ID 存在、属于本次检索且用户有权限;领域评审再判断 span 是否真正支持该声明。引用一份相关文档不等于引用支持句,引用位置越宽越容易掩盖不忠实回答。
可以计算 citation precision 与 coverage,但保留错误案例列表。一个关键数字引用错误可能比三个次要引用正确更危险。对版本化文档,答案应优先最新允许版本,并在问题明确询问历史时引用对应旧版。
拒答是正式能力
加入文档中不存在答案、问题有错误前提、证据互相冲突和权限不足的案例。系统应说明缺少什么,而不是靠常识补全。评估正确拒答、错误拒答和不该回答却回答三类结果。阈值根据业务风险选择,不能只最大化整体回答率。
生成 prompt 明确只依证据回答并要求引用,但 prompt 不是保证。对金额、合规和操作指令增加确定性后验证;无法验证则转人工。即使模型“知道”答案,RAG 产品若承诺基于企业资料,就应遵守资料边界。
LLM judge 是辅助信号
LLM judge 可按 rubric 评估忠实、相关和完整,并帮助扩展人工审查,但它也有偏差和非确定性。固定 judge 模型、版本、prompt、温度与输入格式;在人工标注子集上计算一致性;保存理由但不把流畅解释当真值。模型升级后重新校准。
优先使用确定性指标:检索是否含 gold、引用 ID 是否有效、JSON 是否合格、必需关键词或数值是否匹配。judge 处理难以规则化的语义部分。报告二者,不把一个平均 judge 分数当成整个 RAG 的质量。
固定整条版本链
每次运行记录语料快照、分块代码、embedding、索引、reranker、生成模型、prompt、judge 与评测代码 commit。输出按 case 保存,失败能从文档到回答重放。看总体指标也看文档类别、语言和问题类型分段。
结论
RAG 优化从可诊断基线开始:小而可信的 gold spans、独立检索召回、逐声明引用、正式拒答集和经人工校准的 judge。冻结版本,一次改变一个变量,并保留失败案例。这样“换模型后变好”会变成可验证的哪一阶段、哪类问题、以什么成本变好。