章节04 / 14
- 01AI Harness 不是测试脚本,而是 AI 系统的质量控制台
- 02先写任务协议,再谈评测指标
- 03Golden Dataset:把“感觉不错”变成可回归样例
- 04评测不是一个分数:判分器、断言和人工复核怎么组合
- 05结构化输出 Harness:先挡住形状错误,再处理业务错误
- 06Tool Harness:模型只能提议动作,执行权必须被隔离
- 07RAG Harness:先评检索,再评回答
- 08Agent Harness:把多步智能体变成可暂停、可恢复、可审计的状态机
- 09红队与安全 Harness:把提示注入当成常规回归项
- 10观测 Harness:trace 里该看见什么,不该记录什么
- 11CI 回归门禁:让 Prompt、模型和检索改动都要过关
- 12线上反馈回流:用户反馈怎样变成下一版样例
- 13模型路由与发布 Harness:灰度、回滚和成本风险一起看
- 14综合项目:交付一个 AI Harness Engineering 蓝图
本文目录12 节
评测不是一个分数:判分器、断言和人工复核怎么组合
本单元深入探讨 AI 评测指标的多层组合策略,比较规则断言、LLM judge、人工复核、pairwise 对比和业务指标的边界,并输出一套评测指标矩阵、LLM judge rubric 和人工复核抽样策略。
- 理解基础 Prompt 调试流程
- 了解基础的 JSON Schema 语法
- 能够为特定的 AI 业务场景设计出分层的评测指标矩阵(规则、模型判分、人工)
- 能够编写具备可执行、无歧义的 LLM judge rubric
- 能够设计科学的人工抽样规则,与自动化评测系统形成数据闭环
在建设大语言模型(LLM)驱动的系统时,我们很容易陷入一种盲目追求“全局平均分”(如:通过率 85%,或者 LLM 打分平均 4.2/5)的误区。这种单一维度的平均数字会掩盖致命的系统退化。一个更新了 Prompt 的 RAG 检索系统可能在整体评分上提升了 5%,但却在 2% 的高频核心咨询场景中输出了严重的幻觉信息,甚至泄露了敏感数据。这就是为什么我们需要在测试套件(Harness)中构建分层评测矩阵。
本单元将带你剖析规则断言、LLM 判分器(LLM-as-a-Judge)、人工复核、两两对比(Pairwise Comparison)和业务指标的适用边界,学会用多层证据链替代单一的分数,并最终交付一套可以直接应用于生产流程的指标矩阵与评审策略。
拒绝“单一分数”:为什么平均分会掩盖致命的退化
大模型应用的评测与传统软件测试有着本质区别。传统单元测试的输出是确定性的 Assert.AreEqual(expected, actual),而大模型的输出具有概率性、开放性和语境依赖性。如果我们只使用一个大模型来充当唯一的裁判,并给出一个总体的分数,我们会遇到以下三个无法绕开的挑战:
- 判分器漂移与自身偏置:作为裁判的大模型本身也会产生幻觉、顺从性偏置(倾向于给长回答打高分)以及首尾位置偏置。如果你只看一个最终得分,你无法分清是业务模型的生成变差了,还是判分模型自身在退化。
- 业务安全底线的坍塌:在一个包含 1000 个测试用例的 Regression 集合中,950 个日常闲聊或低风险提问的分数从 4 分升到了 5 分,而 50 个涉及“退款流程”、“账号注销”的安全边界用例直接输出了错误引导。从统计平均值来看,分数上升了,但对于线上生产来说,这是一场灾难。
- 缺乏诊断可解释性:当评测后台警报说“当前分支得分 3.8,低于主干分支 4.1”时,工程师无法直接开展行动。是格式错了?是回答不完整?还是幻觉率升高了?
因此,一个合格的测试套件必须建立多层防御与证据链组合。我们要把一个“好不好”的抽象判断,拆解成从客观到主观、从低成本到高成本的层级漏斗。这就是评测矩阵的核心思想。
规则断言(Assertions):在最外围拦截低级格式错误
如果你的业务对 JSON 格式有硬性依赖,必须在 API 请求中启用 OpenAI Structured Outputs 并定义严格的 JSON Schema,因为这能从协议层面保证格式正确,而仅靠 Prompt 或宽松的 LLM judge 会产生无法解析的坏账。
根据 OpenAI 发布的 Structured model outputs 规范,使用 strict: true 的 JSON Schema 可以确保模型输出完全符合预设的 JSON 拓扑。然而,正如该指南所警示的,Schema 约束形状,却无法保证返回的具体事实、逻辑和业务逻辑的正确性。这意味着,格式正确只是通过了“第一道防线”,我们仍需要非 Schema 的规则断言来进行更细致的、确定性的业务阻击。
Promptfoo 官方文档在介绍评测用例和断言(assertions)时提到,断言可以通过 contains、is-json、javascript 等多种低开销规则快速拦截不合规输出。基于这一设定,我们在评测套件中应该优先把这类低算力消耗、高确定性的断言作为第一道关卡,而不是直接调用昂贵的 LLM judge,从而最大化评测效率并降低测试成本。
规则断言配置示例
在 Promptfoo 配置文件 promptfooconfig.yaml 中,我们可以直接通过配置低成本的正则、JSON Schema 以及包含关系来拦截基础错误,避免进入昂贵的大模型打分环节:
tests:
- description: "测试客服退款政策输出的完整度与合规性"
vars:
user_query: "我想申请七天无理由退款,该怎么操作?"
assert:
# 1. 规则断言:必须输出特定业务链接,否则直接判不通过
- type: contains
value: "https://example.com/refund-policy"
# 2. 规则断言:严禁包含敏感、不合规字样
- type: not-contains
value: "私下转账"
# 3. 规则断言:字数限制,拦截由于模型死循环导致的超长输出
- type: javascript
value: "output.length < 800"
当这些规则断言报错时,评测应当立即阻断,并将该 Case 标记为 FAIL_RULE。只有当这些廉价且高效的规则断言全部通过后,我们才应该把输出投喂给更高级的判分逻辑。
大模型判分(LLM-as-a-Judge):用 Rubric 替代模糊的感官评分
对于“回答是否专业”、“有没有体现同理心”、“是否包含无关的废话”等无法通过正则、字符匹配来解决的问题,我们需要引入大模型判分器(LLM-as-a-Judge)。
但是,直接让大模型“给下面的回答打 1 到 5 分”是毫无意义的。模型会对 4 分和 3 分的界限感到模糊,导致打分结果标准不一、方差极大。我们必须提供无歧义的量化 Rubric(打分量表)。每一档分数(1分、2分、3分、4分、5分)都必须定义清晰的、排他的物理证据支撑。
优秀 Rubric 的设计准则
- 可证伪的描述:不要写“5分:回答非常客气和完美”,而是写“5分:包含欢迎语,清晰解答了用户疑问,主动询问是否有其他问题,且未使用任何生硬的技术术语”。
- 扣分制逻辑:从满分开始,发现一项缺失则明确扣除对应分数,并在输出中给出推理链(Reasoning Chain)。
以下是一个应用于客服场景的 “回答合规与同理心” 评估 Rubric 模板:
你是一个资深的质量控制专家。请评估客服大模型的回答是否满足合规与同理心规范。
# 评分量表 (1-5分)
- **1分 (严重违规)**: 回答中出现了辱骂、挑衅、或者引导用户线下私自转账等安全红线行为。
- **2分 (不合规/错误引导)**: 虽然没有红线违规,但给出了错误的业务流程指导(例如告诉用户不支持退货,而实际上支持),或者直接拒绝服务且态度冷漠。
- **3分 (勉强合格)**: 业务逻辑无硬伤,但完全没有同理心。通篇使用机械化的机器人模板回复,忽略了用户的负面情绪,或未主动提供下一步行动指引。
- **4分 (良好)**: 业务逻辑正确,表达了同理心(如“很抱歉给您带来不便”),并且能够清晰、有条理地解答问题。
- **5分 (卓越)**: 在 4 分的基础上,不仅解答了当前问题,还根据用户所处阶段主动提示了潜在的避坑指南,且语气温暖自然。
# 输出格式约束
请严格按照以下 JSON 格式输出,不要有任何 Markdown 包裹之外的解释:
{
"reasoning": "一步步分析你的打分依据,必须指出具体的句子作为证据。",
"score": 评分数字
}
在 Promptfoo 中,你可以将该 Rubric 配置为一个 llm-rubric 类型的断言:
- type: llm-rubric
value: |
评估回答是否表现出同理心,且业务正确。必须符合 1-5 分的标准。
(此处贴入上面的 Rubric 正文)
双模竞技(Pairwise Comparison):新旧 Prompt 升级时的灰度决策
如果评测指标的变动直接关联到灰度发布门禁,不要直接使用大模型打出的 absolute score(绝对分数),应该采用 Pairwise 对比(两两对比)来判定新旧版本的相对优劣,否则 LLM 判分器自身的偏置和漂移会带来高昂的虚警。
大模型判分器对单一文本打出 4 分或 5 分的绝对值波动较大。例如,今天调用 GPT-4o 判分,由于 API 的微小更新,原本打 4 分的内容可能变成了 3.8 分,造成自动化发布流水线虚假报警。而双模竞技(Pairwise Comparison)则是将同一输入(Prompt + Variable)在两个不同分支(例如:当前线上主干 main vs 待发布开发分支 feature/new-prompt)下跑出来的输出 Output A 和 Output B 随机打乱顺序,交给裁判模型,让其给出 A 优于 B、B 优于 A,还是平局的判断。
Pairwise 对比的评测逻辑
┌───────────────┐
│ User Query │
└───────┬───────┘
│
┌──────────────┴──────────────┐
▼ ▼
┌───────────────┐ ┌───────────────┐
│ Output A │ │ Output B │
│ (Production) │ │ (Candidate) │
└───────┬───────┘ └───────┬───────┘
│ │
└──────────────┬──────────────┘
▼
┌──────────────────┐
│ LLM Judge │ (盲测:打乱顺序)
│ (Which is better?│
└─────────┬────────┘
│
┌──────────────┼──────────────┐
▼ ▼ ▼
[A wins] [Tie] [B wins]
(退化警报!) (安全) (可发布提升)
这种设计极大地抵消了大模型本身的“偏置”。即使判分器的评判尺度今天整体偏严,它在对比两个具体文本时,其相对偏好依然是稳定的。在 CI/CD 上线门禁中,我们可以设置如下发布标准:
B wins(新版胜出) 的比例必须显著高于A wins(旧版胜出)。A wins的占比严禁超过 5%(如果新版在 5% 的场景中发生了退化,即使整体平局率高,也需要挂起发布,进行退化排查)。
人工复核与黄金数据集:如何建立持续演进的真实地面准则(Ground Truth)
引用 OpenAI 官方 Working with evals 指南,评测集(Evals)的建立是为了进行模型质量回归和支撑上线门禁。但在实际工程中,完全自动化的回归套件并不能替代人工对边缘案例(Edge Cases)的复核。我们需要将自动评分与人工标注有机结合,利用自动化评测跑完大批量测试后,针对争议区间(如评分为中等或新旧模型输出不一致的样本)拉起人工复核任务。
人工直接评审 10,000 条输出是不现实且极其低效的。一个科学的 Harness 必须建立人机协同的抽样策略,将最宝贵的人工工时用在刀刃上。
科学的人工复核抽样策略
- 全量通过自动过滤网:凡是规则断言失败、格式校验(Structured Outputs)不通过的,直接自动判定失败,不进入人工复核。
- 边界与争议性样本抽样:对于 LLM Judge 评分落在灰色地带(例如 5 分制下,被打 3 分或 2 分的模糊区间,以及 Pairwise 评估中新旧模型发生“分歧”的用例),系统应自动打标并推送到人工复核队列。
- 高权重业务特征抽样:针对核心付费用户、政企大客户提问、或者涉及退款和法律纠纷的 Case,无论机器打分多高,都要采取固定比例(如 10%)的盲抽复核。
- 回流机制:人工复核如果发现大模型判分器打错了(例如模型判了 5 分,人工判定其实有严重错误),则必须立刻将该 Case 的“标准人工答案及打分”提取出来,作为黄金数据(Ground Truth)沉淀,并更新 LLM Judge 的 Rubric,防止裁判持续犯错。
评测矩阵设计:从生产链路逆向推导的指标组合
为了落实以上策略,我们将不同手段组合起来,设计一个完整的评测指标矩阵(交付物)。在建设一个典型的 AI 业务系统(例如一个金融理财 Agent 助手)时,我们将指标从低到高划分为四个层级,各司其职:
| 评估层级 | 评估指标 | 评估方法 | 适用边界 | 触发与阻断策略 |
|---|---|---|---|---|
| 层级一:基础格式与安全底线 | JSON 结构、黑名单词汇拦截、输入长度控制 | 规则断言(Zod/Regex/JSON Schema) | 校验输出完整性,防止死循环与红线词泄露。 | 只要任何一条规则断言失败,一票否决,直接判定测试失败,禁止上线,且不计入后续 LLM 评分。 |
| 层级二:业务事实正确性 | 信息检索忠实度 (Faithfulness)、上下文召回率 (Context Recall) | LLM-as-a-Judge (基于 RAG 检索原文比对) | 验证生成内容是否有事实性幻觉。 | 设定及格线(如 Faithfulness 必须 $\ge 0.8$)。低于及格线的用例,拦截发布,并自动发送到人工复核池。 |
| 层级三:主观体验与同理心 | 表达流畅度、同理心表达、废话率 | LLM-as-a-Judge (Rubric 扣分制) | 验证生成的语气、语境和对话体验。 | 属于波动性指标。允许轻微浮动(如平均分不低于 4.0 分),若发生大幅波动则触发人工抽查。 |
| 层级四:灰度对比与综合演进 | Win/Loss Rate (新旧分支对比) | Pairwise 两两盲测 (GPT-4o 裁判) | 用于发布前决策,验证升级 Prompt 的整体收益。 | 只有新版本胜出率显著高于旧版本,且旧版本优势用例(Loss)占比 $< 5%$ 时,才允许自动合并。 |
评测方案落地:一个典型 RAG 系统的指标配置与自动化演练
本小节将带你把上述理论落实到可执行的评测套件中。假设我们要测试一个企业内部 HR 政策问答助手,其基于 RAG 检索企业文档并回答员工提问。
1. 业务场景定义
- 输入 (User Query): "我们公司的育儿假有多少天?怎么申请?"
- 检索到的上下文 (Retrieved Context): "根据 2026 版员工手册,符合国家政策生育的员工,在子女满三周岁前,每年可享受 10 天的育儿假。申请人需提前 5 个工作日在 HR 系统提交配偶生育证明并上传证明附件。"
- 预期输出要求: 回答必须准确指出“10天”和“满三周岁前”,必须提及“提前 5 个工作日”和“HR 系统申请”。严禁出现虚假的福利承诺。
2. 测试集 Harness 配置文件 (promptfooconfig.yaml)
这是一个用于跑自动化评测的 Promptfoo 配置文件模板(未真实执行,请在实际环境中结合 API key 进行调试):
prompts:
- "你是一个专业的 HR 助手。请根据以下已知信息,用专业、温暖的语气回答员工的问题。回答必须客观,严禁胡乱承诺福利。\n\n已知信息:\n{{context}}\n\n员工提问:\n{{user_query}}"
providers:
- openai:gpt-4o-mini
tests:
- description: "测试育儿假规则的提取与同理心表达"
vars:
user_query: "我们公司的育儿假有多少天?怎么申请?"
context: "根据 2026 版员工手册,符合国家政策生育的员工,在子女满三周岁前,每年可享受 10 天的育儿假。申请人需提前 5 个工作日在 HR 系统提交配偶生育证明并上传证明附件。"
assert:
# 1. 规则层校验:必须包含申请的系统和天数,防止关键信息缺失
- type: contains
value: "10天"
- type: contains
value: "HR系统"
# 2. 规则层校验:防止把 2026 错写成其他年份
- type: not-contains
value: "2025版"
# 3. LLM-as-a-judge 校验:无幻觉/忠实度评估
- type: llm-rubric
value: |
你是一个严谨的 HR 合规审计员。你需要对比[员工手册上下文]与[AI的回答]。
如果 AI 的回答中出现了[员工手册上下文]中没有提及的任何额外福利承诺(例如‘带薪全额发放’、‘可以申请报销’等上下文没写的东西),或者遗漏了‘提前5个工作日’的要求,请直接判定为不合格。
合格请给 5 分,不合格请给 1 分。请给出极简的判定理由,并输出 JSON 格式:
{"reasoning": "原因", "score": 1或5}
# 4. LLM-as-a-judge 校验:语气同理心评估
- type: llm-rubric
value: |
作为一个温暖的 HR 助手,评估以下回答是否语气合适。
不能显得过于生硬。如果是机械、毫无情感的复述,扣 1 分;语气温柔且有专业度给满分。请使用 1-5 分制。
3. 失败诊断与排查路径
当你在终端运行 promptfoo eval 时,可能会遇到各种评测失败。以下是常见的失败状态、原因诊断和优化方案:
- 状态一:
FAIL_RULE(规则断言失败)- 现象:输出中不包含 "HR系统"。
- 原因:模型把 "HR系统" 替换成了 "内部办公软件",或者由于上下文关联不够,直接略过了申请入口说明。
- 排查路径:检查 Prompt,强化对流程入口的要求(如在 Prompt 中加入:
请务必明确说明申请所需的具体系统名称和提前时间)。
- 状态二:
FAIL_LLM_RUBRIC(忠实度校验失败)- 现象:大模型判分器给出 1 分,理由是 AI 输出了 "并且可以根据部门业务情况弹性休假"(上下文并无此话,模型发生了泛化幻觉)。
- 原因:System Prompt 的约束不够强,允许了模型使用先验知识或进行“合理联想”。
- 排查路径:在 System Prompt 中加入强限制性指令:
你只能基于已知信息进行回答,如果信息中未提及,请直接回答'抱歉,已知信息中未包含此项规定',严禁脑补和发散。
4. 练习与自测验收
只有当规则断言全部通过、且 LLM judge 对关键业务属性评分为优时,才能将该条测试用例标记为 Pass,除非你明确接受某些容错场景,否则任何单层防御的失效都会导致坏例(Bad Cases)直接流向线上用户。
为了检验你是否掌握了本单元的多层评测设计,请完成以下练习任务,并在你本地的项目中进行配置:
- 练习任务:针对一个“在线理财账单纠错 Agent”设计一个评测用例。该 Agent 的任务是核对用户的信用卡账单,指出消费总额是否正确,并给出明细。
- 具体交付物标准:
- 写出至少 2 个规则断言(例如限制其输出中必须包含特定计算公式或币种符号)。
- 设计一个 4 分制的 LLM Judge Rubric,清晰区分“算术错误(1分)”、“数值正确但解释混乱(2分)”、“数值正确且步骤清晰但语气冷冰冰(3分)”、“完美解答(4分)”。
- 制定一套 人工抽样策略:在这个金融场景中,哪些打分区间的样本必须进人工池复核?抽样比例是多少?
请将你的 promptfooconfig.yaml 补充配置片段以及人工抽样 PDF 方案写入你本周的作业库,供评测系统及助教复核。
评测不是一个分数:判分器、断言和人工复核怎么组合:把判断写进 Harness 证据链
这一节要把模糊的 AI 行为拆成可检查动作,并保留能复盘的证据。本课交付物是 一套评测指标矩阵、LLM judge rubric 和人工复核抽样策略,它必须能被复跑、复核、追踪和复盘。
如果 评测不是一个分数:判分器、断言和 还没有对应的输入样例,先不要讨论自动化覆盖率,因为没有样例就无法判断 harness 是否真的抓住问题。 如果一次评测失败会影响发布,应该保留失败输入、模型输出、工具轨迹和判分理由,否则团队只能凭记忆争论。 如果你准备把某个结果自动放行,必须先写清人工复核的例外条件,只有例外条件明确时,门禁才不会变成新的风险源。
结构化输出失败时不要只重试,要区分 schema 错误、业务错误和事实错误。围绕 评测不是一个分数:判分器、断言和人工 做排查时,记录步骤、样例、指标、风险、修复动作和复测结果。
OpenAI 的《Working with evals》说明:支撑 AI 输出评测集、评测运行、模型质量回归和上线门禁。;这意味着 评测不是一个分数:判分器、断言和 要把来源转成可执行断言。OpenAI 的《Structured model outputs》提醒:支撑结构化输出、JSON Schema 约束、校验和输出 harness。;因此本课必须写清自动判断和人工判断的边界。Promptfoo 的《Promptfoo documentation》提供的证据是:支撑 prompt/model 评测、测试用例、断言、CI 集成和红队用例。;所以当前结论按 2026-05-28 的来源状态使用。
复核触发条件要具体:如果模型 API/SDK、评测工具版本、Agent 框架、RAG 指标、安全规范、合规政策、平台 release/changelog 或关键来源页面变化,需要重新运行本课样例并更新 harness。
练习验收:把 评测不是一个分数:判分器、断言和人工 接入一个最小 AI 应用,提交包含输入样例、评测结果、失败诊断、来源证据、人工复核结论和下一步修复动作的记录。缺少任何一项,都标记为 needs_review。