1. 为什么“LLM 当裁判”这件事开始不够用了
过去一年多,只要聊到 Agent 评估,绝大多数团队的第一反应都是同一套打法:找一个能力更强的 LLM,把 Agent 的输入、输出、中间步骤一股脑塞进提示词里,让它打个分、给个评语、判个对错。这套做法在圈子里被叫做“LLM as a Judge”,中文一般叫“大模型当裁判”。它上手快、门槛低、几乎不需要标注数据,早期确实帮很多人省下了大量人力。但只要你真正在业务里跑过一段时间 Agent,就会发现这套评估方式的问题开始一个接一个冒出来。
我自己第一次对 LLM 裁判产生怀疑,是在一个多轮工具调用的场景里。Agent 需要先查库存、再算价格、最后生成报价单,中间任何一步出错都会导致最终结果偏差。我用当时最强的模型做裁判,让它判断“这次执行是否正确”。结果它给出的评语洋洋洒洒几百字,结论却是“整体流程合理,建议关注细节”。可实际上 Agent 在第二步就把单位搞错了,报价直接差了十倍。裁判模型没看出来,因为它压根没有真正“执行”这套逻辑,它只是在做文本层面的语义相似度判断。
这就是 LLM 裁判最根本的软肋:它评估的是“看起来像不像对的”,而不是“逻辑上是不是对的”。文本相似、语气合理、结构完整,这些表面特征很容易骗过一个语言模型,但业务正确性、约束满足、状态一致性这些硬指标,它往往抓不住。更麻烦的是,LLM 裁判本身还有位置偏见、长度偏见、自我偏好等一堆已知问题,同一个答案换个顺序打分就能差出一大截。你拿它当唯一标准,等于把评估的稳定性交给了一个本身就不稳定的东西。
Jev 这个项目之所以值得单独拿出来聊,就是因为它换了一条路:不再让 LLM 去“感觉”对错,而是用决策模型去“计算”对错。这个思路的转变,才是 Agent 评估从“玄学打分”走向“工程化度量”的关键一步。下面我会把 Jev 这套决策模型评估的完整逻辑拆开,从设计动机、核心原理、实操落地到踩坑经验,尽量讲透,让你看完能直接在自己的 Agent 项目里复现一套。
2. Jev 决策模型评估的整体设计思路
2.1 从“语义判断”转向“决策建模”的核心动机
要理解 Jev 为什么用决策模型,得先想清楚 Agent 评估到底在评什么。一个 Agent 的一次执行,本质上是一串状态转移:初始状态经过若干动作,逐步迁移到最终状态。评估的核心问题其实是——这条状态转移路径,是否满足我们预设的目标和约束。
LLM 裁判的做法是把这条路径翻译成自然语言,然后让模型判断“好不好”。而 Jev 的做法是:把 Agent 的执行过程抽象成一个决策问题,用决策模型去检验每一步动作在给定状态下是否是最优或可接受的。这两者的差别,就像“让一个文学评论家判断这道数学题解得漂不漂亮”和“直接用数学规则验证每一步推导是否成立”。
决策模型在这里扮演的角色,是一个可计算、可复现、可解释的判据。它不依赖语言模型的概率输出,而是依赖明确定义的状态空间、动作空间、转移规则和奖励/约束函数。只要这些定义是清晰的,评估结果就是确定的——同样的输入永远得到同样的分数,不会因为提示词换了个说法就飘。
这个动机背后还有一个很现实的考量:Agent 评估需要能回归、能对比、能进 CI。你今天调了一版提示词,想知道效果是变好还是变差,如果评估标准本身每次都在变,那这个对比就毫无意义。决策模型的确定性,正好补上了这块短板。
2.2 决策模型评估与传统评估框架的差异对比
为了让你更直观地看到差异,我把几种主流评估方式放在一起对比。这里说的传统框架,包括基于规则匹配的、基于 LLM 打分的,以及像 Ragas 这类偏 RAG 场景的评估工具。
| 评估方式 | 判据来源 | 确定性 | 可解释性 | 对多步 Agent 的适配 | 落地成本 |
|---|---|---|---|---|---|
| 规则匹配 | 人工写死的字符串/正则 | 高 | 高 | 差,难覆盖复杂路径 | 低但维护贵 |
| LLM 裁判 | 语言模型概率输出 | 低 | 中,评语泛泛 | 中,易被表面特征骗 | 低 |
| Ragas 类框架 | 检索+生成指标 | 中 | 中 | 偏 RAG,弱于工具调用 | 中 |
| Jev 决策模型 | 状态-动作-约束建模 | 高 | 高,可定位到具体步骤 | 强,天然支持多步 | 中高,需建模 |
从表里能看出来,Jev 的定位很明确:它牺牲了一部分“开箱即用”的便利,换来了确定性和对多步执行的强适配。这个取舍是否值得,取决于你的 Agent 是不是真的复杂到需要逐步校验。如果你的 Agent 只是单轮问答,那 LLM 裁判可能够用;但只要涉及工具调用、多轮状态、业务约束,决策模型的价值就会立刻显现。
2.3 适用场景与不适用场景的边界
Jev 这套思路不是万能的,我踩过的坑告诉我,得先分清边界。
适合的场景:多步工具调用 Agent、有明确业务规则的任务(如订单处理、审批流、数据校验)、需要进 CI 做回归的 Agent、对评估可解释性有要求的团队。这些场景的共同点是——“什么算对”是可以被形式化描述的。
不太适合的场景:纯开放式创作(写诗、写故事)、主观审美类任务、目标本身模糊且无法拆解的任务。这类任务里,“对错”本身就是个伪命题,硬套决策模型只会把评估搞得更别扭。这时候 LLM 裁判的模糊性反而是一种优势。
提示:判断你的场景适不适合 Jev,有个简单测试——你能不能把“一次成功执行”拆成若干可验证的中间状态?如果能,决策模型就有用武之地;如果不能,先别急着上。
3. 决策模型评估的核心原理拆解
3.1 状态、动作与转移:把 Agent 执行变成可计算对象
Jev 评估的基石,是把 Agent 的一次执行形式化成一个马尔可夫决策过程的变体。别被这个词吓到,拆开看很简单。
- 状态(State):Agent 在某一时刻掌握的全部信息。比如“已查询到库存=100,当前用户等级=VIP,已选商品单价=50”。
- 动作(Action):Agent 在这一时刻采取的操作。比如“调用价格计算工具”“向用户发起确认”“写入数据库”。
- 转移(Transition):动作执行后状态如何变化。比如“调用价格计算工具后,状态新增字段 total_price=5000”。
- 约束(Constraint):哪些状态或动作是允许的。比如“未确认前不得写入数据库”。
把这四样东西定义清楚,Agent 的执行轨迹就变成了一条状态序列。评估要做的,就是检查这条序列里每一步是否合法、是否朝着目标推进、最终是否到达目标状态。
我举个具体例子。假设一个客服 Agent 处理退款,状态里包含“订单状态”“退款金额”“用户确认标记”。约束规定“退款金额不得超过订单金额”“必须用户确认后才能执行退款”。如果 Agent 在用户没确认的情况下就调用了退款接口,决策模型会立刻在对应那一步标记违规,而不是等到最后给个模糊的“整体不佳”。这种步骤级定位能力,是 LLM 裁判给不了的。
3.2 奖励函数与约束惩罚:如何量化“好”与“坏”
光有状态序列还不够,评估需要给出一个可比较的分数。Jev 用的是奖励函数 + 约束惩罚的组合。
奖励函数负责衡量“做对了多少”。它可以是稀疏的(只在最终状态给分),也可以是稠密的(每一步都给分)。稠密奖励对 Agent 评估更有用,因为它能告诉你“虽然最终失败了,但前 80% 的步骤是对的”,这对调试极其关键。
约束惩罚负责衡量“做错了多少”。违反硬约束(如金额超限、越权操作)应该给重罚,违反软约束(如效率偏低、多余调用)给轻罚。惩罚权重的设定需要结合业务,不能拍脑袋。
一个简化的打分公式大概是这样:
总分 = Σ(每步奖励) - Σ(约束违反惩罚) 归一化总分 = 总分 / 理论最大奖励这里有个实操细节:归一化很重要。不同任务的步骤数不一样,不归一化的话,步骤多的任务天然占便宜或吃亏,横向对比就失真了。我一般会把最终分数压到 0 到 1 之间,方便跨任务比较。
3.3 为什么决策模型能规避 LLM 裁判的偏见
回到最开始的问题,决策模型到底怎么绕开 LLM 裁判的那些偏见?
位置偏见:LLM 裁判对答案在提示词里的位置敏感,放前面放后面分数不同。决策模型不看位置,只看状态序列的逻辑关系,位置无关。
长度偏见:LLM 裁判倾向于给长答案高分。决策模型只看动作是否合法、状态是否达标,跟文本长短没关系。
自我偏好:LLM 裁判偏爱和自己风格相似的输出。决策模型的判据是人工定义的规则,不涉及模型偏好。
不可复现:LLM 裁判受温度、采样影响,同样输入两次结果可能不同。决策模型是确定性的,同样输入永远同样输出。
这四点加起来,就是 Jev 敢说“比 LLM 裁判更靠谱”的底气。当然,代价是你得先把规则定义清楚,这部分工作量是省不掉的。
4. 实操落地:从零搭一套 Jev 式评估
4.1 环境准备与依赖梳理
动手之前,先把环境理清楚。Jev 本身是一个评估框架,实际使用时通常和 Agent 运行环境配合。我建议的依赖清单如下:
- Python 3.10 以上,低于这个版本有些类型标注和异步特性会别扭。
- 一个 Agent 运行框架(你自己写的也行,只要能把执行轨迹导出成结构化数据)。
- 结构化日志能力,最好每一步都能记录状态快照。
- 一个配置管理工具,用来存放状态定义、约束规则、奖励权重。
关键点在于轨迹导出。你的 Agent 必须能把“每一步的状态和动作”吐出来,格式最好是 JSON。如果 Agent 现在只输出最终答案,那你得先改造它,加上中间步骤的埋点。这一步是很多团队卡住的地方,因为改造 Agent 往往比写评估逻辑还费劲。
# 轨迹数据结构示例 trace = { "task_id": "refund_001", "steps": [ {"step": 0, "state": {"order_status": "paid", "amount": 500}, "action": "query_order"}, {"step": 1, "state": {"order_status": "paid", "amount": 500, "refund_amount": 500}, "action": "calc_refund"}, {"step": 2, "state": {"order_status": "paid", "refund_amount": 500, "user_confirmed": False}, "action": "execute_refund"} ], "final_state": {"order_status": "refunded", "refund_amount": 500} }4.2 定义状态空间与约束规则
这是整个评估里最需要动脑的部分。状态空间定义得太粗,评估就抓不住细节;定义得太细,维护成本爆炸。我的经验是按业务关键字段来定,只保留那些会影响对错判断的字段。
约束规则分三类:
- 前置约束:某动作执行前必须满足的条件。如“执行退款前 user_confirmed 必须为 True”。
- 后置约束:某动作执行后状态必须满足的条件。如“退款后 order_status 必须变为 refunded”。
- 全局约束:整个执行过程中始终成立的条件。如“refund_amount 始终不超过 amount”。
用代码表达大概是这样:
constraints = [ {"type": "pre", "action": "execute_refund", "condition": "state.user_confirmed == True"}, {"type": "post", "action": "execute_refund", "condition": "state.order_status == 'refunded'"}, {"type": "global", "condition": "state.refund_amount <= state.amount"} ]注意:约束条件尽量写成纯函数式的判断,不要在里面调用外部服务或做复杂计算,否则评估本身会变得不可靠。
4.3 奖励函数设计与参数计算
奖励函数的设计直接决定评估的区分度。我一般用分层奖励:
- 基础分:每执行一个合法动作给固定分,比如 1 分。
- 目标分:到达目标状态给大分,比如 10 分。
- 效率分:用最少步骤完成给额外奖励,步骤越多扣得越多。
参数怎么定?我通常先跑一批已知正确和已知错误的轨迹,看分数分布,再调整权重,让正确轨迹明显高于错误轨迹。这个过程类似调参,但因为有确定性,调一次就固定了,不像 LLM 裁判每次都要重新校准。
一个可参考的权重配置:
| 奖励项 | 权重 | 说明 |
|---|---|---|
| 合法动作 | 1 | 每个通过约束的动作 |
| 到达目标 | 10 | 最终状态匹配目标 |
| 步骤效率 | -0.5/步 | 超出最优步数后每步扣分 |
| 硬约束违反 | -20 | 每次违反重罚 |
| 软约束违反 | -3 | 每次违反轻罚 |
这套权重不是标准答案,你得根据自己的业务调。但有个原则:硬约束的惩罚必须大到让违规轨迹不可能得高分,否则评估就失去了威慑力。
4.4 跑通一次完整评估的现场记录
我把一次真实评估过程记录下来,你可以照着复现。
第一步,准备三条轨迹:一条完全正确、一条中间违规、一条最终失败。
第二步,加载约束和奖励配置,逐条轨迹跑评估。
第三步,输出每步的判定结果和总分。
def evaluate(trace, constraints, reward_config): total = 0 violations = [] for step in trace["steps"]: for c in constraints: if not check(c, step, trace): violations.append({"step": step["step"], "constraint": c}) total += reward_config["hard"] if c["type"] != "soft" else reward_config["soft"] total += reward_config["legal_action"] if match_goal(trace["final_state"]): total += reward_config["goal"] return {"score": total, "violations": violations}跑下来,正确轨迹得分 13,中间违规轨迹得分 -7,最终失败轨迹得分 2。三条轨迹区分得非常清楚,而且违规轨迹能直接定位到第 2 步的 execute_refund 违反了前置约束。这种定位能力,在调试 Agent 时价值巨大。
5. 常见问题与排查技巧实录
5.1 状态定义遗漏导致的误判
最常见的坑是状态字段没记全。比如 Agent 实际上读取了用户等级,但轨迹里没记录这个字段,评估时就会误判某个动作“缺少依据”。解决办法是在 Agent 埋点阶段就把所有可能影响决策的字段都记下来,宁可多记。
排查方法:如果发现某条轨迹的判定结果和人工判断不符,先检查是不是状态字段缺失。我一般会拿人工标注的几条轨迹做对照,快速定位问题。
5.2 约束冲突与优先级处理
当约束变多时,可能出现冲突。比如一条约束要求“尽快完成”,另一条要求“每步都要二次确认”,两者在效率上就打架。这时候需要给约束排优先级,硬约束永远优先于软约束,业务安全优先于效率。
我的做法是在配置里给每条约束加一个 priority 字段,冲突时按优先级裁决,并在评估报告里标注“因优先级裁决导致的扣分”,方便后续复盘。
5.3 评估结果与人工判断不一致怎么办
这种情况一般有三个原因:状态缺失、约束写错、奖励权重不合理。排查顺序建议是——先看状态,再看约束,最后调权重。因为前两者是逻辑问题,后者是标定问题,逻辑错了调权重是白调。
我整理了一个速查表:
| 现象 | 可能原因 | 排查动作 |
|---|---|---|
| 正确轨迹得分低 | 状态字段缺失 | 补埋点 |
| 错误轨迹得分高 | 约束太松 | 加硬约束 |
| 分数区分度低 | 权重不合理 | 重标定 |
| 结果不稳定 | 约束里有随机/外部调用 | 改纯函数 |
| 步骤定位不准 | 轨迹粒度太粗 | 细化埋点 |
5.4 独家避坑经验
说几个文档里不会写、但实际会遇到的坑。
第一,别在评估逻辑里调用 LLM。有些人为了“智能一点”,在约束判断里塞了个 LLM 调用,结果评估又变回不确定了,前功尽弃。
第二,轨迹版本要管理。Agent 改版后轨迹结构可能变,评估配置要跟着版本走,否则老配置跑新轨迹会各种报错。
第三,先小规模验证再全量。我一般先拿 20 条轨迹跑通,确认判定符合预期,再上全量,避免大规模跑完发现规则写错。
第四,保留人工复核通道。决策模型再确定,规则也是人写的,定期抽样人工复核,能发现规则本身的盲区。
6. 决策模型评估的扩展方向
跑通基础版之后,这套评估还能往几个方向扩展。一是多 Agent 协作场景,把多个 Agent 的状态空间合并,评估它们之间的交互是否合规。二是在线评估,把决策模型接进 Agent 运行时,实时拦截违规动作,而不只是事后打分。三是评估结果反哺训练,把违规步骤作为负样本,用于 Agent 的策略优化。
我自己在实际操作中的体会是,决策模型评估最大的价值不在于给出一个分数,而在于它逼着团队把“什么算对”想清楚。很多 Agent 项目的问题,根源不是模型不行,而是团队自己都没定义清楚成功标准。Jev 这套思路,某种程度上是在用工程手段倒逼业务澄清。这个副作用,比评估本身还值钱。