助手 · 观测 / 审计 / 评测怎么分工
日期: 2026-08-28 | 作者: sunxin(+ Cursor AI 结对讲解)
你问:「assistant_audit是专门用来检测回答对不对的吗?第一组有问题但答对了,单测测不出来,是不是只能自己看指标改代码?」
姊妹篇:W5 事实校验与审计怎么读 · 评测集两轨 · 心智地图 §3 · 幻觉与 grounding
TL;DR
AI 应用的难点不是「写完能答」,而是 「答对了但链路不健康」这种隐性问题怎么被发现。
- RAG 指标不替代用户判断对错。 它把一次回答拆开:检索有没有命中、阈值过没过闸、生成是否忠实、有没有降级、错因在哪一层。
- 听感答对 ≠ 健康地直接答对。 第 1 句海拔数字对,但
rag_degraded=1:系统靠 W5 丢掉模型原答、摘录兜底才答对,不是两道闸都过。不看指标会当成「全对」,误杀会一直在。 assistant_audit+rag_*+assistant_feedback+rag-cases.json是同一类东西的轻量自建版。 大厂用 LangSmith / Phoenix / OpenAI Tracing+Evals:记 trace、建 dataset、跑 eval、做人审、做实验对比。我们刻意不做 W7 大盘,一行 SQL 就是看板。- 单测锁已知规则,发现未知靠线上 trace。 Mock 测不到 DeepSeek 把「那边」抽成目的地、写成「的湖面」。闭环:手测 → 看审计 → 收成夹具 → 改那一层 → 回归。
1. 「审计」在这里是什么意思
软件里 audit / 审计 = 把一次请求发生了什么记下来,事后能复盘。航班黑匣子、支付对账、权限操作日志,都是同一类东西。
它回答的是:
这一轮走了哪条路?检索 query 是什么?top1 哪一篇、几分?W5 有没有把模型答案丢掉?花了多少 token?
它不回答:
海拔 2073 在现实里对不对?(那是知识库作者的责任)
这句话好不好听?(那是产品/人工抽检)
用户满不满意?(那是assistant_feedback点赞)
所以:审计 = 可观测性(observability),不是金标准(golden answer)。
| 东西 | 表 / 文件 | 干什么 | 不干什么 |
|---|---|---|---|
| 运行时审计 | assistant_audit | 每轮一行,含 RAG 闸门字段;Web「对话审计」Tab | 不判「标准答案」 |
| 用户反馈 | assistant_feedback | 点了有用/没用才有行;Web「用户反馈」Tab | 没点赞的对话不出现在这个 Tab |
| 分诊评测 | eval-cases.json | 这句话该判成哪个意图 | 不管攻略正文漂不漂亮 |
| 检索评测 | rag-cases.json | top-1 是不是那一篇、数字能否对回夹具 | 不管 DeepSeek 会不会写成「的湖面」 |
| 编排单测 | *ToolTest | stub 检索/生成,锁「过闸后代码怎么走」 | stub 不到真模型临场发挥 |
Web 助手质量是 两个 Tab、同一条 trace:对话审计 = 全量 Runs;用户反馈 = 人审队列。手测不必点赞就能在审计 Tab 看到闸门。
2. 为什么「答对了」审计里仍可能是红的
知识问答有 两道闸(详见 W5 手册):
- 检索闸:top1 分数 ≥ 阈值 →
rag_grounded=1,否则拒答。 - 生成闸(W5):答案里的数字 / 月份 / 地名必须能在 本轮 topK 片段 里对回原文 → 对不上就丢掉模型答案,改摘片段。
用户看见的是最终气泡。降级摘录往往仍含正确数字(「根据资料,湖面海拔约 2073 米」),所以 听感上答对了。审计记的是第二道闸有没有开火:rag_degraded=1 + rag_verify_fail_reason=places=…。
这不是「指标造假」,是两套尺子:
| 尺子 | 问的是 | 谁用 |
|---|---|---|
| 用户听感 | 最后这句话能不能用 | 你手测、用户点赞 |
| 闸门 | 模型有没有说出片段对不回的事实;正则有没有误杀 | 你查审计、改代码 |
第一组「赛里木湖海拔」:检索对、数字对,但旧正则把「赛里木湖的湖面」当成新地名 → 降级。结果可用,过程误杀。 不看审计就当「全对」了,误杀会一直在。
两种「答对」不要混:
| 用户看到 | 审计 | 含义 | |
|---|---|---|---|
| 健康地答对 | 模型组织的话 + 来源 | grounded=1 degraded=0 | 检索对、生成也对回片段 |
| 兜底答对 | 「根据资料,」+ 摘录,数字往往仍对 | grounded=1 degraded=1 | 生成闸开火(真幻觉或正则误杀),靠摘录才没把错的给用户 |
指标的价值正在这里:把第二种从「感觉还行」里捞出来,告诉你该修校验规则,而不是去改知识库或换模型。
3. 单测为什么测不出来(以及不该指望它测出来)
单测要 可重复:输入固定,输出断言固定。所以检索/生成在测试里是 stub(你写死「模型会回这句」)。
线上 DeepSeek 每次措辞不同:
| 线上真实发生 | 默认 Mock / 单测 |
|---|---|
| 「赛里木湖的湖面海拔…」 | 夹具写的是「赛里木湖海拔是 2073 米」,旧正则不炸 |
| 「去青海湖…正规观景区…环湖」 | 没人预先把「观景区」写进断言 |
把「那边」抽成 destinationKeywords=["那边"] 盖掉会话里的赛里木湖 | Mock 把「那边」当停用词丢掉,会话槽还在,W4 单测一直绿 |
不是单测没用,是单测锁的是「已知规则」。 未知措辞只能:线上打到审计 → 把这一次收成新夹具 → 下次回归才红。
行业里这叫 eval(评测集)+ observability(线上观测) 两手。没有谁靠「把模型所有说法写进 JUnit」过关。
4. 和大厂那套是同一件事(只是轻量)
大厂不靠「相信模型」,也不靠人工看每条答案。共同做法是:
记录 trace(这一轮内部每一步)
→ 建 dataset(固定问法 / badcase)
→ 跑 eval(回归:改完有没有把别的搞坏)
→ 抽检标注(人审:听感 + 错因)
→ 实验对比(换 Prompt / 阈值 / 模型,看指标)产品形态是 LangSmith、Arize Phoenix、OpenAI Tracing / Evals、Langfuse 一类。共同点都是 trace + 指标看板 + 抽检 + badcase 集 + 回归实验。
本项目对应关系(ADR-054:加厚现有审计行,不新建 rag_trace、不上 W7 大盘):
| 大厂能力 | 本项目现在 | 还没做(有意) |
|---|---|---|
| Trace / span | assistant_audit 一行 = 一轮;rag_query / rag_top1_* / rag_grounded / rag_degraded / rag_verify_fail_reason | 独立 rag_trace 表、逐步 span 瀑布图 |
| Online 看板 | 助手质量「对话审计」Tab 读 RAG 列(不做按天折线) | Grafana / W7 大盘 |
| Dataset | eval-cases.json(分诊)· rag-cases.json(检索) | 自动从线上灌 badcase |
| Evals / 回归 | AssistantEvalRunner · RagEvalRunner · Checker 单测 | 托管实验、A/B |
| 人审 | assistant_feedback + 你自己看审计 | 标注平台、抽检队列 |
补 rag_* 的目的,就是从「感觉回答还行」推进到:知道它为什么行、哪里不健康、该修哪一层(检索 / 阈值 / W5 正则 / 会话改写 / 知识库)。
B 端怎么摆这两路数据:列表分 Tab(对话审计 / 用户反馈),点开后是同一条 trace。不是合成一个命中率,也不是两个互不认识的页面。见 ADR-056。
5. 「看指标再改代码」是不是很大一摊
大厂会做仪表盘、周报、自动 badcase 池。我们现在 刻意不做 W7 大盘。
维护者一周够用的闭环:
手测 6 句(或用户反馈一条)
→ SELECT id, message_preview, rag_query, rag_grounded, rag_degraded, rag_verify_fail_reason
FROM assistant_audit ORDER BY id DESC LIMIT 20;
→ 分清:检索失败 / W5 真拦幻觉 / W5 误杀 / 会话槽被覆盖
→ 误杀或覆盖:加 1 条 Checker 或 ToolTest,改正则 / strip 指代词
→ 重启后再问同一句,看新行 rag_degraded=0你不需要先上「全命中率」「忠实度均值」才能开工。那些是量变之后的统计。现在每一行审计已经带齐闸门字段,一行 SQL 就是指标。
工作量大的部分其实是:正则误杀会随模型措辞冒出来,要一次收一条。这是概率系统的常态(心智地图 §1),不是你漏做了某张表。
6. 和「全命中」相关的词,别混
| 你可能听到的 | 在本项目里实际指 |
|---|---|
| Recall@1 / 命中正确篇 | 检索 top-1 是不是评测里标注的那篇(rag-cases.json) |
| grounded | 检索闸:分数过阈值,不是「答案客观正确」 |
| degraded | 生成闸开火,模型原答被丢掉 |
HIT(outcome) | 这轮给了用户一份非拒答回复(含降级摘录) |
| 忠实(faithfulness) | 答案事实能否对回给定片段(W5 / 评测 check) |
| 有用/没用 | 用户点的,跟闸门无关 |
所以:不要用 outcome=HIT 当「模型全对」。 HIT + rag_degraded=1 恰恰是「给了可用回复,但生成没过闸」。
7. 手测 6 句时建议盯的列
重启后端(新正则 + 新 strip 才生效;第一次攻略问答仍可能慢,那是建索引)。
| # | 问 | 期望审计(过程) | 用户听感 |
|---|---|---|---|
| 1 | 赛里木湖海拔多少米? | grounded=1 degraded=0,reason 空 | 可保留模型原句,不必再套「根据资料」 |
| 2 | 青海湖有什么注意事项? | 同上;若仍 places=黑马河 可能是 该 chunk 没写黑马河(真缺实体,不是「去青海湖」误杀) | 注意点来自青海湖篇 |
| 3 | 赛里木湖攻略 | degraded=0(环湖/观景区不再误杀) | 攻略长文 + 来源 |
| 4 | 同一 session:那边怎么去 | rag_query 含「赛里木湖」,不要 top1 北京 / 拒答 | 交通类攻略 |
| 5 | 迪拜购物中心有哪些品牌? | grounded=0 拒答 | 明说没有 |
| 6 | 赛里木湖有什么活动? | RAG 列空,SEARCH_ACTIVITIES | 活动卡不是攻略 |
查法:message_preview 对上问句,看同 session_id 的第 4 句 rag_query。
8. 要测多少条?160 个新疆景点要不要全测?
不要。 自动评测按 失败模式 / 问法类型 / 易混对 覆盖,不按「库里有 N 篇就写 N 套题」。160 篇全测一遍,测的是内容运营(这篇攻略写没写错),不是助手链路。
大厂也不会对知识库每条文档做完整问答回归。他们分三层:
| 层 | 覆盖什么 | 规模感 | 本项目现在 |
|---|---|---|---|
| 黄金集(手写,进 CI) | 问法类型 + 硬负例 + 易混地名 | 大约 20~50 条 RAG 就够用很久;意图集可以到几十条 | rag-cases 16 条(5 个种子目的地 + 冰岛/迪拜拒答);eval-cases 68 条 |
| 参数化冒烟(可扫 160 篇) | 只用标题/地名当 query,看 top-1 是不是自己这篇。不调 DeepSeek | 1 个测试 × N 篇,不是 N×6 道问答题 | 还没有;库涨到上百篇时再加最划算 |
| 线上全量(用户问到才测) | 降级率、拒答、易混检索 | 160 篇都会「被测到」,靠 assistant_audit 按 rag_top1_source 聚合 | rag_degraded / rag_verify_fail_reason 已有 |
第 1 句那种「数字对但 degraded=1」,不是再给赛里木湖加 159 条同类海拔题能发现的。它是一种失败模式(「的湖面」误杀),黄金集里锁 1 条措辞 + 1 条 Checker 即可;160 个湖都会受益。
8.1 黄金集里该有什么(不是 160 个「海拔多少」)
每种问法 1~2 个代表目的地就够:
- 事实:海拔 / 季节 / 距离
- 攻略口吻:注意事项、怎么去、住哪、适不适合第一次
- 分诊边界:「有什么活动」走找局,「XX攻略」走知识,「N 日游」走计划
- 硬负例:库外(冰岛、迪拜)必须拒
- 易混对(新疆库越大越重要):赛里木湖 vs 青海湖、喀纳斯 vs 禾木、天山天池 vs 赛里木湖——问 A 不能 top-1 成 B
你现在缺的主要是 易混对,不是「每个景点一条」。库从 5 篇涨到 160 篇,检索串篇风险上升,黄金集加 5~10 条易混 比加 160 条海拔题有用得多。
8.2 160 篇真正该做的两件事
- 内容对不对:写库的人抽检正文(数字、季节、注意事项)。W5 不负责这个。
- 检索会不会捞错篇:标题冒烟(上面那一层)+ 易混对黄金集 + 线上看
rag_query/rag_top1_source。
大厂的日常是:黄金集保持小而稳(改切块/阈值必跑)→ 线上 100% 记 trace → 按 degraded、点踩、串篇 挖 badcase 再灌回黄金集。所以你会觉得「永远测不完」:不是 160 没测完,而是模型措辞会冒出新失败模式,一次收一条。
一个人维护时,够用的目标不是「160 全覆盖」,而是:
- CI:现有 16 条 RAG + 68 条意图 不退步;易混对随新疆热点补几条。
- 线上:每周扫一眼
rag_degraded=1的 reason,重复的收成单测。 - 不要为每个新景点写 6 句自动问答。