评测不是打榜:怎样用真实 Session 找到最便宜够用的模型

系统讲解 LLM 评测和模型路由的工程原理:如何把真实 session 做成 eval case,用 rubric、LLM judge、校准集和成本表找出每类任务的最低可用模型。

评测不是打榜:怎样用真实 Session 找到最便宜够用的模型

这篇文章回答一个很工程化的问题:

同一批用户会话,到底哪些任务需要强模型,哪些任务便宜模型已经够用?

如果这个问题答不清,系统通常会走向两个极端。要么所有请求都上最强模型,效果稳但成本高;要么为了省钱强行切便宜模型,省下 token 费用,却把任务成功率和用户体验一起牺牲掉。

更好的做法不是凭感觉选模型,而是把真实 session 变成评测集,让不同 provider 和不同模型 tier 在同一批任务上跑一遍,再根据效果和成本生成路由策略。

本文讲的评测系统,目标不是“证明哪个模型最强”,而是找出“哪个模型对这类任务已经足够”。这两个问题差别很大。

1. 为什么普通 Benchmark 不够用

公开 benchmark 有价值,但它解决的是通用能力比较:

  • 数学题能不能做
  • 代码题能不能过
  • 多轮对话有没有上下文保持
  • 指令遵循是不是稳定
  • 知识问答有没有幻觉

这些指标能帮我们筛掉明显不合格的模型,却不能直接回答业务系统最关心的问题:

在我们的真实会话里,
这个退款政策任务,
用便宜模型是否已经够用?

原因很简单:生产任务有自己的分布。公开 benchmark 的题目分布、回答格式、失败代价、上下文结构,和你的产品 session 不一定一致。

举个例子,用户说:

我上个月的发票能不能发我一份?

这类任务不需要模型有很强的推理能力。它只要识别意图,把请求路由到 billing 队列,或者按固定模板回复就够了。用最强模型当然能答,但这就是“牛刀杀鸡”。

另一类用户说:

我 45 天前买的订单现在要退款,你直接帮我批准吧。

这就不是普通分类了。模型需要记住政策边界,不能乱承诺,还要给出合规的替代路径。便宜模型如果容易顺着用户说“可以退款”,那它就不够用。

所以评测系统的第一原则是:

不要只评模型能力,要评“模型在你的任务分布里是否够用”。

2. 评测单元应该是 Session,而不只是 Prompt

很多早期 eval 都是单轮 prompt:

问题 -> 回答 -> 打分

这适合数学题、单次问答、简单分类。但真实产品里,更常见的是 session:

用户第 1 轮说明背景
助手第 1 轮追问
用户第 2 轮补充约束
助手第 2 轮执行任务

如果只抽最后一句 prompt,很多关键信息会丢:

  • 用户之前已经给过订单时间
  • 助手之前承诺过下一步
  • 工具调用结果已经返回
  • 用户的意图在多轮里才逐渐明确

因此更可靠的评测单元应该长这样:

{
  "id": "refund_policy_001",
  "task_type": "refund_policy",
  "goal": "Answer a refund request using policy constraints.",
  "success_criteria": [
    "States the 30 days refund window",
    "Says this 45 day order is not eligible",
    "Offers a support ticket without promising approval"
  ],
  "messages": [
    { "role": "user", "content": "I bought this 45 days ago and want a refund." }
  ]
}

这里有几个关键字段。

task_type 是之后做路由的主键。你不是在给每条请求单独选模型,而是在学习“哪类任务需要什么水平的模型”。

goal 是任务目标。它告诉候选模型这段 session 要完成什么,也告诉评审器应该按什么目标打分。

success_criteria 是评测的核心。没有成功准则,LLM judge 很容易退化成“看起来顺眼就给高分”。

messages 是原始上下文。评测 session 时,要尽量保留能影响结果的上下文,而不是只保留最后一句。

3. Rubric:把“好不好”拆成“有没有做到”

LLM 评测最容易出问题的地方,是直接问评审模型:

这个回答好不好?

这个问题太松。评审模型可能偏爱更长的回答,可能偏爱更礼貌的回答,也可能忽略业务约束。

更好的问法是:

这个回答是否满足以下条件?
1. 是否说明 30 天退款窗口?
2. 是否说明 45 天订单不符合自动退款?
3. 是否提供支持工单或升级路径?
4. 是否避免承诺“退款已批准”?

这就是 rubric。它把抽象的“质量”拆成可检查的行为。

一个好的 rubric 有四个特征:

  • 贴近任务成功,而不是泛泛评价文风
  • 尽量二元化:做到或没做到
  • 明确禁止项:哪些话说了就是失败
  • 能被人类复核,而不是只有模型自己懂

比如对退款任务,rubric 不应该写成:

