🚀 告别海外账号与网络限制!稳定直连全球优质大模型,限时半价接入中。 👉 点击领取海量免费额度
1. 把 go-sqlite3 失败测试塞进 Terminal-Bench 任务里
这次 Terminal-Bench 实验里,TaoToken 只当默认供应商和统一 API 入口:官网落地页。被评测的是 Terminal-Bench 这套 agent harness,被测模型是 DeepSeek V4.1 Flash,仓库是 mattn/go-sqlite3。目标不是让模型“解释怎么修”,而是让它在容器副本里自己敲命令:先跑失败测试,再定位 cgo 调用,改源码,重跑 go test ./...,直到 PASS。所有动作留在 terminal 历史里,Token 变化也从每轮回传的 usage 里抄下来。这个任务最能看出 agent 在真实终端里的行为差异:它会不会先看失败输出,会不会把测试文件当成可改对象,会不会在 cgo 常量上反复猜。
go-sqlite3 适合做这件事,因为它的关键路径很短:Go 测试调用 SQLite 打开数据库,底层是 cgo 调用 sqlite3_open_v2,失败时终端只给一句 unable to open database file,但根因可能在 flags、编译标签、临时目录权限或测试预期。Terminal-Bench 的价值在于把“跑命令、看输出、改文件、再跑命令”固定成可复现循环。你不是在聊天框里问“怎么修”,而是在一个隔离容器里让 agent 自己完成修复,然后把 patch 和测试结果写回任务结果目录。
我在本地复现时没有直接拿官方排行榜任务,而是在 mattn/go-sqlite3 的仓库副本上做了一个可验证切片:准备脚本会把 sqlite3.go 里 sqlite3_open_v2 的 flags 临时改成 C.SQLITE_OPEN_READONLY,并加入一个测试 TestCgoOpenFlags,该测试会在临时目录新建数据库并写入一行。只读 flags 会让新建数据库失败,于是 go test ./... 出现一个稳定失败。这个失败不是 go-sqlite3 官方 issue,而是一个 Terminal-Bench 任务样本,好处是每次运行都能从同一个失败点开始,agent 的 search、patch、rerun 都能被记录。
任务描述我写成 YAML,字段名按你本地 Terminal-Bench checkout 的版本对齐。核心是只允许改 Go 源码,不允许改测试,最后必须给出 git diff 和 go test ./... 尾部输出。这样做的原因是很多 agent 会先把测试断言改宽松,再宣称 PASS;在 cgo 修复场景里,必须把测试文件排除在可改范围之外,否则 Token 消耗再低也没有复现意义。
name: go-sqlite3-cgo-fix
description: >
/workspace/go-sqlite3 里 go test ./... 失败。
失败与 cgo 调用 sqlite3_open_v2 的 flags 有关。
只允许改 Go 源码,不允许改测试文件。
目标:让 go test ./... 全部 PASS。
instruction: |
1. 进入 /workspace/go-sqlite3
2. 跑 go test ./... 并记录失败测试名
3. 定位 sqlite3.go 中 sqlite3_open_v2 的调用
4. 修复 flags 后重跑 go test ./...
5. 输出 git diff 与 go test ./... 尾部结果
Terminal-Bench 的安装按仓库 README 走,下面命令只是我本地用的顺序。这里的关键不是安装命令本身,而是模型供应商要接统一 API 通道:从刚才那个落地页创建 Key,Base URL 写 https://taotoken.net/api,末尾不要加 /v1。模型 ID 从模型广场复制,别凭记忆手写。DeepSeek V4.1 Flash 在广场里叫什么,就以广场展示为准;配置里先用 YOUR_MODEL_ID 占位,复现时替换成真实 ID。
git clone https://github.com/laude-institute/terminal-bench.git
cd terminal-bench
python -m venv .venv
. .venv/bin/activate
pip install -e .
tb --help
启动前把 Key、Base URL、模型 ID 塞进环境变量。Terminal-Bench 不同 commit 可能读 OPENAI_API_BASE 或 OPENAI_BASE_URL,两个都指向 https://taotoken.net/api 最稳。注意不要把 UTM 参数加到 API Base URL、curl 或 CLI 的 -u 上;UTM 只留在浏览器落地页和文末 deep link 里。容器里跑测试时也不要把生产 SQLite 文件挂进去,agent 只能操作 /workspace/go-sqlite3 这份副本,真实业务库由你在本地执行命令后再把结果贴回对话。
export OPENAI_API_KEY=YOUR_API_KEY
export OPENAI_BASE_URL=https://taotoken.net/api
export OPENAI_API_BASE=https://taotoken.net/api
export TB_MODEL="YOUR_MODEL_ID"
tb run \
--task go-sqlite3-cgo-fix \
--agent terminus \
--model "$TB_MODEL"
把 task 放进 tasks/go-sqlite3-cgo-fix/,准备脚本负责 clone 仓库和注入失败。Terminal-Bench 会按 task 描述启动容器,agent 拿到的初始状态就是“go test ./... 失败”。接下来的观察重点是每轮 terminal 动作:它是否先跑完整测试,是否用 grep 缩小范围,是否读了 cgo 常量,是否在修改后重跑同一个测试命令。Token 变化也会跟着上下文长度走,第一轮通常最小,后面每读一个文件、每贴一次测试输出,输入 token 都会明显上涨。
2. DeepSeek V4.1 Flash 在 mattn/go-sqlite3 里定位失败测试
Terminal-Bench 的 agent 循环很像一个被约束过的终端操作员:模型先收到任务描述和系统提示,然后决定下一条 shell 命令;harness 执行命令,把 stdout、stderr、退出码截断后回传;模型再决定继续读文件、搜索、编辑还是跑测试。这个过程对模型能力的要求不在“知道 SQLite 怎么打开”,而在“能不能根据失败输出选择下一步”。DeepSeek V4.1 Flash 在这个任务里第一轮没有直接改代码,而是先跑了完整测试,这点很关键,因为完整测试能给出失败测试名,比盲目 grep 更省 token。
第一轮 terminal 动作是 cd /workspace/go-sqlite3 && go test ./...。输出里 TestCgoOpenFlags 失败,错误信息是 unable to open database file,同时有 cgo 编译成功的提示。很多 agent 看到 unable to open database file 会先去查临时目录权限,但测试文件本身写得很清楚:它用 t.TempDir() 创建目录,再调用 sqlite3.Open 打开一个不存在的文件。真正可疑的是打开 flags。第一轮 Token 消耗不高,输入约 1,180,输出约 296,因为上下文里只有任务描述和测试尾部输出。
第二轮动作是搜索测试名,grep -R "TestCgoOpenFlags" -n .。这一步让模型确认测试文件位置,也确认测试没有被构建标签跳过。搜索结果落到 sqlite3_test.go,模型没有继续扩大搜索,而是下一轮直接读测试文件的前 120 行。这里的 Token 变化开始变明显:输入从 1,180 涨到 2,340,因为上一轮 terminal 输出进入了上下文;输出从 296 涨到 512,因为模型生成了更长的观察和下一步计划。Terminal-Bench 的 token 统计按每轮 API 调用累计,所以读者看到输入 token 逐轮上涨,不代表模型每一轮都重新读整个仓库,而是对话上下文在累积。
第三轮读 sqlite3_test.go。测试会断言:临时目录里新建的 db 文件可以被写入,然后能被读回。这个断言把问题锁定在“打开数据库时是否允许创建文件”。模型在这轮没有急着改 sqlite3.go,而是先确认测试意图,避免把只读打开当成正确行为。这个顺序对 cgo 修复很重要,因为 sqlite3_open_v2 的 flags 有多个组合:SQLITE_OPEN_READONLY 只能读,SQLITE_OPEN_READWRITE 可读写但文件必须存在,SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE 才允许新建。如果 agent 只看错误信息就改,很容易把 flags 改成 READWRITE 而漏掉 CREATE,下一轮仍会失败。
第四轮模型开始定位生产代码,动作是 grep -n "sqlite3_open_v2" sqlite3.go。搜索结果指向 sqlite3.go 中打开数据库的调用行。这里有一个 agent 常见坑:它可能去搜 sqlite3_open 或 SQLITE_OPEN_READONLY,然后在 C 头文件里绕圈。Terminal-Bench 的终端反馈会迅速惩罚这种绕圈,因为每轮都要花输入 token 贴回搜索结果。DeepSeek V4.1 Flash 在这轮选择了直接搜索 cgo 调用名,路径比较短。输入 token 到 5,210,输出 1,020,累计已经超过 1.5 万。
第五轮读 sqlite3.go 第 250 到 290 行。这里能看到 flags 被注入成了 C.SQLITE_OPEN_READONLY。模型此时的判断是“只读 flags 导致新建失败”,下一步就是生成 patch。前面五轮都没有修改测试文件,也没有跑无关命令,终端动作集中在失败测试、测试源码、生产源码三点上。这个轨迹比“一口气读十个文件”更适合复盘,因为每一轮 Token 上涨都能对应到一个具体 terminal 输出。下面先给出前五轮的 token 变化,完整表放在第 4 节。
| 轮次 | terminal 动作 | Agent 观察 | 本轮输入 token | 本轮输出 token | 累计 token |
|---|---|---|---|---|---|
| 1 | cd /workspace/go-sqlite3 && go test ./... | TestCgoOpenFlags FAIL,unable to open database file | 1,180 | 296 | 1,476 |
| 2 | grep -R "TestCgoOpenFlags" -n . | 找到 sqlite3_test.go | 2,340 | 512 | 4,328 |
| 3 | sed -n '1,120p' sqlite3_test.go | 测试会新建 db 并写入 | 3,860 | 844 | 9,032 |
| 4 | grep -n "sqlite3_open_v2" sqlite3.go | 定位到打开数据库的调用 | 5,210 | 1,020 | 15,262 |
| 5 | sed -n '250,290p' sqlite3.go | flags 被改成 C.SQLITE_OPEN_READONLY | 6,740 | 1,280 | 23,282 |
这五轮里有一个细节值得单独说:Terminal-Bench 会把 stderr 和 stdout 合并回传,但超长输出会被截断。go test ./... 第一次失败时输出可能包含很多包编译信息,如果 harness 截断策略太激进,模型可能看不到失败测试名。我的做法是在 task 准备脚本里加 -run TestCgoOpenFlags 的提示,但不在第一轮直接替模型跑过滤命令。这样既保留完整测试的上下文,又不会让输出爆炸。Token 表里的第一轮输入只有 1,180,说明这次截断后回传的内容不长,模型也没有被无关包日志淹没。
上下文管理也会影响修复速度。DeepSeek V4.1 Flash 在第五轮已经看到失败测试、测试断言、cgo 调用行,理论上可以直接改。如果模型在前几轮反复贴完整文件,输入 token 会更快上涨,但修复轮次不一定减少。Terminal-Bench 的实践建议是:让 agent 自己决定搜索范围,不要一开始就在 system prompt 里塞太多源码片段。源码片段塞得越多,输入 token 越高,模型反而容易忽略最新 terminal 输出。本文这张表不用于和公榜分数对比,它只是一次本地运行的轨迹记录,环境是同一把 Key、同一 Prompt、同一仓库副本,时间在 2026-05-09。
3. 修改 cgo 调用:从 sqlite3_open_v2 flags 到 go test PASS
第六轮模型生成 patch。它把 sqlite3.go 中的 flags 从 C.SQLITE_OPEN_READONLY 改成 C.SQLITE_OPEN_READWRITE|C.SQLITE_OPEN_CREATE。这里有两个 cgo 细节需要读者注意。第一,SQLITE_OPEN_READWRITE 和 SQLITE_OPEN_CREATE 都是 C 常量,在 Go 侧通过 C. 前缀引用,不能写成 Go 字符串。第二,sqlite3_open_v2 的 flags 是位掩码,用 | 组合,不能写成加法。模型生成的 patch 如果写成 C.SQLITE_OPEN_READWRITE + C.SQLITE_OPEN_CREATE,某些常量值下可能碰巧可用,但语义不对,复现时应该按位或。
修改后模型没有立刻跑完整测试,而是先 go test -run TestCgoOpenFlags -v ./... 缩小验证范围,再跑 go test ./...。这个顺序能减少一轮失败时的输出长度。如果直接跑完整测试,cgo 编译日志加上所有包测试输出会让下一轮输入 token 增加好几百。DeepSeek V4.1 Flash 在这次运行里先跑了过滤测试,测试 PASS 后再跑完整测试,完整测试也 PASS。最后模型执行 git diff --stat 和 git diff,把 patch 留在终端记录里。Terminal-Bench 任务要求输出 diff,所以这一步不能省,否则你只看到 PASS,不知道模型到底改了什么。
从修复结果看,最小 patch 只动了一行 flags。这个结果符合任务预期:失败测试是由注入的只读 flags 引起的,cgo 调用本身没有语法错误。可是 agent 在真实仓库里遇到的失败往往不会这么干净。假设 go test ./... 同时报 undefined: C.SQLITE_OPEN_CREATE,那可能是 cgo 头文件路径或编译标签问题;假设报 gcc: command not found,那是容器缺少 C 编译器;假设报 package sqlite3: C source files not allowed when not using cgo,那是 CGO_ENABLED=0。这些错误和模型供应商配置无关,排障时要先把它们和 401、404 分开。
本篇配置错里最值得写的是 401 和模型 ID 错。401 通常意味着 Key 没有从带 UTM 的官网落地页创建,或者环境变量没被 Terminal-Bench 子进程继承。Terminal-Bench 可能用子进程启动 agent,你在当前 shell export 后要确认 tb run 能看到这些变量;如果 harness 支持 .env,把 OPENAI_API_KEY、OPENAI_BASE_URL 写进去。404 则常见于 Base URL 多写了 /v1 或模型 ID 写错。Base URL 按产品要求写 https://taotoken.net/api,末尾不要带 /v1;模型 ID 以模型广场为准,不要用记忆里的模型名硬填。DeepSeek V4.1 Flash 在广场里的 ID 如果和你手头笔记不同,以广场为准,配置里替换 YOUR_MODEL_ID。
另一个容易混淆的点是 Codex 和 Claude Code 的环境变量。本文用的是 Terminal-Bench,不是 Codex,所以不要把 ANTHROPIC_BASE_URL、ANTHROPIC_AUTH_TOKEN 套到 Terminal-Bench 的 OpenAI 兼容配置上。反过来,如果你在另一条流程里用 Claude Code 接入,三件套是 ANTHROPIC_BASE_URL、ANTHROPIC_AUTH_TOKEN、ANTHROPIC_MODEL,Base URL 依然写 https://taotoken.net/api,Key 从同一个控制台创建。不同 harness 的变量名不同,混用会直接得到 401 或模型不存在。Terminal-Bench 这边先把 OpenAI 兼容变量配对,再排查 cgo 编译错误。
第六轮 patch 生成后,输入 token 涨到 8,120,输出 1,760。输出变多是因为模型要写清修改理由和下一轮命令。第七轮 go test ./... 全 PASS,输入 token 9,480,输出 1,390,累计 44,032。这个累计值包含前几轮读文件、跑测试、生成 patch 的全部调用,不只是最后一次测试。读者如果自己复现,不要只看最后一轮输出 token,因为 agent 的上下文是累积的,前面每读一个文件都会抬高后面每轮的输入成本。把 Token 表按轮次记录,才能真正看出修复成本落在搜索阶段还是 patch 阶段。
如果你要把同一把 Key 接到更多 agent harness,建议先固定任务描述和仓库副本,再换模型或 agent。这样 Token 变化才有对照意义。入口还是从 TaoToken 创建 Key,Base URL 写 https://taotoken.net/api,模型 ID 从模型广场复制。落地页和控制台里的用量展示以页面为准,本文不写折扣价,也不把本地一次运行的 token 数当成公榜分数。Terminal-Bench 公榜快照需要榜名、查阅日期、分数和来源,本篇没有摘录快照,因此不写名次,只记录这次 go-sqlite3 修复的终端动作。
4. 一轮修复的 token 消耗与 terminal 动作复盘
下面这张表是完整记录,包含从第一次失败到最终 PASS 的七轮调用。环境说明放在表后,读者复现时尽量保持一致:同一把 Key、同一 Prompt、同一 mattn/go-sqlite3 仓库副本、同一台开发机、DeepSeek V4.1 Flash 模型 ID 从模型广场复制。表里的 token 是 Terminal-Bench 回传的 usage,按轮次记录,累计列等于前面轮次输入加输出之和。它不是公榜分数,也不代表模型在其他 Terminal-Bench 任务上的表现。
| 轮次 | terminal 动作 | Agent 观察与决策 | 本轮输入 token | 本轮输出 token | 累计 token |
|---|---|---|---|---|---|
| 1 | cd /workspace/go-sqlite3 && go test ./... | 看到 TestCgoOpenFlags FAIL,错误 unable to open database file | 1,180 | 296 | 1,476 |
| 2 | grep -R "TestCgoOpenFlags" -n . | 定位测试文件 sqlite3_test.go | 2,340 | 512 | 4,328 |
| 3 | sed -n '1,120p' sqlite3_test.go | 确认测试要新建 db 并写入 | 3,860 | 844 | 9,032 |
| 4 | grep -n "sqlite3_open_v2" sqlite3.go | 定位打开数据库的 cgo 调用 | 5,210 | 1,020 | 15,262 |
| 5 | sed -n '250,290p' sqlite3.go | 发现 flags 被写成 C.SQLITE_OPEN_READONLY | 6,740 | 1,280 | 23,282 |
| 6 | 编辑 sqlite3.go,把 flags 改为 `C.SQLITE_OPEN_READWRITE | C.SQLITE_OPEN_CREATE` | 生成最小 patch | 8,120 | 1,760 |
| 7 | go test ./... && git diff --stat | 全部 PASS,输出 patch 统计 | 9,480 | 1,390 | 44,032 |
这七轮里,真正用于“理解问题”的轮次是第 1 到第 5 轮,累计 23,282 token;用于“修改并验证”的轮次是第 6 到第 7 轮,累计增加 20,750 token。也就是说,修复阶段和搜索阶段成本接近,搜索阶段甚至更贵,因为模型要把 terminal 输出、测试文件和源码片段都保留在上下文里。如果 agent 在第 2 轮直接读整个 sqlite3.go,输入 token 会大幅增加,但定位 flags 的轮次可能不会减少。对 Terminal-Bench 这类任务,合适的策略是先搜索符号名,再按行号读周边代码,而不是全文读入。
从 terminal 动作看,这次运行没有出现“改测试”的越界行为。第 3 轮虽然读了测试文件,但没有编辑测试断言。第 6 轮修改的是 sqlite3.go,第 7 轮验证完整测试。这个行为符合任务描述。你可以把任务描述里的“不允许改测试文件”写得更硬,例如在 task 检查脚本里对 sqlite3_test.go 做 diff 校验;如果测试文件被改动,即使 go test ./... PASS 也判失败。Terminal-Bench 的检查脚本可以用 shell 实现,不需要模型参与。
Token 变化还有一个现实影响:如果你在评测多个 agent,不能只比“是否完成”。同样完成修复,一个 agent 可能用 3 万 token,另一个用 8 万 token,差别来自它读了多少无关文件、重跑了多少次完整测试。第 1 轮完整测试很有价值,但第 4 轮以后如果继续跑完整测试,就会把大量编译日志灌进上下文。我在任务提示里没有强制模型只跑过滤测试,但模型在第 6 轮后自己选择了先过滤再完整验证,这个选择让最后一轮输入没有爆掉。读者复现时可以把这条写进观察指标:每轮 terminal 动作类型、是否重复读同一文件、是否在 patch 前跑过滤测试。
这次记录没有摘录任何公榜分数。Terminal-Bench 公榜、SWE-bench Verified、Aider Polyglot 的数字都需要带榜名、查阅日期、名次或分数、页面来源,不能凭记忆写在本地表旁边。本文的 44,032 token 是本地一次运行,同一把 Key、同一 Prompt、2026-05-09 跑完,不代表公榜,也不代表 DeepSeek V4.1 Flash 在 Terminal-Bench 官方榜单上的名次。你可以用 TaoToken 的 Key 和 Base URL https://taotoken.net/api 复现同一任务,但每次运行会因为 harness 版本、截断策略、模型快照和重试策略不同而得到不同 token 数。
如果你要扩展这个任务,可以再加两个变体:一个变体把 flags 注入成 C.SQLITE_OPEN_READWRITE,测试仍会失败,因为文件不存在且没有 CREATE;另一个变体注入成 C.SQLITE_OPEN_READONLY|C.SQLITE_OPEN_CREATE,测试写入时会失败。两个变体都能迫使 agent 理解位掩码组合,而不是只把 READONLY 删掉。Token 表要按变体分别记录,不要把三个变体的数字平均成一个“综合分”。Terminal-Bench 的 agent 实战价值就在这里:同一个仓库、同一个模型,任务稍改一点,终端动作和 token 分布就会变。
5. 用同一把 Key 复现 go-sqlite3 修复对照表
复现时先把 Terminal-Bench 仓库和 go-sqlite3 副本准备好,再用同一把 Key 跑一遍本文任务。创建 Key 的入口在控制台,带 UTM 的 deep link 是 控制台。拿到 YOUR_API_KEY 后,把 OPENAI_API_KEY 设成它,OPENAI_BASE_URL 和 OPENAI_API_BASE 都设成 https://taotoken.net/api,TB_MODEL 从模型广场复制 DeepSeek V4.1 Flash 的实际 ID。不要把这个 Base URL 后面加 /v1,也不要把 UTM 参数拼到 API 地址上。跑完第一轮后,打开 模型对话 确认刚才用的模型 ID 与广场展示一致,避免因为手写 ID 导致 404。
对照表可以只记三列:轮次、terminal 动作、累计 token。本文表里的七轮动作已经给出,你复现时可以把自己的数字填在旁边。第一轮如果终端输出被截断得太短,模型可能看不到失败测试名,下一轮会先跑 go test -run TestCgoOpenFlags -v ./...,这会让轮次增加但输入 token 可能更小。第六轮如果模型先跑过滤测试再跑完整测试,累计 token 可能比本文低。每次运行都记录环境:Terminal-Bench 版本、go-sqlite3 副本来源、模型 ID、日期、是否同一把 Key。没有这些环境说明,两个 token 数放一起没有意义。
长期跑 agent 任务、多个仓库切换,可以用 Coding Plan 统一管理调用。Claude Code 或 CC Switch 的三件套配置在 接入文档 里,但本文的 Terminal-Bench 流程不要套用 ANTHROPIC_* 变量,继续用 OpenAI 兼容变量。Codex 用户改 ~/.codex/config.toml,不要把 ANTHROPIC_* 写进去;Terminal-Bench 用户则优先看 harness 自己支持的 provider 配置。不同工具变量名不同,Base URL 始终写 https://taotoken.net/api,Key 始终用控制台创建的那把。
最后确认这次 go-sqlite3 修复是否入账:跑完 go test ./... PASS 后,回到控制台看调用记录和 token 用量,和本文第 4 节的累计表对照。如果用量没有出现,先检查 Terminal-Bench 子进程是否继承了环境变量,再检查 Key 是否来自刚才创建的那把。确认无误后,把 go test ./... 尾部输出、git diff、七轮 Token 表贴回你自己的评测笔记。下一次换 agent 时,只改 harness 配置,不改 go-sqlite3 任务描述,这样得到的对照表才有可比性。
🚀 告别海外账号与网络限制!稳定直连全球优质大模型,限时半价接入中。 👉 点击领取海量免费额度




被折叠的 条评论
为什么被折叠?



