🚀 告别海外账号与网络限制!稳定直连全球优质大模型,限时半价接入中。 👉 点击领取海量免费额度
1. 任务:OpenHands 跑 SWE-bench Verified 子集,patch 才算通过
这次跑 OpenHands 的 SWE-bench Verified 子集,我先把默认 LLM 供应商接到 TaoToken。在官网创建 Key 后,OpenHands 的 Base URL 填 https://taotoken.net/api,模型 ID 从模型广场复制,避免模型 ID 不一致导致的 404。OpenHands 负责组织工具调用和写文件,TaoToken 只负责把 LLM 请求送到目标模型;两边职责分开,后面换模型时不必重装环境。
之所以把“有效 patch”作为通过指标,是因为 SWE-bench Verified 的每个实例都不是一道问答:它包含一个真实 GitHub issue、一个 base_commit 对应的代码库,以及一组 FAIL_TO_PASS 测试。模型光在对话里说“应该改这里”不算完成;OpenHands 必须在沙箱里把代码改出来,产出一个可应用的 diff,并且让相关用例从失败变成通过。只看“对话有没有结束”会漏掉大量半成品,所以这里以 patch 是否落盘且可用为准。
本文的“子集”是指从官方 swe-bench/swe-bench 仓库的 verified.jsonl 里取前几条实例。全量 500 个实例跑起来很重,不适合当作日常复现基线;取一小批既能验证 OpenHands 与 TaoToken 的链路是否通,也能观察每次调用的 token 消耗。后面我会给出表头和回填规则,但不会拿这一批数据冒充公榜。
2. 把 TaoToken 配成 OpenHands 的默认供应商:Key、Base URL 与模型 ID
先回官网创建 Key:注册、查看模型广场、翻用量记录都走 TaoToken 同一个地址。创建后把 Key 存在本地环境变量里,不要在正文里贴出真实值;我下面的命令和示例统一用 YOUR_API_KEY 占位。遇到任何“401 / invalid api key”,不要急着换模型,先回控制台确认 Key 是不是复制完整,以及是否创建在同一个账号下。这一步虽然基础,却是多数接入问题里最先出错的位置。
Base URL 写 https://taotoken.net/api,不要写 https://taotoken.net/api/v1,也不要加 UTM 参数。OpenHands 在请求时会自己拼具体的模型路径,如果你把 /v1 或查询串一起填进 Base URL,可能出现双路径或参数串位,日志里表现为 404 或路由错误。CLI 探活命令里同样保持干净:taotoken cc -k YOUR_API_KEY -u https://taotoken.net/api -m YOUR_MODEL_ID。这个命令来自 TaoToken CLI,装好后直接跑;它只验证 Key、Base URL、模型 ID 三者能不能对上一个完整请求,不参与 OpenHands。
模型 ID 怎么写:以模型广场展示出来的 ID 为准。同一个模型在不同聚合通道里可能有不同写法,甚至同一厂商不同批次也有别名;广场给什么,OpenHands 的 LLM_MODEL 就填什么。不要根据记忆把模型名改写成“更正式的版本”,也不要为了对齐某张榜单而套用榜单上的显示名。TaoToken 侧只按你填的 ID 路由,不负责把“近似名字”换算成目标模型。
启动 OpenHands 时,默认供应商配置对应三个环境变量:LLM_BASE_URL、LLM_API_KEY、LLM_MODEL。它们的值分别来自上面的 Base URL、Key 和模型广场 ID。把 ANTHROPIC_BASE_URL 或 Codex 的配置习惯搬过来是常见的错:OpenHands 不读 ANTHROPIC_AUTH_TOKEN,也不读 ~/.codex/config.toml,它只认自己的三件套。用环境变量启动容器的方式如下:
export LLM_BASE_URL=https://taotoken.net/api
export LLM_API_KEY=YOUR_API_KEY
export LLM_MODEL=YOUR_MODEL_ID
docker run -it --rm \
-v /var/run/docker.sock:/var/run/docker.sock \
-v "$PWD/workspace":/opt/workspace \
-e LLM_BASE_URL \
-e LLM_API_KEY \
-e LLM_MODEL \
ghcr.io/all-hands-ai/openhands:main
如果你 clone 的 OpenHands 版本已经改用 OPENHANDS_* 前缀,README 里会有环境变量映射表;本文按 LLM_* 展开,因为它仍是多数版本接受的写法。重点是把 LLM_BASE_URL 保持为不带 /v1 的地址,这份配置就不会在供应商切换后突然失效。换模型时,只改 LLM_MODEL 一个变量;Key 和 Base URL 不用动。
3. Harness 与工作区准备:子集怎么取、上下文怎么控
先从官方 GitHub 仓库拉取 SWE-bench Verified 数据。仓库地址是 swe-bench/swe-bench,里面的 verified.jsonl 就是 Verified 集合。用下面的脚本取出前 5 条,存成单独文件,避免后面反复读大文件:
git clone https://github.com/swe-bench/swe-bench.git
cd swe-bench
python - <<'PY'
import json
from pathlib import Path
with open("verified.jsonl") as f:
rows = [json.loads(line) for line in f]
subset = rows[:5]
Path("verified_subset.json").write_text(json.dumps(subset, indent=2))
print(len(subset))
PY
这个脚本把 5 个实例写到本地 JSON,方便后续逐条读取。每个实例包含 instance_id、repo、base_commit、problem_statement、FAIL_TO_PASS、PASS_TO_PASS 等字段。注意 FAIL_TO_PASS 是判据:模型产生的 patch 必须让这些测试用例通过,才算真正修复;如果只有 patch 但测试仍失败,我会记为不通过。
上下文的控制要单独说。OpenHands 会把任务描述放进对话开头,SWE-bench 的 problem_statement 有时候很长,所以不需要把整个 verified.jsonl 一次性塞给模型。一次只取一个实例,把 problem_statement 作为唯一任务描述,仓库则 checkout 到该实例的 base_commit。这样上下文窗口只装一个 issue 相关的信息,而不是五个 issue 混在一起;混在一起会让 Agent 分不清该改哪个仓库,patch 也容易张冠李戴。
还有一点容易被忽略:有效 patch 不要求与官方参考 patch 内容一致。SWE-bench 的参考 patch 只是用来确认测试语义的,模型只要改动后让 FAIL_TO_PASS 全过,就符合这个实例的修复要求。如果测试没过,即使看起来有 diff 也不能记通过;如果测试全过,哪怕 diff 很短也应视为有效。判断时始终以测试结果为准,而不是比对该实例的原始 patch。
Token 消耗我以 TaoToken 控制台显示的请求记录为准,不另开日志工具。每次请求的输入 token、输出 token、模型 ID 都能在对应记录里查到;把这些数字和上面的实例 ID 对应起来,就能估算单条 issue 的大致成本。控制台入口不变,还是 TaoToken 的官网。注意这里讨论的是用量统计,不是公榜能力排名。
最后加一条安全边界:让 OpenHands 操作的是本地独立目录或容器里的副本,不是生产仓库。它改坏了不影响原代码。Agent 拿到的 Shell 权限应限制在当前 workspace;不要在提示词里给它生产库地址,也不要让它直接连生产数据库执行 SQL。需要执行的命令由人确认后再放到真实环境里跑。这个原则与是否用 TaoToken 无关,但接上任何大模型工具时都值得先想清楚。
4. 跑通这一步:OpenHands 启动、Issue 注入与 Patch 落盘
上一节的启动命令已经准备好,这一步把它接进完整流程。先把某个实例的仓库 clone 到 workspace,并 checkout 到该实例的 base_commit。然后在 OpenHands Web UI 里新建会话,把 problem_statement 粘贴进去,末尾补一句“请把最终修改输出为 /workspace/patch.diff”。它会在 workspace 里读代码、改文件、跑测试。会话结束后直接看文件是否存在,而不是问模型“你完成了吗”。
强制落盘 patch 是一个值得养成的习惯。SWE-bench harness 也是这么做的:不依赖模型的“最终回答”,而是依赖文件系统里的产出。如果 Agent 只在对话里贴出 diff,你还要手工复制、清理格式,既容易出错也没法批量记录。让 Agent 自己写文件,后续验证和记录表都更干净。下面是本次使用的任务模板,problem_statement 按实例替换:
请阅读当前仓库与下面 issue 相关的内容,完成修复。
完成后把最终改动写进 /workspace/patch.diff。
只输出你做了什么,不要把 diff 内容重复在对话里。
[problem_statement 粘贴到此处]
每个实例单独开会话,不要让上一条 issue 的上下文残留到下一个。OpenHands 会维护自身会话历史,五个实例用一个会话会让它在后边引用前边的仓库路径和无关文件,patch 里出现不属于当前实例的改动。单独会话虽然更慢,但记录清晰,排障容易。
有效 patch 的三条检查:
test -s /workspace/patch.diff
git apply --check /workspace/patch.diff
第一条检查文件非空,第二条检查补丁能否无冲突地应用到仓库。SWE-bench 实例的 FAIL_TO_PASS 测试列表也要单独跑一遍。测试命令因 repo 而异,有的要装依赖,有的要跑 Django 迁移,以该实例的 repo 和 base_commit 为准;不要在五个不同仓库上复用同一个测试命令。网络环境里可能没有对应依赖,所以先在容器里装好项目依赖再执行测试。
下面这张是记录表模板,不是公榜成绩:
| 实例 ID | patch.diff 非空 | git apply --check | FAIL_TO_PASS 结果 | 是否记为通过 |
|---|---|---|---|---|
| 从 verified.jsonl 复制 | 填是/否 | 填通过/报错 | 填通过用例数/失败用例数 | 满足前两列且测试全过才填“是” |
每次跑完一个实例就回填一行。同一个模型 ID、同一个 Key、同一批实例,多跑几次可能得到不同结果;这跟采样温度、模型版本更新都有关。所以这张表是“某次运行的复现记录”,不代表该模型的公榜水平。
明确声明:本文不含排行分数。我没有把 SWE-bench Verified 的官方榜单或者任何其他公榜结果抄录进来。TaoToken 也不是这些榜单的参与方;榜单上排名的是模型本身,读者只是通过 TaoToken 的 Key 和 Base URL 接上同一个模型。要在本地做对照,就固定实例和 Prompt,只换模型 ID,再用同一张表记录。
5. 记录表怎么读、常见配置错在哪
这张表不能和公榜混读。公榜上的名次、百分比或 ELO 是模型全体评测样本的结果,而这张本地表只有少量实例,它只能告诉你“在这一次运行里,OpenHands 经 TaoToken 接入某模型后,有没有产生可用 patch”。两者用途完全不同。要是你想快速评估多个模型,可以用同一个 Key 同一批实例跑完填表,先看哪几个实例全都过,再看哪几个全挂,再做更完整的评测。
最常出现的配置问题有三个。第一,Base URL 写错成 https://taotoken.net/api/v1,或者末尾带了一个查询参数。OpenHands 会在 Base URL 后继续拼路径,多余的 /v1 会让请求落到不存在的路由。第二,API Key 填成了占位符 YOUR_API_KEY 本身,或者从聊天记录里复制带了引号、空格。第三,把别的工具的配置习惯带进来。Claude Code 用 ANTHROPIC_BASE_URL 与 ANTHROPIC_AUTH_TOKEN;Codex 用 ~/.codex/config.toml;OpenHands 不能照搬这两套,它读的是 LLM_BASE_URL、LLM_API_KEY、LLM_MODEL。
如果启动后提示模型不存在,回到模型广场复制当时的模型 ID。请求已发出但控制台没有记录,则检查 Key 与 Base URL 是否来自同一个账号,以及是否把 UTM 误加到 Base URL 上。UTM 只放在官网链接里,不能进入任何 API 请求地址。控制台的时间范围要覆盖 OpenHands 运行时段;OpenHands 的调用是分批的,每批会有多个请求记录。对照表可以先按模型 ID 过滤,再按实例 ID 标到行为主键。如果看见了请求记录但模型 ID 与广场不一致,多半是配置改了但容器环境变量没重载。
现在回到控制台,把这次 OpenHands 会话覆盖的时段拉出来,看是否每一轮模型调用都入账。入口还是开头创建 Key 的那个地址:TaoToken。如果记录为空,优先排查上面三个配置点,而不是换一个模型继续试。确认链路没问题后,把记录表复制一份,按自己的 Key 跑同样的子集,填出自己的版本。
🚀 告别海外账号与网络限制!稳定直连全球优质大模型,限时半价接入中。 👉 点击领取海量免费额度




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



