Codex 实时语音
通过 GPT-Load 接入 Codex 语音:先配置账号与客户端,再按网络条件选择音频直连或网关中继。
接入前准备§
- 准备支持实时语音的 Codex 客户端,允许麦克风访问。功能入口和实验参数是否可用,取决于客户端版本。
- 在 GPT-Load 创建 Codex 订阅分组,完成 OAuth 授权或导入订阅凭据。上游账号必须实际具有语音权限;文本请求成功不代表语音也已获授权。
- 创建访问密钥并允许该 Codex 分组。若设置了协议过滤,需要同时允许 codex-live(语音)和 openai-responses(文本);若设置了模型过滤,也要放行客户端请求的语音模型。
语音模型不必加入分组的模型列表,客户端指定的模型会原样交给上游;未指定时使用 gpt-live-1-codex。这里使用 Codex 订阅渠道,不是把普通 OpenAI API Key 当作订阅凭据。
选择语音模式§
在「系统设置 → 连接与超时 → 实时语音」设置默认模式。Codex 分组的「高级配置/运行参数」可以继承或覆盖;不同分组可以使用不同模式。
| 模式 | 音频与数据通道 | 网络要求 |
|---|---|---|
| 直连上游(默认) | 客户端直接连接上游媒体端点 | 客户端能访问上游;服务器无需额外映射媒体 UDP |
| 网关中继 | 经过 GPT-Load,可使用其出站代理 | 客户端能访问服务器媒体 IP 与 UDP 端口 |
| 关闭 | 该分组不参与语音调度 | 不影响文本请求;会结束受影响的现有语音会话 |
两种启用模式都由 GPT-Load 鉴权、选择账号、创建会话并代理控制 WebSocket。上游账号令牌不会交给客户端。区别只在媒体路径:直连模式下,服务器的出站代理无法替客户端代理音频。
早期实现始终使用中继;当前未设置模式时默认直连。依赖服务器传输音频的部署,升级后请显式选择「网关中继」。
配置 Codex 客户端§
优先在 GPT-Load 首页选择访问密钥和 Codex,复制生成的配置。下面给出完整示例:将地址替换为客户端可访问的网关地址,将 YOUR_TEXT_MODEL 替换为已开放的文本模型。
model = "YOUR_TEXT_MODEL" model_provider = "gpt-load" experimental_realtime_webrtc_call_base_url = "http://127.0.0.1:3001/v1" experimental_realtime_ws_base_url = "http://127.0.0.1:3001/v1" [model_providers.gpt-load] name = "OpenAI" base_url = "http://127.0.0.1:3001/v1" model_catalog_url = "http://127.0.0.1:3001/v1/models" env_key = "GPT_LOAD_API_KEY" wire_api = "responses" supports_websockets = true [features] api_key_model_discovery = true realtime_conversation = true
export GPT_LOAD_API_KEY="YOUR_ACCESS_KEY" codex
合并到已有配置时不要重复创建同名 TOML 节;两个 experimental_realtime_* 参数属于顶层,要放在任何 [section] 之前。远程部署请使用 HTTPS 地址,并让客户端进程继承 GPT_LOAD_API_KEY。
这些语音参数属于实验配置,以当前管理台生成内容为准。保存后重新启动客户端,从其语音入口开始会话。supports_websockets 控制文本 Responses WebSocket,不等于打开语音;语音还需要独立的实时连接配置和 realtime_conversation。
部署网关中继§
仅选择「网关中继」时需要这一节。所有中继分组共用服务端媒体地址和 UDP 范围,不必逐个分组开端口。
- 下载与所运行版本一致的 docker-compose.voice.yml,放在主 docker-compose.yml 旁边。
- 在 .env 设置客户端可达的服务器媒体 IP。不要填写域名、容器内网地址或示例 IP。
CODEX_LIVE_PUBLIC_IP=YOUR_PUBLIC_IP CODEX_LIVE_UDP_PORT_MIN=50000 CODEX_LIVE_UDP_PORT_MAX=50127
- 在云安全组、系统防火墙及 NAT 映射中放行同一 UDP 范围,然后使用两个 Compose 文件启动。
docker compose -f docker-compose.yml -f docker-compose.voice.yml up -d
- 在系统或 Codex 分组设置中选择「网关中继」,保存后新建语音会话。以后更新容器也要带上这两个 Compose 文件。
原生进程使用相同环境变量,并放行相同 UDP 端口;修改媒体环境变量后需要重启。复杂 NAT 场景可通过 CODEX_LIVE_ICE_SERVERS 配置 STUN/TURN,格式见环境变量参考。
HTTP 反向代理负责建连和控制连接,必须转发 WebSocket Upgrade;它不会自动代理媒体 UDP。已有 Nginx 代理中可加入以下设置,保留原来的 TLS 和访问控制。
proxy_pass http://127.0.0.1:3001; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_buffering off; proxy_read_timeout 3600s;
会话与费用§
创建会话时使用全局加权调度和重试预算。明确的 403/429 拒绝可以换候选,401 可刷新凭据后重试;结果不明的超时或成功建连后的断线,不会自动换账号重建通话。语音准入失败不会拉黑该账号的文本能力。
建立后,会话固定账号和媒体路径。客户端必须接入控制 WebSocket:直连创建后 30 秒未接入,或控制断线后 30 秒未重连,会清理会话。更换或停用访问密钥、撤销权限也会终止受影响的通话。
每通会话结束后记录一条日志。语音目前不估算音频费用,不扣减访问密钥的音频成本;已有成本限额仍会影响新通话准入。未计价不代表上游免费。
上游挂断失败时返回 502,会话保持待结束并占用并发名额;服务器会重试挂断,期间不能重新接入控制连接。日志中的「未完成」也不等于已确认上游停止。
接通验证与排障§
先确认文本请求正常,再开始一次短语音会话,检查双向音频,主动挂断后查看请求日志。仅模型列表可读或建连接口成功,不能证明媒体链路已通。
| 现象 | 优先检查 |
|---|---|
| 没有语音入口 | 客户端版本、实验功能开关、是否重启及麦克风权限 |
| 无可用候选或权限错误 | Codex 分组、语音模式、访问密钥的分组/协议/模型过滤,以及账号语音权限 |
| 建连成功但没有声音 | 直连查客户端到上游的网络;中继查媒体 IP、UDP 映射和防火墙 |
| 约 30 秒后断开 | 控制 WebSocket 是否建立,反向代理是否转发 Upgrade |
| 中继通过代理仍连接失败 | 所选出站代理及网络是否支持实际使用的媒体传输 |