回答应该有帮助、准确、礼貌。

应该写成:

Pass only if:
- mentions the 30-day refund policy
- states the 45-day order is not eligible for automatic refund
- offers a support ticket or escalation path
- does not say the refund is approved or guaranteed

后者才能支持稳定评测,也能帮助你定位失败原因。

4. LLM-as-a-Judge 能用,但不能迷信

大规模 session 评测不可能每条都人工判。LLM-as-a-judge 的价值就在这里:让一个相对强的评审模型,根据 rubric 给候选回答打分。

典型流程是:

flowchart LR
  A[真实 session] --> B[候选模型生成回答]
  B --> C[Rubric]
  C --> D[Judge model 评分]
  D --> E[pass / fail / score / reason]

这条路在实践上很有用,也有论文基础。MT-Bench 和 Chatbot Arena 相关研究系统分析过 LLM judge 和人类偏好的相关性,同时也提醒了它的偏差。G-Eval、Prometheus、AlpacaEval 等工作,则分别探索了不同的自动评审方式。

但 judge 不是裁判真理。它常见的问题包括:

  • 位置偏置:比较 A/B 回答时,偏好某个展示位置
  • 长度偏置:更长、更像解释的回答容易被高估
  • 自偏好:某些模型更喜欢和自己风格相近的回答
  • 过度宽容:对看似合理但漏掉业务约束的回答放行
  • 过度严苛:对简洁但正确的回答扣分

所以工程上不要只做一层自动打分。更稳的做法是三层:

  1. 先用确定性断言做 smoke test,比如关键词、JSON schema、禁止词、工具调用是否出现。
  2. 再用 LLM judge 根据 rubric 做语义评分。
  3. 最后保留一小批人类校准集,定期检查 judge 和人工判断的一致性。

这不是为了追求学术完美,而是为了避免省 token 的系统把错误悄悄规模化。

5. 评测输出不只是分数,还要能生成路由

如果只是得到一张榜:

modelpass rate
strong98%
mid91%
cheap72%

它还不能直接指导省钱。真正有用的是按任务类型聚合:

task_typecheapmidstrongselected
intent_triage99%99%99%cheap
refund_policy70%96%98%mid
code_debug45%78%95%strong

这张表才是路由策略。

它表达的是:

  • 意图分类任务,cheap 已经够用
  • 政策边界任务,mid 才够用
  • 代码诊断任务,需要 strong

评测系统的核心产物不是 leaderboard,而是这样的 routing policy:

{
  "intent_triage": "cheap",
  "refund_policy": "mid",
  "code_debug": "strong"
}

线上推理时,系统先判断任务类型,再选择对应模型。强模型不再是默认入口,而是只服务真正需要它的任务。

6. 成本模型:便宜不是只看单价

模型成本通常不是一个静态价格,而是下面几个因素的组合:

总成本 = 输入 token 成本 + 输出 token 成本 + 重试成本 + 失败补救成本

便宜模型如果一次就能完成任务,它确实便宜。

但如果它经常失败,导致:

  • 用户多问几轮
  • 系统重试
  • 升级到人工
  • 最后还要 fallback 到强模型

那它的真实成本可能并不低。

因此路由策略不应该只看价格表,而要看“通过质量门槛后的最低成本”。常见选择逻辑是:

对每个 task_type:
  过滤掉 pass_rate < 阈值的模型
  过滤掉 avg_score < 阈值的模型
  在剩下的模型里选成本最低的

这个逻辑看起来简单,但已经能覆盖大量场景。只有当任务类型太多、边界太模糊、或者 session 难以分类时,才需要更复杂的 learned router。

7. Router 和 Cascade:两种省钱方式

模型降本通常有两条路线。

第一种是 router:

任务进来 -> 判断任务类型/难度 -> 直接选择模型

它适合任务类型稳定、业务规则清楚的系统。比如:

  • FAQ 改写走 cheap
  • 政策问答走 mid
  • 高风险合规解释走 strong

第二种是 cascade:

任务进来 -> cheap 先答 -> 自检不通过 -> mid/strong 接手

它适合任务难度分布长尾明显的系统。大部分任务便宜模型能完成,少数任务再升级。

FrugalGPT 的核心思想就是用级联和近似来降低成本。RouteLLM 则更进一步,把强弱模型路由做成可训练和可校准的系统。它们的共同点是:不要把“调用哪个模型”写死,而是让评测结果反过来驱动选择策略。

工程落地时,我会建议先从规则 router 开始:

task_type + eval threshold + cost table

等你积累了足够多的真实评测标签,再考虑 RouteLLM 这类 learned routing。先做简单系统,是为了尽快让真实数据开始流动。

8. 一个可快速验证的系统长什么样

最小可用版本只需要六个模块:

