项目记录

质量门禁与评测闭环

用入库、效果、性能三类门禁和线上错例回流,建立 RAG 系统持续评测与发布验收闭环。

发布于
本文目录 · 8 节

第 1 篇开头留了一个问题:改一行阈值,FAQ 直出准确率从 92% 掉到 71%,没人发现。

这一篇就是回答它。RAG 系统的退化是静默的——不报错、不崩溃、接口 200, 只是答案慢慢变得不对。所以必须有一套不依赖人的机制,在它变坏的时候亮红灯。

先给结论

text
一道闸管入库   :脏数据不许进库
二道闸管效果   :召回、排序、关键词覆盖、命中路径不能掉
三道闸管性能   :首 token 和总耗时不能劣化
一道闭环管演进 :线上错例 -> 回归用例 -> 下一轮被门禁拦住

门禁负责拦住退化,闭环负责让系统变好。 两者缺一不可。

一、评测集:三种问题,三套数据集

评测集回答什么问题文件
主链路回归本来能答对的问题现在还答得对吗eval_sets/enterprise_knowledge_regression.json
多轮追问带上下文的追问还接得住吗eval_sets/enterprise_knowledge_followup.json
性能基线延迟有没有劣化eval_sets/enterprise_knowledge_performance.json

回归集的样本是带契约的,不是一堆问题文本:

json
{
  "case_id": "enterprise_vpn_faq",
  "source_filter": "it",
  "tenant_id": "default", "dataset_id": "default",
  "visibility": "public", "user_role": "public",
  "query": "VPN 连不上应该怎么处理?",
  "expected_hit_type": "faq_direct",
  "expected_keywords": ["网络", "账号", "重启", "IT 工单"],
  "expected_source_contains": ["VPN 连不上应该怎么处理", "it_support.md"]
}

注意 expected_hit_type 和 expected_source_contains:它不只断言答案包含什么, 还断言走了哪条路径、来源是哪个文件。 否则「答案碰巧对了,但路径错了」这种退化会被漏掉—— 而路径错误往往意味着下一步就会出错。

二、四道闸的默认阈值

效果门禁(scripts/quality/check_evaluation_gate.py):

text
Recall@K                 >= 0.8    预期来源有没有被召回
MRR                      >= 0.6    正确来源排得够不够前
关键词覆盖率             >= 0.7    答案关键事实是否覆盖
命中路径准确率           >= 0.7    FAQ / RAG / 边界判断是否正确
FAQ 直出准确率           >= 0.7
Prompt Profile 路由准确率 >= 0.7
场景隔离准确率           == 1.0    越权这类错误零容忍
错误率                   == 0

注意最后两项的特殊待遇:场景隔离必须 100%,错误率必须为 0。 其余指标可以设阈值容忍波动,但「跨租户串数据」和「样本直接报错」不可以。 门禁阈值按指标性质分开对待,比一刀切更有意义。

入库门禁(前一篇讲过)和性能门禁同理:

text
平均总耗时      <= 15s      首 token 平均 <= 8s
P95 总耗时      <= 30s      首 token P95  <= 15s
错误率          == 0

首 token 单独设阈值是刻意的:流式问答的用户感知发生在首 token,不在总耗时。 总耗时 3 秒但首 token 2.5 秒,体感比总耗时 8 秒、首 token 0.8 秒差得多。

三、闭环:让错例变成资产

没有闭环的评测是消耗品——每次发布都要重新发现同一批问题。这里的闭环是三段:

text
1. 从评测报告里抽取失败样本      scripts/extract_bad_cases_from_report.py
2. 人工确认(判断是真错还是标注问题)
3. 提升为回归用例                scripts/promote_bad_cases_to_regression.py
   -> 下一轮门禁自动拦住它

用户反馈也有对应出口:scripts/export_feedback_bad_cases.py 把差评反馈导出成候选错例。 关键是第 2 步不能省——自动提升会把标注错误一起固化进评测集, 之后你会花时间优化一个本来就不该是问题的样本。

scripts/quality/run_v1_quality_cycle.py 把这一整套串成无人值守循环: 跑评测 → 出门禁判定 → 归档报告。适合放在 CI 里每日跑一次。

四、测试体系:30 个用例文件,覆盖主链路每一环

text
意图与策略    test_intent_and_scenarios / test_intent_decision_gateway / test_intent_model_governance
检索与生成    test_retrieval_and_prompt / test_answer_confidence / test_stream_query_events
缓存与性能    test_enterprise_cache / test_retrieval_cache / test_retrieval_warmup_wait
入库与质量    test_ingestion_incremental_contract / test_quality_gates / test_threshold_calibration
治理与版本    test_mysql_metadata_stores / test_v1_maintenance_bindings

这些用例能跑得快,是因为关键逻辑都是纯函数或带假依赖的(tests/conftest.py), 不需要真的起 Milvus 和 LLM。第 6 篇说的「把置信度抽成纯逻辑」,回报就在这里。

五、交付验收:发布前跑一遍

powershell
# 1. 单元与契约测试
python -m pytest tests -q --tb=short

# 2. 项目护栏:目录结构、约定、禁止事项
python scripts/check_project_guardrails.py

# 3. 主链路评测 + 效果门禁
python scripts/evaluate_core_chain.py --dataset eval_sets/enterprise_knowledge_regression.json --limit 20 --output reports/evaluation/enterprise_knowledge_latest.json
python scripts/quality/check_evaluation_gate.py --report reports/evaluation/enterprise_knowledge_latest.json

# 4. 追问门禁与性能门禁
python scripts/evaluate_followup_chain.py --dataset eval_sets/enterprise_knowledge_followup.json
python scripts/quality/collect_performance_baseline.py --dataset eval_sets/enterprise_knowledge_performance.json --limit 6 --output reports/performance/enterprise_knowledge_latest.json
python scripts/quality/check_performance_gate.py --report reports/performance/enterprise_knowledge_latest.json

# 5. 发布校验
python scripts/verify_v1_release.py

五步全绿,才叫「这次改动没有把系统改坏」。所有报告都落盘到 reports/, 管理端还有 /api/admin/gate_reports 直接查看历史门禁结果。

取舍与边界

  • 评测集小则统计意义弱:--limit 20 适合日常快速回归, 正式发布应该跑全量;样本少于几十条时,1% 的波动可能只是一个样本。
  • 阈值太严会「红灯疲劳」:天天失败的门禁等于没有门禁。 阈值应该跟着当前真实水平走,每次优化后上调一点,而不是一开始就设在理想值。
  • 评测集必须跟着业务演进:业务新增了知识分类,评测集要同步补样本, 否则你会为一条过期的预期反复调试。

收尾:这一整套东西到底买到了什么

回头看这 8 篇,SHUN RAG 的每一个设计都在回答同一个问题: 怎么让系统在没人盯着的时候也不出错。

text
架构分层      让链路可读、可调试、无旁路
启动校验      让环境问题在启动时暴露,而不是在用户面前
数据契约      让知识入库有边界
切分与指纹    让检索准、更新省
意图与策略    让不同问题走不同的路
混合检索重排  让正确答案进得了上下文
置信度与引用  让不该答的就不答
缓存版本隔离  让多租户、多版本 coexist
门禁与闭环    让退化被拦住,让错例变资产

如果只带走一句话:RAG 的工程量,绝大部分不在模型上,而在这些「不回答」和「不通过」的判断里。