Codex 接入本地 llama.cpp
llama-server 提供 /v1/responses 后,Codex 可以通过自定义 model provider 连接本地模型。本文使用 Qwen3.6-27B,服务端配置见使用 llama.cpp、Claude Code 和 Codex 搭建本地 Agent 编码环境。
这篇文章既有当前配置,也保留了 Codex CLI 0.144.6 上遇到的兼容性问题。带明确版本号的错误分析是当时的运行记录,不代表后续版本一定还有同样表现。
用独立 profile 隔离本地模型
Codex 的用户配置位于 $CODEX_HOME,默认是 ~/.codex。当前官方配置参考说明,profile 文件与 config.toml 放在同一目录,文件名是 <profile-name>.config.toml,通过 --profile <profile-name> 选择。
这里创建:
- Linux:
~/.codex/local-qwen.config.toml - Windows:
%USERPROFILE%\.codex\local-qwen.config.toml
启动 Codex 前先在当前终端设置与 llama-server 相同的 API Key:
export LLAMA_API_KEY='在这里填入你的实际 key'Windows PowerShell 使用:
$env:LLAMA_API_KEY = '在这里填入你的实际 key'profile 内容如下:
model = "Qwen3.6-27B"
model_provider = "llama_cpp_qwen"
model_context_window = 262144
model_auto_compact_token_limit = 200000
model_reasoning_summary = "none"
approval_policy = "never"
sandbox_mode = "workspace-write"
web_search = "disabled"
[features]
apps = false
plugins = false
tool_search = true
standalone_web_search = false
[model_providers.llama_cpp_qwen]
name = "llama.cpp Qwen3.6-27B"
base_url = "http://localhost:30104/v1"
env_key = "LLAMA_API_KEY"
wire_api = "responses"
request_max_retries = 2
stream_max_retries = 5
stream_idle_timeout_ms = 3000000
[sandbox_workspace_write]
network_access = true
[tui]
status_line = ["model-with-reasoning", "context-remaining", "current-dir", "git-branch"]原来的配置直接使用 experimental_bearer_token。当前 Codex 官方配置参考已经明确建议改用 env_key,所以这里让配置文件只保存环境变量名称,实际 key 留在进程环境中。
base_url 写到 /v1,Codex 会请求 Responses API。wire_api = "responses" 是当前支持的协议值。
status_line 位于交互式命令行底部,显示模型、剩余上下文、当前目录和 Git 分支。输入 /status 可以查看 token 使用、审批策略和可写目录。
沙箱和审批策略
workspace-write 允许 Codex 读取项目、修改当前工作区并执行命令,network_access = true 允许沙箱内的命令访问网络。approval_policy = "never" 表示请求不会弹出人工确认,越界操作直接失败。这仍然不是完整系统权限。
这里的配置只适合可信项目。如果希望越界时由人决定,改成:
approval_policy = "on-request"
sandbox_mode = "workspace-write"不要为了连接本地模型而顺手放开整个系统。provider、审批策略和沙箱是三件事,可以独立调整。
为什么关闭 Web Search 和部分扩展
当时使用的 llama.cpp Responses 适配器不能处理 Codex 发出的所有工具类型。web_search = "disabled" 用来避免把 Web Search 工具发给本地后端。
apps、plugins、tool_search 和 standalone_web_search 会改变可用工具。上面的配置保留 tool_search = true,便于观察本地模型能否使用延迟加载工具;如果服务端持续出现不兼容的 namespace 警告,可以临时关闭:
[features]
apps = false
plugins = false
tool_search = false
standalone_web_search = false关闭这些功能会让对应工具不可用,不是没有代价的“优化”。
启动和验证
启动交互式会话:
codex --profile local-qwen也可以只覆盖本次运行的审批和沙箱设置:
codex --profile local-qwen --ask-for-approval never --sandbox workspace-write配置修改后应新开会话,再输入 /status 核对模型、审批策略、沙箱模式和可写目录。
单次测试普通回复:
codex exec --profile local-qwen "只回复 OK"然后在 Git 仓库中测试真实工具调用:
codex exec --profile local-qwen "必须调用终端执行 git rev-parse --show-toplevel,然后原样输出命令结果"“只回复 OK”只能证明文本生成可用。看到真实仓库路径和工具调用记录,才能确认终端工具已经接通;如果模型只是猜了一个看似正确的路径,应按工具调用失败处理。
我的实际测试中,普通回复和终端命令都能完成,Codex 也会读取项目里的 AGENTS.md。
排查模型目录错误
使用 Codex CLI 0.144.6 连接 llama-server 时,我遇到过三条日志:
failed to load models cache: missing field `supports_reasoning_summaries`
failed to decode models response: missing field `slug`
Model metadata for `Qwen3.6-27B` not found. Defaulting to fallback metadatasupports_reasoning_summaries 来自共享的 %USERPROFILE%\.codex\models_cache.json。当时 Codex 桌面应用和独立安装的 npm CLI 共用这个缓存,但两端的内部模型目录格式不同,CLI 无法解析另一端写入的数据。
slug 是另一处格式差异。llama-server 的 /v1/models 返回 OpenAI 兼容的 data[].id,当时的 Codex 内部模型目录还要求 models[].slug 以及上下文、推理和工具元数据。解析失败后,Codex 会退回默认元数据。
我的命令最终仍返回 OK,说明 Responses API 推理正常,失败的是模型元数据加载。models_cache.json 由 Codex 管理,不必手工补字段。需要排除缓存影响时,应先完全退出 Codex 桌面应用,再删除文件:
Remove-Item -LiteralPath "$env:USERPROFILE\.codex\models_cache.json"桌面应用再次启动后会重新生成缓存。只要实际推理和工具调用通过,就不用围着这条日志手写 JSON。
排查认证错误
启动后一直显示 Reconnecting,最终没有结果时,应检查真实请求。API Key 不一致时,/v1/responses 会返回:
{
"error": {
"message": "Invalid API Key",
"type": "authentication_error",
"code": 401
}
}使用本文的 env_key = "LLAMA_API_KEY" 时,先确认 Codex 进程确实继承了该环境变量,并且值与启动 llama-server 时使用的 key 相同。
排查工具类型警告
Codex CLI 0.144.6 执行真实编码任务时,llama-server 曾反复输出:
W srv server_chat_: unsupported Responses tool type 'namespace' skipped
W srv server_chat_: unsupported Responses tool type 'web_search' skipped这是工具兼容性警告,不是模型推理错误。日志后面如果还有 processing task、print_timing 和 release,请求已经跑完;但标记为 skipped 的工具没有传给模型。能聊天不等于 Agent 工具可用。
当时的问题是两端对工具格式的理解不同:Codex 会发送 namespace 和 web_search,llama.cpp 只转换 type = "function",其余类型记录警告后跳过。这和 API Key、量化版本、上下文长度或 CUDA 参数无关,调整 llama-server 启动参数解决不了。
web_search = "disabled" 可以去掉 Web Search 警告。namespace 是否仍会出现,要看当前 Codex 与 llama.cpp 版本。升级后应重新执行一次真实终端任务,不要直接沿用旧结论。
要保留 Apps、插件和命名空间工具,需要后端完整支持 Codex Responses 工具格式,或者在 Codex 与 llama.cpp 之间增加双向转换层。代理层必须同时处理请求工具定义和响应工具调用,只改请求 JSON 不够。
安全边界
客户端与模型服务在同一台机器时可以使用 localhost。跨机器连接时,不要通过公网 HTTP 发送 Bearer Token,应使用 HTTPS 反向代理、VPN 或 SSH 隧道。
provider 配置应放在用户级 $CODEX_HOME 中,不要提交到项目仓库。项目级 .codex/config.toml 也不会接管用户级 provider 和 profile 选择。