一个可复现的小型测试工具:在代码模型接触你的代码库之前先对比

Dev.to AI 2026-08-07T12:21:15.699537

问题
大多数 AI 编码模型的对比都是道听途说。有人贴出一条提示词,瞟一眼输出,然后写下结论。这几乎说明不了任何问题——同一个模型在全新的代码片段上可能表现惊艳,一放到你真实的代码库里就溃不成军。更好的做法是:在把任何模型接入你的日常工作流之前——更别提给它一个仓库的写权限——先用一组小而固定的任务配上自动化检查跑一遍。这篇文章带你搭一个可以直接复制、运行和扩展的测试 harness。它刻意做得无聊。无聊才能让结果可复现。

这个 harness 做什么
思路分三块:

所有操作都在临时目录中进行,不接触你真实项目的网络。最后这一点很重要:模型评测绝不应该和你在意的代码共享同一个工作目录。

任务文件

# tasks.yaml
tasks :
  - id : fizzbuzz-edge
    prompt : >
      Write a Python function classify(n) that returns 'fizzbuzz' for multiples of 15, 'fizz' for multiples of 3, 'buzz' for multiples of 5, and the number as a string otherwise. Handle 0 and negatives.
    check : python test_fizzbuzz_edge.py
  - id : json-repair
    prompt : >
      Write a Python function repair_json(s) that accepts JSON with trailing commas and returns a parsed dict, raising ValueError on anything still invalid.
    check : python test_json_repair.py

每个 check 都是一个小的 pytest 文件,你要在看任何模型输出之前写好它。先写测试才是关键——这样你就不会因为偏爱某个模型而悄悄降低标准。

运行脚本
这是刻意保持最小化的——把 call_model 存根换成你实际用的任何客户端:

# runner.py — pseudocode-level skeleton, adapt the client call
import subprocess , time , json , yaml , pathlib
def call_model ( prompt : str ) -> str :
    """ Replace with your model client.
返回生成的代码文本。
"""
raise NotImplementedError

def run_task(task, workdir: pathlib.Path) -> dict:
    code = call_model(task["prompt"])
    (workdir / "solution.py").write_text(code)
    start = time.time()
    result = subprocess.run(
        task["check"].split(),
        cwd=workdir,
        capture_output=True,
        text=True,
        timeout=60,
    )
    return {
        "id": task["id"],
        "passed": result.returncode == 0,
        "latency_s": round(time.time() - start, 2),
        "stderr_tail": result.stderr[-500:] if result.returncode else "",
    }

def main():
    tasks = yaml.safe_load(open("tasks.yaml"))["tasks"]
    workdir = pathlib.Path("./scratch")
    workdir.mkdir(exist_ok=True)
    results = [run_task(t, workdir) for t in tasks]
    print(json.dumps(results, indent=2))

if __name__ == "__main__":
    main()

每个模型把整套任务完整跑三遍,重点看结果的一致性,而不只是单次最好成绩。一个任务两次通过、一次失败的模型,和三次全部通过的模型,在实际使用中完全是两回事——即使单看最好成绩两者一模一样。

把输出变成决策

问题 阈值 理由
三次运行中的通过率 每个任务 ≥ 80% 低于这个线,反复重试会把你省下的时间全吃掉
失败模式 语法错误 vs. 隐蔽的逻辑错误 语法错误很容易抓出来;但那些看着像模像样、实际是错逻辑的问题,代价就高得多
延迟波动 要稳定,不只是快 忽快忽慢的延迟比一直慢更打断节奏
失败运行的成本 接近于零 如果一次失败会消耗真金白银或配额,阈值就该往上调

最后一行才是真正让人注意到免费额度价值的地方。失败运行不花钱,就可以用更大的任务集、跑更多轮,而这恰恰是评测结果可信的关键。我正是在这个背景下关注到 MonkeyCode 的——它提供免费的模型访问,还支持在免费服务器上运行,上面这套评测工具可以尽情跑,不用担心每次重试都在烧账单。

信息披露:本文是 MonkeyCode 产品推广活动的一部分。
```

具体来说,这套流程是这样的:把 call_model 指向你想评估的模型,tasks.yaml 和预先写好的检查脚本保持不变,然后每个模型各自输出一份 JSON 结果。因为任务和检查项从不改动,结果上的任何差异都只能归因于模型本身——而不是你今天发挥得好不好。免费服务器选项还有一个不太显眼的价值:它杜绝了“为了省钱而缩小任务集”的冲动,而大多数自建评测项目恰恰就是死在这上面。

局限——这段请认真看

这套框架测的是玩具级任务,不是你的真实代码库。一个能轻松修好 repair_json 的模型,到了四千行老旧的遗留模块上照样可能搞得一团糟。建议把它当做一个过滤器:用来筛掉明显不合适的模型,而不是证明某个模型真的够格。下一步应该是用同一套框架,指向你自己代码里抽出来的三个真实函数。

另外记住,跑三次只是下限,不是上限。采样存在不确定性,样本太少容易得出误导性结论。如果两个模型结果接近,先多跑几轮再下结论。

免费额度是会变的。任何免费访问方式——包括 MonkeyCode 在内——都可能随时调整限制、排队机制或服务条款。设计框架时,最好把替换客户端做成五行的改动,并且在正式依赖它跑 CI 之前重新确认当前条款。

不要让生成代码在无沙箱环境里运行。上面那个临时目录只是图个方便,不是安全边界。如果执行模型输出而你还没过目,务必放在容器或虚拟机里跑,不要挂载任何凭证。最近关于 AI 代理越权问题的讨论这么火,不是没有原因的。

谁不需要这套东西

如果你只是拿 AI 助手回答些零散问题,从不让它的输出在任何地方运行,那这套框架纯属多余——直接用工具就行。同样,如果团队已经有现成的评估管线,用它就好;这套框架的价值,是为那些目前没有任何可复现对比方式、只能靠感觉选模型的人和小团队准备的。

如果你正好属于后者,那就从自己一周的工作里挑五个任务,先把检查写出来,再把框架跑起来——MonkeyCode 的免费档是个低门槛的起点,不过这框架适用于任何你能塞进 call_model 的客户端。

关键在于这个工具本身:一旦运行器搭好,以后再评估任何模型,都只是一下午的活儿,而不是一次凭感觉的赌注。

查看原文