OpenAI Codex CLI 接入国产大模型(智谱 GLM 等)实战教程

该文章已生成可运行项目,

AI 时代程序员必备技能

Codex、Claude Code、Cursor、Hermes Agent、OpenClaw等工程化实战专栏 ,讲透 AI 如何接管脏活累活

在 Codex CLI 中使用 GLM-5.1 模型:完整配置指南

本文详细记录如何将智谱 GLM-5.1 模型接入 OpenAI Codex CLI,涵盖代理服务搭建、配置文件编写、密钥管理与一键启动脚本,助你在本地环境中无缝切换至国产大模型。


1. 背景与原理

Codex CLI 原生支持 OpenAI Responses API 协议,而智谱 GLM 系列模型使用的是 Chat Completions API 协议。两者在请求/响应格式、流式事件结构上存在差异,因此需要一个本地代理服务将 Codex 发出的 Responses API 请求转换为智谱兼容的 Chat Completions 请求,并将响应转译回 Responses API 格式。

整体架构如下:

Codex CLI  ──(Responses API)──▶  本地代理 (127.0.0.1:8787)  ──(Chat Completions API)──▶  智谱 API
                ◀──(SSE 流)──                         ◀──(SSE 流)──

2. 环境要求

依赖最低版本说明
Node.js≥ 22Codex CLI 运行时
Python≥ 3.10代理服务运行时
pip最新Python 包管理
curl任意健康检查与调试

安装 Codex CLI:

npm install -g @openai/codex

验证安装:

codex --version
# 期望输出类似:codex-cli 0.125.0

3. 获取智谱 API Key

  1. 访问 智谱开放平台,注册并登录。
  2. 进入「API Keys」页面,创建新的 API Key。
  3. 复制 Key 并妥善保管,后续步骤将使用。

4. 搭建本地代理服务

4.1 项目结构

codex-glm-proxy/
├── app.py              # FastAPI 代理主程序
├── requirements.txt    # Python 依赖
├── run.sh              # 手动启动脚本
└── README.md

4.2 Python 依赖

requirements.txt

fastapi>=0.115,<1.0
uvicorn[standard]>=0.30,<1.0
httpx>=0.27,<1.0

安装依赖:

cd codex-glm-proxy
python3 -m venv .venv
.venv/bin/pip install -r requirements.txt

4.3 代理核心逻辑(app.py)

代理服务的核心职责:

  1. 请求转换:将 Codex 发出的 Responses API 请求体转换为智谱 Chat Completions 格式
  2. 流式转发:将智谱返回的 Chat Completions SSE 流转译为 Responses API SSE 事件序列
  3. Usage 字段归一化:将智谱的 prompt_tokens / completion_tokens 映射为 Codex 期望的 input_tokens / output_tokens / total_tokens
  4. Function Call 支持:处理工具调用场景下的消息格式转换

关键代码片段——请求体转换:

def to_chat_messages(input_field, instructions=None):
    messages = []
    if instructions:
        messages.append({"role": "system", "content": instructions})

    if isinstance(input_field, str):
        messages.append({"role": "user", "content": input_field})
        return messages

    if isinstance(input_field, list):
        for item in input_field:
            if not isinstance(item, dict):
                continue
            item_type = item.get("type")
            if item_type == "message":
                role = item.get("role", "user")
                if role == "developer":
                    role = "system"
                messages.append({"role": role, "content": _flatten_text(item.get("content", ""))})
            elif item_type == "function_call_output":
                # 工具调用结果转译
                call_id = item.get("call_id", item.get("id", ""))
                call_meta = _CALL_REGISTRY.get(call_id)
                if call_meta:
                    messages.append({
                        "role": "assistant",
                        "content": "",
                        "tool_calls": [{
                            "id": call_id,
                            "type": "function",
                            "function": {
                                "name": call_meta.get("name", ""),
                                "arguments": call_meta.get("arguments", "{}"),
                            },
                        }],
                    })
                messages.append({
                    "role": "tool",
                    "tool_call_id": call_id,
                    "content": str(item.get("output", "")),
                })
    return messages

关键代码片段——Usage 归一化:

def _normalize_usage_for_responses(usage) -> dict:
    if not isinstance(usage, dict):
        usage = {}
    out = dict(usage)
    inp = out.get("input_tokens") or out.get("prompt_tokens")
    outp = out.get("output_tokens") or out.get("completion_tokens")
    inp_i = int(inp) if inp is not None else 0
    outp_i = int(outp) if outp is not None else 0
    tot = out.get("total_tokens")
    tot_i = int(tot) if tot is not None else inp_i + outp_i
    out["input_tokens"] = inp_i
    out["output_tokens"] = outp_i
    out["total_tokens"] = tot_i
    return out

