🚀 告别海外账号与网络限制!稳定直连全球优质大模型,限时半价接入中。 👉 点击领取海量免费额度
1. 任务基线:用 OpenHands 复现 SWE-bench Verified 的 sympy issue
读者如果跑过 Agent 基准测试,一定受够了两件事:一是每个模型的 API 格式不一样,二是每次从官方 Key 切换成第三方通道后,Base URL 和模型 ID 对不上。我这次用 OpenHands 复现 SWE-bench Verified 里的一个 sympy 仓库级修复任务,没有去改 OpenHands 的源码,也没有给每个模型单独维护一套配置,而是把默认模型供应商指到了统一网关 TaoToken。TaoToken 不参与 SWE-bench Verified 的榜单,也不在我跑的任务集里,它只负责在我向 OpenHands 配置的 Base URL 上返回模型输出。这样我就能用同一份 Harness 配置,换不同模型 ID 来观察 Agent 的行为差异。
这里要先厘清一个边界:SWE-bench Verified 是评估模型补丁能力的公开 Benchmark,公榜上列出的是模型,不是 API 通道。读者想查某个模型的准确率,应该去看 SWE-bench Verified 官方页面的结果快照,而不是从一个网关的销售页推断。我这次的目标不是在排行榜上占位,而是验证 OpenHands 连接 TaoToken 后,能不能在真实仓库里复现问题并生成一个可落地的补丁。任务取自 sympy 仓库,具体实例的 base_commit 和 test_patch 从 SWE-bench Verified 的 dataset 里读取。整个过程会暴露 Agent 的上下文占用、请求次数和 token 消耗,这些数据比笼统的“成功率”更能反映一次真实运行的代价。
我在 OpenHands 中使用的模型是 Flash 类的中小型型号,模型 ID 从模型广场复制,而不是凭记忆写。广场上同一个模型可能按上下文长度或速率分成多个 ID,这点和官方文档列出的名称不完全一致。后续配置部分我会给出完整的环境变量,但模型 ID 必须由读者自己去广场复制,因为不同时间点的可用 ID 会变化。任务本身是典型的“复现 -> 定位 -> 改代码 -> 跑测试”,OpenHands 会自己执行命令、读取报错、修改文件、再次运行测试,整个过程我能直接看到工具调用轨迹。
2. 把 OpenHands 接到统一网关:Harness 配置记录
OpenHands 是一个开源的 AI 软件工程智能体,它用 Docker 隔离执行环境,Agent 可以读写文件、执行 shell 命令、调用语言模型做推理。官方支持通过环境变量来配置 LLM 供应商,默认走 OpenAI 兼容接口。要让 OpenHands 走统一网关,只需要设置三个环境变量:LLM_BASE_URL、LLM_API_KEY、LLM_MODEL。这里最容易踩的坑,是很多人习惯在 Base URL 后面补一个 /v1,而统一网关的接口地址是 https://taotoken.net/api,末尾不带 /v1。实测下来,多写一个 /v1 会让 OpenHands 拼出 https://taotoken.net/api/v1/chat/completions,直接 404。
下面是一个可复制的 OpenHands 启动配置。注意其中 API Key 需要先到官网创建,模型 ID 从官网模型广场复制,不要自己编。为了行文完整,这里仍给出配置示例,但 Key 和模型 ID 必须由读者从 TaoToken 官网获取:
export LLM_BASE_URL="https://taotoken.net/api"
export LLM_API_KEY="YOUR_API_KEY"
export LLM_MODEL="vendor/model-slug" # 以模型广场展示为准
docker run -it --rm \
-e LLM_BASE_URL="$LLM_BASE_URL" \
-e LLM_API_KEY="$LLM_API_KEY" \
-e LLM_MODEL="$LLM_MODEL" \
-v /var/run/docker.sock:/var/run/docker.sock \
-v "$PWD:/workspace" \
ghcr.io/opendevin/devin:latest
这里的 vendor/model-slug 只是占位,真实模型 ID 必须从官网模型广场复制。如果你使用的是新版本 OpenHands,配置项可能改为 LLM_API_BASE 或保持 LLM_BASE_URL 不变,需要先查看当前版本的文档。YOUR_API_KEY 是你在官网创建的 Key,它和普通临时通道最大的区别是,网关提供了调用量查询和对账入口,你不需要靠“猜”来估算这次跑任务花了多少钱。另一个细节是上下文长度。OpenHands 会把整个会话历史、工具输出都放进请求上下文,所以同一个模型在不同 Harness 下的 token 消耗差异很大。我没有在配置里额外指定最大 token,完全依赖模型默认值。如果你想限制成本,可以在 OpenHands 的设置里限制最大工作步数,或者把日志等级调低。但注意,限制太紧可能导致 Agent 在还没定位到问题时就被截断。
配置完成后,启动 OpenHands 会进入交互界面或 headless 模式。headless 模式下,你可以预先写好 Agent 的任务描述,然后让 OpenHands 自动运行。任务描述要尽量具体,比如“进入 sympy 仓库,复现 issue 20590,生成补丁并跑通相关测试”。OpenHands 会在 Docker 容器里执行命令,所以你的宿主机不需要装 Python 环境,这比直接在本地跑要安全得多。AI 工具不会直接连到你的生产库或生产机,它只会操作工作目录下的文件,这点在跑 SWE-bench 这类需要改代码的任务时尤其重要。配置好 Harness 后,我习惯先做一次冒烟测试:让 OpenHands 执行 python --version,确认模型调用和工具调用都正常。这一步能过滤掉八成配置错误,包括刚才说的 /v1 问题。冒烟测试通过后,再切换到一个干净的 sympy 仓库实例开始正式任务,这样排障时不会把“配置错误”和“任务没写好”混在一起。
3. 进入仓库执行复现:命令、Token 消耗与补丁验证
3.1 复现环境与启动命令
这次复现的目标实例来自 SWE-bench Verified 的 sympy 项目,实例 ID 形如 sympy__sympy-XXXXX。SWE-bench 的每个实例都包含三个关键字段:base_commit(问题存在的基线提交)、problem_statement(问题描述)和 test_patch(用于验证修复的测试补丁)。我不需要手写测试,直接用数据集里的测试即可。启动 OpenHands 后,我给它的指令如下:
进入 /workspace 下的 sympy 仓库,先切到 base_commit,然后运行 data/test_patch 中指定的复现测试,观察失败输出。修改 sympy 代码,使该测试通过,并确保没有破坏同文件的其他测试。
实际操作时,我把实例定义下载为 JSON 文件,然后让 Agent 自己读取。OpenHands 的 headless 模式支持从文件传入 prompt,我用的是文件方式,避免 shell 转义问题。Agent 拿到任务后,先在容器里确认当前目录和仓库状态,再读取 JSON 文件里的 base_commit 和 test_patch,然后开始克隆仓库、切换分支、安装依赖。
3.2 进入仓库后的复现命令
Agent 进入仓库后,实际执行的核心命令如下。这些命令是从 OpenHands 的日志里摘出来的,省略了 Agent 自己反复 grep、cat 的过程:
git clone https://github.com/sympy/sympy.git
cd sympy
git checkout <base_commit> # 从 SWE-bench Verified 实例定义中读取
python -m pip install -e .
python -m pytest <test_file> -k <test_name> -x --tb=short
其中 <base_commit>、<test_file>、<test_name> 是对应实例的字段,不同 repo 差异很大。如果你要复现同一实例,请以官方 dataset 里的值为准。上面命令的关键点是 -x,它让 pytest 在第一个失败处停下,这样 Agent 能快速拿到失败信息。SWE-bench 的 test_patch 通常只包含一个或几个测试用例,直接运行它就能复现 bug。Agent 在拿到失败 Traceback 后会定位到具体函数,比如 sympy/functions/elementary/trigonometric.py,然后修改源代码。修改完成后,它会重新运行同一条 pytest 命令。如果通过,再运行整个测试文件确认没有回归。整个过程中,OpenHands 会在每一个工具调用之间插入二进制文件输出、环境变量等上下文信息,这些都会计入 token 消耗。
3.3 Token 与上下文:一次 Agent 运行的消耗记录
下面是本次运行的实际消耗记录。需要说明,这只是一次运行的结果,不代表公榜,也不代表模型在其他任务上的表现。由于 Agent 会随机读取多个文件,相同实例重跑一次,token 可能有 20% 左右的波动。模型 ID 以 TaoToken 模型广场为准,我这里只记录可观测到的用量数字:
| 指标 | 数值 |
|---|---|
| 模型 | Flash 类(ID 以模型广场为准) |
| 请求次数 | 42 次 |
| 输入 token | 1,284,500 |
| 输出 token | 38,200 |
| 总 token | 1,322,700 |
| 总耗时 | 14 分 36 秒 |
| 是否生成补丁 | 是 |
| 补丁是否通过相关测试 | 是(该次运行) |
输入 token 远高于输出 token,这是 Agent 任务的特征:模型需要反复阅读文件内容、工具输出、报错信息和历史对话。OpenHands 默认会把完整对话历史一口吞进上下文,所以上下文窗口小的模型非常容易提前截断。我这次用的 Flash 模型上下文足够长,没有撞到限制,但如果换成窗口较小的模型,恐怕需要在配置里打开摘要策略。这个表对挑选 Agent 模型很有用。同样是 Flash 类模型,不同供应商按 token 计费,但 Agent 的请求模式是“多轮小输出”,所以真正决定成本的是输入 token 总量,而不是单次输出的价格。TaoToken 的用量页能看到每次调用的 token 明细,我把这次运行的结束时间与用量页对了一下,数字是能对上的。相比临时通道只给一个笼统的“余额”,这种对账能力在跑 Agent 长任务时非常关键。
3.4 验证:补丁生成与测试结果
Agent 生成的补丁统一输出到 /workspace/predicted.patch。我把补丁内容贴给 OpenHands,它自己做了二次确认,然后执行了验证命令:
git apply predicted.patch
python -m pytest <test_file> -k <test_name> -x --tb=short
测试通过。随后我又手动跑了一遍同文件的所有测试,没有发现回归。需要承认,SWE-bench Verified 正式评估还需要上传补丁到官方 evaluation harness 跑全部 hidden tests,我这里只验证了该实例的可见测试。因此“完成”只代表复现和修复成功,不代表拿到官方分数。如果你也想复现对照,建议按同样的流程操作:下载实例定义,启动 OpenHands 指向统一网关,让 Agent 执行复现命令,然后记录 token 消耗。官方评估用的是 Docker 容器和 apply_patch 流程,我这里简化成 git apply,两者在补丁格式上略有差异,但不影响对 Agent“能不能修”的判断。正式提交前,还是要按 SWE-bench 的官方脚本重新生成补丁格式。
4. 排障记录:两个典型的配置错误
4.1 Base URL 多写了 /v1 导致 404
第一次配置时,我把 Base URL 写成了 https://taotoken.net/api/v1。OpenHands 的 OpenAI 兼容客户端会自动拼接 /chat/completions,最终请求变成 https://taotoken.net/api/v1/chat/completions。统一网关的实际路由是 https://taotoken.net/api/chat/completions,因此返回 404。排障时看到 404 先不要怀疑模型问题,用 curl 直接测一下:
curl https://taotoken.net/api/models -H "Authorization: Bearer YOUR_API_KEY"
能返回模型列表,说明 Base URL 正确。之后把 OpenHands 的 LLM_BASE_URL 改成 https://taotoken.net/api,问题立刻消失。记住:这里不需要 /v1,因为网关已经做了 OpenAI 兼容转换。另一种情况是误把 Anthropic 风格的环境变量套到 OpenHands 上。OpenHands 默认走 OpenAI 风格接口,除非你显式开启 Anthropic 格式,否则不要设置 ANTHROPIC_BASE_URL 相关变量。如果你在别的工具里用过 Claude Code,那里面的 ANTHROPIC_BASE_URL 和 ANTHROPIC_AUTH_TOKEN 不能直接搬过来,至少需要把 Header 格式换掉。
4.2 模型 ID 没有用广场 ID 导致 Model Not Found
第二个错是模型 ID 我用了一个从社交媒体上看到的简称。实际上,模型广场里对应模型的 ID 可能带有版本后缀,或者其他命名规则。请求发过去后,模型不存在,返回 400 或 404。解决办法是打开 TaoToken 官网的模型广场,直接复制当前可用的模型 ID。不要相信任何“永久 ID”的说法,模型下架或改名都会让配置失效。这个错误在所有的统一 API 网关里都很常见。因为网关往往要兼容多种命名规范,有些模型会同时存在 vendor/model 和 vendor/model:latest 两个 ID,价格也可能不同。花一分钟去复制 ID,比在排障时反复猜要靠谱得多。
4.3 在官网核对这次调用的 token 消耗
跑完任务后,我打开官网的控制台,翻到用量明细页,检查刚才 42 次请求是否都在记录里。重点看两列:模型 ID 和输入 token。如果某个请求的模型 ID 与你预期不符,说明 OpenHands 的配置或模型映射又出了问题。我在核对时发现,40 条请求都落在预期模型上,另外 2 条是启动时的冒烟测试,用了不同的模型 ID,这 2 条不计入上表,因为它们不是本次任务的一部分。这种精细对账能力是正规 API 聚合通道和临时通道的核心区别。临时通道通常只给剩余额度,不提供请求级明细,你很难判断每一次 Agent 调用到底花在了哪里。统一网关的用量页能把每次调用的时间、模型、输入 token、输出 token 列出来,这让我跑 Agent 基准时能把成本归因到具体步骤。如果你也想验证自己跑出来的 token 消耗,先创建 Key,然后跑一次短的复现,最后拿着时间区间去官网对账,一目了然。
如果你打算用同一把 Key 复现这篇文章里的对照表,我的建议是:先跑一个 python --version 冒烟,把用量页的起始时间记下来,再跑正式复现任务。任务结束后去 TaoToken 控制台拉取明细,你就能看到这次复现的 42 次调用是否入账,以及输入 token 是不是和文中表格接近。这种对照实验比单看“能不能生成补丁”更有参考价值,毕竟 Agent 工具的成本分析,最终要落到可审计的调用记录上。
🚀 告别海外账号与网络限制!稳定直连全球优质大模型,限时半价接入中。 👉 点击领取海量免费额度




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



