1. 项目概述什么是 agent-skills它不是插件也不是 SDK而是一套可组合、可复用、可验证的智能体能力单元“agent-skills”这个词最近在开发者社区里频繁出现但很多人第一次看到时会下意识把它理解成“AI Agent 的技能列表”或者“某个大模型平台的插件市场”。其实完全不是。我从去年底开始系统性地构建和落地多个生产级智能体系统从内部运维助手到面向客户的客服中台踩过无数坑之后才真正厘清agent-skills 是一种工程范式核心目标是把“让 AI 做事”这件事从模糊的 prompt 工程变成像调用函数一样清晰、可测试、可版本化、可权限管控的软件工程实践。它不依赖特定模型、不绑定某家 API 平台、也不需要你去学一套新框架——它本质上就是一组符合约定规范的、带元数据描述的、能被标准 CLI 工具识别和调度的独立功能模块。你可以把它想象成 Linux 系统里的命令行工具ls、grep、curl各自解决一个明确问题它们之间不耦合可以自由组合比如ps aux | grep nginx每个命令都有清晰的输入输出契约、帮助文档、错误码定义。agent-skills 就是为 LLM 智能体设计的这一整套“命令行生态”。它解决的不是“怎么让模型更聪明”而是“怎么让聪明的模型可靠、安全、可控地完成具体任务”。比如一个叫fetch-weather的 skill它的职责就只是根据城市名返回结构化天气数据不负责决定要不要查天气、不负责格式化成自然语言、更不负责判断用户情绪——这些决策层逻辑由上层 agent 编排器orchestrator来处理。这种分层直接决定了整个系统的可维护性和扩展性。为什么现在突然火起来因为大家终于意识到光靠堆 prompt 和微调模型根本撑不起真实业务场景。我们团队去年上线的一个合同审核 agent初期用纯 prompt 实现结果上线两周内触发了 37 次误判——不是模型错了而是 prompt 在不同语境下行为不稳定且无法做单元测试。换成 agent-skills 架构后我们将“提取甲方名称”、“比对付款条款”、“识别违约风险点”拆成三个独立 skill每个都自带 mock 数据集和断言规则CI 流水线自动跑回归测试。现在每次模型升级或规则变更都能在 5 分钟内确认是否影响核心业务逻辑。这背后CLI命令行接口和 slash commands斜杠命令不是炫技而是这套范式的基础设施它们提供了最轻量、最通用、最易集成的调用协议而 skills 本身就是一个个可独立部署、可灰度发布、可按需授权的 API 封装体。它不挑模型——你用 DeepSeek、Qwen 还是 Claude只要 skill 的输入输出契约不变上层逻辑就无需重写。这才是它真正值得深挖的价值。2. 核心设计哲学与架构选型为什么必须绕开“大模型中心主义”回归软件工程本质2.1 拒绝“模型即一切”的陷阱skills 不是 prompt 的包装盒很多团队一上来就想用 skills 包装一段复杂的 prompt比如写个summarize-reportskill里面塞进 800 字的 system prompt few-shot 示例 输出格式约束。这是典型的本末倒置。我见过最夸张的一个案例某金融客户把整套反洗钱规则引擎的逻辑全写进一个 skill 的 prompt 里结果每次模型 token 超限、输出格式错乱、甚至因 prompt 中一个标点符号变化导致合规判断翻车。skills 的第一铁律是它必须是一个确定性函数deterministic function而非概率性生成器probabilistic generator。换句话说给定相同输入它必须返回相同输出或明确的错误而不是“大概率返回正确结果”。所以真正的 skills 只有三类数据获取类如query-crm查客户信息、fetch-stock-price拉股价、read-pdf解析 PDF 文本。这类 skill 的核心是封装 API 调用、数据库查询或文件 IO内部绝不包含任何 LLM 推理。规则执行类如validate-email邮箱正则校验、calculate-vat增值税计算、check-credit-score调用风控 API 返回布尔值。这类 skill 的核心是硬编码业务规则或调用确定性服务输出是明确的结构化数据bool/int/float/dict/list。LLM 协同类如extract-entities-from-text从文本中抽人名/地名/时间、translate-to-chinese翻译、classify-sentiment情感分类。这类 skill 才真正需要 LLM但它的职责被严格限定只做单一原子操作输入必须是干净文本输出必须是 JSON Schema 定义的结构化结果且必须内置 fallback 机制比如当 LLM 返回非 JSON 时自动重试或返回预设 error code。提示如果你的 skill 名称里带“analyze”、“understand”、“reason”这类模糊动词90% 的概率它违反了原子性原则。立刻拆解——把“analyze customer feedback”拆成clean-feedback-text→extract-sentiment→group-by-topic三个 skill。2.2 CLI 作为统一入口不是为了命令行情怀而是为了解决协议碎片化为什么所有主流 skills 生态Codex CLI、ZCode CLI、OpenSpec CLI都强制要求 CLI 接口因为这是目前唯一能跨平台、跨语言、跨部署环境达成“零适配成本”的协议。HTTP API 看似通用但实际落地时你得处理鉴权头Bearer vs API Key vs OAuth2、超时配置3s 还是 30s、重试策略指数退避还是固定间隔、错误码映射400 是参数错还是 quota 超。而 CLI 的契约极其简单skill-name --input {city: shanghai} --output-format json。它的 stdin/stdout/stderr 天然就是数据管道bash、Python、Node.js 甚至 Excel 都能无缝调用。更重要的是CLI 让 skills 具备了“可发现性”运行codex list就能看到所有已安装 skills 及其版本、描述、输入 schema运行codex run fetch-weather --help就能获得完整文档——这比翻几十页 Swagger UI 效率高得多。我们内部做过对比测试同样一个send-smsskill在 HTTP 方式下前端、后端、测试脚本各自维护一套请求构造逻辑平均每次接口变更要修改 7 处代码换成 CLI 后所有调用方只需更新一行命令CI 自动校验参数兼容性。更关键的是CLI 天然支持 Unix 管道pipe这为 skills 组合提供了最优雅的语法糖。比如get-order-id --order-no 12345 | query-erp | format-invoice-json | send-email --to financecompany.com。这条命令链里每个环节都是独立进程、独立日志、独立监控任何一个环节失败管道自动中断并返回错误码——这比写一个巨长的 Python 脚本或复杂 workflow DSL 清晰十倍。2.3 Slash Commands从用户侧打通“意图到能力”的最后一公里如果 CLI 是后端工程师的武器那么 slash commands斜杠命令就是产品经理和终端用户的桥梁。它解决的是“用户想做什么”和“系统有什么能力”之间的语义鸿沟。传统做法是让用户在对话框里打字“帮我查一下上海今天天气”然后靠 NLU 模型去识别意图、提取槽位、路由到对应 service。但 NLU 模型永远有误识别率且无法覆盖长尾需求。slash commands 的思路是把能力显性化、标准化、可探索化。用户输入/weather shanghai系统立刻知道这是调用fetch-weatherskill参数是shanghai无需任何 NLU 解析。这不仅提升了准确率更改变了交互范式——用户不再需要“猜”系统能做什么而是通过/触发自动补全看到所有可用 commands 及其说明。我们上线 slash commands 后客服工单中“用户不知道系统能做什么”这类咨询下降了 68%。更有趣的是它倒逼产品设计更合理因为每个 command 必须有清晰的动词宾语结构/create-ticket、/track-shipment迫使团队提前梳理清楚业务动作边界避免出现“/do-something-smart”这种反模式。技术上slash commands 的实现极其轻量前端监听/开头的输入匹配预定义 command 表拼装 CLI 参数后通过 Web Worker 或 iframe 调用本地 CLI runtime如 Codex CLI 的 WASM 版本全程不经过服务器——这意味着即使后端 API 全挂用户依然能用/help查看所有 commands体验降级可控。3. Skills 开发全流程实操从零写出第一个可交付、可测试、可上线的 skill3.1 初始化选择骨架模板与目录结构以 Python 为例不要从零写 Makefile 或 setup.py。直接用成熟的 skills scaffold 工具。我们团队统一使用codex-cli initv2.3它生成的标准目录结构如下my-first-skill/ ├── skill.yaml # 核心元数据name, version, description, input_schema, output_schema, author ├── main.py # 主程序入口解析 CLI 参数执行核心逻辑输出 JSON ├── tests/ # 单元测试目录 │ ├── test_main.py # 测试 main.py 的核心函数 │ └── fixtures/ # 测试用的 mock 数据API 响应、PDF 文件等 ├── examples/ # 使用示例bash 脚本、curl 命令、Python 调用片段 └── README.md # 技术文档适用场景、输入输出说明、错误码、性能指标skill.yaml是灵魂文件它定义了 skills 的“身份证”。一个生产级的fetch-weatherskill.yaml 示例name: fetch-weather version: 1.2.0 description: Get current weather and forecast for a city using OpenWeatherMap API author: dev-teamcompany.com input_schema: type: object properties: city: type: string minLength: 2 maxLength: 50 description: City name in English, e.g. Shanghai units: type: string enum: [metric, imperial] default: metric required: [city] output_schema: type: object properties: city_name: type: string temperature: type: number description: Current temperature in Celsius (if unitsmetric) humidity: type: integer minimum: 0 maximum: 100 forecast_24h: type: array items: type: object properties: time: type: string format: date-time temp: type: number required: [city_name, temperature, humidity] error_codes: - code: API_UNAVAILABLE message: OpenWeatherMap API is down or unreachable - code: CITY_NOT_FOUND message: The specified city does not exist in OpenWeatherMap database这个 YAML 文件的作用远不止文档codex-cli validate会据此校验main.py的输入参数解析逻辑是否符合 schemacodex-cli test会据此生成 fuzz 测试用例CI 流水线会据此检查版本号语义化是否合规1.2.0 不能回退到 1.1.9。它让 skills 的契约从“口头约定”变成了“机器可验证的合约”。3.2 核心逻辑编写main.py 的黄金三段式写法main.py必须遵循“输入解析 → 业务执行 → 输出序列化”三段式且每段都要有防御性编程。以下是我们团队强制要求的模板#!/usr/bin/env python3 # -*- coding: utf-8 -*- fetch-weather skill: Get weather data from OpenWeatherMap API. This script is designed to be called by codex-cli with standardized arguments. import json import sys import argparse import requests from typing import Dict, Any, Optional # 1. 输入解析严格遵循 skill.yaml 的 input_schema def parse_args() - argparse.Namespace: parser argparse.ArgumentParser(descriptionFetch weather data) parser.add_argument(--input, typestr, requiredTrue, helpJSON string input) parser.add_argument(--output-format, typestr, defaultjson, choices[json]) return parser.parse_args() # 2. 业务执行核心逻辑隔离外部依赖便于 mock 测试 def fetch_weather_from_api(city: str, units: str metric) - Dict[str, Any]: Call OpenWeatherMap API. This function should be easily mockable in tests. Real implementation uses requests.get(...), but tests can patch this function. api_key your-api-key-here # Production: read from env var or secret store url fhttps://api.openweathermap.org/data/2.5/weather?q{city}units{units}appid{api_key} try: response requests.get(url, timeout5) response.raise_for_status() raw_data response.json() # Transform raw API response to our output_schema return { city_name: raw_data[name], temperature: raw_data[main][temp], humidity: raw_data[main][humidity], forecast_24h: [] # Simplified for example; real impl would call forecast endpoint } except requests.exceptions.Timeout: raise RuntimeError(API_UNAVAILABLE: Request timed out) except requests.exceptions.ConnectionError: raise RuntimeError(API_UNAVAILABLE: Network connection failed) except requests.exceptions.HTTPError as e: if e.response.status_code 404: raise RuntimeError(CITY_NOT_FOUND: City not found in OpenWeatherMap) else: raise RuntimeError(fAPI_ERROR: HTTP {e.response.status_code}) except KeyError as e: raise RuntimeError(fAPI_SCHEMA_CHANGED: Missing expected field {e}) # 3. 输出序列化严格遵循 output_schema捕获所有异常 def main(): args parse_args() try: # Parse input JSON input_data json.loads(args.input) # Validate required fields (basic check; full validation via JSON Schema in prod) if city not in input_data: raise ValueError(Missing required field city) # Execute business logic result fetch_weather_from_api( cityinput_data[city], unitsinput_data.get(units, metric) ) # Output as JSON (stdout) print(json.dumps(result, ensure_asciiFalse)) except json.JSONDecodeError as e: print(json.dumps({error: INVALID_INPUT_JSON, message: str(e)}, ensure_asciiFalse)) sys.exit(1) except ValueError as e: print(json.dumps({error: VALIDATION_FAILED, message: str(e)}, ensure_asciiFalse)) sys.exit(1) except RuntimeError as e: # Map to standard error codes defined in skill.yaml error_parts str(e).split(: , 1) error_code error_parts[0] if len(error_parts) 0 else UNKNOWN_ERROR message error_parts[1] if len(error_parts) 1 else str(e) print(json.dumps({error: error_code, message: message}, ensure_asciiFalse)) sys.exit(1) except Exception as e: # Catch-all for unexpected errors print(json.dumps({error: INTERNAL_ERROR, message: fUnexpected error: {type(e).__name__}}, ensure_asciiFalse)) sys.exit(1) if __name__ __main__: main()这段代码的关键细节输入解析独立成函数方便单元测试传入不同参数也便于未来替换为其他解析库如 Pydantic。业务逻辑函数可 mockfetch_weather_from_api是纯函数不依赖全局状态测试时可直接 patch 替换为返回 mock 数据的函数。错误分类精准区分INVALID_INPUT_JSON输入格式错、VALIDATION_FAILED业务规则错、API_UNAVAILABLE外部服务错、INTERNAL_ERROR代码 bug每种错误对应不同处理策略重试、告警、用户提示。exit code 语义化sys.exit(1)表示失败但具体错误类型由 stdout 的 JSON 决定CLI 工具据此做精细化处理。3.3 测试驱动开发为什么 80% 的 skills bug 都发生在边界条件Skills 的测试不是可选项而是上线前的强制闸门。我们要求每个 skill 必须达到100% 分支覆盖率coverage run -m pytest3 类测试缺一不可单元测试unit、集成测试integration、契约测试contract单元测试test_main.pymock 所有外部依赖只测main.py逻辑。重点覆盖正常路径输入合法 JSONAPI 返回成功输出符合 schema。边界输入空字符串 city、超长 city 名51 字符、非法 units 值。错误路径JSON 解析失败、fetch_weather_from_api抛出各种 RuntimeError。# tests/test_main.py import json import pytest from unittest.mock import patch, MagicMock from my_first_skill.main import main, fetch_weather_from_api class TestMain: def test_valid_input_returns_json(self, capsys): # Mock the API call to return known data mock_response { name: Shanghai, main: {temp: 23.5, humidity: 65}, weather: [{main: Clouds}] } with patch(my_first_skill.main.fetch_weather_from_api) as mock_fetch: mock_fetch.return_value mock_response # Simulate CLI call with valid input test_input json.dumps({city: Shanghai}) with pytest.raises(SystemExit) as e: # Patch sys.argv to simulate CLI args import sys sys.argv [main.py, --input, test_input, --output-format, json] main() # Check exit code is 0 (success) assert e.value.code 0 # Check stdout contains expected JSON captured capsys.readouterr() output json.loads(captured.out) assert output[city_name] Shanghai assert output[temperature] 23.5 def test_invalid_json_input_exits_with_error(self, capsys): with pytest.raises(SystemExit) as e: import sys sys.argv [main.py, --input, {invalid json}, --output-format, json] main() assert e.value.code 1 captured capsys.readouterr() error_output json.loads(captured.out) assert error_output[error] INVALID_INPUT_JSON集成测试不 mock真实调用 OpenWeatherMap API但用测试专用 key配额独立。验证网络层、认证、超时、重试是否工作。我们用 pytest 的--integrationmarker 标记CI 中单独运行。契约测试这是最容易被忽视但最关键的一环。它验证main.py的实际输出是否严格符合skill.yaml中定义的output_schema。我们用jsonschema库实现# tests/test_contract.py import json import jsonschema from jsonschema import validate from jsonschema.exceptions import ValidationError from my_first_skill.main import fetch_weather_from_api def test_output_conforms_to_schema(): # Load the output_schema from skill.yaml (in real code, parse YAML) schema { type: object, properties: { city_name: {type: string}, temperature: {type: number}, humidity: {type: integer, minimum: 0, maximum: 100} }, required: [city_name, temperature, humidity] } # Get real output from API call (or use fixture) result fetch_weather_from_api(Shanghai) try: validate(instanceresult, schemaschema) except ValidationError as e: pytest.fail(fOutput does not conform to schema: {e.message})注意契约测试必须在 CI 中强制运行。我们曾因一个 skill 的temperature字段在某些城市返回nullAPI bug而 schema 定义为number导致下游 agent 解析失败。契约测试在 PR 阶段就捕获了这个问题避免了线上事故。3.4 打包与发布如何让 skills 真正“可交付”写完代码、跑通测试只是完成了 30%。真正的交付意味着别人能一键安装、安全运行、无缝集成。codex-cli publish命令背后是严格的打包流程静态分析扫描main.py是否包含危险操作os.system()、eval()、硬编码密钥不符合则拒绝发布。依赖冻结生成requirements.txt但只允许requests2.31.0这样的精确版本禁止requests2.0避免依赖冲突。二进制打包可选对 Python skill用pyinstaller打包成单文件可执行程序彻底消除 Python 环境依赖。命令codex-cli build --target linux-x64。签名与校验用团队私钥对 package 进行 GPG 签名codex-cli install时自动验证签名防止供应链攻击。仓库发布推送到内部 Nexus Repository生成唯一坐标com.company.skills:fetch-weather:1.2.0。发布后的 skills可通过多种方式安装全局安装推荐codex-cli install fetch-weather1.2.0从 Nexus 拉取本地安装codex-cli install ./my-first-skill/开发调试用Docker 镜像docker run -it --rm -v $(pwd):/workspace codex-cli:latest install /workspace/my-first-skill实操心得我们曾因一个 skill 的requirements.txt里写了pandas实际没用到导致安装时下载 200MB 依赖拖慢整个 agent 启动。现在强制要求pipreqs . --force自动生成最小依赖集并在 CI 中检查pipdeptree --reverse确认无冗余依赖。4. Skills 生产环境治理权限、监控、灰度与生命周期管理4.1 权限模型为什么 skills 不能“生而平等”一个delete-databaseskill 和一个get-current-timeskill显然不能拥有相同的权限。我们采用基于 capability 的 RBACRole-Based Access Control模型但粒度更细Capability能力skills 声明自己需要的最小权限集。例如# skill.yaml capabilities: - network:outbound:https://api.openweathermap.org - environment:read:OPENWEATHER_API_KEY - filesystem:read:/tmp/weather-cacheRole角色定义哪些 capabilities 可被授予。如weather-reader角色包含network:outbound:https://api.openweathermap.org。Assignment分配将 role 绑定到 agent 实例或用户组。例如客服 agent 只分配weather-reader而运维 agent 分配database-admin。权限检查发生在 CLI runtime 层当codex-cli run fetch-weather执行时runtime 读取当前 agent 的 role检查fetch-weather声明的 capabilities 是否全部被允许。如果fetch-weather尝试访问https://api.hack-site.comruntime 直接拦截并返回PERMISSION_DENIED错误连网络请求都不会发出。这比在 skill 内部做鉴权更安全、更统一。注意所有 secretsAPI keys必须通过 runtime 注入严禁硬编码或从文件读取。codex-cli支持--secret OPENWEATHER_API_KEYxxx参数或从 Vault/Kubernetes Secret 自动注入。4.2 监控与可观测性skills 不是黑盒必须暴露健康信号Skills 上线后不能只看“是否成功”更要监控“是否健康”。我们为每个 skill 强制注入 3 类指标指标类型示例指标采集方式告警阈值调用层skills.fetch-weather.success.count,skills.fetch-weather.error.count,skills.fetch-weather.latency.p95CLI runtime 自动埋点OpenTelemetry错误率 5% 持续 5 分钟业务层skills.fetch-weather.cities.queried统计查询城市数,skills.fetch-weather.temperature.avg平均温度skill 代码中手动metrics.gauge(temperature, temp)城市查询数突降 80%可能上游 API 变更资源层process.cpu.percent,process.memory.rssOS 层 cgroup 监控内存持续 500MB内存泄漏嫌疑所有指标统一上报到 PrometheusGrafana 看板按 skill 分组。特别重要的是错误码分布图它能快速定位问题根源。比如fetch-weather的错误码中CITY_NOT_FOUND占 95%说明是用户输入问题如果API_UNAVAILABLE突增则是 OpenWeatherMap 服务故障需切换备用 provider如 WeatherAPI。4.3 灰度发布与回滚如何让 skills 更新像数据库 migration 一样安全Skills 更新不是git pull restart。我们采用Canary Release Automated Rollback流程金丝雀流量新版本fetch-weather1.3.0上线后仅 5% 的 agent 实例加载它其余 95% 仍用1.2.0。实时对比监控系统自动对比两组实例的指标success_rate_canaryvssuccess_rate_baselinelatency_p95_canaryvslatency_p95_baselineerror_code_distribution_canaryvserror_code_distribution_baseline自动决策如果success_rate_canary下降超过 0.5%绝对值或API_UNAVAILABLE错误率翻倍则自动触发 rollback将金丝雀实例的 skill 切回1.2.0并发送 Slack 告警。人工确认若 30 分钟内无异常自动提升至 100% 流量。这个流程让我们在过去一年中实现了 skills 更新 0 次线上 P0 事故。最典型的一次1.3.0版本因优化了缓存逻辑导致在高并发下出现KeyError但金丝雀检测到错误率从 0.1% 升至 1.2%5 秒内完成 rollback用户无感知。4.4 生命周期管理skills 不是“一次编写永久运行”Skills 有明确的生命周期我们用skill.yaml的lifecycle字段声明lifecycle: status: active # active | deprecated | archived deprecation_date: 2024-12-01 # If status is deprecated replacement: fetch-weather-pro # If deprecated, point to new skill retirement_date: 2025-03-01 # If deprecated, when it will be removedActive正常使用接受 bug fix 和 minor 版本更新。Deprecated停止新功能开发但继续维护。codex-cli list会显示黄色警告“This skill is deprecated, please migrate to fetch-weather-pro”。Archived只读不可安装但历史数据保留。用于审计和回溯。我们每月运行codex-cli audit --lifecycle自动扫描所有 skills生成报告哪些 skills 已 deprecated 超过 30 天但仍有 agent 在调用哪些 skills 的replacement字段为空却标记为 deprecated哪些 skills 的retirement_date将在未来 7 天内到达这份报告驱动产品团队主动联系使用者推动迁移。过去半年我们成功将 12 个 deprecated skills 的调用量归零彻底清理了技术债。5. 常见问题与实战排查指南那些文档里不会写的坑5.1 “API Error: 400 This models maximum context length is 1048576 tokens” —— 这根本不是 skills 的问题这个错误信息极具迷惑性它出现在 LLM 调用日志里但根源往往在 skills 的输入处理上。我们遇到过三次典型场景场景一skills 返回了超长原始文本问题read-pdfskill 未做内容截断直接返回 200 页 PDF 的全文约 1.2M tokens。解决在read-pdf的main.py中强制添加max_pages10参数并在输出前用text[:100000]截断100K chars ≈ 250K tokens。验证codex-cli run read-pdf --input {path:/tmp/big.pdf} | wc -c确保输出 100KB。场景二skills 的输入参数被错误拼接问题summarize-textskill 的输入是{text: ...}但上游 agent 错误地把多个 skill 的输出 JSON 拼成一个超大字符串传入。解决在summarize-text的parse_args()中添加长度校验input_data json.loads(args.input) if len(input_data.get(text, )) 50000: # 50K chars ~ 125K tokens raise ValueError(INPUT_TOO_LONG: text exceeds 50000 characters)场景三skills 的错误处理返回了冗长 trace问题fetch-weather在 API 超时时except Exception as e:分支打印了完整的 stack trace 到 stdout导致 JSON 输出被污染。解决严格遵守“stdout 只输出 JSON”的契约。所有 debug 信息写入 stderr或记录到日志文件。print(json.dumps({...}))前确保没有其他print()。排查技巧当看到此类错误第一步不是查 LLM而是用codex-cli run your-skill --input ... --output-format json 2/dev/null | jq .看输出是否为纯净 JSON。如果不是问题一定在 skills 本身。5.2 “Permission denied while trying to connect to the Docker API” —— CLI 权限的隐形陷阱这个错误通常出现在codex-cli尝试调用docker run启动 skills 容器时。根本原因不是 Docker 服务没开而是 CLI 进程没有访问/var/run/docker.sock的权限。标准解法Linux/macOS# 将当前用户加入 docker 组 sudo usermod -aG docker $USER # 重新登录或重启 shell newgrp docker # 验证 codex-cli run hello-world # 应该成功但生产环境更推荐禁用 socket 直连在codex-cli config set docker.socket /dev/null强制 CLI 使用 Docker REST API需配置DOCKER_HOSTtcp://localhost:2375。使用 rootless Docker避免权限问题且更安全。codex-cli支持--docker-rootless参数。Windows WSL2 用户注意WSL2 的 Docker Desktop 集成默认启用但codex-cli可能找不到 socket。解决方案在 WSL2 中运行export DOCKER_HOSTtcp://$(cat /etc/resolv.conf | grep nameserver | awk {print $2}):2375然后codex-cli config set docker.host $DOCKER_HOST。5.3 “No API key for provider route deepseek-official” —— secrets 管理的致命疏忽这个错误表明 CLI runtime 尝试调用 DeepSeek API但未找到对应的 API key。常见原因原因一key 名不匹配DeepSeek 官方要求 key 名为DEEPSEEK_API_KEY但你在codex-cli config set deepseek.api_key xxx时CLI 默认存为DEEPSEEK_OFFICIAL_API_KEY。解决查看 CLI 的 secrets 存储位置codex-cli config show手动编辑~/.codex/config.yaml确保字段名为DEEPSEEK_API_KEY。原因二key 未注入到 runtime 环境codex-cli run默认不传递环境变量。必须显式指定codex-cli run llm-call --input {model:deepseek-chat,prompt:hi} \ --secret DEEPSEEK_API_KEYxxx更优方案在skill.yaml中声明environment:read:DEEPSEEK_API_KEY然后codex-cli config set deepseek.api_key xxxCLI 会自动注入。原因三key 过期或被 revokeDeepSeek 控制台的 key 有有效期或被管理员 revoke。解决方案登录 DeepSeek 控制台生成新 key并更新 CLI 配置。实操心得我们为所有 API provider 建立了统一的 secrets 命名规范{
阅读完成 · 觉得有帮助?