news 2026/9/18 11:57:21

OpenHands 实战:TaoToken 跑通本地仓库的单测修复

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
OpenHands 实战:TaoToken 跑通本地仓库的单测修复

🚀 告别海外账号与网络限制!稳定直连全球优质大模型,限时半价接入中。 👉 点击领取海量免费额度

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 在里面执行pytestgitpython都不会污染宿主机环境。对单测修复这种要反复跑命令的任务,隔离环境能省掉很多「依赖装串了」的麻烦。

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-ai

pip 方式下 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_MODELLLM_API_KEYLLM_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 类任务该有的安全姿势。

🚀 告别海外账号与网络限制!稳定直连全球优质大模型,限时半价接入中。 👉 点击领取海量免费额度

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/18 11:53:46

通达信麟龙四量图原理与可运行公式详解

简介:本资源是一份面向股票技术分析初学者与通达信公式开发者的实用教程,详解麟龙四量图指标的原理、逻辑与实盘应用要点。文档系统拆解MID核心价线、牛线(20日加权平滑)、马线(牛线6日均线)及多层STICKLIN…

作者头像 李华
网站建设 2026/9/18 11:51:34

EKF与神经网络融合的锂电池SOC估算方案:原理、实现与Matlab代码

1. 锂电池SOC估算为什么值得死磕做BMS(电池管理系统)的同行都有一个共识:SOC(State of Charge,荷电状态)估算是整个系统里最核心、也最让人头疼的一块。说它核心,是因为SOC直接决定了续航显示、…

作者头像 李华
网站建设 2026/9/18 11:48:35

MIUI纯净官改ROM更新:11款机型去广告精简,保留完整功能与Root

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/18 11:46:55

KV Cache 原理与显存优化:大模型推理 OOM 排查实战

前几天帮一个朋友看他本地部署的推理服务,8G 显存的卡,模型权重放进去还剩一点余量,单条对话跑得好好的,结果他把并发调到 8,还没跑到第二轮就开始报显存不足。他把模型换小了一档,问题照旧;把m…

作者头像 李华