这个东西跟ai大模型的关系很大
一、先说这套系统在干什么(目的)
你这个 Token Pool / Gateway 系统,本质是在做一件事:
把多个上游 AI key / provider,统一包装成一个 OpenAI API,让客户端随便用,还能自动切换、重试、抗失败。
也就是一个“AI 代理路由器”:
客户端(Codex / OpenAI SDK)
↓
你的 Gateway(key.tengokukk.com)
↓
多个 provider / key pool
↓
真正的 OpenAI / Claude / Responses API二、你遇到的问题本质是什么(一句话)
不是 AI 坏了,是“请求调度系统 + 重试系统 + 流式协议”三者一起错位了。
三、拆成 4 个核心知识点(你这次全部踩到了)
① SSE 流式协议(Server-Sent Events)
它是干嘛的?
AI streaming 的本质:
不是一次性返回结果
而是一边生成一边推:
data: token1
data: token2
data: token3
...
data: DONE最后必须有:
response.completed你的问题:
👉 有些请求 最后这个“结束信号丢了”
于是客户端就会:
“我还在等结果,但连接断了?”
表现就是:
-
卡住
-
或报:
stream closed before response.completed
② 请求生命周期(Request Lifecycle)
一个完整 AI 请求不是一条直线,是:
请求开始
↓
选择 provider
↓
发起 stream
↓
持续输出 tokens
↓
正常结束 / error
↓
必须 emit completion你这里的问题:
有些路径变成了:
stream 断了
但 completion 没发👉 客户端以为“被掐线了”
③ Retry 机制(重试系统)
正常设计应该是:
失败 → 换 provider → 再试 → 再失败 → 再换你这里的问题有两个:
❌ 问题1:没有真正换 provider
日志显示:
retry 1/5 → provider A
retry 2/5 → provider A
retry 3/5 → provider A👉 等于“假装在重试,其实一直打同一个坏节点”
④ Bug:null 被当成 0(非常关键)
这是你最核心的隐蔽 bug:
原始设计意图:
null = 没配置 → 用默认值实际代码发生了:
Number(null) === 0后果:
maxCredentialSwitchRetries = null
↓
被当成 0
↓
= 禁止重试👉 系统直接“不会切 key 了”
四、为什么症状会“变来变去”
你看到的现象变化,其实是不同层被修复后“漏斗往下掉”:
第一阶段(协议问题)
stream断 + completion丢👉 SSE 层坏
第二阶段(修完 SSE)
卡几秒 → 消失👉 变成 retry + routing 问题
第三阶段(修 retry)
开始 retry 但没效果👉 provider selection bug
第四阶段(发现根因)
null → 0
= retry 被关闭👉 控制层逻辑错误
五、这些模块分别在系统里干什么
你可以这样理解整个架构:
1️⃣ 协议层(SSE / Responses)
👉 负责“怎么把 AI 输出流给用户”
问题类型:
-
token 不流
-
completion 丢
-
连接断
2️⃣ 路由层(Provider Selector)
👉 负责“用哪个 AI key”
问题类型:
-
老是选同一个坏节点
-
没做隔离
-
retry 不生效
3️⃣ 控制层(Retry / Policy)
👉 负责“失败了要不要换”
问题类型:
-
null 被当 0
-
retry 被关闭
-
策略丢失
4️⃣ 观测层(Logs / Debug / Health)
👉 负责“你怎么知道发生了什么”
问题类型:
-
看不到真实 provider 切换
-
retry 不透明
-
请求链不可追踪
六、为什么会出现这种“工业级复杂 bug”
因为你这个系统其实是:
一个 AI 网关 + 负载均衡 + 代理 + 流式协议 + 多 key 调度器
这类系统天然有 3 个特性:
① 状态很多(stateful)
-
请求状态
-
provider 状态
-
retry 状态
② 时间敏感(streaming)
-
token 是实时的
-
不能补发
-
不能回滚
③ 失败模式复杂
-
网络断
-
provider 失败
-
quota 失败
-
SSE 断
七、最关键的“本质理解”
如果只记一句话:
这次不是一个 bug,而是 “流式协议 + 路由系统 + 重试策略”三层错位导致的级联失败
八、给你一个更直觉的比喻
你这个系统其实像:
一个外卖调度系统
SSE = 外卖送达通知
(告诉你“送到了/结束了”)
provider = 外卖员
retry = 换骑手
maxCredentialSwitchRetries = 最多能换几次骑手
你的 bug 就是:
-
外卖员死活送不来(provider坏)
-
系统还不换人(retry=0)
-
而且送达通知还丢了(SSE缺 completion)
九、如果你要继续深入(建议)
你这个系统下一步应该补三件“工程级能力”:
1. request trace ID 全链路追踪
👉 每一步都能看到:
req → providerA → fail → providerB → success2. provider isolation(隔离失败节点)
👉 一个坏节点不能被连续打
3. 强类型 config(避免 null=0)
👉 所有 policy 必须:
undefined = default
null = default
0 = explicitly disable如果你愿意,我可以帮你把这一套整理成:
“Token Pool / AI Gateway 工业级架构图 + 设计规范(对标 OpenAI / Anthropic)”
会比现在这个 debug 版本更系统化。