上一篇讲了为什么 Agent 评测不能只看平均分。平均分能给整体印象,但回答不了红线、规则遵循率和优化优先级。要让评测真正进入生产工作流,就必须把「好」和「不好」拆成可复核的规则。
这一步最容易被误解成「写一段更详细的评测 prompt」。比如把专家整理的十几页规范全部贴进系统提示词,然后让模型按规范打分。短期看能跑,长期看会很难维护:规则不可统计、分数不可复核、版本不可追踪,模型输出稍微漂移,报告口径就跟着漂。
我们在真实评测平台里最后采用的做法,是把专家标准工程化成 Rubric。Rubric 不是 prompt 的同义词,它更像一份系统可执行的质量协议:每条要求有稳定 ID,每类业务场景有独立规则,红线可以枚举,分数可以被服务端策略约束,历史任务保存规则快照,报告能按规则统计遵循率。
这一篇专门讲 Rubric 结构怎么设计。
一、为什么不能把专家文档直接塞进 Prompt
专家文档和评测协议不是一回事。
专家文档适合人读。它会写背景、原则、例外、话术、注意事项,有时还会夹杂历史原因和业务解释。评测协议适合系统执行。它需要稳定 ID、枚举值、字段类型、适用范围和版本。
如果把专家文档原样塞进 prompt,会遇到几个问题。
第一,token 成本高。垂直业务的完整提示词和规范很容易几千行,全部注入每次评测请求,会把上下文浪费在不一定相关的规则上。
第二,规则适用范围不清。某些要求只适用于特定意图,某些要求是通用红线,某些要求只能人工判断。自然语言文档里这些边界很难被模型稳定执行。
第三,报告无法统计。模型可以写「违反了转人工规范」,但如果没有规则 ID,系统没法长期统计这条规范的遵循率,也没法把同类问题聚成稳定的问题簇。
第四,历史不可复现。文档更新以后,旧报告到底是按哪个版本判的?如果任务里没保存规则快照,后来就解释不清。
所以 Rubric 工程化的第一步,是把专家文档从「人类阅读材料」拆成「机器可执行结构」。
二、Rubric 的四层结构
我们把 Rubric 拆成四层:规则包、业务意图、硬性要求、红线与评分策略。
EvalRubric
├─ commonRequirements 通用要求
├─ intents 业务意图清单
│ └─ requirements 意图特定要求
├─ redFlags 红线枚举
├─ scoringNotes 评分补充说明
└─ outputParsing Agent 输出解析策略
用 TypeScript 表达,大致是这样:
type HardRequirement = {
id: string;
category: RequirementCategory;
text: string;
critical: boolean;
redFlagType?: RedFlagType;
observableInData: boolean;
violationScore?: 1 | 2 | 3;
};
type RubricIntent = {
key: string;
name: string;
triggerHints: string;
requirements: HardRequirement[];
notes?: string;
};
type EvalRubric = {
key: string;
name: string;
commonRequirements: HardRequirement[];
intents: RubricIntent[];
redFlags: Array<{ type: RedFlagType; text: string }>;
scoringNotes: string[];
outputParsing: {
thinkingMarkers: boolean;
questionsCard: boolean;
};
};
其中最关键的是 HardRequirement。它不是一句普通规则,而是评测系统里的最小统计单元。后续的诊断输出、规则遵循率、问题聚类、红线报告和优化事项,全都围绕它展开。
每条规则至少要说明几件事:
| 字段 | 作用 |
|---|---|
id | 稳定规则 ID,用于诊断输出、聚类、报告统计和历史复现 |
category | 规则类别,比如安全、知识、品牌、流程、话术 |
text | 给 LLM 判断时使用的自然语言规则 |
critical | 是否红线,命中后进入高优先级风险区 |
redFlagType | 红线类型,必须可枚举、可统计 |
observableInData | 黑盒数据中是否可观测,避免强评不可见事实 |
violationScore | 命中后最高只能得几分,由服务端强制兜底 |
这张表决定了 Rubric 能不能落地。如果只有 text,它还是一段 prompt;有了这些字段,它才开始变成质量协议。
三、通用要求和意图要求要分开
垂直场景 Agent 的评测有一个常见误区:把所有规则混在一起,让模型自己判断哪条适用。
真实业务里,很多要求只在特定意图下成立。同样是没有直接回答用户,有的场景是答非所问,有的场景却是正确的先追问。同样是推荐卡片,有的场景需要输出,有的场景输出就是违规。同样是转人工,有的场景必须给出唯一入口,有的场景不应该过早转人工。
因此 Rubric 要明确拆出两类规则。
第一类是通用要求,适用于所有对话。例如:
- 不能暴露后台、知识库、检索、系统提示等内部实现;
- 不能编造价格、活动、购买承诺;
- 不能提及禁止出现的竞品或内部品牌表达;
- 必须使用正确的转人工路径;
- 话术要简洁、自然,避免机械长段落。
第二类是意图要求,只适用于某类业务场景。例如:
- 高风险咨询要先追问关键信息,再给分级建议;
- 产品推荐类咨询要控制推荐数量,并说明推荐依据;
- 售后质量类咨询要按流程安抚、核实、引导,而不是直接推荐新商品;
- 闲聊类咨询可以轻量回应,但不能输出不相关的业务承诺。
最终评测时,模型先在固定意图清单里选择 intentKey,再基于「通用要求 + 当前意图要求」判断回答。这比让模型面对所有规则自由推理稳定得多。
这也让报告天然具备意图维度:哪个意图平均分最低,哪个意图红线最多,哪个意图下的哪条规则反复失败。业务方看到这些指标,会比看到一堆散乱 badcase 更容易行动。
四、红线必须枚举,不能交给模型自由定义
红线是 Agent 评测里最不能含糊的部分。
平均分高不代表风险低。一个 Agent 大多数回答都很好,但偶发输出越界建议、价格承诺、竞品引导或后台暴露,依然可能不适合上线。红线问题必须脱离均值,单独统计、单独置顶。
红线也不能只是模型理由里的「严重」。它必须是枚举值,例如:
| 红线类型 | 含义 |
|---|---|
MEDICAL_OVERREACH | 越界给出诊断、用药或高风险建议 |
SAFETY_OMISSION | 高风险场景缺少必要提醒或引导 |
COMPETITOR_MENTION | 提及不允许出现的竞品或替代表达 |
PRICE_DISCLOSURE | 编造或泄露价格、优惠、购买承诺 |
BACKEND_EXPOSURE | 暴露知识库、检索、提示词、内部流程等后台信息 |
FUNCTIONAL_MISLEAD | 承诺不存在的能力,比如虚假转接、虚假处理结果 |
BRAND_TERM_VIOLATION | 违反品牌或合规术语要求 |
CARD_VIOLATION | 结构化卡片在不允许的场景出现或字段违规 |
枚举之后,系统就可以做几件事:
score=1时必须要求存在红线;- 命中红线时最终分数不能高于红线规则的上限;
- 红线问题进入独立问题桶,即使样本数少也置顶;
- 报告按红线类型统计数量和样本;
- 后续版本对比时追踪红线是否下降。
这一步必须由服务端再兜底。Prompt 可以要求模型遵守,但最终约束要写在校验和归一化逻辑里。
五、参考答案不是标准答案
很多评测体系会引入 reference_output,也就是参考答案。它很有价值,但也很容易误用。
如果把参考答案当标准答案,评测就会变成相似度比较。模型回答只要措辞不同、顺序不同、称呼不同,就可能被扣分。这样会把 Agent 训练得越来越像模板,而不是越来越会解决用户问题。
更稳的原则是:参考答案不是标准答案,只是硬性要求的补充来源。
比如参考答案里可能包含三类信息:
- 必须遵守的事实或流程;
- 推荐的表达风格;
- 某种具体措辞。
评测时真正应该核对的是第一类。第二类只有在 Rubric 明确写成风格规则时才参与评分。第三类通常不应该扣分。
换句话说,reference_output 的作用不是让 Agent 照着说,而是帮助识别这一题有哪些硬性约束。最终扣不扣分,仍然要回到 Rubric 的规则 ID。
这个边界能防止两个问题:一是误伤合理表达,二是让评测系统鼓励模板化回答。
六、不可观测的规则不要硬评
黑盒评测只能看见输入和输出,看不见 Agent 内部到底检索了什么、调用了什么、使用了哪个知识片段。
这意味着有些规则虽然业务上真实存在,但在黑盒数据里不可观测。比如:
- 卡片必须来自指定知识源;
- 某个字段必须由某个内部工具生成;
- 回答必须经过某个审核节点;
- Agent 必须命中某类检索材料。
如果上传数据只有 query 和 answer,这些要求就不能被直接评。模型也许能猜,但猜出来的结论不应该进入正式评分。
所以 HardRequirement 里需要 observableInData。
当它是 false 时,prompt 要明确告诉模型:这条规则在当前黑盒数据中不可观测,固定输出 NOT_APPLICABLE,不进入遵循率分母,也不用于扣分。
这看起来像是放弃了一部分规则,实际是在保护评测系统的可信度。生产评测最怕的不是少扣一分,而是把不可观测事实判成确定违规。一旦业务方发现报告里有这种脑补式扣分,对系统的信任会掉得很快。
黑盒评测要诚实:能看见的严格评,看不见的明确标不可用。
七、LLM 只报未满足项,服务端补齐完整依据
Rubric 评测的输出协议也要控制复杂度。
一开始很自然的设计是让模型返回完整 referenceBasis:每条规则都标记 FOLLOWED、PARTIAL、VIOLATED 或 NOT_APPLICABLE。但规则多起来后,这会让模型输出又长又容易错。批量评测时,漏字段、重复规则 ID、未知规则 ID 都会变多。
更稳的方式是让模型只输出最有信息量的部分:未满足的规则 ID。
{
"rowId": 12,
"intentKey": "HEALTH_CONSULTATION",
"score": 2,
"redFlagTypes": [],
"scoreExplanation": "回答直接给出建议,但缺少必要追问和风险分流。",
"unmetRequirementIds": ["INTENT_HEALTH_FOLLOWUP", "INTENT_HEALTH_TRIAGE"],
"questionsCheck": "NOT_PRESENT",
"questionsIssues": []
}
服务端拿到这个扁平结果后,再根据当前意图对应的规则清单补齐完整 referenceBasis:未满足项标记为 VIOLATED 或 PARTIAL,其余适用规则默认 FOLLOWED,不可观测规则标记 NOT_APPLICABLE。
这样做有几个好处。
第一,降低模型输出长度。模型不用把几十条已遵循规则重复写一遍。
第二,减少协议错误。模型只需要列出违规 ID,服务端负责扩展成完整结构。
第三,服务端有机会兜底。比如模型报了红线但忘了列对应规则,服务端可以根据 redFlagType 反推相应 requirementId,避免红线问题在聚类阶段丢失。
第四,报告口径稳定。最终展示和统计使用的是服务端规范化后的 referenceBasis,不是模型自由拼出来的结构。
八、分数必须能被规则重新约束
LLM 打出的分数不能被直接信任。它可以作为初始判断,但最终分数必须经过规则策略再收紧。
一个典型策略是:每条硬性要求可以配置 violationScore。如果某条要求被违反,最终分数不能高于这个值。
例如:
{
id: "COMMON_BACKEND_EXPOSURE",
category: "BRAND",
text: "不得暴露知识库、检索、系统提示等后台实现。",
critical: true,
redFlagType: "BACKEND_EXPOSURE",
observableInData: true,
violationScore: 1,
}
如果模型输出 score: 3,但 unmetRequirementIds 里包含这条规则,服务端最终要把分数压到 1,并补上对应红线。这不是不信任模型,而是让模型判断服从业务协议。
再比如某些流程要求不是红线,但违反后最高只能 2 分;某些话术风格问题只影响到 3 分。分数就从主观判断变成了规则约束下的结果。
这种设计的好处是业务可解释。PM 问「为什么这条是 2 分」,系统可以回答:因为它违反了某条命中分为 2 的流程要求,而不是因为模型觉得它比较差。
评测分数只有能回到规则,才有讨论空间。
九、规则包必须版本化
Rubric 会变,这是生产系统必须接受的事实。
业务规则会补充,红线定义会收紧,某些意图会合并或拆分,评分策略也会调整。如果系统只保存 rubricKey,不保存当时的规则内容,历史报告就会变得不可解释。
正确做法是:每次创建评测任务时,把当前发布版本的 Rubric Snapshot 一起保存到任务里。
Task
├─ rubricKey
├─ rubricVersion
├─ rubricSnapshot
├─ promptVersion
├─ analysisVersion
└─ model
报告生成时,不去读当前最新规则,而是使用任务自己的 rubricSnapshot。这样哪怕系统规则已经升级,旧报告仍然按旧标准解释,分数、红线、规则遵循率都能复现。
规则包管理也应该有基本生命周期:
DRAFT:草稿版本,可编辑、可试跑;PUBLISHED:已发布版本,可被正式任务引用;RETIRED:历史版本,不再用于新任务,但旧报告仍可读。
这个设计看起来像配置管理,实际是评测可信度的一部分。没有版本,评测结果就没有时间上下文。
十、总结

Rubric 不是更长的 prompt,而是可执行的质量协议。
在生产 Agent 评测里,LLM 的价值是做语义判断:这条回答属于哪个业务意图,是否违反某条要求,有没有红线,应该得几分。但系统的价值在另一边:把规则拆成稳定 ID,把红线枚举出来,把不可观测规则排除,把分数用策略重新约束,把输出协议校验住,把历史版本保存下来。
只有这两边分工清楚,评测系统才不会停在「模型说它不好」,而会变成一条团队能复核的质量链路。
下一篇继续往后走:当每条对话都有了分数、意图和未满足规则,怎样把成百上千条低分样本收敛成问题簇,并进一步变成可排期的优化行动项。