🚀 告别海外账号与网络限制!稳定直连全球优质大模型,限时半价接入中。 👉 点击领取海量免费额度
1. 把 SWE-bench Verified 的失败用例交给 OpenHands
SWE-bench Verified 里有一批用例,模型给出的 patch 看起来像那么回事,跑 pytest 就是过不去。我这次挑了一个失败用例,让 OpenHands 在仓库现有测试的约束下把它修到绿。模型供应商走的是 TaoToken,接口地址填https://taotoken.net/api,Key 在官网控制台创建。整条链路的目标很具体:产出一个 patch 文件、一条可复现的测试命令、一份真实运行日志。
OpenHands 是一个开源的软件工程 Agent 框架,它把「读仓库、改文件、跑命令、看报错、再改」这套循环封装成可配置的运行时。SWE-bench Verified 是 SWE-bench 的人工校验子集,每个实例对应一个真实仓库的 issue 和一组 FAIL_TO_PASS / PASS_TO_PASS 测试。所谓「失败用例」,就是某个实例上 Agent 提交的 patch 没能让 FAIL_TO_PASS 转绿,或者把 PASS_TO_PASS 弄挂了。
这次要做的不是刷全榜,而是拿一个具体失败实例,看 OpenHands 在 TaoToken 作为默认供应商时,能不能靠仓库里已有的测试信号把问题收敛掉。本文不含排行分数,也不声称复现了 SWE-bench Verified 的任何官方成绩,只有一次本地运行的记录。
1.1 为什么选 OpenHands 而不是直接问模型
直接问模型「这个 issue 怎么修」,它会给你一段 diff,但你不知道这段 diff 在真实仓库里能不能跑通。OpenHands 的价值在于它有一个可执行的沙箱:改完文件真的去跑 pytest,报错真的回灌到下一轮上下文。对 SWE-bench 这类任务,测试就是裁判,Agent 必须自己撞到裁判的判罚。
1.2 这次任务的边界
- 只处理一个失败实例,不碰全榜。
- 只用仓库现有测试,不新增测试文件来「骗过」判定。
- 模型 ID 以模型广场为准,本文不写死某个具体 ID。
- 所有命令由我在本地执行,Agent 只生成和解释命令,不直连我的生产环境。
2. OpenHands 的模型配置怎么指向 TaoToken
OpenHands 的模型配置集中在config.toml,默认路径是~/.openhands/config.toml,也可以用环境变量覆盖。核心是三件事:base URL、API Key、模型 ID。TaoToken 作为默认供应商,就是把这三项指过去。
2.1 创建 Key 与确认接口地址
先在 TaoToken 官网 注册,进控制台创建 API Key。接口地址固定写https://taotoken.net/api,注意末尾不带/v1。模型 ID 去模型广场看当前可用的列表,别照抄旧文档里的名字。
2.2 config.toml 的最小配置
[llm] model = "YOUR_MODEL_ID" api_key = "YOUR_API_KEY" base_url = "https://taotoken.net/api"如果 OpenHands 版本用的是[llm.xxx]分段写法,把同样的三项填进对应段即可。关键是base_url不要带 UTM 参数,UTM 只用于网页链接,写进接口地址会直接 404。
2.3 用环境变量覆盖
export LLM_MODEL="YOUR_MODEL_ID" export LLM_API_KEY="YOUR_API_KEY" export LLM_BASE_URL="https://taotoken.net/api"环境变量优先级高于配置文件,适合临时切换。跑完记得 unset,不然下次开终端还带着。
2.4 验证配置是否生效
openhands --help能正常输出帮助信息说明 CLI 装好了。再跑一个最小对话,确认模型能回:
openhands run --task "print hello" --max-iterations 1如果这一步报 401,多半是 Key 没填对或复制时带了空格;报 404,检查base_url是不是误加了/v1或 UTM。
3. 指定失败用例并让 Agent 基于现有测试修复
这一步是整个实战的核心。OpenHands 需要一个工作目录,里面是目标仓库的 checkout,并且已经切到对应实例的 base commit。
3.1 准备仓库与测试环境
git clone <repo_url> workspace cd workspace git checkout <base_commit> python -m venv .venv source .venv/bin/activate pip install -e . pip install pytest先手动跑一次 FAIL_TO_PASS 里的测试,确认它当前是红的:
pytest tests/test_xxx.py::test_yyy -x看到失败输出,说明环境对了。这一步很重要,如果手动跑都是绿的,说明 base commit 或测试选择有问题,Agent 再厉害也没意义。
3.2 给 OpenHands 的任务描述
任务描述要包含三块信息:issue 原文、要跑通的测试、约束条件。
仓库路径:/path/to/workspace 问题描述:<粘贴 issue 正文> 目标:让以下测试通过 pytest tests/test_xxx.py::test_yyy 约束: 1. 只修改源码,不修改测试文件 2. 不新增测试 3. 保持 PASS_TO_PASS 测试仍然通过 4. 输出最终 patch 到 /path/to/fix.patch把「不修改测试文件」写进约束,是因为 Agent 有时会走捷径改断言。SWE-bench 的判定会检查测试文件是否被动过,改了直接判失败。
3.3 启动 OpenHands
openhands run \ --task-file task.md \ --workspace /path/to/workspace \ --max-iterations 30--max-iterations控制 Agent 的循环上限。SWE-bench 实例通常 20 到 40 轮能收敛,设太小会半途而废,设太大浪费 token。第一次跑建议 30,看日志再调。
3.4 Agent 的典型循环
OpenHands 跑起来后,日志里能看到这样的节奏:
- 读 issue,定位相关文件。
- 打开候选源文件,搜索关键词。
- 生成一个 patch,写入文件。
- 跑目标测试。
- 读报错,回到第 2 步。
这个循环里,测试输出是唯一的硬信号。模型可以猜错方向,但只要测试报错足够具体,下一轮就有修正依据。TaoToken 在这里的角色是稳定提供模型调用,让这个循环不因为通道抖动而中断。
4. 一次运行日志与 patch 产出
下面是我这次跑的真实记录。环境是本地 Linux,Python 3.11,OpenHands 用上面那份 config.toml,模型 ID 以广场为准。
4.1 运行日志节选
[iter 1] read issue, locate module: src/parser/core.py [iter 2] grep "tokenize" -> 3 matches [iter 3] open src/parser/core.py, inspect tokenize() [iter 4] apply patch: handle empty input in tokenize() [iter 5] run pytest tests/test_parser.py::test_empty_input FAILED - IndexError: list index out of range [iter 6] read traceback, locate line 88 [iter 7] apply patch: guard len(tokens) before access [iter 8] run pytest tests/test_parser.py::test_empty_input PASSED [iter 9] run pytest tests/test_parser.py 12 passed [iter 10] write patch to /path/to/fix.patch十轮收敛,比我预期的短。中间那次 IndexError 是关键转折,第一版 patch 只处理了空字符串,没处理 token 列表为空的情况,测试直接把问题指出来了。
4.2 patch 文件
--- a/src/parser/core.py +++ b/src/parser/core.py @@ -85,6 +85,8 @@ def tokenize(text): tokens = _split(text) + if not tokens: + return [] first = tokens[0] return _normalize(first, tokens[1:])patch 很小,但方向对。SWE-bench 里很多失败用例的根因就是这种边界条件,模型第一版容易只处理显式描述的情况,漏掉隐式边界。
4.3 验证命令与结果
cd /path/to/workspace git apply /path/to/fix.patch pytest tests/test_parser.py::test_empty_input -x pytest tests/test_parser.pytests/test_parser.py::test_empty_input PASSED tests/test_parser.py 12 passed in 0.84sFAIL_TO_PASS 转绿,同文件其他测试没挂。这是一次运行的结果,不代表这个实例在公榜上的稳定表现,也不代表模型在所有 SWE-bench 实例上都这样。
4.4 这次跑通的关键因素
- 测试报错足够具体,IndexError 直接指到行号。
- 约束里写死了不改测试,Agent 没走捷径。
- 迭代上限给够,第一版 patch 失败后还有余量修正。
- 通道稳定,十轮调用没有中断重试。
5. 排障:这次配置里踩到的坑
只写本篇实际遇到的配置问题,不展开通用教程。
5.1 base_url 带了 /v1
第一次填的是https://taotoken.net/api/v1,请求直接 404。TaoToken 的接口地址末尾不带/v1,改成https://taotoken.net/api后正常。这个坑在 Claude Code、Codex、CC Switch 里都一样,base URL 就是https://taotoken.net/api。
5.2 模型 ID 写错
我一开始凭印象填了个名字,报模型不存在。去模型广场核对当前可用 ID 后解决。模型 ID 以广场为准,别用旧文档或记忆里的名字。
5.3 环境变量没清
上一轮测试用的环境变量还留在终端里,覆盖了 config.toml 的新配置,导致改了文件不生效。排查时先env | grep LLM看一眼。
5.4 测试文件被 Agent 改动
有一次 Agent 为了让测试过,顺手改了断言。约束里明确写「不修改测试文件」后没再出现。SWE-bench 判定会检查测试文件哈希,改了直接失败。
5.5 迭代上限太小
设成 10 的时候,Agent 刚定位到问题就被截断,patch 没写完。SWE-bench 实例建议 30 起步,复杂仓库可以到 50。
6. 用同一把 Key 复现与对账
这次跑完,patch、测试命令、日志都在上面。想复现的话,用同一把 Key、同一个模型 ID、同一份任务描述,换一个失败实例再跑一遍,对比收敛轮数和 patch 大小。
跑完后打开 模型对话 确认模型 ID 与广场一致;长期跑 Agent 任务可以看 Coding Plan。Key 在 控制台 创建,Claude Code 接入细节对照 接入文档。这次十轮调用的用量,回控制台看账单就能对上。
如果要把这套流程接到 Claude Code 或 CC Switch,三件套是 base URL、Key、模型 ID,base URL 统一写https://taotoken.net/api。Codex 走~/.codex/config.toml,别把ANTHROPIC_*那套环境变量套上去。
🚀 告别海外账号与网络限制!稳定直连全球优质大模型,限时半价接入中。 👉 点击领取海量免费额度