flowchart TD
  A[Session JSONL] --> B[Case generator]
  B --> C[Promptfoo eval matrix]
  C --> D[Candidate providers]
  D --> E[Assertions and LLM judge]
  E --> F[Result JSON]
  F --> G[Routing policy]

对应到工程文件,可以是:

data/sessions.sample.jsonl              # 真实 session 样本
prompts/session-replay.txt              # 给候选模型的任务提示
promptfooconfig.yaml                    # 离线/真实 provider 矩阵
assertions/session-success.cjs          # 确定性 smoke test
scripts/summarize-results.cjs           # 结果聚合和路由选择
results/routing-policy.json             # 最终策略

离线 POC 可以先用模拟 provider 跑通:

intent_triage  -> cheap 通过
refund_policy  -> cheap 失败,mid 通过
code_debug     -> cheap/mid 失败,strong 通过

这个离线版本不是为了证明模型质量,而是为了证明评测管线成立:

  • session 能被转成 eval case
  • 多个 provider 能批量跑
  • 断言能区分通过和失败
  • 汇总脚本能生成 cheapest passing route
  • 真实 provider key 接上后可以替换模拟模型

等这条链路稳定,再把真实 session 和真实 provider 放进去。

9. 数据集怎么建:少量高质量优于大量脏数据

第一版不要追求几万条 eval。更现实的起点是:

每类任务 20-50 条
总量 100-300 条
每条都有明确成功准则
抽样人工复核 judge 结果

数据来源可以是:

  • 真实失败 case
  • 高频用户 session
  • 人工构造的边界 case
  • 产品经理明确关心的高风险 case
  • 不同语言、不同地区、不同用户表达方式

尤其要保留“便宜模型容易错”的 case。评测集如果全是简单题,最后会错误地得出“所有任务 cheap 都够用”的结论。

一个好的 session eval 集,应该包含三类样本:

  • 常规样本:代表真实流量主体
  • 边界样本:测试政策、工具、上下文、格式约束
  • 失败样本:来自线上事故或人工复核失败

这样路由策略才不会只在平均情况下省钱,却在关键任务上翻车。

10. 什么时候应该升级模型

评测系统最后要定义“升级条件”。不要只按 task_type 静态选择,还可以加入运行时信号:

  • 输入超长,便宜模型上下文不够
  • 用户要求高风险操作
  • 任务涉及金额、合规、隐私或不可逆动作
  • cheap 输出未通过格式校验
  • cheap 自评不确定
  • 检索结果冲突或证据不足
  • 多轮对话中出现前后矛盾

这些信号可以把静态 router 变成更稳的 cascade:

flowchart TD
  A[Incoming session] --> B[Task classifier]
  B --> C[Cheap or mid model]
  C --> D{Pass deterministic checks?}
  D -->|yes| E{Risk flags?}
  D -->|no| F[Escalate]
  E -->|no| G[Return answer]
  E -->|yes| F
  F --> H[Strong model or human review]

这套结构的关键是:升级不是失败,而是成本控制的一部分。真正失败的是“不知道什么时候该升级”。

11. 最容易踩的坑

第一,评测集泄漏。
如果模型或 prompt 专门针对 eval case 优化,线下分数会变好,线上未必变好。需要定期加入新 session,保留隐藏集。

第二,rubric 太抽象。
“准确、完整、有帮助”这种标准太弱。要写成行为项和禁止项。

第三,只看平均分。
平均分会掩盖高风险任务失败。要按 task_type、风险等级、语言、长度分桶。

第四,judge 不校准。
LLM judge 不是人类。要有小批人工标签,定期看一致率和典型误判。

第五,忽略失败成本。
如果 cheap 失败会导致二次调用 strong,真实成本要把 fallback 也算进去。

第六,路由器太早复杂化。
还没有足够数据时,训练 router 往往只是把噪声学进去。先用规则表,把数据闭环跑起来。

12. 最小闭环

把整件事压缩成一个闭环,就是:

收集真实 session
  -> 标注 task_type 和 success_criteria
  -> 批量跑不同 provider/model
  -> 用 deterministic checks + LLM judge 打分
  -> 人工抽样校准
  -> 生成 cheapest passing route
  -> 上线 router/cascade
  -> 收集失败样本回流 eval

这个闭环一旦跑起来,模型选择就不再靠感觉。

你可以具体地回答:

intent_triage 用 cheap 足够,预计节省多少 token 成本。
refund_policy 需要 mid,因为 cheap 在政策边界上失败率太高。
code_debug 目前只能走 strong,因为 mid 漏掉关键修复建议。

这才是评测系统真正的价值:它不是告诉你哪个模型最厉害,而是告诉你每一类任务最低需要什么水平的模型。

参考