
最近半个月我一直在折腾一件事把自部署的代码模型服务 GPT-Rosalind 通过 API 接入 Codex CLI。GPT-Rosalind 是我用开源底座微调的一套模型主要面向代码生成与仓库级任务对外走 OpenAI 兼容 API。Codex 是 OpenAI 开源的命令行编程代理能自动读文件、改代码、执行命令。两者接在一起本质上就是搭建一套完全私有化的 AI 编程工作流Codex 负责规划与工具调用GPT-Rosalind 负责生成最终内容。但真正开始对接后我才发现模型能写代码和能用 Codex 跑通完全是两个难度级别。Codex 对后端模型 API 的格式要求极其严格——工具调用参数必须是合法 JSON、模型名必须与注册名一致、上下文满了要触发 compact任何一个环节对不上都会抛出一连串看着很吓人的错误。这篇文章会把我的整个接入过程、配置方案和几十次踩坑后沉淀的问题排查方式都写出来。如果你也在折腾“自建模型 Codex”按这条链路走可以省下大量试错时间。这个内容主要适合两类人一是自己部署了模型想用 Codex 的 agent 能力但不想让代码数据离开本地环境的开发者二是企业内部有统一模型服务想给研发团队接入 Codex却被 API 兼容问题卡住的运维或平台工程师。1. 先理清链路GPT-Rosalind 和 Codex 各自负责什么1.1 GPT-Rosalind 的角色模型服务与 OpenAI 兼容 APIGPT-Rosalind 是整套链路里的“大脑”。它有自己的一套模型权重推理阶段我用 vLLM 加载并暴露成 HTTP 服务。为了让 Codex 这种通用客户端能直接调用我对外提供的是 OpenAI Chat Completions 风格的接口也就是POST /v1/chat/completions请求体和响应体都按照 OpenAI 的标准格式来。这里有个关键认知Codex 并不关心你的模型是怎么训练的也不关心推理框架是 vLLM 还是 llama.cpp它只认 HTTP API 的格式。所以 GPT-Rosalind 要负责的事就是把标准的 OpenAI 请求翻译成模型能理解的 prompt再把模型输出翻译回标准响应。中间涉及 tool call 的解析、JSON schema 的校验、上下文窗口的管理。这一步听起来不复杂实际上绝大多数对接问题都出在这个翻译层上。举个例子Codex 发过来的请求里带着一串 tools 定义每定义一个 JSON Schema。模型在生成回复时如果决定调用工具就要在相应字段里输出一个结构完整的参数对象。这个对象如果少了必填字段或者多了一个结尾逗号Codex 端收到后直接判定请求非法返回 400。我最初接入时遇到最多的就是这类 schema 校验错误。1.2 Codex 的角色Agent 编排与工具调用Codex 在链路里担任“调度中心”。它不是简单地发一次请求拿一次响应而是维护一个多轮对话状态先理解你的自然语言指令然后决定要不要调用工具。比如搜索文件、查看代码片段、执行 shell 命令、批量修改多个文件。每次工具调用的结果都会被当作新的上下文喂回模型模型再决定下一步动作直到任务完成。我打个比方Codex 像一个项目经理GPT-Rosalind 是执行具体方案的工程师。项目经理负责拆任务、验收结果工程师负责产出代码。但问题在于这个项目经理用的是非常严格的“沟通协议”——工具调用的参数必须是合法 JSON必须符合约定的 schema哪怕少一个字段整个对话就会中断。这也是我最开始在日志里看到400 invalid schema for function artifact这类错误的根本原因。理解了这个分工之后很多报错就好定位了凡是和工具调用格式相关的错误基本都出在“模型输出有问题”或“兼容层转换有问题”凡是和模型名、鉴权相关的错误基本都出在 Codex 配置层。先把这两类问题分开排查的时候就不会像无头苍蝇一样乱撞。2. 方案选型为什么用 Codex而不是自己搭 Agent2.1 对比 LangChain 方案的优劣在决定用 Codex 之前我其实先用 LangChain 搭过一版 Agent。LangChain 的优势是灵活什么组件都能自己接但劣势也很明显工具调用的稳定性、会话管理的复杂度、代码仓库级任务的执行效率都需要自己一步步调。尤其是多文件修改这种场景LangChain 的默认实现还不够“手熟”写出来的 agent 经常把文件改到一半就停住。Codex 不一样它在设计上就是冲着代码库任务去的。它内置了文件读取、文件编辑、shell 执行这些工具还能自动维护“当前打开了哪些文件”“改到哪一步了”这种状态。对于我这种只想替换模型、不想重复造 agent 轮子的人来说Codex 是比较省事的编排层。当然Codex 也不是没有缺点。它对后端模型的要求偏高尤其是函数调用能力。如果你的模型连工具调用的 JSON 都生成不稳定那接入之后大概率会频繁中断。所以后面我会专门讲如何在接入前先评估模型的函数调用能力。2.2 为什么必须做 OpenAI 兼容层这里还有个很现实的问题Codex CLI 原生支持 OpenAI 的两种 API 协议一种是 Chat Completions一种是较新的 Responses API。自研模型服务想接入要么直接实现这两种协议要么在中间加一层转换网关。我选择的是自建兼容层理由有三一是 GPT-Rosalind 的推理框架已经提供了基础的 OpenAI 兼容接口再包一层主要是为了补全函数调用相关的能力二是以后如果换底座模型只需要改这一层的映射逻辑Codex 那边不用动三是可以在兼容层里集中处理日志、限流、鉴权方便排查问题。兼容层我建议做成一个独立服务不要和模型推理进程混在一起。这样模型更新、兼容层升级可以互不影响。代码量不需要很大核心就是把请求里的 tools、tool_choice、messages 这些字段解析出来转成模型推理所需的格式再把模型返回的 tool_calls 规范成 OpenAI 标准结构返回。2.3 自建模型和第三方模型的接入差异如果你手头没有自建模型其实也可以先用 DeepSeek 这类第三方开放 API 验证 Codex 的配置是否正常。方法几乎一样把 base_url 指向对方服务把 api key 换成对方的 key模型名改成对方服务里真实存在的名字。我建议所有准备接入自建模型的人都先拿一个稳定的外部 API 跑通 Codex再切回本地这样能把“Codex 配置问题”和“模型服务问题”区分开排查效率会高很多。不过第二方的差异同样明显。外部服务往往已经把函数调用、上下文压缩这些能力打磨得比较完善自建模型则要自己解决。我实际体验下来最容易出现差距的有两块一是工具调用 JSON 的稳定性二是长上下文下保持指令跟随的能力。这两块决定了一个自建模型在 Codex 里是“能跑”还是“好用”。3. 实操完整接入 Codex CLI 的过程3.1 环境准备Codex 安装与模型服务启动Codex CLI 的安装方式取决于你的使用偏好。我习惯用 npm 全局安装一条命令就完事如果你在 macOS/Linux 环境下更追求版本控制也可以直接拉 Rust 源码编译。Windows 用户需要注意Codex 的 shell 工具默认依赖类 Unix 环境建议用 WSL 或 Git Bash 来跑否则部分命令执行会失败。npm install -g openai/codex codex --version安装完成后先确认版本号正常。如果出现命令找不到多数情况是 npm 全局目录没加到 PATH 里检查一下npm bin -g的输出路径即可。Windows 上如果双击安装包打不开多半是系统弹窗拦截右键选择以管理员身份运行或者用命令行工具安装会更稳。模型服务这边我用 vLLM 加载 GPT-Rosalind 的量化权重监听 8000 端口。启动前要确认几个参数上下文窗口长度、最大输出 token 数、是否开启函数调用支持。vLLM 有一个参数叫--enable-auto-tool-choice如果你的模型权重没有显式注册工具调用能力这个参数要不要加取决于框架版本。建议启动后先用 curl 手动请求一次chat/completions确认能返回合法的补全结果再继续往下走。3.2 配置 model_providerbase_url、env_key 与 wire_apiCodex CLI 的配置入口是~/.codex/config.toml支持全局配置和项目级配置两种。接入 GPT-Rosalind 的关键是定义一个自定义 provider并把它设置为默认的模型来源。下面是我稳定跑通的配置model gpt-rosalind model_provider rosalind [model_providers.rosalind] name Rosalind Local API base_url http://127.0.0.1:8000/v1 env_key ROSALIND_API_KEY wire_api chat这里逐个解释model代表 Codex 使用的模型名必须和模型服务端注册的模型名完全一致。很多model is not supported的报错就是因为这个名字对不上。model_provider指定走哪一套 provider 配置。base_url指向模型服务的根地址。注意 vLLM 的 OpenAI 兼容接口默认挂在/v1下所以这里要写带/v1的完整前缀。env_key环境变量名Codex 会从这个环境变量读取 API key。wire_api指定使用 Chat Completions 协议还是 Responses 协议。我这里明确写chat是因为自建模型的兼容层通常对 Chat 协议支持更完善Responses 协议对函数调用的约束更严格容易踩坑。3.3 环境变量与密钥管理配置里写了env_key ROSALIND_API_KEY下一步就是在终端里设置这个环境变量。即使是本地模型服务我也建议保留一个 key 校验免得局域网内其他设备误连上来。示例export ROSALIND_API_KEYlocal-rosalind-key-2024如果你用 zsh可以把这行加到~/.zshrc用 bash 就加到~/.bashrc。设置完记得重开终端或者source一下。这里有个小坑Codex 读取 env_key 是在启动时就完成的如果你在 Codex 运行中途修改了环境变量它不会自动感知必须重启 Codex 进程。Windows PowerShell 下对应写法是$env:ROSALIND_API_KEY local-rosalind-key-2024如果要永久生效可以在系统环境变量设置里加一条或者在 PowerShell profile 里写入。3.4 第一次跑通从 exec 到交互模式配置完成后的第一个验证命令我建议用非交互模式跑一个简单任务codex exec 写一个 Python 函数计算斐波那契数列前 N 项如果能看到模型生成的代码说明最基本的补全链路已经通了。接着再试一个需要工具调用的任务codex exec 在当前目录创建 hello.py并运行它这个任务要求 Codex 先调用文件写入工具再调用 shell 执行工具能完整跑通就说明函数调用的 schema 校验、工具结果回填这些环节都没问题。我第一次跑这种双工具任务时就卡在了invalid schema for function artifact上后面专门花了很长时间排查。如果非交互模式能通再进入交互模式体验codex交互模式下可以直接对话Codex 会实时展示它的思考过程、工具调用和结果更适合看整体工作流是否顺畅。不过我建议调试阶段还是以非交互模式为主输出更可控出错了也容易复现。4. 核心难点函数调用、schema 校验与上下文管理4.1 从 invalid schema 错误说起前面提到的400 invalid schema for function artifact错误是我遇到最典型的对接问题。Codex 在执行任务时会向模型声明一批可用工具比如artifact、shell、apply_patch等每个工具都有自己的参数 JSON Schema。模型返回“我要调用 artifact 工具”的意图时Codex 会检查参数是否符合 Schema不符合就直接返回 400。问题通常出在 GPT-Rosalind 生成的 tool call 参数不够规范。比如模型生成了参数 JSON但结尾多了一个逗号或者某个字段的值是空字符串而 Schema 要求的是对象类型。vLLM 在转换模型输出时如果没有做严格的后处理就会把这些非法 JSON 原样传给上层。Codex 拿到之后校验不通过于是抛出invalid schema。这类问题的排查和规避我放在第 5 节详细说。这里先给两个最有效的规避手段一是把采样温度调到 0 附近减少模型生成 JSON 时的随机性二是在兼容层里增加一个 tool call 参数的二次校验发现非法 JSON 就尝试修复或重新生成而不是直接转发给 Codex。4.2 model 命名与账号校验问题另一个高频坑是model is not supported when using codex with a chatgpt account。这个报错多半是 Codex 使用 ChatGPT 账号登录而不是 API key 认证导致的。Codex 在登录类型为 ChatGPT 账号时会认为你只能使用它内置的官方模型列表自定义 provider 里的模型名会被拒绝。解决办法很简单不要用 ChatGPT 登录态改用 API key 认证。在config.toml里去掉或注释掉和 ChatGPT 登录相关的字段确保环境变量里的 key 是有效的 API key。同时检查model字段——如果你在自定义 provider 下写了一个服务端不存在的名字比如随手写了个gpt-5.6-sol而 GPT-Rosalind 服务端只注册了gpt-rosalind同样会触发类似的 model 解析错误。这类问题之所以高频是因为很多人直接从网上复制了一段config.toml没有把模型名改成自己的。我建议每次改完配置后先用codex exec hi做一次冒烟测试确认模型能正常响应再跑复杂任务。4.3 context 溢出与 compact 任务失败Codex 的对话是长上下文的它会把多轮工具调用结果都放进模型上下文里。自建模型如果上下文窗口只有 8K 或者 16K很容易跑几个步骤就满了。这时候 Codex 会尝试执行 compact把前面的对话压缩成摘要重新塞回模型继续后面的任务。但 compact 依赖后端模型有足够强的摘要能力而且需要服务端支持额外的请求模式。如果 GPT-Rosalind 的兼容层不支持 compact 对应的接口就会报error running remote compact task: codex ran out of room in the models context。我的处理方式有两个方向一是给 Codex 明确配置更大的上下文窗口比如在 config 里设置model_context_window 131072前提是模型服务端真的能处理这么大的输入二是调低--max-turns让单次任务不要铺得太大从源头避免上下文膨胀。这里要特别注意model_context_window是告诉 Codex“后端模型最多能接收多少 token”这个值必须和模型服务端的实际配置一致。如果设得比实际大Codex 会在上下文还没到上限时就把 compact 任务发出去后端接到超长请求直接报错如果设得比实际小模型会在还有余量时就开始清理上下文浪费能力。最好在启动 vLLM 时把上下文长度固定再把这个值同步到 Codex 配置里。4.4 服务端 500 与 llama-server 崩溃前面提到我用 vLLM 做推理框架但在早期调试阶段我也试过用 llama.cpp 的 llama-server 来加载模型。llama-server 的好处是部署简单、显存占用可控但稳定性一般。我遇到过http 500: llama-server process has terminated通常发生在请求并发较高或者上下文长度超出可用内存的时候。进程直接挂掉服务端返回 500Codex 重试三次后放弃。这种问题没有银弹只能从资源维度去压减少并发请求把max_tokens调低切换到更低 bit 的量化版本实在不行就换 vLLM 这类管理更精细的框架。我在同一台机器上把并行度从 2 降到 1崩溃频率立刻下降了一个数量级。生产环境建议给推理服务加一个健康检查和自动重启脚本挂掉之后能自愈。我用的自愈方案很简单一个 cron 脚本每隔 30 秒请求一次/health接口三次失败就杀掉进程并重新拉起。配合 systemd 的 Restarton-failure基本能保证服务在 1 分钟内恢复。5. 常见问题速查表与几个值得记住的经验5.1 高频报错速查表把这次调试中遇到的典型错误整理成一张表方便以后速查。报错信息常见原因处理建议400 invalid schema for function artifacttool call 参数 JSON 不合法或兼容层未做 schema 校验调低 temperature在兼容层增加 tool call 二次校验升级推理框架版本model is not supported when using codex with a chatgpt account使用 ChatGPT 账号登录态自定义模型被拒绝改用 API key 认证检查 model 名是否与服务端一致api call failed after 3 retries: http 500推理服务崩溃或超时检查显存/内存降低并发配置自愈脚本remote compact task: ran out of room上下文窗口超限或 compact 接口不支持调大 model_context_window减少 max-turns部署 compact 兼容接口failed to connect to the docker api at npipe:////pipe/docker_engineCodex 需要调用 Docker 工具但 Docker 引擎未启动启动 Docker daemon检查 Windows 下 npipe 是否可用这张表的值在于让你能快速定位方向。真遇到问题时先看报错的阶段如果是发起请求阶段多半是配置或模型名问题如果是请求后 Codex 返回错误多半是 schema 校验问题如果任务中途失败多半是上下文或进程稳定性问题。5.2 排查问题时的调试技巧整个对接过程中最受益的一个习惯是先“绕过 Codex”验证模型服务本身。也就是说当 Codex 报错时我不会第一时间改 Codex 配置而是用一个简单的 Python 脚本直接请求 GPT-Rosalind 的 API复现同样的工具调用请求看返回是否符合预期。这样能快速定位是模型输出问题还是 Codex 这边的问题。下面是当时用来排查artifactschema 错误的参考脚本核心是模拟 Codex 发送带工具定义的请求import json import requests url http://127.0.0.1:8000/v1/chat/completions headers {Authorization: Bearer local-rosalind-key-2024} payload { model: gpt-rosalind, messages: [{role: user, content: 在当前目录创建 hello.py 并运行}], tools: [ { type: function, function: { name: artifact, parameters: { type: object, properties: { kind: {type: string, enum: [text, code, patch]}, description: {type: string}, content: {type: string} }, required: [kind, description, content] } } } ], temperature: 0 } resp requests.post(url, headersheaders, jsonpayload, timeout60) print(resp.status_code) print(json.dumps(resp.json(), ensure_asciiFalse, indent2))如果这一步返回的tool_calls参数是完整且合法的 JSON那问题大概率出在 Codex 端如果返回本身就是残缺的就回到模型服务和兼容层去修。5.3 接入前值得检查的四个维度最后分享四个接入前值得先检查的维度。第一模型服务是否真的支持 tool calling。很多开源底座虽然能对话但函数调用能力很弱直接用会导致工具调用反复出错建议先拿公开的 function calling benchmark 大概测一下。第二上下文窗口的余量。Codex 的任务普遍需要 32K 以上的上下文如果你的模型只有 4K 或 8K体验会非常痛苦。第三输出稳定性。工具调用 JSON 的生成稳定性比代码生成质量更影响整体跑通率。第四鉴权和网络安全。内网部署也不能裸奔至少加一层 API key定期清理日志中的敏感信息。这次把 GPT-Rosalind 接进 Codex 的整个过程前后折腾了半个多月。坦白说模型本身能生成代码不算难难的是让它成为一个“严格按协议办事的工具”。Codex 对格式的要求近乎苛刻但也正是这种苛刻保证了 agent 在复杂任务里不会频繁出错。我的体会是如果你第一次接入自建模型务必先把函数调用的 schema 校验和上下文窗口这两个点吃透这会决定你的调试过程是几天还是几周。后续我打算再往 GPT-Rosalind 上加一个外部知识库检索的 MCP 工具让 Codex 在改代码时能直接查内部文档等跑通了再回来分享。