定位质量损失与生产演进
一个最终错误答案可能来自文档缺失、解析丢字、检索没中、重排压低、上下文丢弃或生成读错。先记录每一阶段,再做替换实验:把人工确认的证据直接交给生成器,若恢复正确,先修检索链;仍然错则检查提示、模型、输出校验和问题本身。
相关文档命中不等于回答正确
Recall@k衡量标注相关文档中找回了多少;MRR关注第一个相关结果的倒数排名。假设问题需要两个片段,MRR满分仍可能漏掉一半证据。
def metrics(ranked, relevant, k):
if not isinstance(k, int) or isinstance(k, bool) or k < 0:
raise ValueError("k must be a nonnegative integer")
hits = ranked[:k]
recall = len(set(hits) & relevant) / len(relevant) if relevant else None
rr = next((1 / (i + 1) for i, x in enumerate(hits) if x in relevant), 0)
return recall, rr
assert metrics(['a', 'x'], {'a', 'b'}, 2) == (0.5, 1.0)
assert metrics(['x', 'b', 'a'], {'a', 'b'}, 3) == (1.0, 0.5)
assert metrics([], set(), 3) == (None, 0)
assert metrics(['x', 'x', 'a'], {'a'}, 3) == (1.0, 1 / 3)
assert metrics(['a', 'a'], {'a'}, 2) == (1.0, 1.0)
assert metrics(['a'], {'a'}, 0) == (0.0, 0)
print('metrics passed')无相关文档的问题单列为不可回答集,不把 None 偷换成满分或零分掺进召回平均。召回分子按唯一 ID 计数;倒数排名保留原列表的位置,不能删除重复项后把第三名提升为第二名。重复 ID 还应作为独立管道异常上报。问题需要多跳链条时,除单片段相关性还需标注联合证据是否完整。
把实验变成可比较的变更
固定问题集和文档快照,对比一个变量:切分、embedding、重排或模型。按语言、长文、否定、更新后政策、跨租户、无答案分层看结果;总体平均提升可能掩盖少数高风险组下降。生成质量分别检查正确性、事实支持、引用准确和拒答质量。LLM评审要盲化版本、交换顺序、人工标定,不用同一个模型“自信评分”充作事实。
服务层还要记录召回延迟、重排延迟、生成首字时间、总耗时、输入输出 token和费用。P95来自足够请求的分位数,单次计时没有代表性。将查询改写、召回候选和最终证据关联到同一trace;资料和提示含隐私时采用受控存储及脱敏。
生产缓存不能只按问题文本:还需租户、权限版本、文档快照、模型和提示配置。文档删除后相关向量与答案缓存同时失效。新索引先影子查询,确认旧问题与变更问题均达标后灰度切换,保留旧索引以回滚。检索服务超时可回退词法索引,但要向调用者标记降级;不能假装低质量回退与正常路径等价。
成熟系统常保留传统关键词RAG和新向量/图路径并行。维护旧系统先读取其分词、分数方向、过滤行为和文档ID契约,再引入新模型;“改成新框架”本身不是修复证据。
练习
新增指标 hit@k:有至少一个相关文档则为1,否则0。用正文第一个测试说明 hit=1、MRR=1 为什么仍不能证明资料完整。
完成后查看本章答案。
返回完整学习路线。