RAG · 主线案例 · 约90–120分钟 · Python 3.11+标准库

引用ID对了,900天为什么仍然错?

知识助手回答“订单签收后900天内可以申请退款”,脚注准确打开了当前退款政策。你点开文档,原文却是45天。链接存在、文档有权读取、版本也是新的,这句话仍然没有被证据支持。学完这一课,你会亲自跑一条检索管线,沿实际span找到错误,并让自动评测捕获它。

先修是Python函数、字典、循环和终端运行脚本;不需要向量数据库、云账号或模型密钥。实验中BM25、RRF、版本与指标是真实代码;dense、rerank、选择策略是教学替身。我们先把可核对的工程边界跑通,再讨论真实模型的语义质量。

先取得一份能回钻的运行

下载完整ZIP并解压,进入包含raglab.py和data目录的文件夹。执行前写下两项预测:把45改成900,但不动引用ID,会影响“引用可解析”还是“引用支持”?删掉必需的一条claim,分母应不应该跟着变小?

cd production-rag-lab
python3 -B raglab.py qualify
python3 -B -m unittest discover -s tests -v

Windows可用py -3替代python3。应看到pass为true、四组实验通过、18条测试通过。它们把结果保存到artifacts,不依赖网站localStorage。详细文件与排错见实验说明。若依赖/路径报错,先修运行环境;报错不是“知识质量低”。

从用户问题到候选,先不碰生成模型

打开data/queries.jsonl,找到q-error-e42。问题是“订单错误码E42是什么意思”,当前用户在acme的support-l1组。检索输入同时包含问题与可信身份,服务端先过滤租户、授权组、有效期和删除状态,再对可访问文档排序。将员工身份改为另一个组与把内容排后一点是两件不同的事;权限不足的内容不能借低分进入context。

BM25用词项命中、词频饱和、逆文档频率和长度归一化排序。E42这类标识值得保留原始串;只靠“意思相近”可能召回另一种错误。读artifacts/02-retrieval/bm25-results.jsonl,看该题的第一条是否为acme-error-codes,追到snapshot和score。然后读ablation-summary.json:这个小集有10题有相关集合,BM25的Recall@5为1,MRR为0.95、nDCG@5约0.963093。都找到了,仍可能有某题把正确文档排在第二位。

这里的dense_fixture只是同义词展开与token余弦;它得1分不说明某个embedding模型更强。RRF用排名而非直接相加两种不同量纲的分数。取常数60:文档A在两路第1/3位,融合分为1/61+1/63≈0.03227;文档B在两路第2/1位,分为1/62+1/61≈0.03252,B略高。可以手算后对照实现。没有进入任何候选的证据,不能由生成器凭记忆补齐。

教学计算图:两份排名如何变成RRF分数,常数60
文档BM25名次第二路名次两项贡献融合分
A131/61 + 1/630.03226646
B211/62 + 1/610.03252247
融合只使用名次,两路原始score无需同量纲。B在第二路更靠前,补偿了BM25的一位落后,最终略胜;未进入某一路候选时,该路贡献为0。遮住最后一列,用公式独立重算。

让一句claim走完四截链

检索返回当前退款文档,原文的一段是“订单 签收 后 45 天 内可以申请退款”。claim不是整篇答案,而是一条可核对事实。实验把它识别为refund_days=45,保存实际字符起止位置。引用不仅要回到文档ID,还要回到这个snapshot的这段文本;文档更新后,哈希与span必须跟随新快照。

合法ID仍可能无法支持claim:需要逐项比对值、span、版本和授权claim:900天数值不同:不支持span:45天来自实际正文本次context候选真正进入这里snapshot v4当前 + 有权 + 未删后面三格全通过,不能替第一格的事实支持签字。
从右向左核对:访问允许与ID可解析是必要条件;数字、条件与实际span相符才支持这条事实。

ground命令会生成answers.jsonl与claim-citation-audit.jsonl。评分器重新从claim.text解析限定事实,再从实际source span解析值。它不信任候选自报supports,也不把候选提供的context_ids当作真实检索记录。独立context manifest说明本次究竟交给生成侧哪些证据;合法但从未进入本次context的文档,也不能事后补成一次有依据的回答。

