key.tengokukk.com OpenAI v1 网关现状与修复记录(2026-04-23)
- Canonical target:
E:\My Project\Atramenti-Console\codex\plugins\obsidian\data\docs\codex-knowledge\20-operations-key-tengokukk-openai-v1-gateway-status-2026-04-23.md - Audience: 未来自己、接手运维的人、需要继续打磨这条 AI 网关的人
- Status date: 2026-04-23
一句话结论
截至 2026-04-23,这条链路已经从“能用但不稳”推进到:
- 正式公网入口已经切到
170.106.179.226:443 - 这台 Windows 本机的
WLAN网卡 DNS 已真改到公共解析源8.8.8.8 / 1.1.1.1 - Codex 现在直接走正式域名
https://key.tengokukk.com/openai/v1 - 所有本地代理/代理环境变量都已撤掉
responsesunary 已恢复正常responsesstreaming 已恢复成一致的事件序列chat/completionsunary 已能在tool_choice=required下返回tool_calls- 170 上 nginx 已补 streaming 友好参数
当前更准确的状态已经从“124 做边缘转发”推进到:
正式公网主链路:
外部域名 -> 170 nginx :443 -> 127.0.0.1:3301 -> 127.0.0.1:3000/openai-codex-oauth/v1
当前这台 Windows 机器上的 Codex 实际链路:
Codex -> https://key.tengokukk.com/openai/v1 -> 170 nginx -> 170:3301当前真实状态
本机 Codex 当前配置
C:\Users\ASUS-KL\.codex\config.toml 当前有效关键配置:
model_provider = "crs"
model = "gpt-5.4"
[model_providers.crs]
base_url = "https://key.tengokukk.com/openai/v1"
wire_api = "responses"
requires_openai_auth = trueHKCU:\Environment 当前每用户代理环境变量:
HTTPS_PROXY=
HTTP_PROXY=
NO_PROXY=C:\Users\ASUS-KL\.codex\auth.json 当前 key:
{
"auth_mode": "apikey",
"OPENAI_API_KEY": "sk-tengokukk-crs-20260422"
}本机之前卡住的已确认根因
这次“你使用时卡住不动”的根因,已经在这台机器上确认清楚:
- 之前这台机器的
WLAN网卡 DNS 还是旧解析源 - 所以
key.tengokukk.com一直被解析到旧值170.106.179.226 - 这台机器到
170.106.179.226:443的 TCP 直连失败 - 同一台机器到旧边缘
124.220.233.126:443的 TCP 直连成功 codex-tui.log里实际出现的是stream disconnected - retrying sampling request
所以,当时不是“服务器没修好”,而是:
本机网卡 DNS 指向旧解析源 -> 域名被解析到旧的 170 -> 而这台机器又直连不了 170 -> responses 采样流断开 -> 你看到卡住不动本机最终收敛方案
现在这台机器已经不需要任何本地代理,最终状态是:
WLAN网卡 DNS:8.8.8.8/1.1.1.1C:\Users\ASUS-KL\.codex\config.toml:正式域名https://key.tengokukk.com/openai/v1HKCU:\Environment:不再保留HTTP_PROXY/HTTPS_PROXY/NO_PROXY- 本地代理
127.0.0.1:4411与127.0.0.1:4412都不再是当前活跃链路
这个方案最终解决了:
- 本机默认 DNS 旧值问题
- 旧代理依赖问题
- 本机到
170:443的直连失败对正式域名使用的影响
170 服务器上的当前收敛结构
- 公网域名:
key.tengokukk.com - 正式公网入口:
170.106.179.226:443 - 当前公共 DNS 权威解析:
170.106.179.226 - nginx 站点:
/etc/nginx/sites-available/key.tengokukk.com - 内层 AIClient2API:
http://127.0.0.1:3301 - 当前主 provider:
openai-custom - 上游 codex oauth 网关:
http://127.0.0.1:3000/openai-codex-oauth/v1
124 边缘机上的当前状态
- 边缘机:
124.220.233.126 - nginx 站点:
/etc/nginx/sites-available/key.tengokukk.com.conf - 证书:
/etc/letsencrypt/live/key.tengokukk.com/ - 当前角色:旧边缘/备用入口,不再是权威正式入口
- 现存转发目标:
http://170.106.179.226:3301/v1/ - 现存根路径行为:
/ -> 302 /openai/v1/models
170 nginx 当前关键配置
当前 170 的根路径与 /openai/ 都已具备正式入口所需行为:
location = / {
return 302 /openai/v1/models;
}
location /openai/ {
proxy_pass http://127.0.0.1:3301/;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Connection '';
proxy_buffering off;
proxy_request_buffering off;
proxy_cache off;
proxy_read_timeout 3600;
proxy_send_timeout 3600;
chunked_transfer_encoding on;
gzip off;
}这次真正修掉了什么
1. 确认了问题不在 170 nginx,而在上游协议选型
直接实测 127.0.0.1:3000/openai-codex-oauth/v1 后确认:
/chat/completions正常/responsesunary 返回400 Bad Request/responsesstreaming 也返回 error event
这意味着:
- 让 3301 的主 provider 使用
openaiResponses-custom是错误的收敛方向 - 正确方向应该是:
- 3301 对上游走
openai-custom/chat/completions - 对外继续同时兼容
responses与chat/completions responses兼容交给 converter 与 fallback 处理
- 3301 对上游走
2. 把 3301 主 provider 切回 openai-custom
170 上 configs/config.json 已从:
"MODEL_PROVIDER": "openaiResponses-custom"切到:
"MODEL_PROVIDER": "openai-custom"原因:上游 3000 的健康路径是 chat/completions,不是 responses。
3. 给 openai-custom unary 增加 stream fallback 聚合
修改文件:/srv/aiclient-2-api-mock/src/providers/openai/openai-core.js
新增能力:
- 当 unary
chat/completions请求携带tools - 且上游 direct unary 未返回
tool_calls - 自动改走 streaming 聚合
- 最终重新收敛成标准 unary
chat.completion
这一步解决了:
- upstream streaming 有
tool_calls,但 unary 没有tool_calls的不一致 tool_choice=required时原来只返回空 assistant 的问题
4. 修正 openai -> responses 的流式状态机
修改文件:/srv/aiclient-2-api-mock/src/converters/strategies/OpenAIConverter.js
修复点:
convertStreamChunk()现在会把requestId传给toOpenAIResponsesStreamChunk()toOpenAIResponsesStreamChunk()现在按单请求维护状态,不再每个 chunk 都当成新响应response.created/response.in_progress/response.output_item.added只在同一请求里初始化一次- 文本 delta 会稳定累加为同一个
msg_* - 完成时会输出一致的
response.output_text.done/response.completed
这一步解决了:
responsesstreaming 里重复出现多个response.created- 文本被拆成多个无关 response id
- unary fallback 里误继承
chat.completion.chunk的对象类型
当前验证结果
已确认通过
2026-04-23 新增 Soxio 备用池节点
本次又新增了一组 OpenAI 兼容上游到 provider pool:
- 上游基址:
https://apikey.soxio.me/openai/v1 - 目标模型:
gpt-5.4 - 加入池子位置:
openai-custom,节点名:soxio-openai-v1openaiResponses-custom,节点名:soxio-responses-v1
这次不是只把配置写进文件,而是做了真实调用验证。
真实探测结果
直接对 Soxio 上游实测得到:
POST https://apikey.soxio.me/openai/v1/chat/completionsstream=true时返回200,可稳定输出流式 deltastream=false时返回400,提示必须开启 streaming
POST https://apikey.soxio.me/openai/v1/responses- 也要求
stream=true
- 也要求
所以这组上游更适合作为:
responses/chat.completions的流式执行节点- 尤其适合当前 Codex 这类
responses流式采样链路
经 AIClient2API 转发后的真实验证
新增节点后,已经确认 AIClient2API 外层主池实际选中了:
soxio-openai-v1
日志里可见:
getServiceAdapter, provider: openai-custom ... (soxio-openai-v1)Increasing usage count for openai-custom ... (soxio-openai-v1) after successful stream request
并且以下链路已经真实通过:
-
http://127.0.0.1:3301/v1/responsesstream=true- 返回完整
response.created -> response.completed - 输出内容:
INNER-SOXIO-OK
-
http://127.0.0.1:3301/v1/chat/completionsstream=true- 正常返回
chat.completion.chunk - 输出内容:
INNER-SOXIO-CHAT-OK
-
https://key.tengokukk.com/openai/v1/responsesstream=true- 公网真实返回完整响应事件流
- 输出内容:
PUBLIC-SOXIO-OK
当前更准确的结论
截至这次变更后:
openai-codex-oauth原来的账号限流问题仍然存在- 但
openai-custom池已经不再只有那一条坏上游 Soxio节点已经能作为实际可工作的流式备用上游- 当前
responses流式公网链路已经重新回到可用状态
-
http://127.0.0.1:3301/v1/responsesstreaming- 返回一致的
response.created -> delta -> done -> completed - 文本可稳定拼成
STREAM-OK
- 返回一致的
-
http://127.0.0.1:3301/v1/responsesunary- 返回
200 - 返回体内容为
UNARY-OK
- 返回
-
http://127.0.0.1:3301/v1/chat/completionsunary +tool_choice=required- 返回
200 - 返回
message.tool_calls finish_reason = "tool_calls"
- 返回
-
https://127.0.0.1/openai/v1/responses+Host: key.tengokukk.com- 也就是 nginx loopback 闭环
- 返回
200 - 返回体内容为
GATEWAY-OK
-
124边缘机上的https://127.0.0.1/openai/v1/chat/completions+Host: key.tengokukk.com- 返回
200 - 返回体内容为
SERVER124-LOOPBACK-CHAT-OK
- 返回
-
124边缘机上的https://127.0.0.1/openai/v1/responses+Host: key.tengokukk.com- 返回
200 - 返回体内容为
SERVER124-LOOPBACK-RESPONSES-OK
- 返回
-
外部机器直接打
https://124.220.233.126/openai/v1/chat/completions+Host: key.tengokukk.com- 返回
200 - 返回体内容为
EDGE-124-OK
- 返回
-
外部机器直接打
https://124.220.233.126/openai/v1/chat/completions的tool_choice=required- 返回
200 - 返回
message.tool_calls - 参数里已携带
{\"command\":\"echo TOOL-EDGE-OK\"}
- 返回
-
外部机器直接打
https://124.220.233.126/openai/v1/responsesstreaming- 返回稳定事件序列
- 文本可稳定拼成
STREAM-EDGE-124-OK
-
公共 DNS 已切换到
124
tccli dnspod DescribeRecordList --Domain tengokukk.com8.8.8.8查询结果都是124.220.233.126
- 多个外部探测节点访问
https://key.tengokukk.com/
check-host.net的西班牙 / 法国 / 印度 / 瑞典 / 乌克兰节点都返回302- 解析到的实际目标 IP 都是
124.220.233.126
- 这台 Windows 本机上的正式域名
https://key.tengokukk.com/openai/v1/chat/completions
- 在零代理下直接返回
DIRECT-DNS-NO-PROXY-OK
- 这台 Windows 本机上的
codex exec "Reply exactly CODEX-DIRECT-DNS-NO-PROXY-OK."
- 在零代理下直接返回
CODEX-DIRECT-DNS-NO-PROXY-OK
- 这台 Windows 本机上的正式域名
codex exec真实动作测试
- 在零代理下直接成功
- 已创建
C:\Users\ASUS-KL\.codex\.tmp\direct-domain-no-proxy-action-ok.txt - 文件内容为
DIRECT-DNS-NO-PROXY-ACTION-OK
- 这台 Windows 本机上的旧 proxy-env 方案
- 在 DNS 真改好之前曾用于过渡
- 现在已经不再需要
- 这台 Windows 本机上的旧 path proxy
4411方案
- 更早阶段用于过渡
- 现在已经不再需要
- 这台 Windows 本机上的旧 path proxy 动作测试
- 已实际调用 shell
- 已创建
C:\Users\ASUS-KL\.codex\.tmp\local-hang-repro\action-ok.txt - 文件内容为
ACTION-OK
2026-04-23 补充:170 直连 443 为什么之前不通
这条后来已经查清并修掉了,真实根因不是 nginx、本机 DNS,也不是 Linux ufw/iptables:
-
170.106.179.226实际是 Tencent Lighthouse 实例,不是普通CVM- 这就是为什么前面用
tccli cvm DescribeInstances查不到 - 实例真实 ID:
lhins-nd7hu039
- 这就是为什么前面用
-
真正挡住公网
443的层是 Lighthouse 防火墙- 修复前规则里只有
22 / 80 / 3000 / 3301 / ICMP - 没有
TCP 443 - 所以外部连
170.106.179.226:443会超时,而不是收到 nginx 响应
- 修复前规则里只有
-
已执行的最小修复
- 使用
tccli lighthouse CreateFirewallRules --region na-siliconvalley - 给
lhins-nd7hu039新增0.0.0.0/0 -> TCP 443 - 回读规则后
FirewallVersion已从3变为4
- 使用
-
修复后的结果
- 这台 Windows 本机
Test-NetConnection 170.106.179.226 -Port 443现在已返回TcpTestSucceeded=True curl -k -I https://170.106.179.226现在已直接返回HTTP/1.1 200 OK- 所以“这台 Windows 本机直连不了
170:443”已经不再成立
- 这台 Windows 本机
当前仍需明确区分的现象
-
170 服务器访问“自己的公网域名/IP”可能卡住
- 这更像云主机对自身公网地址的 hairpin/回环限制
- 不等于 nginx 或 3301 本身坏了
- 同一台机器使用
https://127.0.0.1+Host: key.tengokukk.com的 loopback 闭环是正常的
-
旧的
124 -> 170:3301边缘链路仍然存在- 但它现在只是旧边缘/备用层,不再是权威正式入口
- 当前正式域名
https://key.tengokukk.com/openai/v1的权威解析已经切到170.106.179.226
2026-04-23 补充:正式入口已从 124 切到 170
这一步后来也已经执行完成,当前“正式公网主入口仍然是 124”这条旧判断已经失效。
本次已完成的切换动作:
-
170 站点补齐正式入口行为
- 在
/etc/nginx/sites-available/key.tengokukk.com新增:location = / { return 302 /openai/v1/models; }
- 所以
170现在不只是能接/openai/,根路径行为也和之前124的正式入口一致
- 在
-
DNSPod 权威记录已改
key.tengokukk.com的A记录RecordId=2280257289- 已从
124.220.233.126改到170.106.179.226
-
外部非本机实测证据
- 在
124.220.233.126这台外部机器上:getent ahosts key.tengokukk.com已返回170.106.179.226curl -k -I https://key.tengokukk.com的REMOTE_IP已是170.106.179.226curl -k https://key.tengokukk.com/openai/v1/chat/completions带真实 key 的返回体内容为CUTOVER-REMOTE-124-OK
- 这说明现在不是“只有当前 Windows 本机能通”,而是外部非本机也已经通过正式域名命中 170
- 在
-
DNS 传播说明
8.8.8.8 / 1.1.1.1 / 223.5.5.5现在都已经解析到170.106.179.226- 如果某台机器短时间仍看到
124,优先判断为该机器自己的旧缓存尚未过期,不再是权威记录未切换
2026-04-23 补充:本地 127.0.0.1:4411/openai/v1/responses 的 413 已修掉
这条后来也单独修过,根因是两层叠加:
-
本地
4411代理仍然硬编码指向旧边缘124.220.233.126- 文件:
C:\\Users\\ASUS-KL\\.codex\\.tmp\\local-key-proxy\\key-edge-proxy.mjs - 同目录下的
key-connect-proxy.mjs也把key.tengokukk.com固定映射到124
- 文件:
-
124与170两层 nginx 都没有显式放大请求体上限- 默认会命中 nginx 的请求体限制
- 在大一点的
responses请求上返回413 Payload Too Large
已执行的修复:
- 把本地
4411/4412旧代理都改为指向170.106.179.226 - 给
170站点/etc/nginx/sites-available/key.tengokukk.com增加client_max_body_size 50m; - 给
124站点/etc/nginx/sites-available/key.tengokukk.com.conf增加client_max_body_size 50m; - 已重载两台机器的 nginx,并重启本地
4411代理
修复后的验证结论:
- 同一份约
1.2MB的responses请求体,再打4411已不再返回413 - 同一份约
1.2MB的请求体,打124也不再返回413 - 修复后返回的是后端
500,说明请求已经穿过请求体限制层,413这层问题已解除 - 也就是说,这次修掉的是“请求在 nginx/body-size 层被拦住”,不是顺手重做
responses上游语义
建议如何理解这条链路
不要再把它理解成“responses 上游已经完全健康”。
更准确的理解是:
上游真实健康面:chat/completions
3301 对外兼容面:responses + chat/completions
responses 的稳定性来自 converter + stream fallback
不是来自上游原生 /responses 健康证据文件
本次文档对应的一次性验证输出已落到:
E:\My Project\Atramenti-Console\codex\plugins\obsidian\data\docs\codex-knowledge\_artifacts\2026-04-23-key-tengokukk-gateway-verification.txtE:\My Project\Atramenti-Console\codex\plugins\obsidian\data\docs\codex-knowledge\_artifacts\2026-04-23-key-tengokukk-default-nslookup.txtE:\My Project\Atramenti-Console\codex\plugins\obsidian\data\docs\codex-knowledge\_artifacts\2026-04-23-key-tengokukk-google-nslookup.txtE:\My Project\Atramenti-Console\codex\plugins\obsidian\data\docs\codex-knowledge\_artifacts\2026-04-23-key-tengokukk-remote-checks.txtE:\My Project\Atramenti-Console\codex\plugins\obsidian\data\docs\codex-knowledge\_artifacts\2026-04-23-key-tengokukk-public-chat.txtE:\My Project\Atramenti-Console\codex\plugins\obsidian\data\docs\codex-knowledge\_artifacts\2026-04-23-key-tengokukk-local-codex-proxy-validation.txtE:\My Project\Atramenti-Console\codex\plugins\obsidian\data\docs\codex-knowledge\_artifacts\2026-04-23-key-tengokukk-global-edge-cutback-to-124.txtE:\My Project\Atramenti-Console\codex\plugins\obsidian\data\docs\codex-knowledge\_artifacts\2026-04-23-key-tengokukk-direct-domain-proxyenv-cutover.txtE:\My Project\Atramenti-Console\codex\plugins\obsidian\data\docs\codex-knowledge\_artifacts\2026-04-23-key-tengokukk-final-direct-dns-cutover.txtE:\My Project\Atramenti-Console\codex\plugins\obsidian\data\docs\codex-knowledge\_artifacts\2026-04-23-170-lighthouse-443-fix.txtE:\My Project\Atramenti-Console\codex\plugins\obsidian\data\docs\codex-knowledge\_artifacts\2026-04-23-local-4411-413-fix.txt
下一步最自然的工作
如果继续往“工业级”推进,优先级建议是:
-
给 openai/custom unary fallback 加更明确的日志标记
- 区分 direct unary 成功
- direct unary 缺 tool_call 后自动回退 stream
- unary final object 已重建
-
视需要把这组补丁回写到真正受控的 repo/工作副本
- 当前修改已经在 170 运行面生效
- 但如果要长期维护,最好同步到正式代码仓库而不是只留在线上机器
-
再决定是否要让
170:443承担正式公网入口- 现在它已经可以直连
- 但当前生产稳定链路已经收敛到
124 -> 170:3301 - 如果要切主入口,应单独评估证书、DNS、回滚和外部验证
2026-04-23 补充:第二把 Soxio key 已加入池子
本次又把同一组 Soxio 上游的第二把 key 加进了 provider pool,不再只依赖单节点:
openaiResponses-customsoxio-responses-v1soxio-responses-v1-b
openai-customsoxio-openai-v1soxio-openai-v1-b
第二把 key 对应值为:
cr_75927920e39d477d32474e98c1228ee90731fb846cce8cd8f87cac954ca6a656这次不是只把 key 写进去,而是确认了它已经真实被初始化并参与选路:
configs/provider_pools.json中可见:customName: "soxio-responses-v1-b"customName: "soxio-openai-v1-b"
logs/app-2026-04-23.log中可见:Initialized node: soxio-openai-v1-bgetServiceAdapter ... (soxio-openai-v1-b)
2026-04-23 补充:Soxio 的“流式上游”已被外层补成稳定 unary
这一步的目标不是让 Soxio 自己学会 unary,而是让外面看起来已经能稳定 unary。
先确认到的上游事实:
https://apikey.soxio.me/openai/v1/chat/completionsstream=true正常stream=false会被上游拒绝
https://apikey.soxio.me/openai/v1/responses- 实际也要求
stream=true
- 实际也要求
所以这次真正落地的不是“换一个更听话的上游”,而是:
- 在
170:/srv/aiclient-2-api-mock/src/providers/openai/openai-core.js - 让 unary 请求先按正常 unary 发
- 如果上游明确报出这是 stream-only
- 就自动回退到
_collectUnaryFromStream(...) - 再把收集到的流式结果重新收敛成外部看到的 unary 响应
本次确认存在的关键代码与日志信号:
- 代码命中:
_shouldFallbackToStreamOnUnaryError_collectUnaryFromStream
- 日志命中:
Unary request rejected by upstream as stream-only. Falling back to stream aggregation.
这意味着现在的真实行为已经变成:
外层 unary 请求
-> 先试 direct unary
-> 如果 Soxio 拒绝 unary
-> 自动改走 stream
-> 再由 AIClient2API 聚合成 unary 返回2026-04-23 补充:这轮的最终实测结果
这轮不是只看配置和日志,而是重新做了真实调用:
-
内层 unary chat
- 目标:
http://127.0.0.1:3301/v1/chat/completions - 返回:
HTTP 200 - 内容:
INNER-UNARY-CHAT-OK
- 目标:
-
公网 unary responses
- 目标:
https://key.tengokukk.com/openai/v1/responses - 返回:
HTTP 200 - 输出:
PUBLIC-UNARY-RESPONSES-OK
- 目标:
-
运行日志
- 已出现 stream-only -> unary fallback 的日志
- 已看到
soxio-openai-v1与soxio-openai-v1-b两个节点都被初始化和命中
所以截至这一步,关于 Soxio 这条上游,更准确的结论应该写成:
Soxio 本身仍然偏 streaming-first
但 AIClient2API 外层现在已经把它补成:
- streaming 可用
- unary 也可用
- 公网看起来是稳定 unary本轮新增证据文件
本轮新增的原始命令与返回结果已落到:
E:\My Project\Atramenti-Console\codex\plugins\obsidian\data\docs\codex-knowledge\_artifacts\2026-04-23-soxio-second-key-and-unary-aggregation.txt