关键代码片段——流式事件转译:

# 将智谱 Chat Completions 的 SSE 事件转换为 Codex 期望的 Responses API 事件序列
yield 'data: ' + json.dumps({"type": "response.created", ...}) + '\n\n'
yield 'data: ' + json.dumps({"type": "response.in_progress", ...}) + '\n\n'
yield 'data: ' + json.dumps({"type": "response.output_item.added", ...}) + '\n\n'
yield 'data: ' + json.dumps({"type": "response.content_part.added", ...}) + '\n\n'
yield 'data: ' + json.dumps({"type": "response.output_text.delta", "delta": text, ...}) + '\n\n'
yield 'data: ' + json.dumps({"type": "response.output_text.done", ...}) + '\n\n'
yield 'data: ' + json.dumps({"type": "response.content_part.done", ...}) + '\n\n'
yield 'data: ' + json.dumps({"type": "response.output_item.done", ...}) + '\n\n'
yield 'data: ' + json.dumps({"type": "response.completed", "response": response_obj}) + '\n\n'
yield 'data: [DONE]\n\n'

4.4 健康检查端点

代理提供 /health 端点,供启动脚本确认服务状态:

@app.get("/health")
async def health():
    return {"status": "ok", "has_key": bool(os.getenv("ZHIPU_API_KEY", "").strip())}

4.5 手动启动代理

export ZHIPU_API_KEY="your_api_key_here"
cd codex-glm-proxy
../.venv/bin/python -m uvicorn app:app --host 127.0.0.1 --port 8787

验证代理运行:

curl http://127.0.0.1:8787/health
# 期望输出:{"status":"ok","has_key":true}

5. Codex CLI 配置

5.1 主配置文件

编辑 ~/.codex/config.toml

# 模型设置
model = "glm-5.1"
model_provider = "glm_proxy"
model_reasoning_effort = "medium"

# 自定义 Provider 定义
[model_providers.glm_proxy]
name = "GLM Local Proxy"
base_url = "http://127.0.0.1:8787/v1"
wire_api = "responses"           # 关键:指定使用 Responses API 协议
requires_openai_auth = false     # 无需 OpenAI 认证

配置项详解:

字段说明
model"glm-5.1"传递给代理的模型名称,代理会原样转发至智谱 API
model_provider"glm_proxy"对应 [model_providers.glm_proxy] 的键名
wire_api"responses"告知 Codex 使用 Responses API 格式发送请求
base_url"http://127.0.0.1:8787/v1"代理服务地址,Codex 会在后面拼接 /responses
requires_openai_authfalse设为 false 后 Codex 不要求 OpenAI Key

5.2 密钥管理

创建 ~/.codex/auto.json 存放智谱 API Key(此文件不应提交至版本控制):

{
  "GLM_API_KEY": "your_zhipu_api_key_here",
  "auth_mode": "local"
}

⚠️ 安全提示auto.json 包含敏感凭据,务必将其加入 .gitignore

5.3 用户级指令(可选)

编辑 ~/.codex/instructions.md 可自定义 AI 行为偏好:

# User-level instructions

## Code and architecture
- Use first-principles thinking when analyzing problems.
- Follow DRY, KISS, SOLID, and YAGNI when coding.
- Keep functions under ~500 lines; split when necessary.

## Language and communication
- Communicate with the user in their preferred language.

6. 一键启动脚本

手动先启动代理再启动 Codex 比较繁琐,推荐使用一键启动脚本 ~/.codex/scripts/codex-with-auto-json.sh

#!/usr/bin/env bash
set -euo pipefail

CODEX_HOME="${CODEX_HOME:-$HOME/.codex}"
AUTO_JSON="${CODEX_HOME}/auto.json"
PROXY_DIR="$HOME/Project/codex_project/custdown_test/codex-glm-proxy"
PROXY_HOST="127.0.0.1"
PROXY_PORT="8787"
PROXY_HEALTH_URL="http://${PROXY_HOST}:${PROXY_PORT}/health"
PROXY_LOG="$CODEX_HOME/log/proxy.log"
PROXY_AUTO_RESTART="${PROXY_AUTO_RESTART:-1}"

# 1. 从 auto.json 加载 GLM_API_KEY
if [[ -f "$AUTO_JSON" ]]; then
  GLM_KEY="$(python3 -c "
import json, os
p = os.path.expanduser('~/.codex/auto.json')
try:
    with open(p, encoding='utf-8') as f:
        d = json.load(f)
    print((d.get('GLM_API_KEY') or '').strip())