这是有限schema教学评测:目前接受退款天数、日志天数、境外审批、住宿上限、E42等明确句式,允许少量空白/标点差异。任意同义改写、复杂条件蕴含与开放领域事实还需要独立标注和校准过的语义评审。规则无法解析的句子会失败,不能声称实现了通用事实裁判。

故意造错,证明分数不会永远漂亮

打开mutation-results.json。当前12个必需事实全覆盖,precision与coverage为1。把合法当前引用下的45改成900,citation_resolvability仍可维持1,citation_precision降到11/12≈0.916667。反转“必须双重批准”成“不需要批准”,合法ID也救不了它。只删去一条必需claim,coverage降到11/12;分母来自独立expected_fields,不能因答案短了就自动变小。

三种不同的错误,不用一个总分掩盖
变式首先失效的条件应下降的指标
900天,引用ID仍合法claim值与实际span不等citation_precision
引用不存在的snapshotID不能解析citation_resolvability
少答一个必需事实任务覆盖不足claim_coverage

你来做一次红→绿:在自己的副本暂时移除值相等检查,运行数字篡改测试,它应失败;解释为什么“文档名看起来对”不能给这句话签字。恢复检查,再跑完整18条。验收产物是修改前后测试输出、对应claim与source span,而不是截图里的一个100%。正常授权请求仍应通过,不能靠一律拒答获得安全分。

为什么运行时不能偷看qrels?

qrels是评测者对相关证据的标注,像考试答案。用户问退款例外时,运行时可以解析出refund_days与refund_exception这两个请求字段,检索并观察它们是否有实际证据;它不能先读gold文档ID,再把“找全答案”当真实停止能力。现在agent_run只接请求、策略和预算;query中的可观察信息形成请求合同,qrels只在运行结束后进入evaluate_agent_run。

试验三种策略时每次都用top_k=3、最大4次预算。当前小集三者成功率同为1,iterative平均调用约1.166667,其余为1。多一次循环没有质量增益,就不应无条件加循环。单次策略只用一次检索;迭代策略使用固定的教学子查询,不代表LLM规划能力。改变gold标注可能改变离线分数,但同一个请求的events与stop_reason必须不变。18条测试中有一条专门锁住这个边界。

同一租户还不是同一个授权范围

主管查询得到内部例外政策,员工问同一句。若缓存key只有tenant、acl_epoch和query,两个人在同一时刻仍然撞键;权限从未变化,也会泄露。key要加入可信principal_scope摘要,必要时到subject级,再包含索引/策略版本与时效边界;命中后复核证据权限和tombstone。另一个问题才是“主管后来被撤权”:要更新权限版本,并让旧缓存失效。两种反例要分别写,不要只测试epoch增加。

实验实现了检索前ACL与删除对账,没有共享答案缓存。你在这一节交付缓存失败测试设计,不能把设计写成已经跑过。具体机制可接Ch20;版本化删除与回滚接Ch22。回滚索引指针不能复活已撤权内容,答案缓存也必须独立清理或按版本隔离。

换一份数据,检验你有没有理解

在自己副本把corpus-v2当前ACME退款45改成52,其他请求不变。先预测、再qualify:当前source应产生新hash与snapshot,生成器应抽取52;候选仍写45时必须判unsupported。提交两份manifest、两条claim audit、一条数字篡改失败用例和修复后测试输出。再回答:把qrels里相关集合清空,为什么可以改变成绩却不能改变已经发生的工具轨迹?

独立做完再看参考答案与通过条件

内容改变产生新snapshot;合法当前ID只证明可定位,45与新span的52不等仍失败。qrels是评分输入,runtime_request剔除gold标签,agent_run没有qrels参数;工具事件与停止只依赖请求/证据/预算。通过需同时保留正常请求成功、未知/无权限请求不出证据、数字反例被捕获、完整测试通过。缓存设计区分principal变化与权限版本变化;未实现部分明确写“测试设计”。

接下来读什么

你已经能把“没找到、没进入context、claim歪曲、来源失效”分开。继续Ch18分层评测,用同一query set比较方案;要扩展语料生命周期,读Ch06/08/22。先保住这条证据链,再选真实embedding/生成adapter。

Elastic官方RRF公式:带着A/B排名例子核对rank从1开始;Azure官方用户/组安全过滤:读之前先列出主管与员工的principal差异,不要把过滤字段误当认证本身。