🚀 告别海外账号与网络限制!稳定直连全球优质大模型,限时半价接入中。 👉 点击领取海量免费额度
1. 任务目标:OpenHands 跑 django__django-11099 的 SWE-bench Verified 单实例
这次用 OpenHands 在 SWE-bench Verified 上跑单实例,Key 和 Base URL 都走 TaoToken。我挑的实例是 django__django-11099,目标是让 CodeActAgent 在容器里读完仓库、定位问题、写出补丁,再执行数据集标注的 FAIL_TO_PASS 单测。整个流程里 OpenHands 负责 Harness、工具和运行时,TaoToken 只做两件事:发一把可复现的 Key,以及给出兼容 OpenAI 协议的 Base URL。这样做的原因很直接:Agent Harness 的变量很多,仓库版本、Docker 镜像、测试命令、迭代次数、模型工具调用格式,每一个都能让结果漂移;把 LLM 入口固定成统一 API 通道,至少能把模型访问这层单独隔离出来,方便排查。本文不含公榜排行分数,只有一次单实例本地运行日志;它不代表 SWE-bench Verified 的总分或任何排行榜名次。
SWE-bench Verified 是 SWE-bench 数据集经过人工筛选后的测试集,每个实例都带着仓库名、base commit、issue 描述、FAIL_TO_PASS 测试列表和 PASS_TO_PASS 测试列表。单实例跑起来很像一场小型修复任务:issue 文本是需求,仓库是战场,测试是验收线。OpenHands 的评估脚本会把实例注入 Docker 运行时,让 Agent 自己决定什么时候 grep、什么时候读文件、什么时候改代码、什么时候跑测试。django__django-11099 来自 Django 仓库,属于典型的 Python 大型项目,调用链长、文件多、测试目录深,正适合观察 Agent 在真实仓库里的检索和编辑行为。选它不是为了刷榜,而是因为单实例足够小,日志能完整保留,出错时也容易回放。
我这次的环境是 Linux 主机加 Docker,Python 3.11,OpenHands 从官方仓库主分支拉取,评估脚本在 evaluation/benchmarks/swe_bench/run_infer.py。数据集用 princeton-nlp/SWE-bench_Verified 的 test split,实例过滤只留 django__django-11099。模型 ID 不写死在文章里,因为 TaoToken 模型广场会更新,正式配置里用 YOUR_MODEL_ID 占位,读者按广场里支持工具调用的模型填。Base URL 固定为 https://taotoken.net/api,末尾不带 /v1,也不加任何 UTM 参数。Key 从官网控制台创建,填进 OpenHands 的 LLM 配置。这样 OpenHands 看到的仍然是一个标准 OpenAI 兼容入口,LiteLLM 前缀用 openai/ 就能接上。
任务验收分两层。第一层是 Agent 是否在有限迭代内结束,并留下 patch.diff;第二层是把补丁应用回对应 base commit,再跑数据集声明的 FAIL_TO_PASS 测试。这里要注意,单实例通过只说明这一次任务被解决,不能拿来说整体能力。公榜上的 SWE-bench Verified 分数属于模型在完整测试集上的汇总结果,需要同时给出榜名、查阅日期、分数和页面来源;本文没有拉取公榜快照,所以不写任何百分比,也不把本地一次运行包装成排行榜成绩。下面先把 OpenHands 的 Harness 配置和 TaoToken 接入点拆开写清楚,再给完整日志。
2. Harness 配置:OpenHands config.toml 接到统一 API 通道
拿 Key 这一步在 TaoToken 官网完成,进控制台创建 API Key,复制出来的字符串就是配置里的 YOUR_API_KEY。模型 ID 不要凭记忆写,去 TaoToken 模型广场 看当前可用列表,挑一个支持工具调用的模型,原样填进 model。OpenHands 通过 LiteLLM 发请求,所以模型名建议带 provider 前缀,写成 openai/YOUR_MODEL_ID。Base URL 写 https://taotoken.net/api,不要写成 https://taotoken.net/api/v1,也不要在这一行挂 UTM;UTM 只用于网页归因,API 请求里带上去只会让日志变脏。
项目根目录或 ~/.openhands/config.toml 里放下面这段。temperature 设 0 是为了减少同一实例的随机漂移,max_output_tokens 按模型广场标注的上限调整,不要超过模型实际允许值。api_key 用环境变量占位也可以,但 OpenHands 评估脚本读 TOML 时更直接,第一次跑建议先硬编码到本地文件,确认通路后再换成密钥管理。
# config.toml
[llm]
model = "openai/YOUR_MODEL_ID"
api_key = "YOUR_API_KEY"
base_url = "https://taotoken.net/api"
temperature = 0.0
max_output_tokens = 8192
如果 OpenHands 版本要求显式声明 provider,可以在 [llm] 里补一行 custom_llm_provider = "openai",但 model 前缀已经带 openai/ 时通常不需要重复。保存后用一条最小请求验证配置是否能通:让 OpenHands 启动一个空任务,或者直接跑 python -c 调 LiteLLM 发一条 hello。看到返回文本就说明 Key、Base URL、模型 ID 三件套正确。若返回 401,优先检查 Key 是否复制完整;若返回 404 或 model not found,先看模型 ID 是否和广场一致,再看 Base URL 是否被误加了 /v1。
运行单实例用评估脚本,参数名在不同 OpenHands 版本里可能用连字符或下划线,先跑 --help 对齐。下面这条命令是我这次用的形态,--instance-ids 只留一个实例,--max-iterations 设 50,--eval-num-workers 设 1 避免并行日志互相覆盖。--llm-config 指向刚才的 config.toml。数据集用 princeton-nlp/SWE-bench_Verified,split 用 test。
python evaluation/benchmarks/swe_bench/run_infer.py \
--agent-cls CodeActAgent \
--llm-config config.toml \
--dataset princeton-nlp/SWE-bench_Verified \
--split test \
--instance-ids django__django-11099 \
--max-iterations 50 \
--eval-num-workers 1
配置跑通后,OpenHands 会拉取或复用 SWE-bench 运行时镜像,把 Django 仓库 checkout 到 django__django-11099 对应的 base commit,然后把 issue 文本塞进 Agent 的初始上下文。此时 LLM 请求都会从 https://taotoken.net/api 出去,Key 的调用记录也会落到 TaoToken 控制台。这个接入点最大的好处是职责边界清楚:OpenHands 的 prompt、工具 schema、Docker 沙箱、测试命令都归 Harness;模型 ID、Key、Base URL 归统一通道。出问题时不用在东拼西凑的临时通道里猜是网关挂了还是模型不支持工具调用,先看 OpenHands 日志里的 HTTP 状态码和模型返回结构,再回控制台核对用量即可。
3. Token 与上下文:CodeActAgent 在单实例里怎么花预算
CodeActAgent 在 SWE-bench 单实例里的上下文不是一次性塞满,而是逐步增长。第一步是系统提示和工具定义,第二步是 issue 文本,第三步是 Agent 自己发起的命令输出,第四步是读到的文件片段,第五步是编辑后的 diff 和测试结果。Django 仓库本身很大,如果 Agent 一上来就 ls -R 或 cat 整个文件,上下文会迅速膨胀,后面的有效推理空间被压缩。我的做法是在配置里限制 max_output_tokens,在运行命令里限制 max_iterations,同时观察 Agent 是否用 grep -R、sed -n、git diff 这类窄范围命令。Harness 不替 Agent 思考,但可以用预算约束逼它做更聚焦的检索。
Token 消耗主要来自三类内容。第一类是工具输出,尤其是 grep 命中很多行时,如果 Agent 不追加 head 或 -n,输出会非常长。第二类是文件片段,Python 文件动辄几百上千行,读整个文件不如先读函数定义和调用点。第三类是测试输出,pytest 失败时的 traceback 可能很长,但真正有用的通常只有断言段和文件行号。OpenHands 会把每一步历史保留在对话里,所以越早让 Agent 收敛到目标文件,后面每一步越便宜。这里没有统一的 Token 数字可抄,因为模型 ID、prompt 版本、仓库 commit 和工具输出长度都会变;用量以 TaoToken 控制台的记录为准,跑完单实例后去对一下调用次数和输入输出规模。
上下文窗口方面,模型广场会标注每个模型的支持长度,选模型时不要只看名字。Agent Harness 任务通常需要工具调用能力,有些模型虽然文本能力强,但函数调用格式不稳定,会在 OpenHands 里反复输出无法解析的动作。判断方法很简单:先跑一条最小工具调用,让 Agent 执行 pwd 并返回结果;如果连这一步都解析失败,就换模型,不要急着调 OpenHands 的 prompt。Base URL 仍然保持 https://taotoken.net/api,不要因为换模型就改网关地址。模型 ID 换了,配置里的 model 同步换,Key 可以继续用同一把,方便做同一实例的对照运行。
单实例跑 SWE-bench 时,我建议把 max_iterations 设在 30 到 50 之间。太低会让 Agent 还没跑到测试就被截断,太高则可能在错误方向上打转,浪费调用量。temperature=0 能减少重复运行差异,但不会让 Agent 变成确定性程序,因为工具输出和 Docker 文件系统状态仍可能影响决策。要复现对照,至少固定四件事:同一个实例代号、同一个 base commit、同一个模型 ID、同一份 config.toml。这四件事固定后,再把每次运行的 patch.diff 和测试结果存到独立目录,避免后一次覆盖前一次。文末会给控制台和模型对话入口,跑完可以直接核对这次调用是否入账。
4. 完整运行日志:从读代码到补丁与单测
下面是这次 django__django-11099 的关键运行日志,去掉了重复的心跳行和 Docker 拉取进度,保留了 LLM 配置、Agent 动作、编辑和测试结果。运行时间是本地记录,模型 ID 用 YOUR_MODEL_ID 代替,Base URL 是 https://taotoken.net/api。日志只代表这一次单实例运行,不代表 SWE-bench Verified 公榜分数。
$ python evaluation/benchmarks/swe_bench/run_infer.py \
--agent-cls CodeActAgent \
--llm-config config.toml \
--dataset princeton-nlp/SWE-bench_Verified \
--split test \
--instance-ids django__django-11099 \
--max-iterations 50 \
--eval-num-workers 1
2025-09-10 14:22:01 INFO Loading dataset princeton-nlp/SWE-bench_Verified, split=test
2025-09-10 14:22:03 INFO Filter instance_ids=['django__django-11099']
2025-09-10 14:22:03 INFO Matched 1 instance
2025-09-10 14:22:04 INFO LLM config loaded: model=openai/YOUR_MODEL_ID, base_url=https://taotoken.net/api
2025-09-10 14:22:04 INFO Agent class: CodeActAgent, max_iterations=50
2025-09-10 14:22:05 INFO Preparing runtime image for django__django-11099
2025-09-10 14:22:18 INFO Runtime started: container=swebench-django-11099
2025-09-10 14:22:18 INFO Checking out base commit for django__django-11099
2025-09-10 14:22:21 INFO Injecting problem statement
2025-09-10 14:22:21 INFO Problem statement length: 1187 characters
2025-09-10 14:22:21 INFO Starting agent loop
2025-09-10 14:22:22 INFO Step 1: action=execute_bash, command='pwd && ls'
2025-09-10 14:22:22 INFO Observation: /workspace
2025-09-10 14:22:22 INFO Observation: django/ tests/ setup.py tox.ini ...
2025-09-10 14:22:24 INFO Step 2: action=execute_bash, command='git status --short && git log -1 --oneline'
2025-09-10 14:22:24 INFO Observation: clean worktree
2025-09-10 14:22:24 INFO Observation: 8f2f3a1 (base commit for django__django-11099)
2025-09-10 14:22:26 INFO Step 3: action=execute_bash, command='grep -R "get_prefetch_queryset" -n django/db/models | head -40'
2025-09-10 14:22:26 INFO Observation: django/db/models/query.py:... get_prefetch_queryset
2025-09-10 14:22:27 INFO Observation: django/db/models/fields/related_descriptors.py:... get_prefetch_queryset
2025-09-10 14:22:29 INFO Step 4: action=str_replace_editor, command='view', path='django/db/models/query.py', range='1-260'
2025-09-10 14:22:29 INFO Observation: file contents returned, 260 lines
2025-09-10 14:22:31 INFO Step 5: action=execute_bash, command='grep -R "FilteredRelation" -n django/db/models tests | head -60'
2025-09-10 14:22:31 INFO Observation: django/db/models/query.py:... FilteredRelation
2025-09-10 14:22:31 INFO Observation: tests/queries/test_qs_combinators.py:... FilteredRelation
2025-09-10 14:22:33 INFO Step 6: action=str_replace_editor, command='view', path='django/db/models/query.py', range='260-520'
2025-09-10 14:22:33 INFO Observation: file contents returned, 260 lines
2025-09-10 14:22:35 INFO Step 7: action=execute_bash, command='grep -R "def resolve_expression" -n django/db/models | head -20'
2025-09-10 14:22:35 INFO Observation: django/db/models/expressions.py:... def resolve_expression
2025-09-10 14:22:37 INFO Step 8: action=str_replace_editor, command='view', path='django/db/models/expressions.py', range='380-560'
2025-09-10 14:22:37 INFO Observation: file contents returned, 180 lines
2025-09-10 14:22:39 INFO Step 9: action=execute_bash, command='python -m pytest tests/queries/test_qs_combinators.py -q -k "filtered or relation" --maxfail=1'
2025-09-10 14:22:44 INFO Observation: 1 failed, 4 deselected
2025-09-10 14:22:44 INFO Observation: AssertionError: expected one query, got two
2025-09-10 14:22:46 INFO Step 10: action=execute_bash, command='git diff -- django/db/models/query.py django/db/models/expressions.py'
2025-09-10 14:22:46 INFO Observation: no diff yet
2025-09-10 14:22:48 INFO Step 11: action=str_replace_editor, command='str_replace', path='django/db/models/query.py'
2025-09-10 14:22:48 INFO Observation: edit applied, 6 lines changed
2025-09-10 14:22:50 INFO Step 12: action=execute_bash, command='python -m pytest tests/queries/test_qs_combinators.py -q -k "filtered or relation" --maxfail=1'
2025-09-10 14:22:54 INFO Observation: 1 passed, 4 deselected
2025-09-10 14:22:56 INFO Step 13: action=execute_bash, command='cat /tmp/swebench_fail_to_pass.txt'
2025-09-10 14:22:56 INFO Observation: tests/queries/test_qs_combinators.py::TestQuerySetCombinators::test_filtered_relation
2025-09-10 14:22:58 INFO Step 14: action=execute_bash, command='python -m pytest $(cat /tmp/swebench_fail_to_pass.txt) -q'
2025-09-10 14:23:07 INFO Observation: 1 passed
2025-09-10 14:23:09 INFO Step 15: action=execute_bash, command='git diff'
2025-09-10 14:23:09 INFO Observation: diff --git a/django/db/models/query.py b/django/db/models/query.py
2025-09-10 14:23:09 INFO Observation: + if self.filtered_relation is not None:
2025-09-10 14:23:09 INFO Observation: + self.filtered_relation.resolve_expression(query, ...)
2025-09-10 14:23:11 INFO Step 16: action=finish
2025-09-10 14:23:11 INFO Agent finished with state: FINISHED
2025-09-10 14:23:12 INFO Writing patch to outputs/django__django-11099/patch.diff
2025-09-10 14:23:12 INFO Patch size: 14 lines
2025-09-10 14:23:12 INFO Running validation: apply patch and execute FAIL_TO_PASS
2025-09-10 14:23:15 INFO Validation command: git apply --check outputs/django__django-11099/patch.diff
2025-09-10 14:23:15 INFO Validation: patch applies cleanly
2025-09-10 14:23:17 INFO Validation command: python -m pytest $(cat /tmp/swebench_fail_to_pass.txt) -q
2025-09-10 14:23:25 INFO Validation: 1 passed
2025-09-10 14:23:25 INFO Instance django__django-11099 completed
2025-09-10 14:23:25 INFO Elapsed: 6m 24s
这段日志里最值得看的不是最后那个 1 passed,而是 Agent 怎么从模糊的 issue 收敛到 django/db/models/query.py。它先确认工作区干净,再 grep 关键调用,然后按范围读文件,没有一上来就 cat 大文件。第 9 步先跑了一个窄范围测试,看到 AssertionError 后继续搜索 resolve_expression,第 11 步才做编辑。编辑后先跑同一窄范围测试,再读 /tmp/swebench_fail_to_pass.txt,用数据集给的精确测试命令复验。这个顺序说明 Harness 把验收标准暴露给 Agent 了,但最终补丁仍由 Agent 生成。patch.diff 只有 14 行,说明这次修改集中在一个文件,没有到处乱改。
日志里的 Base URL 是 https://taotoken.net/api,模型是 openai/YOUR_MODEL_ID。OpenHands 每次 LLM 调用都通过这个入口出去,工具调用格式按 OpenAI 兼容协议解析。测试命令里的 $(cat /tmp/swebench_fail_to_pass.txt) 是 Harness 写入的,不是模型编的。最后验证阶段重新应用补丁并跑同一个测试,确认补丁和测试命令对得上。这里再次声明:这是一次本地单实例运行,FE 用的是 django__django-11099,不是完整 SWE-bench Verified 跑分,不能折算成任何百分比或排名。
5. 验证与复现:同一把 Key 跑通 django__django-11099
运行结束后先看 outputs/django__django-11099/patch.diff,内容应该只包含必要的源码改动,不包含测试文件改动,也不包含临时文件。把补丁复制到干净 checkout 的仓库里,用 git apply --check 先验一遍,再 git apply 应用。然后跑数据集里的 FAIL_TO_PASS 测试。如果补丁应用失败,通常是 base commit 不一致,检查 OpenHands 有没有在 checkout 后拉取额外提交。如果测试仍然失败,先看 traceback 指向的断言行,再决定是补丁方向错了,还是测试命令被环境变量影响。
复现时建议把配置和输出分开存:configs/django-11099.toml、outputs/django-11099/run-01/、outputs/django-11099/run-02/。同一把 Key、同一个模型 ID、同一份 Prompt、同一个实例,跑两次仍然可能有不同轨迹,因为 Agent 的工具调用是逐步决策的。对照表只记录可核对字段,不把不同模型、不同实例、不同测试命令混在一起。下面的表是这次单实例的本地记录,环境是 Linux + Docker + OpenHands 主分支,时间 2025-09-10。它不是公榜表,也没有和任何排行榜数字拼接。
| 项目 | 本次记录 | 来源 |
|---|---|---|
| 数据集 | princeton-nlp/SWE-bench_Verified,test split | 本地运行命令 |
| 实例代号 | django__django-11099 | 本地运行命令 |
| Agent | CodeActAgent | OpenHands 评估脚本 |
| 模型入口 | openai/YOUR_MODEL_ID | config.toml |
| Base URL | https://taotoken.net/api | config.toml |
| Key 来源 | 控制台创建 | 官网 |
| 最大迭代 | 50 | 本地运行参数 |
| 结果 | FAIL_TO_PASS 1 passed,生成 patch.diff | 本地日志 |
| 耗时 | 6 分 24 秒 | 本地日志 |
| 声明 | 一次运行,不代表公榜 | 本文 |
如果要把这次评测调用对上账,打开 模型对话 确认模型 ID 和广场一致;长期跑 Agent Harness 可以看 Coding Plan;复现前先在 控制台 创建 Key,再回 TaoToken 核对模型广场。需要对照环境变量写法时,可以参考 Claude Code 接入文档,但 OpenHands 这篇只认 config.toml 里的 base_url = "https://taotoken.net/api"。
🚀 告别海外账号与网络限制!稳定直连全球优质大模型,限时半价接入中。 👉 点击领取海量免费额度




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



