作者建议用自己 git 历史中的真实任务(而非公开 benchmark)评估模型,配有提取脚本、runner 和评分表,回答「这个模型适合我吗」而非排名问题。
每次换模型前,先用自己的 Git 历史跑一遍
发布说明说它更快。发布推文说它吊打一切。我关注的三个人已经切换过去了。然而,每次我只是基于这些就切换过去,通常不到两周就会悄悄换回来——因为模型把迁移脚本搞得一塌糊涂,或者信誓旦旦地解释了一个根本不存在的 bug。
差距很简单:公开基准测试测的是基准测试关心的东西。我每天的活儿是一堆半生不熟的 Django 内部实现、反复搜索的 shell 一行命令、以及一个没人会再读的函数的 docstring。这些都不会出现在排行榜上。所以我不争论排名,而是搞了一套小仪式:当一个值得关注的模型出现时,我花一个晚上让它重跑我近期的工作。这篇文章就是这套仪式——语料格式、运行器、以及我用来做判断的评分卡。
问三个独立的问题,而不是一个
"这个模型好吗?"这个问题无解。以下不是:
它能适配我的 prompt 形态吗?我的 prompt 很丑:截断的栈跟踪、两个文件前后粘贴、指令类似"不要改公开 API"。
我能不在乎成本地用它吗?一个完美但昂贵的模型只留给特殊场合,那就意味着它实际上从来帮不上忙。
它怎么出错?安静、有保留的不确定,我可以用。凭空捏造的确定,我没法用。
没有任何一个数字能回答这些问题。十几个来自你自己历史的 prompt 可以。
从自己的历史中挖掘测试用例
我把所有东西放在一个文件夹里,每个案例一个 YAML 文件,都是从过去一个月我实际问过的问题里提取的。我目前有十二个案例,分四类:
Untangle(理乱)——"这个 bash 脚本某处有引号 bug,找到它并解释为什么只在带空格的文件名上才会坏。"通过 = 说出真正的引号问题,而不是"找个地方加引号"这种表面答案。
Write-under-constraint(约束下写作)——"给这个函数加上类型提示,不要改变任何运行时行为,兼容 Python 3.9。"通过 = 文件仍然可以导入,且 mypy 无报错。
Diagnose(诊断)——"这是 traceback 加上周围 30 行。问题在哪?"通过 = 正确的文件和正确的推理。
Translate(翻译)——"把这段 jQuery 片段改写成原生 JS,保留 debounce。"通过 = 行为保持不变,不残留任何库。
每个文件长这样:
id: bash-quoting-02
category: untangle
prompt: |
This script breaks on filenames with spaces. Why?
<script pasted here>
checks:
mechanical: "bash -n answer.sh" # syntax must be valid
by_eye:
- "identifies word splitting, not just 'add quotes somewhere'"
- "doesn't rewrite the whole script unprompted"
故意设置两种检查。任何机器能验证的——解析、编译、合法 JSON、退出码为零——都让机器验证。人的注意力留给那些正确性需要主观判断的情况,因为那才是模型最容易耍花招的地方。
运行器(刻意写得无聊)
一个文件,除了标准库没有任何依赖,兼容任何 OpenAI 兼容端点:
#!/usr/bin/env python3
"""replay.py — feed a YAML prompt corpus to any chat-completions endpoint."""
import json, os, subprocess, sys, time
from pathlib import Path
from urllib import request
def ask(base, key, model, prompt):
payload = json.dumps({
"model": model,
"messages": [{"role": "user", "content": prompt}],
"temperature": 0, # we're comparing models, not sampling moods
}).encode()
req = request.Request(
f"{base.rstrip('/')}/chat/completions", data=payload,
headers={"Authorization": f"Bearer {key}",
"Content-Type": "application/json"})
start = time.monotonic()
with request.urlopen(req, timeout=180) as resp:
data = json.loads(resp.read())
return data["choices"][0]["message"]["content"], time.monotonic() - start
脚本其余部分遍历 YAML 文件,将每个答案写入 answers/<model>/<id>.md,对提取的代码块运行任何机械检查,然后打印一行总结。每个模型跑一次:
REPLAY_KEY=... python replay.py ./cases https://endpoint.example/v1 model-name
几个看起来不起眼但实际影响很大的选择:
温度为零。你要的是模型的默认行为,而不是一次幸运的采样。(警告:零温度并不能保证确定性,所以任何可疑结果都要重跑。)
答案存为文件,而非打印。在编辑器里对比两个模型的答案,比任何分数都更有价值。
超时设得宽松。慢但正确的答案是数据;被 kill 掉的请求只是噪音。
让评估足够便宜,才能真的去做
这里有一个诚实的原因,解释为什么大多数人不这么做:每次新版本发布都要跑十几个长 prompt,成本太高,所以我们把决定权外包给推特上嗓门最大的人。解决办法不是靠自律,而是让实验几乎免费。
我目前的方案:让运行器指向 MonkeyCode,它提供免费模型访问和免费服务器选项,我纯粹把它当作评估台——新模型一出现,当晚就跑十二个案例,不产生任何账单。披露:本文是 MonkeyCode 产品推广的一部分。如果你想定制复刻这个方案,monkeycode.ai 的免费版对这个规模的语料来说足够了;运行器本身是端点无关的,所以不会把你锁死在某个提供商上。
这种分离是关键:免费层做实验,值得信赖的付费端点跑生产。任何带出去的东西。评估流量可以忍受限流和排队;生产流量不行。
决定用的评分卡
机械检查跑完、我读完剩余答案之后(十五分钟,配杯咖啡),每个模型按以下标准评判:
吹牛是唯一的致命缺陷。在 Diagnose 类别里,一个回答"大概率是 X,但验证一下 Y"的模型,是一个合格的同事。一个编造听起来精确的根因的模型,比没有模型还差,因为它恰恰在你准备信任它的那个时刻让你失望。
按类别打分,从不汇总。我目前用的是一个模型做 untangle/diagnose 类的工作,另一个模型做 write-under-constraint 类的任务,因为过去测过三个"整体最强"的模型,每个都在至少一个类别里输得很惨。赢家轮换,但类别不轮换。
这套方案的局限
十二个案例是烟雾报警器,不是实验室。它可靠地能 catch"对我的工作明显更差",但检测不出"平均略好"。如果你需要后者,你需要真正的评估套件。
肉眼审查不可扩展。那是你为测试自己真正在乎的东西付出的代价。接受它。
免费端点有真实限制——节流、排队、随时可变的条款。拿来做一个晚上的实验没问题;永远不要把它们接入用户-facing 的路径。
在大多数推理引擎上,零温度并不保证确定性。任何可疑结果在得出结论前要跑两遍。
如果你一个月只 prompt 两次,或者你的雇主指定了模型,整个仪式可以跳过——你的精力应该花在安全审查上,而不是个人评估框架。
当下一个版本铺天盖地给你推送图表时,你不需要对那些图表发表意见。你需要一个晚上、十二个来自你自己历史的 prompt、和一个惩罚吹牛的评分卡。能扛过你真实工作的模型才能获得一个位置;其余的都是带着 API key 的发布营销。
如果你在这条路上走得更远——尤其是更好的自动化"肉眼"那一半的方法——非常期待在评论区交流心得。