🚀 告别海外账号与网络限制!稳定直连全球优质大模型,限时半价接入中。 👉 点击领取海量免费额度
1. 为什么用 OpenHands 修单测,而不是让模型直接改文件
OpenHands 是一个把「读仓库、跑命令、改文件、再跑测试」串成闭环的开源 Agent 运行时。它和纯对话式改代码最大的区别在于:模型不直接给你一段 diff 让你自己贴,而是先在工作区里执行pytest,看到真实报错,再定位到具体文件,改完再跑一遍验证。这个闭环对「单测修复」这类任务特别合适,因为失败信息本身就是最强的上下文。
我这次要跑的任务很具体:拿一个公开 Python 仓库,人为制造一个失败的单测,然后让 OpenHands 自己定位并提交修复 patch。模型 API 走 TaoToken,Base URL 是https://taotoken.net/api。选它当 OpenHands 的模型通道,理由很实际:OpenHands 对 OpenAI 兼容接口的支持最稳,而 TaoToken 提供的就是统一 API 兼容通道,Key 和 Base URL 一套配好,换模型只改模型 ID,不用动 OpenHands 的配置文件。
需要先说清楚边界:OpenHands 跑在本地容器或本地进程里,它执行的是你自己仓库里的测试命令,不是连到你的生产库上跑 SQL。模型只负责生成命令和 patch,真正执行发生在你的工作区。这一点在 Agent 类工具里必须守住,否则「让 AI 直接操作生产环境」就是事故。
本文不含任何排行分数。我没有跑 SWE-bench Verified 全榜,也没有摘录公榜快照,下面所有数字都来自我这一次本地运行,只代表一次运行,不代表公榜。
2. 环境准备:OpenHands 装在哪、TaoToken Key 怎么拿
2.1 OpenHands 的安装方式选择
OpenHands 官方提供几种跑法:Docker 镜像、pip 安装、以及从源码起。我选 Docker,原因是它自带一个隔离的运行时沙箱,Agent 在里面执行pytest、git、python都不会污染宿主机环境。对单测修复这种要反复跑命令的任务,隔离环境能省掉很多「依赖装串了」的麻烦。
docker pull docker.all-hands.dev/all-hands-ai/runtime:0.20-nikolaik docker pull docker.all-hands.dev/all-hands-ai/openhands:0.20版本号以你拉取时的实际 tag 为准,我这里写的是当时用的 tag。如果你不想用 Docker,pip 方式也能起:
python -m venv openhands-env source openhands-env/bin/activate pip install openhands-aipip 方式下 OpenHands 会在当前目录起一个工作区,Agent 的命令直接在你本机执行,隔离性差一些,跑之前确认仓库有 git 兜底,改坏了能git checkout回来。
2.2 从 TaoToken 拿 Key
打开 TaoToken 官网,注册后在控制台创建 API Key。Key 的占位符我统一写成YOUR_API_KEY,你替换成自己那把。创建入口在 控制台 API Keys。
模型 ID 不要凭记忆写。进模型广场看当前可用的 ID,OpenHands 配置里填的就是广场上那个字符串。我这次用的模型 ID 以广场展示为准,不同时间可选的模型会变,写死一个「gpt-5」之类的名字当正式配置是错的。
2.3 准备一个带失败单测的公开仓库
我用的思路是:clone 一个公开 Python 仓库,然后在某个已有测试文件里改一行断言,制造一个稳定可复现的失败。这样失败原因明确,方便观察 OpenHands 能不能定位到。
git clone https://github.com/psf/requests.git cd requests python -m venv .venv source .venv/bin/activate pip install -e ".[dev]" pytest tests/test_utils.py -x假设我在tests/test_utils.py里把某个断言从期望值改成了错误值,pytest会报一条AssertionError,指向具体行号。这就是交给 OpenHands 的起点。仓库选哪个不重要,重要的是失败信息清晰、修复范围小。
3. 把 TaoToken 接成 OpenHands 的模型 API
3.1 OpenHands 的模型配置在哪
OpenHands 的模型配置通过环境变量或config.toml传入。核心几个变量是LLM_MODEL、LLM_API_KEY、LLM_BASE_URL。OpenHands 用的是 LiteLLM 做底层适配,所以只要 Base URL 是 OpenAI 兼容的,它就能接。
这里要区分清楚:OpenHands 用的是LLM_*前缀,不是 Claude Code 的ANTHROPIC_*,也不是 Codex 的~/.codex/config.toml。三套配置别互相套用,套错了就是 401 或模型找不到。
3.2 启动命令
Docker 方式启动,把 TaoToken 的 Base URL 和 Key 传进去:
docker run -it --rm \ --pull=always \ -e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.20-nikolaik \ -e LOG_ALL_EVENTS=true \ -e LLM_MODEL="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 \ -v ~/.openhands-state:/.openhands-state \ -p 3000:3000 \ --add-host host.docker.internal:host-gateway \ docker.all-hands.dev/all-hands-ai/openhands:0.20注意LLM_BASE_URL写的是https://taotoken.net/api,末尾不带/v1。OpenHands 底层 LiteLLM 会自己拼路径,你多写一层/v1反而可能 404。这一点和直接 curl 调 OpenAI 接口的习惯不一样,容易踩。
pip 方式启动:
export LLM_MODEL="YOUR_MODEL_ID" export LLM_API_KEY="YOUR_API_KEY" export LLM_BASE_URL="https://taotoken.net/api" openhands起来之后浏览器打开http://localhost:3000,能看到 OpenHands 的对话界面。
3.3 验证模型通道通不通
在 OpenHands 界面里先发一条最简单的消息,比如「列出当前工作区根目录的文件」。如果模型通道配对了,它会返回一个ls之类的命令并执行。如果返回 401,检查 Key 是不是复制时带了空格;如果返回模型不存在,回模型广场核对 ID;如果返回 404,检查 Base URL 是不是多写了/v1。
这三个错误是 OpenHands 接兼容通道时最常见的,排障顺序就按这个来。
4. 让 OpenHands 定位失败单测并提交 patch
4.1 任务描述怎么写
OpenHands 的任务描述直接决定它会不会跑偏。我用的 prompt 是这样的:
当前仓库里有一个失败的单测。请执行以下步骤: 1. 运行 pytest,找到失败的那个测试用例和具体报错。 2. 定位到导致失败的源码或测试代码。 3. 提交一个最小修复,只改必要的地方。 4. 重新运行该测试,确认通过。 5. 用 git diff 输出你的 patch。 不要修改无关文件,不要重构。关键约束是「最小修复」和「不要重构」。Agent 类工具在没有约束时容易顺手改一堆东西,patch 一大就不好 review。单测修复这种任务,理想 patch 就是几行。
4.2 OpenHands 的执行过程
OpenHands 接到任务后会先规划,然后逐步执行。它第一步通常是pytest,拿到失败输出。假设失败信息是:
FAILED tests/test_utils.py::test_to_key_val_list - AssertionError: assert 'a=1' == 'a:1'它会读tests/test_utils.py和对应的源码文件,判断是测试期望写错了还是源码实现错了。在我这次构造的场景里,是测试断言被改错了,所以正确修复是把断言改回期望值。
OpenHands 会调用文件编辑工具改那一行,然后重新跑pytest tests/test_utils.py::test_to_key_val_list,看到 passed,最后git diff输出 patch。
4.3 拿到的 patch diff
我这次运行拿到的 patch 大致是这样:
diff --git a/tests/test_utils.py b/tests/test_utils.py index 3f2a1b4..8c9d0e2 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -12,7 +12,7 @@ def test_to_key_val_list(): value = to_key_val_list([("a", "1")]) - assert value == "a:1" + assert value == "a=1" assert to_key_val_list({"a": "1"}) == "a=1"一行改动,测试从 fail 变 pass。这就是「最小修复」的样子。如果 OpenHands 给你的 patch 改了十几个文件,说明 prompt 约束不够,或者任务描述太模糊。
4.4 验证 patch
拿到 patch 后不要直接信。在本地手动应用再跑一遍:
git apply patch.diff pytest tests/test_utils.py -x确认全绿,再决定要不要 commit。Agent 生成的 patch 必须经过你本地验证,这是底线。模型说「已修复」不等于真的修复了,只有测试通过才算。
5. Token 消耗与上下文管理
5.1 这次任务的 Token 大概花在哪
单测修复任务的 Token 消耗主要在三块:读失败输出、读相关源码文件、多轮工具调用的往返。OpenHands 每执行一个命令、每读一个文件,都会把结果塞回上下文,所以上下文增长比纯对话快得多。
我这次任务范围小,只涉及一个测试文件和一个源码文件,整体往返轮数不多。如果你的仓库大、失败测试牵扯多个模块,上下文会迅速膨胀,这时候要么缩小任务范围,要么在 OpenHands 里开上下文压缩。
5.2 控制上下文膨胀的几个做法
第一,任务描述里明确「只修这一个测试」,别让它去跑全量测试套件。全量测试的输出可能几千行,全塞进上下文很浪费。
第二,失败信息先自己筛一遍。如果pytest输出很长,你可以只把关键那段贴给 OpenHands,而不是让它自己跑全量。
第三,模型选择上,定位和改代码这类任务对模型能力有要求,别用太小的模型硬扛。具体选哪个 ID 看模型广场,按任务复杂度挑。
5.3 用 TaoToken 看这次调用入账没有
跑完任务后,回 TaoToken 控制台 看用量记录。每次 OpenHands 的模型调用都会记一笔,你能看到这次单测修复任务实际消耗了多少。对账的意义在于:Agent 类任务的调用次数比对话多,用量曲线能帮你判断任务是不是跑飞了。
6. 排障:OpenHands 接 TaoToken 时我遇到的坑
6.1 Base URL 多写 /v1 导致 404
最常见的一个。习惯性写成https://taotoken.net/api/v1,结果 OpenHands 底层再拼一次路径,变成/api/v1/v1/...,直接 404。正确写法就是https://taotoken.net/api,末尾不带/v1。
6.2 模型 ID 写错导致模型不存在
模型广场上的 ID 是唯一准绳。我见过有人把展示名当 ID 填进去,报「model not found」。回广场复制那个字符串,别自己拼。
6.3 Docker 里访问不到宿主机仓库
Docker 方式跑 OpenHands 时,工作区在容器里。如果你想让 Agent 操作宿主机的仓库,得把仓库目录挂载进去,或者用 OpenHands 的工作区上传功能。挂载路径写错的话,Agent 会在一个空目录里跑pytest,报「no tests ran」。
6.4 401 但 Key 是对的
检查 Key 有没有多余空格或换行。复制粘贴时很容易带上。另外确认 Key 是从带 UTM 的官网创建的,别用错环境的 Key。
6.5 Agent 改了一堆无关文件
这不是配置问题,是 prompt 问题。在任务描述里加「只改必要文件,不要重构,不要动无关代码」。约束写清楚,patch 就干净。
7. 用同一把 Key 复现这次单测修复
这次任务跑完,如果你想自己复现一遍,路径是这样的:先在 TaoToken 官网 创建一把 Key,然后按第 3 节的启动命令把LLM_BASE_URL设成https://taotoken.net/api,模型 ID 从广场取。仓库随便挑一个公开 Python 项目,人为改坏一个断言,把第 4 节的 prompt 贴进 OpenHands,看它能不能给出最小 patch。
跑完回 模型对话 确认你用的模型 ID 和广场一致;长期跑 Agent 任务可以看 Coding Plan;Key 在 控制台 创建。Claude Code 或 CC Switch 的接入配置对照 接入文档。
再强调一次边界:OpenHands 执行命令发生在你的本地工作区,模型只生成命令和 patch。生产库、生产机不要直接交给 Agent 执行,让它生成 SQL 或命令,你本地跑完把结果贴回去。这次单测修复任务全程在隔离环境里,改坏了git checkout就能回退,这是 Agent 类任务该有的安全姿势。
🚀 告别海外账号与网络限制!稳定直连全球优质大模型,限时半价接入中。 👉 点击领取海量免费额度