1. 为什么要在 Linux 上用 Docker 跑 OpenHands,还要统一 Key
OpenHands 是一个开源的 AI 软件开发代理平台,前身叫 OpenDevin,它能像真人开发者一样改代码、跑命令、调 API、翻文档,甚至从社区问答里扒代码片段。适合谁?适合那些手头有好几台 Linux 服务器、想给团队搭一个私有 AI 开发助手、又不想把 API Key 散落在各个项目里的后端和运维同学。
我自己的场景是这样的:一台 Ubuntu 22.04 的测试机,平时跑 CI、跑爬虫、偶尔做点代码生成实验。之前用 OpenHands 的时候,每换一个模型就要改一次环境变量,Claude 一个 Key、GPT 一个 Key、国产模型又一个 Key,配置文件里塞得乱七八糟。更麻烦的是,团队里其他人想用,还得把 Key 发来发去,安全上很不放心。
后来我把 OpenHands 的模型 endpoint 统一指向了 TaoToken 的 API 通道,一个 Key 就能切换不同模型,配置文件干净了很多。这篇就按我实际踩过的流程,从 Docker 部署、docker-compose 配置、环境变量模板,到把 endpoint 改到 TaoToken、验证对话和代码生成,一步步写清楚。你照着做,大概二十分钟能跑起来。
核心检索词先明确:OpenHands Docker 部署、Linux AI 开发助手、TaoToken 统一 Key 接入。这三个词贯穿全文,你搜到这篇基本就是你要找的东西。
先说清楚 OpenHands 的架构,不然配置容易懵。它本体是一个 Web 应用,默认监听 3000 端口,但它干活的时候会去启动一个 sandbox runtime 容器,也就是真正执行代码的那个隔离环境。所以你的 Docker 必须把宿主机的/var/run/docker.sock挂进去,让 OpenHands 有权限去拉 runtime 镜像、起容器。这也是为什么它不能简单塞进一个纯前端静态托管里。
模型这块,OpenHands 支持自定义 Base URL 和 Model Name,这就给了我们接入统一 API 通道的空间。你不需要改它的源码,只要在设置界面或者环境变量里把 LLM 的 base_url 指过去就行。下面进入实操。
2. TaoToken 前置准备:拿到统一 Key 和 Base URL
在动 Docker 之前,先把 TaoToken 这边的信息准备好,不然容器起来了你还得回头找。TaoToken 是一个统一的大模型 API 接入通道,你注册后在控制台创建一个 API Key,就能用它去调用不同厂商的模型,不用每个模型单独申请 Key。对 OpenHands 这种需要频繁切换模型的工具来说,省事很多。
第一步,打开官网 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,注册并登录。登录后进控制台,地址是 https://taotoken.net/console?utm_source=taotoken_aicg_blog_end&utm_content=console&utm_campaign=rewrite 。在控制台里找到 API Keys 页面,点创建,复制生成的 Key,形如sk-xxxxxxxx。这个 Key 只显示一次,记得存好。
第二步,确认 Base URL。TaoToken 的 API 入口是 https://taotoken.net/api ,注意这个地址后面不加任何 UTM 参数,配置里就写这个。OpenHands 里填 Base URL 的时候,通常需要带上/v1后缀,也就是https://taotoken.net/api/v1,具体以你调用时的实际路径为准,后面验证环节我会给出 curl 测试命令帮你确认。
第三步,想清楚你要用哪个模型。TaoToken 支持多种模型,你在控制台的模型列表里能看到可用的 Model ID,比如claude-sonnet-4-20250514、gpt-4o这类。OpenHands 的 Model 字段要填的就是这个 ID。建议先选一个你熟悉的,验证通了再换。
这里有个小坑提前说:OpenHands 的模型配置分两部分,一部分是 LLM Provider(提供商),一部分是 Model Name 和 Base URL。如果你用自定义 Base URL,Provider 一般选openai兼容模式或者custom,因为 TaoToken 的接口是 OpenAI 兼容格式的。选错了 Provider,请求会直接 404 或者 401。
准备好这三样东西:API Key、Base URL、Model ID,我们就可以写配置文件了。如果你还没创建 Key,现在去 https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api-keys&utm_campaign=rewrite 建一个,回来继续。
3. 可复制的 docker-compose 配置与环境变量模板
这一节是全文最核心的部分,配置直接抄,改几个值就能用。我不用docker run那种一长串命令,因为参数太多容易漏,改用 docker-compose,配置文件放本地,改起来清楚。
先建目录结构。在 Linux 服务器上执行:
mkdir -p /opt/openhands && cd /opt/openhands然后创建.env文件,把敏感信息和可变参数都放这里,docker-compose 会自动读取。文件内容如下:
# /opt/openhands/.env # TaoToken 统一 API 配置 LLM_API_KEY=sk-你的TaoToken密钥 LLM_BASE_URL=https://taotoken.net/api/v1 LLM_MODEL=claude-sonnet-4-20250514 # OpenHands 运行参数 OPENHANDS_PORT=3000 LOG_ALL_EVENTS=true SANDBOX_RUNTIME_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.14-nikolaik注意LLM_BASE_URL我写的是带/v1的,因为 OpenAI 兼容接口通常在这个路径下。如果你的调用报 404,可以试着去掉/v1再测,后面排障章节会讲怎么判断。
接着创建docker-compose.yml:
# /opt/openhands/docker-compose.yml version: "3.8" services: openhands: image: docker.all-hands.dev/all-hands-ai/openhands:0.14 container_name: openhands-app pull_policy: always ports: - "${OPENHANDS_PORT}:3000" environment: - SANDBOX_RUNTIME_CONTAINER_IMAGE=${SANDBOX_RUNTIME_IMAGE} - LOG_ALL_EVENTS=${LOG_ALL_EVENTS} - LLM_API_KEY=${LLM_API_KEY} - LLM_BASE_URL=${LLM_BASE_URL} - LLM_MODEL=${LLM_MODEL} volumes: - /var/run/docker.sock:/var/run/docker.sock - ./data:/.openhands extra_hosts: - "host.docker.internal:host-gateway" restart: unless-stopped几个关键点解释一下。volumes里挂了/var/run/docker.sock,这是 OpenHands 启动 sandbox 容器必须的,不挂的话它没法执行代码。./data:/.openhands是把配置和会话数据持久化到本地,容器重启不丢。extra_hosts那行是让容器内能通过host.docker.internal访问宿主机,某些网络环境下需要。
environment里我把 TaoToken 的三个参数通过.env注入进去了。OpenHands 0.14 版本支持从环境变量读取 LLM 配置,这样你就不用在 Web 界面里手动填,容器一起来就是配好的。
如果你用的是更新的版本,环境变量名可能略有不同,比如有的版本用LLM_API_KEY,有的用OPENAI_API_KEY。以你拉下来的镜像文档为准。我实测 0.14 这套是能读到的。
配置写好后,启动:
cd /opt/openhands docker compose up -d第一次会拉镜像,OpenHands 本体加上 runtime 镜像,加起来几个 G,网速慢的话等一会儿。拉完后docker compose ps看到openhands-app状态是Up就对了。
如果你更习惯用docker run,等价命令是这样,但我还是推荐 compose:
docker run -it --pull=always \ -e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.14-nikolaik \ -e LOG_ALL_EVENTS=true \ -e LLM_API_KEY=sk-你的密钥 \ -e LLM_BASE_URL=https://taotoken.net/api/v1 \ -e LLM_MODEL=claude-sonnet-4-20250514 \ -v /var/run/docker.sock:/var/run/docker.sock \ -v /opt/openhands/data:/.openhands \ -p 3000:3000 \ --add-host host.docker.internal:host-gateway \ --name openhands-app \ docker.all-hands.dev/all-hands-ai/openhands:0.14两种方式选一种就行,别同时跑,端口会冲突。配置文件里的 Model ID 记得换成你 TaoToken 控制台里实际可用的,写错了请求会返回模型不存在的错误。
4. 验证请求:从 curl 到 OpenHands 对话与代码生成
容器起来不代表模型通了,得一步步验证。我习惯先用 curl 直接打 TaoToken 的接口,确认 Key 和 Base URL 没问题,再去 OpenHands 里测,这样出问题好定位。
第一步,在宿主机上 curl 测试:
curl https://taotoken.net/api/v1/chat/completions \ -H "Authorization: Bearer sk-你的TaoToken密钥" \ -H "Content-Type: application/json" \ -d '{ "model": "claude-sonnet-4-20250514", "messages": [{"role": "user", "content": "回复两个字:通了"}] }'如果返回 JSON 里有choices字段,内容里是「通了」,说明 Key、Base URL、Model 三件套都对。如果返回 401,是 Key 问题;返回 404,多半是 Base URL 路径不对;返回模型不存在,是 Model ID 写错了。这三种情况下一节详细讲。
第二步,进 OpenHands 界面。浏览器打开http://你的服务器IP:3000。首次进入会弹设置窗口,如果你环境变量注入成功,这里应该已经填好了 Provider、Model、Base URL。检查一下 Base URL 是不是https://taotoken.net/api/v1,Model 是不是你设的那个。没问题就点 Save。
第三步,测对话。在输入框里发一句:
请编写一个 bash 脚本 hello.sh,打印 "hello world!"正常情况下,左侧显示你的提示词,右侧 OpenHands 会开始思考,然后给出脚本内容,并可能直接在工作区创建文件。这一步验证的是模型对话链路通了。
第四步,测代码生成和执行。继续输入:
用 HTML 写一个简单计算器,然后启动它OpenHands 会生成 HTML 文件,然后尝试在 sandbox 里起一个服务,最后在对话里输出访问链接。你点开链接,能看到计算器界面,说明 sandbox runtime 也正常工作了。这一步很关键,因为很多人模型通了但 sandbox 起不来,通常是 docker.sock 没挂对。
我实测下来,从 curl 通到界面出结果,整个链路大概十几秒。如果卡在「正在思考」很久,多半是模型响应慢或者网络问题,可以换个 Model ID 试试。
验证通过后,你可以在设置里随时切换模型,只要改 Model ID,Base URL 和 Key 都不用动,这就是统一 Key 接入的好处。团队里其他人用,你只要给他们 OpenHands 的访问地址,不用发 Key。
5. 本篇常见错误排查:401、local proxy failed、reading choices、OAuth
配置过程中最容易撞的几个报错,我按实际遇到的频率排一下,每个都给判断方法和解决动作。
401 Unauthorized。这个最常见,意思是 Key 不对。先检查.env里的LLM_API_KEY有没有多余空格,sk-前缀有没有漏。然后确认这个 Key 在 TaoToken 控制台里是启用状态,没被删。如果 Key 没问题,检查 Base URL 是不是写成了https://taotoken.net/api而漏了/v1,有些兼容层对路径敏感。用上一节的 curl 命令单独测,能快速区分是 Key 问题还是 OpenHands 配置问题。
local proxy failed / connection refused。这个报错通常出现在 OpenHands 容器内访问外部 API 时。原因是容器网络出不去,或者 DNS 解析失败。先docker exec -it openhands-app curl https://taotoken.net/api/v1/models看容器内能不能通。如果容器内不通但宿主机通,检查 docker 的网络模式,别用--network none。另外extra_hosts那行如果写错,也可能导致解析异常。
Error reading choices / choices 字段为空。这个说明请求发出去了,但返回结构不对。常见原因是 Model ID 写错,TaoToken 返回了一个错误对象而不是正常的 chat completion。解决办法是拿 curl 单独测这个 Model ID,看返回里有没有choices。如果没有,去控制台核对模型列表,换一个可用的 ID。还有一种可能是 Base URL 少了/v1,请求打到了错误的路径,返回了 HTML 而不是 JSON。
OAuth / 登录相关报错。OpenHands 某些版本会尝试 OAuth 登录或者校验 GitHub token,如果你没配,它可能报错。这个一般不影响本地使用,在设置里跳过或者用匿名模式即可。如果它强制要 OAuth,检查你的镜像版本,0.14 这套默认是本地设置,不需要 OAuth。如果你用的是带登录的版本,按它的文档配GITHUB_TOKEN之类的环境变量。
容器起来了但 3000 端口打不开。先docker compose ps看状态,再docker compose logs -f openhands看日志。常见是端口被占用,改.env里的OPENHANDS_PORT换个端口。还有可能是防火墙没放行,ufw allow 3000一下。
sandbox 起不来,代码执行一直转圈。九成是/var/run/docker.sock没挂或者权限不对。确认 compose 文件里那行 volumes 在,然后ls -l /var/run/docker.sock看权限,当前用户得能读写。如果是 rootless docker,路径可能不一样,按你的 docker 安装方式调整。
排查的核心思路就一条:先用 curl 把 TaoToken 这层测通,再测 OpenHands 到 TaoToken 这层,最后测 sandbox。分层定位,比瞎改配置快得多。
6. 长期使用建议与接入文档入口
跑通之后,有几个习惯能让这套东西用得更久。第一,.env文件权限设成 600,别让其他用户读到 Key。第二,./data目录定期备份,里面是你的会话和配置。第三,模型 ID 别写死在 compose 里,放.env,换模型只改一行。第四,如果团队多人用,考虑在前面加一层反向代理做访问控制,别直接把 3000 端口暴露到公网。
如果你想把 OpenHands 接到更多模型,或者想了解 TaoToken 支持哪些 Model ID,可以看接入文档:https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite 。文档里有各模型的调用示例和参数说明,比在界面里一个个试快。
想先在线试试模型对话效果,不搭环境的话,可以用模型对话入口:https://taotoken.net/chat?utm_source=taotoken_aicg_blog_end&utm_content=model-chat&utm_campaign=rewrite ,发几条消息感受一下响应速度和格式,再决定用哪个模型接进 OpenHands。
如果你打算长期跑编码任务或者做 Agent 实验,Coding Plan 会更划算,入口在 https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding-plan&utm_campaign=rewrite ,适合高频调用场景。Key 管理还是去 API Keys 页面:https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api-keys&utm_campaign=rewrite 。
最后说个我踩过的坑:OpenHands 的 runtime 镜像版本要和本体版本对上,0.14 的本体配 0.14 的 runtime,混用会报 sandbox 启动失败。升级的时候两个一起升,别只升一个。配置改完记得docker compose down && docker compose up -d重启,光 restart 有时候读不到新的环境变量。