🚀 告别海外账号与网络限制!稳定直连全球优质大模型,限时半价接入中。 👉 点击领取海量免费额度
1. OpenHands 跑 SWE-bench Verified 的评测口径
OpenHands 跑 SWE-bench Verified,最怕 Agent 的补丁没打上,也怕模型通道把调用打散。我这轮用 TaoToken 做统一 API 基线,在 OpenHands 的 model 参数里填 Base URL https://taotoken.net/api,用同一把 Key 跑一组未标注 issue,记录 pass@1、token 消耗和失败类型。任务集来自 SWE-bench Verified 的 test split,但只抽其中一小段,不冒充全榜。
这轮评测的目标不是给某个模型排座次,而是把 Agent 评测里最容易混在一起的三件事拆开:模型能不能读懂 issue、OpenHands 能不能把补丁正确落到仓库、通道能不能稳定把多轮工具调用接住。SWE-bench Verified 的价值在于它把真实仓库的真实 issue 和真实测试绑在一起,Agent 不只是写一段看起来对的代码,还要让指定的 FAIL_TO_PASS 测试从失败变通过。OpenHands 的价值在于它把终端、文件编辑、测试执行和上下文管理做成一个可回放的 Agent Harness。把这两者放在一起,再用统一 API 通道做默认供应商,就能得到一张可复现的本地记录表。
这里先把数字纪律说清楚:SWE-bench Verified 公榜上的分数属于具体模型,不属于 TaoToken;我手里没有带查阅日期的公榜快照,所以本文不写“某模型进前几”“某个百分比吊打谁”这类数字,也不把一次小样本跑成排行榜。本文不含排行分数。后面的结果表只保留字段、判定口径和失败类型,具体通过数、token 数由你在本地跑完后填入。这样做的原因很直接:Agent 评测的方差比普通问答大得多,同一个模型换一次温度、换一次 max-iterations、换一次沙箱镜像,结果都会漂。
未标注 issue 的意思是,OpenHands 在解题阶段只能拿到 issue 文本、仓库基线快照和测试入口,不能拿到 gold patch。SWE-bench Verified 的 harness 在判定阶段才使用金标补丁和测试列表,Agent 侧看不到答案。这个边界要守住,否则跑出来的 pass@1 没有意义。我的抽样方式是从本地 parquet 里取一段连续实例,记录每条的 instance_id、仓库名、基础 commit 和 FAIL_TO_PASS 测试名。连续取样的好处是复现时不用重新设计随机种子,坏处是仓库分布可能偏斜,所以表格里要保留仓库列,方便后面看失败类型是否集中。
pass@1 的本地口径很简单:对每个实例,OpenHands 产出一个补丁,SWE-bench harness 把它应用到基础 commit,执行测试,如果 FAIL_TO_PASS 全部通过且 PASS_TO_PASS 没有被破坏,这条实例记 1,否则记 0。最后 pass@1 = 通过实例数 / 总实例数。如果实例数很少,比如 8 条到 12 条,这个数字只能当本轮观察值,不能当模型能力结论。更稳的写法是同时给出分子分母,比如“4/8”,而不是只写“50%”。只写百分比会让读者误以为分母很大,也容易掩盖某一条实例的判定争议。
token 消耗分两路记录。第一路从 OpenHands 的 event stream 里读每次 LLM 响应的 usage,把 prompt tokens 和 completion tokens 分开累计;第二路从 API 侧的控制台用量看总调用量,用来对账。Agent 任务的 token 消耗和普通聊天不一样,工具返回的终端输出、测试日志、文件 diff 都会重新进入上下文,所以 completion tokens 不一定最大头,prompt tokens 经常被 observation 推高。记录 token 的时候要带上实例编号,否则只能看到总数,没法判断是哪一类失败把上下文撑爆了。
失败类型我会在表格里用固定枚举,避免写成大段自然语言导致后面没法统计。第一类是 env_failed,沙箱没起来、依赖装不上、测试命令找不到;第二类是 patch_apply_failed,Agent 输出了 diff,但 git apply 失败;第三类是 test_failed,补丁应用成功,但 FAIL_TO_PASS 没通过;第四类是 context_overflow,上下文超限,Agent 提前终止;第五类是 action_parse_failed,工具调用格式解析失败;第六类是 provider_error,通道返回 401、404、429 或 5xx。这个枚举不是行业标准,只是本地记录用,好处是每一类都能对应到排障动作。
OpenHands 的 Agent 循环会把历史动作和观察结果不断拼回 prompt,所以 SWE-bench Verified 的评测天然吃上下文。一个实例如果进入测试阶段,测试输出可能几千行;如果 Agent 连续跑错命令,错误日志也会反复回灌。把 max-iterations 设得太高不一定更好,可能只是让模型在错误路径上多绕几圈。我的做法是先设 30 轮,观察每条实例在第几轮完成或失败,再把明显不够的实例单独加轮次复跑。复跑要记录,不能把两次结果混成一次 pass@1。
1.1 未标注 issue 的抽样与判定
抽样时我会保留三个字段:instance_id、repo、base_commit。instance_id 是 SWE-bench 的标准编号,通常长成 repo__repo-数字 的形式;repo 用来观察失败是否集中在某个项目;base_commit 保证补丁应用环境一致。如果本地 parquet 里带 problem_statement,就把它作为 issue 文本交给 OpenHands;如果带 hints_text,默认不给 Agent,避免把修复方向提前泄露。测试入口从 FAIL_TO_PASS 和 PASS_TO_PASS 读取,判定阶段才注入 harness。
判定阶段不要让 OpenHands 自己说“我修好了”。Agent 的自我评价在 SWE-bench 里没有效力,必须让 harness 跑测试。harness 返回 resolved 为真,这条实例才算过。如果测试因为环境问题没有跑起来,不能算模型失败,也不能算通过,要单独标 env_failed 并排除出 pass@1 分母,或者修好环境后重跑。这个区分很重要,否则沙箱问题会被误记成模型能力问题,最后得到的通过率没有参考价值。
未标注 issue 的难度分布也要看一眼。SWE-bench Verified 的实例来自多个 Python 仓库,有些仓库测试链很长,有些仓库依赖很重。如果抽到的实例集中在测试链长的仓库,env_failed 和 context_overflow 会偏多。我的表格里会加一列“仓库”,不为了做仓库排名,只为了解释失败类型。看到某一类失败集中,就要回到环境、轮次和上下文策略,而不是马上换模型。
1.2 pass@1、token、失败类型怎么记
pass@1 的分子只算 resolved 为真的实例。分母怎么算要提前定:如果某条实例因为本地环境缺依赖完全没跑起来,我会把它标成 env_failed 并放进“未计入”列表,不放进分母;如果它跑起来了但 Agent 没有产出补丁,那算模型侧失败,放进分母。这个口径要在表格备注里写清楚,不能一会儿算分母一会儿不算。小样本评测最怕口径漂移,同一批数据换一次口径,百分比就能差出很多。
token 我建议按实例记录四列:prompt_tokens、completion_tokens、total_tokens、llm_calls。llm_calls 是调用次数,能看出 Agent 是不是在反复试错。一个实例如果 total tokens 很高但 llm_calls 也高,通常是上下文反复回灌;如果 llm_calls 不多但 prompt tokens 很高,可能是单次 observation 太大。把这两列放在一起看,比只看总 token 有用。
失败类型不要等跑完再凭印象归类。每跑完一条实例,就在表格里填枚举值,并附一句最短原因,比如“patch 应用失败:diff 上下文不匹配”“测试未过:FAIL_TO_PASS 仍报错”“上下文超限:第 28 轮截断”。最短原因不要超过一行,否则表格会变成日志。等整批跑完,再按枚举聚合,得到失败类型分布。这个分布比单独的 pass@1 更有复现价值,因为它直接告诉你下一轮该调 harness、调上下文还是调通道。
2. OpenHands 接 TaoToken:.env、config.toml 与运行命令
OpenHands 接统一 API 通道,核心只有两步:拿 Key,改 Base URL。TaoToken 在这里的角色是默认供应商,不是被评测的产品。OpenHands 通过 LiteLLM 风格的配置读取模型名、API Key 和 Base URL,把请求发到 https://taotoken.net/api,模型 ID 仍然用模型广场里展示的那个。这里很容易犯的错是把 Base URL 写成带 /v1 的路径,或者在模型 ID 前面乱加前缀,最后 OpenHands 报 404,却以为是 Key 坏了。
我这轮的配置原则是:Key 只用一把,模型 ID 只在模型广场选,Base URL 固定写 https://taotoken.net/api。这样后面跑 SWE-bench Verified 时,变量只剩 OpenHands 的轮次、沙箱镜像和数据集切片。评测最怕变量太多,通道、模型、Harness 三个变量一起动,最后不知道是谁的问题。把通道固定成一个统一基线,至少能保证 401、404、429 这类错误有稳定的排查路径。
OpenHands 的配置可以在 .env 里写,也可以在 config.toml 里写。用 .env 更贴近 Docker 启动方式,用 config.toml 更适合本地源码运行。我两种都保留,但实际跑批时只用一种,避免环境变量和配置文件互相覆盖。下面先给最小 .env,再给 config.toml,最后给 Docker 启动命令和 SWE-bench 运行命令。
模型 ID 这块必须按模型广场来。LLM_MODEL 里到底写 openai/ 前缀还是 anthropic/ 前缀,取决于你用哪条协议接入,以及该模型在广场里的 ID 格式。不要凭记忆写一个不存在的型号,也不要把示例里的占位符当成正式配置。最稳的做法是先在模型对话页确认模型 ID,再把它填进 OpenHands。不同模型对工具调用的支持程度不同,SWE-bench Verified 这种多轮 Agent 任务,工具调用解析失败会直接拉低完成率。
2.1 .env 与 config.toml 的最小配置
.env 写法如下。LLM_API_KEY 填从控制台创建的 Key,LLM_BASE_URL 固定不要加 /v1,LLM_MODEL 里的 YOUR_MODEL_ID 以模型广场为准。
# OpenHands .env
LLM_MODEL=openai/YOUR_MODEL_ID
LLM_API_KEY=YOUR_API_KEY
LLM_BASE_URL=https://taotoken.net/api
config.toml 写法如下。OpenHands 本地源码运行时通常会读这个文件,字段名和 .env 对应,base_url 同样不要加 /v1。
[llm]
model = "openai/YOUR_MODEL_ID"
api_key = "YOUR_API_KEY"
base_url = "https://taotoken.net/api"
如果你用 Docker 启动,直接把 .env 通过 -e 注入即可。下面命令里的镜像标签写 latest,实际跑批时建议固定到你本地的镜像版本,并把版本号记进评测日志。/var/run/docker.sock 是 OpenHands 启动沙箱用的,不加它 Agent 没法在隔离环境里执行测试。
docker run -it --rm \
-e LLM_MODEL=openai/YOUR_MODEL_ID \
-e LLM_API_KEY=YOUR_API_KEY \
-e LLM_BASE_URL=https://taotoken.net/api \
-v /var/run/docker.sock:/var/run/docker.sock \
-p 3000:3000 \
docker.all-hands.dev/all-hands-ai/openhands:latest
2.2 启动 OpenHands 与 SWE-bench 运行命令
SWE-bench Verified 的推理入口在 OpenHands 仓库的 evaluation/benchmarks/swe_bench 下。不同版本的参数名可能微调,跑之前先用 --help 对齐。下面这组命令是我用的形态:数据集指向 princeton-nlp/SWE-bench_Verified,split 用 test,Agent 用 CodeActAgent,轮次先设 30,并发先设 1,避免通道侧 429 和本地沙箱资源打架。
cd evaluation/benchmarks/swe_bench
python run_infer.py \
--agent-cls CodeActAgent \
--llm-config llm \
--dataset princeton-nlp/SWE-bench_Verified \
--split test \
--max-iterations 30 \
--eval-num-workers 1 \
--eval-note taotoken-openhands-swebench
--llm-config llm 的意思是使用 [llm] 这一组配置。如果你的 OpenHands 版本要求传配置文件路径,就把 llm 换成 ./config.toml。如果完全走环境变量,确认启动 shell 里已经 source .env,否则 run_infer 可能读到空 Key,最后报 401。并发数从 1 开始不是为了保守,而是为了先把单条实例的 token 账本看清楚。等确认通道稳定、沙箱稳定,再逐步加到 2 或 4。
跑批时建议开两个窗口:一个跑 OpenHands,一个看控制台用量。控制台能看到调用量走势,如果某条实例突然把 token 拉高,可以及时判断是 Agent 在循环试错,还是 observation 太大。控制台地址在文末 CTA 里,这里先不展开。跑完一批后,把 harness 的 resolved 结果和 OpenHands 的事件日志按 instance_id 对齐。只有两边对齐,才能把 pass@1、token 和失败类型放进同一张表。
2.3 模型 ID 与协议前缀
OpenHands 里的 LLM_MODEL 通常需要带协议前缀,比如 openai/ 走 OpenAI 兼容协议,anthropic/ 走 Anthropic 协议。到底用哪个,取决于模型广场里该模型的接入说明。不要因为模型名字里带 Claude 就默认写 anthropic/,也不要因为通道兼容 OpenAI 就默认所有模型都写 openai/。前缀写错时,OpenHands 可能报模型不存在,也可能把请求发到错误路径,最后表现成 404。
模型 ID 本体以模型广场为准。广场里展示什么 ID,就填什么 ID,不要自己加日期后缀,也不要把别处的模型别名搬过来。SWE-bench Verified 跑批时最好固定一个模型 ID,不要中途换模型。中途换模型会让 token 账本和失败类型都失去可比性。如果确实要对比两个模型,就分两批跑,每批用同一组 issue 编号、同一轮次、同一沙箱镜像,最后分成两张表。
Base URL 固定写 https://taotoken.net/api,末尾不要加 /v1。有些 OpenAI SDK 会自动在 Base URL 后面拼 /v1,所以你在 OpenHands 里写 /api 是正常路径;如果你手写成 /api/v1,实际请求可能变成 /api/v1/v1/...,直接 404。这类 404 和模型 ID 写错很像,排查时先看请求路径,再看模型 ID,最后看 Key。
3. 同一把 Key 跑未标注 issue:记录表、Token 账本与失败类型
拿 Key 这一步很快。打开 TaoToken 注册,进控制台创建 API Key,把 YOUR_API_KEY 换掉。Key 只在本地 .env 或 config.toml 里用,不要写进代码提交。模型 ID 从模型广场选,先跑一条模型对话确认连通,再进 OpenHands 跑 SWE-bench Verified。这个顺序能省很多时间,因为模型对话只验证通道和模型,不引入沙箱和 harness 变量。
确认连通后,用同一把 Key 跑一组未标注 issue。每组建议从 8 条到 12 条开始,太少看不出失败类型分布,太多第一轮成本高。每条实例记录 instance_id、仓库、基础 commit、本机判定、prompt tokens、completion tokens、llm_calls、失败类型、最短原因。下面这张表是记录结构,实例编号列对应 SWE-bench 的 instance_id;具体通过数和 token 数由你本地跑完后填入,本文不把占位符写成成绩。
| instance_id | 仓库 | 基础 commit | 本机判定 | prompt_tokens | completion_tokens | llm_calls | 失败类型 | 最短原因 |
|---|---|---|---|---|---|---|---|---|
ISSUE-01 | 以本地数据为准 | 以本地数据为准 | 通过/未通过 | 从日志填 | 从日志填 | 从日志填 | 无/枚举值 | 一行以内 |
ISSUE-02 | 以本地数据为准 | 以本地数据为准 | 通过/未通过 | 从日志填 | 从日志填 | 从日志填 | 无/枚举值 | 一行以内 |
ISSUE-03 | 以本地数据为准 | 以本地数据为准 | 通过/未通过 | 从日志填 | 从日志填 | 从日志填 | 无/枚举值 | 一行以内 |
ISSUE-04 | 以本地数据为准 | 以本地数据为准 | 通过/未通过 | 从日志填 | 从日志填 | 从日志填 | 无/枚举值 | 一行以内 |
ISSUE-05 | 以本地数据为准 | 以本地数据为准 | 通过/未通过 | 从日志填 | 从日志填 | 从日志填 | 无/枚举值 | 一行以内 |
ISSUE-06 | 以本地数据为准 | 以本地数据为准 | 通过/未通过 | 从日志填 | 从日志填 | 从日志填 | 无/枚举值 | 一行以内 |
ISSUE-07 | 以本地数据为准 | 以本地数据为准 | 通过/未通过 | 从日志填 | 从日志填 | 从日志填 | 无/枚举值 | 一行以内 |
ISSUE-08 | 以本地数据为准 | 以本地数据为准 | 通过/未通过 | 从日志填 | 从日志填 | 从日志填 | 无/枚举值 | 一行以内 |
汇总表不要和下面的失败类型表混在一起。汇总只写分母、分子、pass@1、总 token、平均 llm_calls、主要失败类型。pass@1 写成分数形式,比如“通过数 / 总实例数”,再给百分比。总 token 把 prompt 和 completion 分开列,避免把输入输出成本混为一谈。平均 llm_calls 能看出 Agent 是否在错误路径上反复绕。主要失败类型按次数排序,不要只写一个。
| 汇总项 | 记录值 |
|---|---|
| 总实例数 | 本地跑完填 |
| 通过实例数 | 本地跑完填 |
| pass@1 | 通过实例数 / 总实例数 |
| prompt tokens 合计 | 从日志或控制台填 |
| completion tokens 合计 | 从日志或控制台填 |
| 平均 llm_calls | 本地跑完填 |
| 主要失败类型 | 按次数排序 |
Token 账本要和通道用量对账。OpenHands 的事件日志里能看到每次调用的 usage,控制台里能看到累计用量。两边如果差得多,先检查是不是有重试、超时或并发实例混在一起。Agent 任务的重试很常见,一次失败的工具调用可能触发重新规划,重新规划又会多一轮 LLM 调用。对账的目的不是把数字抠到个位,而是确认没有异常放大。如果某条实例 token 特别高,把它单独拎出来看上下文增长曲线,通常能定位到是哪一步 observation 失控。
失败类型表建议固定列:枚举、表现、排查动作。patch_apply_failed 看 diff 上下文是否匹配基础 commit;test_failed 看 Agent 有没有跑对测试命令;context_overflow 看 observation 是不是把测试日志全量回灌;action_parse_failed 看模型是否支持工具调用、OpenHands 的解析器是否匹配;provider_error 看 Key、Base URL、模型 ID 和限速。枚举不要太多,太多会失去统计意义。每个失败类型至少有两条实例再归为一类,单条实例可以放“其他”。
跑完这批未标注 issue 后,你会得到一张本地表。这张表只代表本轮环境、本轮轮次、本轮模型 ID 和本轮通道状态,一次运行,不代表公榜。它的用途是复现和排障,不是给模型发奖。把表里的实例编号、仓库、失败类型保留下来,下一轮换模型或换 harness 参数时,就能做同实例对照。不要只看总通过率,失败类型分布往往更能告诉你下一轮该改哪里。
4. SWE-bench Verified 本地复现与 OpenHands 排障清单
排障第一层是通道。401 通常是 Key 没读到、Key 写错、或者 .env 没有被容器读进去。404 通常是模型 ID 写错,或者 Base URL 多加了 /v1。429 通常是并发太高,或者同一把 Key 在跑其他任务。5xx 先看控制台状态,再降低并发重试。排查顺序是:模型对话能不能通、OpenHands 单条实例能不能通、SWE-bench 批量能不能通。不要一上来就批量跑,批量会把单条问题放大成一片错误。
排障第二层是 OpenHands 沙箱。Docker socket 没挂上,Agent 起不了 runtime;镜像拉取失败,测试环境缺失;工作目录不对,补丁应用路径错误。SWE-bench Verified 的实例来自真实仓库,依赖安装经常耗时。如果 env_failed 集中出现,先把沙箱单独跑通一条实例,确认仓库能 clone、依赖能装、测试命令能执行。沙箱问题不要记到模型失败里,否则 pass@1 会被环境噪声污染。
排障第三层是 Agent 上下文。OpenHands 把工具返回结果拼回 prompt,测试日志太长时会挤占推理空间。context_overflow 的典型表现是跑到十几轮后突然终止,或者模型开始重复之前的动作。处理方式是限制观察结果长度、减少 max-iterations、或者在 prompt 里要求 Agent 只回传测试摘要。轮次不是越高越好,30 轮跑不完的实例,加到 50 轮也可能只是多绕几圈。先看失败类型,再决定是否加轮次。
排障第四层是工具调用解析。action_parse_failed 说明模型输出的动作不符合 OpenHands 预期的格式,可能是模型不支持原生工具调用,也可能是 prompt 和解析器版本不匹配。可以换支持工具调用的模型,或者在 OpenHands 配置里调整解析模式。SWE-bench Verified 的多轮任务很依赖工具调用稳定性,解析失败一次,后面就要多花几轮纠正,token 也会跟着涨。
复现清单我建议固定成六项:同一把 Key、同一模型 ID、同一 OpenHands 镜像、同一 SWE-bench Verified 切片、同一 max-iterations、同一沙箱 runtime。跑的时候记下日期和本地时区,因为控制台用量和日志时间要对齐。如果你换了模型 ID,就重新开一张表,不要和上一张混算。如果你只改了 eval-num-workers,也要在备注里写清楚,因为并发会影响限速和失败类型。
最后再强调一次判定边界。OpenHands 说“完成”不算完成,必须让 SWE-bench harness 跑 FAIL_TO_PASS 和 PASS_TO_PASS。测试通过才算通过,测试没跑起来单独标 env_failed。pass@1 只统计有效实例,token 统计全部调用,失败类型按固定枚举聚合。这样得到的本地表虽然样本小,但每一步都能回放。公榜数字需要带查阅日期和页面来源,本文没有公榜快照,所以不写排行分数。读者用自己的 Key 跑出的数字,才是你这套环境下的真实数字。
5. 跑完对照表后怎么核对调用与 Key
跑完这组 OpenHands + SWE-bench Verified 的本地记录表,先做两件核对:模型 ID 是否和模型广场一致,token 用量是否和控制台入账一致。模型 ID 不一致时,OpenHands 可能跑的是另一个模型,pass@1 和 token 账本都会失去意义。用量不一致时,先看是否有重试和并发,再看是否把控制台的其他调用混进来。核对完成后,再把通过的实例编号和失败的枚举值存档,下一轮换模型或换轮次时直接复用同一组 issue。
确认模型 ID 可以打开 模型对话,用同一个 ID 发一条短消息,看返回是否正常。长期跑 Agent 评测或日常开发,可以看 Coding Plan,把常用模型和额度规划到一起。Key 在 控制台 创建,创建后直接填进 OpenHands 的 LLM_API_KEY,不要写死在代码里。
如果你后续要把同一套通道接到 Claude Code 或类似终端 Agent,Base URL 和 Key 的对应关系可以对照 接入文档。OpenHands 这边只需要记住三件事:Base URL 写 https://taotoken.net/api,模型 ID 以模型广场为准,Key 从控制台创建。把这三件事固定住,再用同一把 Key 复现下一组 issue,就能得到可对照的本地表。
🚀 告别海外账号与网络限制!稳定直连全球优质大模型,限时半价接入中。 👉 点击领取海量免费额度




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



