通过可复现的测试流程,在实际代码库上用正确性、编辑局部性、迭代成本三个维度评估AI模型。
大多数 AI 编程模型的对比文章对你没有参考价值。这不是因为作者不诚实,而是因为他们测试的是自己的问题:全新的 LeetCode 风格题目演示 TODO 应用,或者你根本没用过的框架。你的代码库有不同的失败模式——奇怪的构建系统、没人想碰的遗留模块、需要跑 40 分钟的测试。
本文介绍的是一个小型、可复现的测试框架,你花一个下午就能跑起来,用你自己的测试套件而非"感觉"来对编程模型进行评分对比。这个工具大约 120 行 Shell 和 Python 代码,外加一份你可以自行调整的评分规则。
不要问"哪个模型最强?",而要问:在我的代码库中选取一组固定真实任务,哪个模型产生的补丁能通过我的测试、速度最快、需要最少的人工干预?
这给了你三个可衡量的维度:
正确性——生成的 diff 是否通过了相关测试?
编辑局部性——模型是否只动了它应该动的文件?
迭代成本——需要多少轮 prompt 才能达到目标?
最廉价的任务来源是你自己的提交记录。找到修复了 bug 或添加了小功能的提交,然后 checkout 到父提交,让模型复现修复(但不展示真正的修复内容)。
#!/usr/bin/env bash
# extract_tasks.sh — mine candidate tasks from git history
# Usage: ./extract_tasks.sh <repo_path> <count>
set -euo pipefail
REPO="$1"; COUNT="${2:-8}"
cd "$REPO"
# Small, self-contained commits: <= 3 files, <= 80 changed lines, has a test file touched
git log --oneline --no-merges -n 300 | while read -r sha msg; do
files=$(git diff-tree --no-commit-id --name-only -r "$sha" | wc -l)
lines=$(git diff --shortstat "$sha^" "$sha" | grep -oE '[0-9]+ insertion|[0-9]+ deletion' | grep -oE '[0-9]+' | paste -sd+ | bc)
if [ "$files" -le 3 ] && [ "${lines:-999}" -le 80 ]; then
echo "$sha|$files|$lines|$msg"
fi
done | head -n "$COUNT"
手工过滤输出。你要找的是那些提交信息描述得足够清晰、可以作为 prompt 的任务。对每个选中的任务,记录:
{
"id": "task-03",
"parent_sha": "a1b2c3d",
"prompt": "Fix the bug where empty query strings crash the /search handler. Behavior should return HTTP 400 with a JSON error body.",
"test_command": "pytest tests/test_search.py -x",
"forbidden_paths": ["docs/", ".github/"]
}
forbidden_paths 字段是刻意设计的:如果模型通过重写 CI 配置来"修复" bug,应该被扣分。
运行器会 checkout 到父提交、将 prompt 交给模型、应用返回的 diff、运行你的测试命令并评分。评分逻辑刻意做得无聊——无聊的评分才可复现。
# score_run.py — evaluate one model output on one task
import subprocess, sys, json, pathlib
def run(cmd, cwd, timeout=300):
return subprocess.run(cmd, cwd=cwd, shell=True,
capture_output=True, text=True, timeout=timeout)
def score(task, patch_text, workdir):
result = {"task": task["id"], "applied": False,
"tests_pass": False, "files_touched": [],
"forbidden_touched": False, "score": 0}
patch = pathlib.Path(workdir) / "candidate.patch"
patch.write_text(patch_text)
if run(f"git apply --check candidate.patch", workdir).returncode != 0:
return result # score 0: patch doesn't even apply
run("git apply candidate.patch", workdir)
result["applied"] = True
touched = run("git diff --name-only", workdir).stdout.split()
result["files_touched"] = touched
result["forbidden_touched"] = any(
t.startswith(tuple(task["forbidden_paths"])) for t in touched)
t = run(task["test_command"], workdir, timeout=600)
result["tests_pass"] = (t.returncode == 0)
# Scoring rubric (adjust weights to what your team cares about)
s = 0
if result["tests_pass"]: s += 60
elif result["applied"]: s += 10
if result["tests_pass"] and not result["forbidden_touched"]: s += 25
if len(touched) <= 3: s += 15
result["score"] = s
return result
if __name__ == "__main__":
task = json.loads(pathlib.Path(sys.argv[1]).read_text())
patch = pathlib.Path(sys.argv[2]).read_text()
print(json.dumps(score(task, patch, sys.argv[3]), indent=2))
每个任务在干净的工作树中运行(git worktree add /tmp/eval-task-03 <parent_sha>),这样失败不会互相污染。每次运行前都要重置。
这里有一个实际约束:一次严肃的对比——比如 8 个任务 × 3 个模型 × 2 次重复——就是 48 轮完整的编程会话。在按量计费的 API 上,这很快就会累积到让人望而却步,所以大多数人会跳过重复,最终得到统计学上毫无意义的一次性结果。
这就是免费层真正有用武之地的地方,而不是什么花招。我通过 MonkeyCode 运行了这个框架,它提供编程模型的免费访问权限,还有一个免费服务器选项可以指向运行器,所以重复实验的成本是时间,而不是钱。披露:本文是作为 MonkeyCode 产品推广的一部分准备的。
关于任何免费层,MonkeyCode 也不例外,有两个坦诚的注意事项:免费模型阵容和速率限制会变化,所以把具体可用的模型当作快照而非永久配置——并且让你的框架把模型端点做成配置值,而非硬编码假设。如果免费选项下个季度消失了,你的任务集和评分器仍然可以指向下一个替代品继续工作。
只有当任务来自真实代码库时,一些模式才会显现出来:
补丁应用失败比测试失败更常见。模型有时会生成漂亮的解释,却为你的确切文件版本输出了上下文行错误的 diff。git apply --check 能在浪费一次测试运行之前就捕获这个问题。
编辑局部性是审查成本的代理指标。一个模型虽然通过了测试但修改了 11 个文件,比一个只改了 2 个文件的模型产生更差的审查体验——即使两者正确性相当。这就是为什么局部性要纳入评分规则。
重复的重要性超过模型选择。同一个模型在同一个任务上的两次运行之间的差距,有时比两个不同模型之间的差距还大。一次性的对比就是噪声;每个任务至少跑两遍。
小任务偏差。挖掘小提交意味着你的框架只测试小型修复。它不能说明问题的是耗时数小时的重构或架构工作。如果这是你对模型的期望,这个框架会低估它的能力。
你的测试是神谕。弱测试套件会产生虚高的分数。如果一个任务的 test_command 只覆盖了正常路径,要对其分数持怀疑态度。
免费层不是基准测试平台。速率限制会扭曲计时测量,而且通常无法固定模型版本。用免费访问来筛选候选者;在你实际部署的确切付费层/版本上重新确认赢家。
不要在无法分享的代码上运行此框架。除非该服务的数据条款明确涵盖你的用例,否则使用脱敏的或开源的代码库。免费端点不等于合规审查。
如果你的团队已经有了一套可靠的内部评估套件,或者你在受监管的代码库上工作,不允许任何外部模型调用,那么整个方法都是错误的工具。
"哪个模型最强"是一个没有保质期的问题。"哪个模型在我的任务分布上通过我的测试"是一个你可以每季度用相同框架重新回答的问题。一次构建任务集,保持评分器无聊,让端点可替换——下次新模型发布时,你午休时间就能有答案,而不是空洞的热点评论。
如果你想不花一分钱就尝试这个,MonkeyCode 的免费模型访问和免费服务器选项是筛选阶段一个合理的起点——只是在上面积分规则之前要记住那些注意事项,再去信任那些数字。
你的团队用什么作为 AI 生成补丁的神谕——测试、人工审查抽样,还是其他方式?很想知道什么方法真正有效过。