“准确率从 64.2% 提升到 94.8%”很醒目,但若只有 50 道题,每题只算对错,单次整体比例应以 2 个百分点为步长变化。小数不一定错,也可能来自多次平均或不同指标;问题是这些口径有没有写出来。
个人知识库的评估至少拆成三层:是否召回证据、回答是否被证据支持、没有答案时是否克制。不能用一个“准确率”把它们盖住。
平均成功和反复可靠不是一回事
2026 年 10 月 22 日 ThinkingBox 的研究分别报告单次平均、至少一次成功和多次全部成功。它们回答不同问题,排序也可能不同。研究原文
私人 RAG 同样需要记录每次候选与回答,而非只保存一张最终百分比表。先做十几道能人工复核的题,比拿没有标注来源的大表更有价值。
图中的 Recall 为可回答题的 2/3,ABSTAIN 为全部题目中拒答占比 1/4;后者不是拒答正确率。
一个可复算的小评估器
保存为 evaluate.py。金标准和结果都是教学输入,不是某个模型的跑分。
gold = {"Q1": {"A"}, "Q2": {"B"}, "Q3": {"C"}, "Q4": set()} retrieved = {"Q1": ["A"], "Q2": ["B"], "Q3": ["Z"], "Q4": []} answerable = [q for q in gold if gold[q]] unanswerable = [q for q in gold if not gold[q]] recalls = [ len(set(retrieved[q][:1]) & gold[q]) / len(gold[q]) for q in answerable ] mean_recall = sum(recalls) / len(recalls) abstain_rate = sum(not retrieved[q] for q in gold) / len(gold) correct_abstain = sum(not retrieved[q] for q in unanswerable) / len(unanswerable) assert abs(mean_recall - 2/3) < 1e-9 assert abstain_rate == 1/4 assert correct_abstain == 1.0 print({"mean_recall@1": mean_recall, "abstain_rate": abstain_rate, "correct_abstain_on_unanswerable": correct_abstain})每道可回答题在这里恰有一个相关文档,因此平均 Recall@1 恰好等于题目命中率。多相关文档时,两者不能直接画等号。没有不可回答题时,拒答正确率应标不可计算,而不是默认 100%。
把测试集留在调参之外
按时间切分笔记,问题也覆盖时间歧义、同名记录、已删除信息、相互矛盾记录。修改阈值时使用调参集;最终测试集冻结,并记录模型、索引、提示和数据版本。
生成回答另行人工标注“每个事实是否有证据”,不能因为召回命中了就判整句答案正确。耗时与成本也独立记录,模拟分数不能变成设备性能。
本文实跑了指标计算,没有运行检索模型或得到私人库准确率。愿意把分母写清楚,评估才真正帮得上下一次改进。