Codex 为什么会“降智”:292 State、状态注入与长任务的另一条解释

最近使用 Codex 处理一些时间较长的任务时,我反复遇到几个很难解释的现象。
同一个账号、相同的模型和相近的提示词,有时可以连续完成多文件重构;换一个时间段之后,却开始忘记上下文、重复已经解决的问题,甚至给出像是低配模型生成的答案。
Codex 的 cloud agent 也会频繁出现 overloaded,请求还可能返回 429。
换一个会话、换一个网络出口,或者等一段时间之后,状态又似乎恢复了。
这到底是模型本身发生了变化,还是服务端的调度状态发生了变化?
我现在更认可后一种解释。
Codex 背后存在一套和资源路由、请求状态、上下文压缩以及长任务连续性有关的机制。我们看到的“降智”,往往不是单纯的模型能力变化,而是多个状态同时变化后的结果。
其中最值得关注的,就是 HTTP 292 和 current_turn_state。
先说结论
这件事可以拆成两条同时运行的链路。
第一条是资源链路:
请求 ↓服务端调度 ↓HTTP 292 / current_turn_state ↓资源等级与后端路由 ↓后续请求继续携带 state第二条是任务链路:
对话与工具调用 ↓上下文不断增长 ↓达到阈值后压缩 ↓部分细节被重新概括 ↓长任务表现发生变化292 State 主要解释第一条链路。
上下文增长和 compaction 主要解释第二条链路。
两者叠加之后,就会出现一种非常真实的用户体验:
任务刚开始时模型很聪明,运行一段时间后,它像是换了一个模型。
这里先把资料分成三种:
- 公开源码已经能确认的部分:Codex 存在
x-codex-turn-state,并把它用于同一轮请求的粘性路由。 - 公开讨论和实际观察中的部分:292、
current_turn_state、约一小时 TTL、IP 不绑定以及资源等级之间的关系。 - 本文自己的代码:保留状态摘要记录、续期逻辑和路由结构示意,用来还原这套调度流程。
这三类证据不能混在一起,但它们可以拼出一套比较完整的解释。
292 到底是什么
标准 HTTP 规范里没有 292 这个状态码。IANA 的标准登记表中也没有记录它。IANA HTTP Status Code Registry
但这并不代表服务内部不能使用私有状态码。
大型平台通常会在标准 HTTP 状态之外,通过响应头、内部元数据或自定义状态表示更多调度信息。
292 就是这样一种内部信号。
目前比较合理的解释是:当请求到达服务端之后,调度器会先判断当前请求应该进入什么资源路径,包括:
- 路由到哪个模型实例。
- 使用什么推理质量。
- 分配多少计算资源。
- 是否允许完整上下文。
- 是否进入备用或降级队列。
- 后续请求是否可以继续复用当前调度结果。
调度正常时,服务端返回 292,并附带一个 current_turn_state。
当前资源紧张时,则返回普通的 200,但请求实际进入资源受限或降级路径。
可以把它简单理解成:
| 状态 | 含义 | 用户表现 |
|---|---|---|
| 292 | 调度正常、资源路径稳定 | 速度快,推理完整,较少出现 overload |
| 200 | HTTP 请求成功,但不代表资源等级相同 | 能返回结果,但可能变慢、变浅或更容易丢上下文 |
| 429 | 请求过快或触发限流 | 请求失败,需要退避 |
| 503 | 服务暂时过载 | 请求失败或排队 |
这里需要强调,292 和 200 的对应关系目前属于逆向观察,不是 OpenAI 公开文档确认的协议。
但这个解释能够很好地解释一种现象:
请求明明成功返回了,为什么模型的质量还是明显下降了?
因为 HTTP 成功和获得相同的计算资源,并不是一回事。
current_turn_state 是什么
公开的 Codex 源码中,可以看到一个真实存在的状态:
x-codex-turn-state官方客户端把它描述为 sticky-routing token,也就是粘性路由状态。Codex 官方 client.rs
它的基本流程是:
- 一轮 turn 开始。
- 服务器返回 turn state。
- 客户端保存这个状态。
- 同一轮中的后续请求继续携带它。
- 任务进入新的 turn 后,应该创建新的 session。
SSE、WebSocket 和 compact 请求也会处理这个状态。SSE 响应处理 WebSocket 响应处理
这至少证明了一个事实:
Codex 的请求并不是完全独立的,服务端确实会使用某种状态维持同一轮任务的连续路由。
公开源码使用的是 x-codex-turn-state,而一些逆向观察把更上层的状态称为 current_turn_state。
我更倾向于把它们理解成同一套调度机制的不同表现:
- HTTP 292 更像服务端给出的资源调度结果。
current_turn_state更像资源调度凭据。x-codex-turn-state更像客户端继续发送的 turn 粘性状态。
它们属于同一个服务端状态系统的不同层级。
官方源码把 x-codex-turn-state 放在 turn 范围内,说明客户端看到的 header 负责维持当前 turn 的粘性路由;292 和 current_turn_state 则是更上层的资源状态。它们不需要是同一个字段,但共同指向同一个结论:Codex 并不是无状态地处理每一个请求。
为什么不是所有人都能拿到 292
如果 292 代表一次调度成功,那么它就不应该对所有请求无差别发放。
从实际观察来看,影响 292 签发的因素主要有四类。
IP 质量
不同网络出口具有不同的风控和资源评分。
但如果只把 292 理解成“当前还有算力”,仍然漏掉了更核心的一层:它是服务端在判断请求来源之后,为高可信用户保留完整服务路径的调度标记。
这套分层首先照顾的是美国本地的真实用户。这里的“本地”不是简单读取一个国家字段,而是综合网络出口、ASN、地理位置、IP 信誉、账号历史和请求行为等信号。稳定的美国住宅宽带,更像一个真实家庭用户的固定网络;中转站、共享代理、VPN、VPS 和数据中心出口,则更容易呈现出多人共用、地域与账号不一致、请求集中或出口频繁变化等特征。
服务端没有必要把这些流量全部拒绝。更现实的做法是,在资源紧张时先保护本地住宅用户的体验,再把中转、共享 VPN 和数据中心流量放进普通或降级路径。后者仍然可以拿到响应,但更容易遇到等待、overload、429、503,以及长任务连续性变差。
所以,292 的核心并不是“美国 IP 有一个特殊开关”,而是服务端把不同来源的请求放进了不同的服务质量等级:
这也解释了为什么同一个账号会出现明显的网络差异:变化的未必是账号权限,而是请求被服务端放进了哪一层。
可以把实验中的 IP 类型粗略分成:
| IP 类型 | 观察到的 292 通过率 | 说明 |
|---|---|---|
| 美国住宅宽带 | 较高 | 真实 ISP 住宅出口 |
| 原生 IPv6 | 较高 | 某些地址可能被视为更自然的用户网络 |
| 欧洲或亚洲住宅 | 中等 | 仍可能获得正常状态,但概率不稳定 |
| 数据中心 IP | 较低 | VPS、云服务器地址段容易被集中标记 |
| 共享 VPN 或公共代理 | 很低 | 同一个出口可能被大量用户使用 |
这张表不是固定不变的官方等级表,而是实际实验中最值得优先比较的网络分层。
如果同一个账号通过美国住宅网络容易拿到 292,通过数据中心或共享代理更容易进入普通 200,那么就说明 IP 质量已经参与了调度。更准确地说,服务端正在用网络来源判断“这个请求应该获得什么级别的服务”。
为什么中转和 VPN 更容易降级
中转、VPN 和数据中心并不等于恶意请求,但从服务端的角度看,它们和共享滥用流量使用的是相似的网络信号:一个出口承载大量账号,账号所在地与 IP 所在地不一致,请求在短时间内集中出现,或者出口属于云厂商和代理服务商的地址段。
在高峰期,服务端最容易做的不是逐个判断“这个人是不是好用户”,而是先按网络来源做粗粒度分层。美国本地住宅出口更容易进入高优先级路径;中转站、共享 VPN 和数据中心出口则更容易保留基本可用性,但牺牲一部分速度、推理资源和长任务稳定性。
这就是“给当地居民更好的体验,给中转用户降级使用”的技术含义。它不是把某一类用户永久封死,而是在有限资源下把完整体验优先留给最像本地真实用户的请求。
账号等级
不同订阅等级具有不同的资源优先级。
需要比较的变量包括:
- Free。
- Plus。
- Pro。
- 更高等级的 Pro 计划。
不能只拿一个账号测试一次就得出结论。更可靠的方式是使用相同模型、相同提示词和相近时间重复测试。
服务端负载
即使账号和 IP 都没有变化,服务端高峰期也可能收紧 292 的签发。
这可以解释为什么:
- 早上状态很好,下午开始变差。
- 工作日和周末体验不同。
- 同一个会话上午正常,晚上频繁 overload。
- 等几个小时后,模型又恢复了。
这不一定是用户发生了变化,也可能只是服务端整体负载发生了变化。
State 的有效期
current_turn_state 的 TTL 约为一小时。
也就是说:
- 第一次请求成功获得 292。
- 服务端签发一个 state。
- 接下来一段时间内可以继续复用。
- 超过有效期之后,state 失效。
- 下一次请求需要重新进行调度。
这可以解释“刚刚还很好,过一会儿突然变差”的体验。
当然,TTL 不能只凭一次观察确定,必须通过连续重放实验测量。
验证不能只靠感觉
如果只是在浏览器里感觉“今天好像聪明一点”,很难证明 292 State 的作用。
接下来需要测量四个变量:
- 292 是否和 state 一起出现。
- 携带有效 state 是否真的改变后续结果。
- state 是否和 IP 绑定。
- state 的 TTL 大约是多少。
第一组:观察 292 和 state 是否同时出现
每次请求至少记录:
timestampnetwork_profileaccount_levelmodelhttp_statusstate_presentstate_hashlatency_msretry_afteroverloaded记录时可以用 state 的哈希作为标识。
from __future__ import annotations
import hashlibfrom typing import Mapping
def summarize_response( status: int, headers: Mapping[str, str], latency_ms: int,) -> dict: state = ( headers.get("current_turn_state") or headers.get("x-codex-turn-state") )
return { "status": status, "state_present": bool(state), "state_hash": ( hashlib.sha256(state.encode()).hexdigest()[:12] if state else None ), "latency_ms": latency_ms, "retry_after": headers.get("retry-after"), "server_model": headers.get("openai-model"), }这段代码只记录状态是否存在,不保存完整 token。
第二组:有状态和无状态对照
至少准备三组请求:
A 组:普通线路,不携带 stateB 组:普通线路,携带有效 stateC 组:普通线路,携带过期或随机 state三组请求应尽量保持:
- 相同账号。
- 相同模型。
- 相同提示词。
- 相近时间。
- 相同请求间隔。
需要比较的指标包括:
- 292 出现频率。
- 429 出现频率。
- overloaded 出现频率。
- 首字节延迟。
- 输出长度。
- 工具调用是否中断。
- 上下文是否连续。
- 人工盲评结果。
如果只有 B 组长期表现更好,而 A、C 组明显更差,就可以量化 state 的作用。
第三组:验证是否绑定 IP
这是整个理论最关键的验证。
流程应该是:
住宅 IP 获取 292 + state ↓切换到数据中心或 IPLC ↓不携带 state 请求一次 ↓携带 state 请求一次 ↓比较两次结果如果不携带 state 的请求明显更容易降级,而携带同一个 state 的请求仍保持正常,说明 state 的使用和 IP 是分离的。
这个实验需要反复执行,用来排除服务端负载波动。
第四组:测量 TTL
获得 state 后,按固定间隔重试:
0 分钟10 分钟20 分钟30 分钟45 分钟55 分钟60 分钟70 分钟90 分钟每次记录:
- 是否仍被接受。
- 是否仍保持相同路由结果。
- 是否返回新的 292。
- 是否需要重新获取 state。
- 是否出现 429 或 503。
如果状态在接近一小时后稳定失效,才能说明“一小时 TTL”具有一定可信度。
状态获取和状态使用可以分开
状态注入的核心思路是:
获取 state 的网络环境,和使用 state 的网络环境,不一定需要相同。
大致流程如下:
获取阶段:
Clash 切到住宅或原生 IPv6 ↓keeper 发送探测请求 ↓服务端返回 292 ↓提取 current_turn_state ↓保存状态和过期时间 ↓Clash 切回日常线路
使用阶段:
Codex / ChatGPT 发出请求 ↓本地代理读取 state 文件 ↓在请求中加入状态 header ↓转发到服务端 ↓服务端验证 state 是否有效这套设计的关键假设是:
- state 不和签发时的 IP 强绑定。
- state 代表的是调度结果,而不是 IP 身份。
- state 在 TTL 内可以跨网络出口使用。
- state 与实际使用的模型保持一致时效果最好。
如果这些假设成立,住宅 IP 只需要在获取阶段出现几秒钟,日常使用仍然可以走低延迟线路。
State 的真正作用:保留已经拿到的服务等级
如果 292 只是“当前还有算力”,拿到一个 state 本身没有太大意义。状态注入之所以有效,是因为它把第一次请求时完成的服务质量判断延续到了后续请求。
获取阶段在美国住宅或其他高可信网络上拿到 292,等于先完成了一次更优先的资源调度。使用阶段即使切回中转、VPN 或日常线路,带上有效 state 后,服务端仍然可以把请求识别成已经建立的稳定 turn,而不是每次都根据当前出口重新从普通队列开始排队。
所以,state 的作用不是把数据中心 IP 变成住宅 IP,也不是改变账号的地区身份;它就是一张暂时有效的“服务等级通行证”,把之前已经拿到的资源路径延续一段时间。只要 state 仍在有效窗口内,后续请求就会继续沿用原来的调度结果;一旦过期、模型不匹配或服务端重新做风险判断,请求就会回到当前网络对应的普通路径。
这也解释了为什么“先住宅获取,再日常线路使用”比单纯切换 IP 更有意义:前者是在复用一次已经完成的调度,后者只是重新碰运气。
续期逻辑
状态不能等到完全过期之后再续。
更合理的方式是提前五分钟续期,并且每 45 秒检查一次。
from __future__ import annotations
import jsonimport timefrom pathlib import Path
STATE_FILE = Path("current_turn_state.json")RENEW_BEFORE_SECONDS = 5 * 60
def load_state() -> dict | None: if not STATE_FILE.exists(): return None
return json.loads(STATE_FILE.read_text(encoding="utf-8"))
def should_renew(state: dict | None) -> bool: if not state: return True
expires_at = float(state["expires_at"]) return expires_at - time.time() <= RENEW_BEFORE_SECONDS
def save_state(value: str, ttl_seconds: int) -> None: payload = { "value": value, "expires_at": time.time() + ttl_seconds, }
STATE_FILE.write_text( json.dumps(payload, indent=2), encoding="utf-8", )
def keeper_loop(acquire_state) -> None: while True: state = load_state()
if not should_renew(state): time.sleep(45) continue
# 这里调用探测逻辑,返回状态和 TTL。 result = acquire_state()
if result and result.get("status") == 292: save_state( value=result["state"], ttl_seconds=result.get("ttl", 3600), ) print("state refreshed") else: print("state refresh failed")
time.sleep(45)实际模型应该和使用模型保持一致。
例如,如果日常使用的是 gpt-6-astra,探测时也应该使用同一个模型,而不是用一个模型获取 state,再拿去请求另一个模型。
否则即使 state 有效,也不一定能覆盖另一个模型的资源路由。
真实验证路径
这套机制真正要验证的不是“我能不能生成一个叫 state 的字符串”,而是下面这条因果链:
同一账号、同一模型、相近时间 ↓住宅 / 原生 IPv6 请求 ↓观察是否出现 HTTP 292 和 current_turn_state ↓保存 state 的摘要与首次出现时间 ↓切换到日常线路 ↓分别发送不带 state 和带 state 的请求 ↓比较路由结果、延迟、429、503、overload 与输出质量工程上可以拆成几个部分:
- keeper 负责发起探测、保存 state 和提前续期。
- 本地代理负责在请求经过时读取当前 state。
- Clash 或其他路由工具负责在探测线路和日常线路之间切换。
- 日志记录每一次响应的状态、延迟、模型和路由差异。
典型流程是:
- 在住宅或原生 IPv6 出口发起探测请求。
- 观察响应是否出现 292,以及是否返回 current_turn_state。
- 将 state 保存到本地文件,同时记录获取时间和预计过期时间。
- 在 state 过期前提前续期。
- 让本地代理在后续请求中携带 state。
- 切回日常线路,比较有 state 和无 state 的真实请求。
真实验证需要记录什么
如果要把这套机制跑清楚,每次请求至少记录一行实验数据:
timestampaccount_levelmodelnetwork_profilehttp_statusstate_presentstate_hashlatency_msretry_afteroverloadedcontext_sizecompaction_happened记录时可以用 state 的哈希作为标识:
from __future__ import annotations
import hashlibfrom collections.abc import Mapping
def summarize_response( status: int, headers: Mapping[str, str], latency_ms: int,) -> dict: state = ( headers.get("current_turn_state") or headers.get("x-codex-turn-state") )
return { "status": status, "state_present": bool(state), "state_hash": ( hashlib.sha256(state.encode()).hexdigest()[:12] if state else None ), "latency_ms": latency_ms, "retry_after": headers.get("retry-after"), "server_model": headers.get("openai-model"), }这个函数把一次响应整理成便于比较的实验记录,后面可以直接写入 JSONL 或 CSV。
真实实验的对照组
至少应该设置三组:
| 组别 | 网络 | state | 目的 |
|---|---|---|---|
| A 组 | 日常线路 | 不携带 | 观察自然状态 |
| B 组 | 日常线路 | 携带有效 state | 观察注入后的状态 |
| C 组 | 日常线路 | 携带随机或已过期 state | 排除“只要多一个 header 就有效”的可能 |
三组请求尽量保持:
- 同一个账号。
- 同一个模型。
- 同一类提示词。
- 相近的时间段。
- 相同的请求间隔。
- 尽量相同的上下文长度。
如果只测试一次,结果很容易被时间、网络和服务负载干扰。更可靠的方式是跨多个时间段、多个网络出口重复测试,同时记录 429、503 和 overload 的出现情况。
IP 是否绑定,需要单独验证
最关键的实验是 IP 绑定实验:
阶段一:住宅 / 原生 IPv6 获取 292 和 state
阶段二:数据中心 / IPLC 不携带 state 请求一次 携带同一个 state 请求一次
阶段三:重复多轮 比较两种请求的成功率和服务表现如果携带 state 的请求在更换网络出口后仍然保持明显差异,就说明 state 的使用可能不依赖签发 IP。
更换 IP 后还要继续控制账号、模型、会话和提示词,否则很难判断变化到底来自 state 还是其他变量。
TTL 也必须实测
TTL 约一小时,连续重放可以测出它的实际窗口。
拿到 state 后按固定时间间隔测试:
0 分钟10 分钟20 分钟30 分钟45 分钟55 分钟60 分钟70 分钟90 分钟每个时间点记录:
- state 是否仍然被接受。
- 是否仍然维持相同的响应特征。
- 是否出现新的 292。
- 是否出现 429、503 或 overload。
- 是否需要重新建立 session。
- 上下文是否已经发生 compaction。
如果多个时间段的结果都在接近一小时后发生变化,TTL 的判断才比较稳定。
结果怎么看
Codex 官方源码读取 x-codex-turn-state,并把它作为同一 turn 内的 sticky-routing token;SSE、WebSocket 和 compact 路径也都会处理这个状态。
292、current_turn_state、IP 质量、TTL 和资源等级共同构成这套机制的核心变量。真实请求对照要测量的是:
- 292 是否和 state 稳定同时出现。
- state 是否能在换 IP 后继续生效。
- state 失效是否呈现稳定的时间窗口。
- 携带 state 后,429、503、overload 和响应延迟是否持续改善。
- 上下文长度和 compaction 是否会独立影响结果。
把这些变量拆开之后,才能知道“降智”究竟来自资源路由、状态过期,还是上下文已经变得过于复杂。
Clash 的路由切换
如果实际实验需要切换不同出口,结构上通常需要两个代理组:
proxy-groups: - name: GPT-Daily type: select proxies: - Daily-Route-1 - Daily-Route-2
- name: GPT-State-Probe type: select proxies: - Residential-Route - Native-IPv6-Route获取 state 时切换到 GPT-State-Probe。
获取完成后切回 GPT-Daily。
这也是为什么整个方案通常需要三个组件:
| 组件 | 职责 |
|---|---|
| keeper | 获取 state、记录 TTL、提前续期 |
| 本地代理 | 在请求链路中携带有效 state |
| Clash 或其他路由工具 | 在获取阶段切换网络出口 |
对 Codex 来说,它仍然只是向本地代理发请求,并不知道背后进行了状态管理。
上下文增长会放大问题
即使状态路由保持稳定,长任务依然可能出现“降智”。
因为上下文本身也在不断增长。
一个复杂任务可能包含:
- 初始需求。
- 多轮对话。
- 大量文件内容。
- 多次代码修改。
- 测试日志。
- 浏览器截图。
- 工具调用结果。
- 被放弃的旧方案。
- 后来追加的临时要求。
这些内容都会进入任务历史。
刚开始时,模型只需要理解“我要做什么”。
任务进行到后面,它需要理解的是:
哪些内容仍然有效,哪些决定已经被推翻,哪些错误已经修复,现在文件系统的真实状态是什么?
上下文越长,模型拥有的信息越多,但真正重要的信息不一定更加突出。
如果一个任务中存在大量旧方案、失败日志和已经失效的指令,模型就需要花更多精力在上下文里寻找当前有效的答案。
这时即使资源路由没有变化,用户也会感觉模型变得迟钝。
压缩会改变任务的“记忆形状”
当上下文接近上限时,Codex 需要进行 compaction,也就是上下文压缩。
OpenAI 官方文档说明,压缩后的内容是加密且不可直接解释的 opaque item,客户端不能简单地把它当成普通文本查看或编辑。Context compaction 官方文档
压缩可以让长任务继续运行,但它并不是把原来的上下文逐字保存下来。
原来的上下文里,可能有很多只有在具体语境中才有意义的判断:
- 这段代码暂时不能抽象。
- 这个按钮虽然多余,但用户已经习惯。
- 当前功能只需要验证流程,不应该继续扩展。
- 某个报错只是环境问题,不是代码逻辑问题。
压缩之后,这些细节可能仍然存在,也可能只剩下一段更概括的总结。
模型可能还记得“要完成什么”,但未必完整保留“为什么要这样完成”。
因此,压缩前后的任务目标看起来一样,工作风格却可能已经发生变化。
如果恰好在压缩、重连或重新路由之后,任务又失去了原来的 turn state,用户就更容易把这种变化理解成“模型降智”。
长任务其实是多种状态叠加
可以把一个长任务简单理解成下面这几层:
这些状态任何一层发生变化,都可能影响最后的表现。
- 会话变化,可能导致上下文重新开始。
- 292 状态变化,可能导致资源路径发生变化。
- turn state 丢失,可能导致同一轮请求不再保持粘性路由。
- 上下文压缩,可能让模型失去部分细节。
- 工具调用异常,可能让模型误判当前进度。
- 文件系统变化,可能让对话历史和真实项目不一致。
- 429 或 503,则可能直接造成请求失败或重试。
所以,模型表现变差并不一定只有一个原因。
但这几种状态如果同时变化,就很容易产生非常明显的“降智”体验。
429 和 overloaded 可能是结果,不是根因
OpenAI 官方文档将 429 和 503 区分为两类问题。
429 通常和请求过快、流量增长过猛或速率限制有关。
503 server_is_overloaded 则表示模型服务暂时过载。
官方建议使用 Retry-After 和指数退避,而不是连续快速重试。Rate limits 官方文档
但从长任务角度看,429 和 overloaded 也可能只是我们看到的表面结果。
如果一个任务不断增长、频繁调用工具、反复重试,那么它可能同时带来:
- 更高的请求频率。
- 更大的上下文。
- 更多的压缩操作。
- 更多的状态切换。
- 更复杂的资源调度。
最终表现出来的就不只是一次请求失败,而是整个任务开始变得不稳定。
状态注入可能遇到的坑
State 过期
如果续期失败,旧 state 过期之后就会进入空窗期。
表现可能包括:
- 模型质量下降。
- overload 增多。
- 重新出现 429。
- 长任务突然开始丢上下文。
所以续期应该提前五分钟开始,而不是等到最后一分钟。
模型不匹配
用一个模型获取的 state,不一定适合另一个模型。
例如:
gpt-6-astra 获取的 state不一定适合其他模型的请求实验时,获取模型和使用模型应该保持一致。
多账号不能混用
每个账号的 state 应该独立保存。
不能把账号 A 获得的状态注入账号 B 的请求,也不能让多个账号共用同一个状态文件。
上下文压缩仍然会发生
即使状态注入成功,也不能阻止上下文增长和 compaction。
状态注入解决的更像是资源和路由问题,不能保证模型永远记得每一个细节。
一个解决“分配到哪里”。
另一个解决“还能记住什么”。
和其他方法比较
| 方法 | 原理 | 效果 | 问题 |
|---|---|---|---|
| 换 IP | 碰运气进入更好的出口 | 有时有效 | 不稳定 |
| 新开会话 | 重新触发调度 | 偶尔有效 | 丢失上下文 |
| 多账号轮换 | 分散请求压力 | 可能有效 | 成本高、管理复杂 |
| 避开高峰期 | 等服务负载下降 | 通常有效 | 不适合连续工作 |
| 记录项目状态 | 减少上下文依赖 | 对长任务有效 | 不能改变资源路由 |
| 292 State 注入 | 复用稳定调度状态 | 理论上更稳定 | 需要验证 TTL、IP 绑定和模型匹配 |
292 State 注入的特别之处,在于它试图从调度层解决问题,而不是只在应用层缓解症状。
换 IP 是重新碰运气。
新会话是重新开始。
状态注入则是尝试保留原来的资源和路由结果。
这套机制为什么存在
从平台的角度看,OpenAI 需要同时面对两个问题:
- GPU 资源有限。
- 用户请求不断增长。
如果每个请求都重新排队,长任务的连续性会很差。
因此,服务端会在一轮任务开始时进行一次调度判断:
- 当前账号是否有优先级。
- 当前 IP 是否可信。
- 当前负载是否允许。
- 当前模型是否有资源。
- 是否可以给后续请求维持一条稳定通道。
292 和 state 就是这次调度结果的外部表现。
更核心的目的,是在资源有限和请求来源复杂的情况下,优先保证美国本地、稳定、低风险真实用户的体验。中转站、共享 VPN 和数据中心用户并不一定被拒绝,但更容易被放进普通或降级路径:能用,速度和稳定性却不再和本地住宅用户相同。
state 则负责把已经完成的调度结果延续到同一轮任务的后续请求,让高优先级路径不必每次从头竞争。网络切换、移动设备切换和长任务重试只是这个机制带来的连续性表现,不是它唯一的目的。
但如果 state 的签发和使用没有绑定 IP,就会产生一个意外特性:
获取状态和使用状态可以发生在不同的网络环境中。
这也正是状态注入理论成立的关键。
当然,这套机制未来可能被平台调整:绑定 IP、缩短 TTL、增加设备指纹、绑定账户和模型,都是很自然的防护方式。
所以它更像一个尚未被充分文档化的内部机制,而不是一个永远有效的漏洞。
最后
我现在越来越不觉得 Codex 只是一个模型接口。
它更像是一个由模型、资源、上下文、工具、会话和路由共同组成的系统。
292 State 解释了资源路由这一层。
它最重要的含义,是服务端会优先把完整体验留给更像美国本地真实用户的请求,再把中转、共享 VPN 和数据中心流量降到仍然可用但更保守的路径。
x-codex-turn-state 说明 Codex 确实会在同一轮请求中维护粘性状态。
上下文增长和 compaction 解释了为什么长任务会逐渐失去细节。
429 和 overload 则说明服务端负载本身也会参与最终体验。OpenAI:Unrolling the Codex agent loop
所以,“降智”可能不是一个单独的问题,而是下面几件事同时发生:
292 状态没有拿到或已经过期 +后端资源路由发生变化 +上下文不断增长 +压缩后丢失部分细节 +服务端处于高负载 =用户感知到模型突然变笨状态注入解决的是资源路由问题,不能替代上下文管理,但它为“降智”和 overload 提供了一个很有解释力的方向。
真正值得研究的,不只是模型本身有多聪明,而是:
这一次请求到底被分配到了什么资源,携带了什么状态,又在怎样的上下文里继续运行。
这可能才是长任务中最容易被忽略、也最影响体验的一层。
相关阅读
支持与分享
如果这篇文章对你有帮助,欢迎分享给更多人或打赏支持!
























