except Exception:
    print('')
")"
  if [[ -n "$GLM_KEY" ]]; then
    export GLM_API_KEY="$GLM_KEY"
    export ZHIPU_API_KEY="$GLM_KEY"
  fi
fi

# 2. 检查 Key 是否存在
if [[ -z "${ZHIPU_API_KEY:-}" ]]; then
  echo "GLM key is empty. Put GLM_API_KEY into ~/.codex/auto.json"
  exit 1
fi

# 3. 确保代理虚拟环境就绪
if [[ ! -x "$PROXY_DIR/.venv/bin/python" ]]; then
  python3 -m venv "$PROXY_DIR/.venv"
  "$PROXY_DIR/.venv/bin/pip" install -r "$PROXY_DIR/requirements.txt"
fi

# 4. 自动重启代理(确保最新代码生效)
if [[ "$PROXY_AUTO_RESTART" == "1" ]]; then
  pkill -f "uvicorn app:app --host $PROXY_HOST --port $PROXY_PORT" >/dev/null 2>&1 || true
  ZHIPU_API_KEY="$ZHIPU_API_KEY" nohup "$PROXY_DIR/.venv/bin/python" -m uvicorn app:app \
    --host "$PROXY_HOST" --port "$PROXY_PORT" --app-dir "$PROXY_DIR" \
    >> "$PROXY_LOG" 2>&1 &
  # 等待代理就绪
  for _ in {1..16}; do
    sleep 0.5
    curl -fsS "$PROXY_HEALTH_URL" >/dev/null 2>&1 && break
  done
fi

# 5. 设置环境变量后启动 Codex
export GLM_PROXY_DUMMY_KEY="ok"
exec codex "$@"

使用方式:

chmod +x ~/.codex/scripts/codex-with-auto-json.sh

# 启动交互式会话
~/.codex/scripts/codex-with-auto-json.sh

# 或执行单条指令
~/.codex/scripts/codex-with-auto-json.sh exec "解释 main.c 的逻辑"

可将该脚本路径加入 PATH 或创建 alias 以简化调用。


7. 完整目录结构总览

~/.codex/
├── config.toml                          # 主配置:模型 & Provider
├── auto.json                            # 密钥(GLM_API_KEY)
├── instructions.md                      # 用户级 AI 指令
├── AGENTS.md                            # 项目级 AI 指令
├── scripts/
│   └── codex-with-auto-json.sh          # 一键启动脚本
├── log/
│   └── proxy.log                        # 代理运行日志
└── ...

codex-glm-proxy/
├── app.py                               # 代理服务主程序
├── requirements.txt                     # Python 依赖
├── run.sh                               # 手动启动脚本
├── .venv/                               # Python 虚拟环境
└── README.md

8. 常见问题排查

问题原因解决方案
Proxy failed to startPython 虚拟环境未就绪手动运行 python3 -m venv .venv && .venv/bin/pip install -r requirements.txt
Proxy protocol self-check failed代理返回的事件格式不符合 Codex 解析预期检查 app.pyresponse.completed 事件是否包含 input_tokens / output_tokens / total_tokens
GLM key is emptyauto.json 中缺少 GLM_API_KEY编辑 ~/.codex/auto.json,填入有效 Key
Codex 启动后无响应代理未运行或端口被占用运行 curl http://127.0.0.1:8787/health 确认代理状态
流式输出中断智谱 API 限流代理内置 1.2s 节流,必要时调大 _throttle_upstream 参数

9. 总结

通过搭建一个轻量的本地 FastAPI 代理,我们实现了 Codex CLI 与智谱 GLM-5.1 模型的无缝对接。核心思路是:

  1. 协议适配:Responses API ↔ Chat Completions API 双向转换
  2. 流式转译:智谱 SSE 事件 → Codex SSE 事件序列
  3. 字段归一化:Usage 字段名映射
  4. 一键启动:自动加载密钥、启动代理、验证健康状态

这套方案不仅适用于 GLM-5.1,也可扩展至任何兼容 Chat Completions API 的模型服务(如 DeepSeek、Qwen 等),只需修改代理中的上游 API 地址和模型名即可。


本文基于 Codex CLI v0.125.0 与智谱 GLM-5.1 验证通过,环境为 Linux / Node.js 22 / Python 3.10+。

本文章已经生成可运行项目

AI 时代程序员必备技能

Codex、Claude Code、Cursor、Hermes Agent、OpenClaw等工程化实战专栏 ,讲透 AI 如何接管脏活累活

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值