ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

拆一个范本:OpenHands skill 长什么样,从 headless terminal 到 LiteLLM 配置

拆一个范本:OpenHands skill 长什么样,从 headless terminal 到 LiteLLM 配置 1. 从一次终端任务说起OpenHands skill 到底解决什么问题如果你最近在折腾 Agent 编排大概率会碰到一个尴尬模型能选、工具能接但真正跑起来的时候每个 skill 的目录结构、启动方式、模型配置各写各的换一个模型就得改一堆代码。OpenHands 这个 skill 之所以值得单独拆是因为它把「模型无关」这件事做成了工程范本——背后接的是 LiteLLMOpenAI、Anthropic、OpenRouter、DeepSeek、Ollama、vLLM 都能挂而对外只暴露一个 headless terminal 调用入口。先说清楚它是什么。OpenHands 本身是一个开源的软件工程 Agent 框架而这个 skill 是把它包装成一个可被上层编排系统调用的标准单元。它能做的事很具体接收一个任务描述在隔离环境里自主读写文件、执行命令、跑测试最后把结果以 JSON 形式吐回来。适合谁适合那些已经在用 Claude Code 或 Codex 做原生开发、但需要「换模型对比效果」或者「多模型混跑」的团队。因为要 Claude 原生能力就走 claude-code要 OpenAI 原生就走 codex只有当你需要灵活切换 provider 时OpenHands 这条链路才真正发挥价值。我试过把同一个重构任务分别丢给三个 providerOpenHands 的 headless 模式是唯一一个不需要改 skill 代码、只改环境变量就能切换的。这个特性决定了它的目录结构必须足够克制——配置文件、启动脚本、模型映射三者分离谁都不越界。接下来我会把这份 skill 的骨架拆开从目录结构到 headless 启动命令再到 LiteLLM 的配置片段最后用一次真实的终端任务验证整条链路。你照着抄就能得到一个可复用的 skill 范本。2. 目录骨架与 headless terminal 启动链路拆解一个好 skill 的第一特征是「目录会说话」。OpenHands 这份的骨架大致长这样我按职责分层列出来openhands-skill/ ├── SKILL.md # 能力声明与使用边界 ├── config/ │ ├── litellm.yaml # 模型路由配置 │ └── runtime.env # 运行时环境变量 ├── scripts/ │ ├── run_headless.sh # headless 启动入口 │ └── healthcheck.sh # 链路自检 └── workspace/ # 任务执行沙箱目录注意SKILL.md的位置。它放在根目录开头就写清楚「什么时候不该用这个 skill」——比如需要 Claude 原生工具链时应该走 claude-code需要 OpenAI 原生函数调用时走 codex。这个细节很关键好 skill 会主动帮你做选择题而不是让你在报错之后才反应过来选错了。config/目录承担模型接入的全部职责。litellm.yaml定义 provider 和模型映射runtime.env存放 API Key 和 Base URL 这类敏感信息。两者分离的好处是你可以把litellm.yaml提交到版本库做团队共享而runtime.env只留在本地或密钥管理服务里。scripts/run_headless.sh是整条链路的触发点。它的核心就是一行 headless 调用把任务描述、工作目录、模型配置通过参数和环境变量传进去。workspace/则是沙箱Agent 的所有文件操作都被限制在这个目录内避免污染宿主机。这里要强调 headless terminal 的意义。所谓 headless就是没有交互式界面Agent 完全靠命令行参数和标准输入输出完成任务。这对编排系统极其友好——上层只需要拼接一条命令、读取一段 JSON 输出不需要处理 TTY 或伪终端。OpenHands 的 headless 模式通过--json参数把执行过程结构化每一步的工具调用、文件变更、命令输出都能被上层解析。链路顺序是这样的编排层触发run_headless.sh→ 脚本加载runtime.env注入密钥 → LiteLLM 根据litellm.yaml路由到具体 provider → OpenHands 在workspace/内执行任务 → 结果以 JSON 返回标准输出。整条链路没有隐藏状态每一步都可观测、可复现。这也是为什么我说它适合当范本你换任何模型改的只是litellm.yaml里的一行其余部分纹丝不动。3. 可复制的 LiteLLM 配置与 headless 启动命令这一节直接给可复制的片段。先看config/litellm.yaml这是模型路由的核心model_list: - model_name: openhands-default litellm_params: model: anthropic/claude-sonnet-4-20250514 api_base: https://taotoken.net/api api_key: os.environ/TAOTOKEN_API_KEY - model_name: openhands-fast litellm_params: model: openai/gpt-4o-mini api_base: https://taotoken.net/api api_key: os.environ/TAOTOKEN_API_KEY - model_name: openhands-local litellm_params: model: ollama/qwen2.5-coder:7b api_base: http://127.0.0.1:11434 litellm_settings: drop_params: true set_verbose: false三个model_name对应三种场景openhands-default走主力模型做复杂重构openhands-fast走轻量模型做快速补全openhands-local走本地 Ollama 做离线验证。api_base统一指向https://taotoken.net/api密钥通过os.environ/TAOTOKEN_API_KEY从环境变量读取不硬编码在文件里。再看config/runtime.envexport TAOTOKEN_API_KEYsk-你的密钥 export OPENHANDS_MODELopenhands-default export OPENHANDS_WORKSPACE./workspace export LITELLM_CONFIG./config/litellm.yaml然后是scripts/run_headless.sh这是 headless 启动的完整入口#!/usr/bin/env bash set -euo pipefail source ./config/runtime.env TASK_DESC${1:?用法: run_headless.sh \任务描述\} openhands --headless \ --json \ --override-with-envs \ --exit-without-confirmation \ --model $OPENHANDS_MODEL \ --workspace $OPENHANDS_WORKSPACE \ --config $LITELLM_CONFIG \ --task $TASK_DESC四个关键参数逐个说。--headless关闭交互界面--json让输出结构化--override-with-envs允许环境变量覆盖配置里的默认值--exit-without-confirmation让 Agent 执行完自动退出而不是等待人工确认。这四个参数组合起来才构成一个真正可被编排系统调用的无头单元。如果你用的是 Cline MCP 或 Codex 的auth.json体系三件套要写全Base URL 填https://taotoken.net/apiKey 填你的密钥Model ID 填openhands-default或你在litellm.yaml里定义的任意model_name。三者缺一链路就会在鉴权或路由阶段断掉。4. 验证一次终端任务从触发到 JSON 返回配置写完必须验证。我拿一个最小任务来跑让 Agent 在workspace/里创建一个 Python 文件并运行它。触发命令chmod x scripts/run_headless.sh ./scripts/run_headless.sh 在 workspace 下创建 hello.py内容为打印当前目录下所有 .py 文件然后运行它预期返回的 JSON 结构大致如下截取关键字段{ status: completed, model: openhands-default, steps: [ { action: write_file, path: workspace/hello.py, result: success }, { action: run_command, command: python workspace/hello.py, stdout: workspace/hello.py\n, exit_code: 0 } ], elapsed_seconds: 12.4 }看到status: completed和exit_code: 0说明整条链路通了编排层触发脚本 → LiteLLM 路由到openhands-default→ OpenHands 在沙箱内写文件、跑命令 → 结果结构化返回。如果你想验证模型切换是否生效把runtime.env里的OPENHANDS_MODEL改成openhands-fast再跑一次返回 JSON 里的model字段会变成openhands-fast而 skill 代码一行没动。这就是模型无关的价值。再补一个自检脚本scripts/healthcheck.sh用来快速确认 LiteLLM 配置是否可加载#!/usr/bin/env bash set -euo pipefail source ./config/runtime.env python -c import yaml, os cfg yaml.safe_load(open(os.environ[LITELLM_CONFIG])) for m in cfg[model_list]: print(m[model_name], -, m[litellm_params][model]) 跑通这个自检再去触发真实任务能省掉大量「配置写错但报错信息看不懂」的时间。5. 常见报错排查401、local proxy failed 与 reading choices链路跑不通时报错信息往往指向几个固定位置。我按实际踩过的坑逐个对照。401 Unauthorized。最常见的原因是TAOTOKEN_API_KEY没被正确注入。检查顺序先确认runtime.env里export了密钥再确认run_headless.sh里source了该文件最后确认litellm.yaml里写的是os.environ/TAOTOKEN_API_KEY而不是硬编码的空字符串。三者任一断掉都会 401。如果用的是 Codex 的auth.json确认 Base URL 和 Key 字段没有多余空格。local proxy failed。这个报错通常出现在api_base指向本地服务但服务没起来的时候。如果你配的是openhands-local走 Ollama先确认ollama serve在跑、端口11434可访问。如果配的是远程api_base检查网络连通性和 URL 是否漏了/api后缀。https://taotoken.net/api是完整路径少写/api会路由失败。reading choices 相关报错。这类错误一般出现在响应解析阶段根因是 provider 返回的结构和 LiteLLM 预期不一致。排查方向确认litellm.yaml里的model字段格式正确比如anthropic/claude-sonnet-4-20250514这种provider/model的写法不能少 provider 前缀。另外drop_params: true建议保留它能过滤掉某些 provider 不支持的参数减少解析冲突。OAuth 相关报错。如果你在 skill 里集成了需要 OAuth 的工具确认 token 刷新逻辑没有和 headless 模式冲突。headless 环境下没有浏览器回调OAuth 必须走 device code 或预置 token 的方式。把 token 放在runtime.env里注入不要依赖交互式授权。任务卡住不返回。检查--exit-without-confirmation是否生效。如果 Agent 在等待人工确认headless 模式下会一直挂起。另外确认workspace/目录存在且有写权限沙箱目录不可写会导致 Agent 反复重试。排查的通用思路是先跑healthcheck.sh确认配置可加载再用最小任务触发一次看 JSON 里status和steps停在哪一步。报错信息里的关键词——401、proxy、choices、OAuth——基本能定位到具体环节。6. 把这条链路接进你的工作流拆完这份范本你会发现它的可复用性来自三个分离配置与代码分离、模型与逻辑分离、执行与观测分离。你要做的不是照抄每一行而是把这套结构迁移到自己的 skill 里。具体动作先把litellm.yaml的model_list换成你实际要用的 providerBase URL 统一填https://taotoken.net/api密钥走环境变量。然后确认run_headless.sh的四个核心参数齐全尤其是--json和--exit-without-confirmation这两个决定了它能不能被编排系统无头调用。最后用healthcheck.sh加一次最小任务验证看到status: completed再接入生产流程。如果你需要长期跑编码类 Agent 任务建议把模型路由和密钥管理拆到独立配置里方便团队共享和轮换。密钥申请和接入文档可以从 API Keys 页面入手模型对话能力可以在模型对话页面试跑长期编码和 Agent 编排则适合用 Coding Plan 来承载。链路通了之后换模型就是改一行配置的事这才是这份范本真正值钱的地方。
返回列表