☰
【Bug已解决】Codex App 陷入 auto-compaction 无限循环并消耗约 30% 用量额度的排查与修复
2026/9/26 12:22:21 网站建设 项目流程

1. Codex App 自动压缩死循环:30% 额度是怎么被烧掉的

Codex App 的 auto-compaction 本意是好的:当对话上下文快撑满模型窗口时,自动做一次摘要压缩,把历史对话瘦身,好让会话继续下去。但我在一次长会话里撞上了它的失控版本——压缩被反复触发,几秒内跑了十几二十次,每次压缩都要调用模型做摘要,也就每次都扣用量额度。等我反应过来手动停掉时,月度额度已经掉了大约 30%。

这个故障的检索关键词很集中:Codex、auto-compaction、无限循环、Bug、解决方案。如果你正在搜「Codex App entered an infinite auto-compaction loop and consumed ~30% of my usage limit」,说明你大概率也遇到了同一类问题,或者想提前把护栏配好。这篇就按真实排障顺序走一遍:先看清现象,再从日志和配置定位循环触发条件,最后给出可复制的 config.toml 与 settings.json 骨架、CC Switch 切换排查步骤,以及复现和验证循环是否终止的具体动作。

适合谁看:用 Codex App 做长会话编码、跑 Agent 任务、或者把 Codex 接到自动化流水线里的人。只要你的会话会变长、会触发自动压缩,这套排查和护栏配置就用得上。核心结论先放这里——任何「自动触发 + 消耗资源」的操作,执行后触发条件必须趋向不再满足,否则就是烧钱永动机。

2. 前置:TaoToken 接入与 Codex 配置入口

在动手改配置之前,先把接入层理顺。Codex App 走的是 OpenAI 兼容风格的接口,我这边统一用 TaoToken 做模型接入和额度管理,好处是额度消耗、请求日志都能在一个地方看,排查循环烧额度时特别有用。

官网入口:https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=

API 基地址:https://taotoken.net/api(这个不加 UTM,直接填进配置)

几个常用 deep link,按需取用:

  • 模型对话(验证模型是否正常响应):https://taotoken.net/models?utm_source=taotoken_aicg_blog_end&utm_content=model_chat&utm_campaign=rewrite
  • Coding Plan(长期编码 / Agent 场景):https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding_plan&utm_campaign=rewrite
  • 控制台(看额度消耗曲线):https://taotoken.net/console?utm_source=taotoken_aicg_blog_end&utm_content=console&utm_campaign=rewrite
  • API Keys 管理:https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api_keys&utm_campaign=rewrite
  • 接入文档:https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite
  • ClaudeCodeAnthropic 接入说明:https://taotoken.net/claudecode-anthropic?utm_source=taotoken_aicg_blog_end&utm_content=claudecode&utm_campaign=rewrite

注意:先把 API Key 配好、确认单次普通请求能通,再去调 auto-compaction 相关配置。否则你分不清「请求失败」和「压缩循环」哪个在烧额度。

3. 可复制配置:config.toml 与 settings.json 关键骨架

Codex App 的配置分两层:config.toml管模型接入和运行参数,settings.json管应用行为(包括自动压缩的触发与护栏)。下面是我实测能压住循环的骨架,字段名按你本地版本对齐,值可以直接抄。

3.1 config.toml 接入与压缩参数

# ~/.codex/config.toml model = "gpt-5-codex" model_provider = "taotoken" [model_providers.taotoken] name = "TaoToken" base_url = "https://taotoken.net/api" env_key = "TAOTOKEN_API_KEY" wire_api = "chat" # 自动压缩相关:核心是给压缩加冷却和次数上限 [auto_compaction] enabled = true # 触发阈值:上下文占用超过该比例才考虑压缩 trigger_ratio = 0.85 # 压缩后必须真正缩短,否则标记失败不重试 require_shrink = true # 冷却期(秒):刚压过这段时间内不再触发 cooldown_sec = 90 # 单位时间窗口内最多压缩次数 max_per_window = 3 window_sec = 300 # 额度熔断:窗口内消耗超过该比例即暂停自动压缩 quota_burn_alert = 0.10 quota_window_sec = 3600

关键就三行:require_shrink = true保证压缩必须真缩短,cooldown_sec给冷却期,max_per_window加频率护栏。这三条一起上,条件误判也压不了几次。

3.2 settings.json 应用行为与急停

{ "codex.autoCompaction": { "enabled": true, "cooldownSec": 90, "maxPerWindow": 3, "windowSec": 300, "requireShrink": true, "quotaCircuitBreaker": { "enabled": true, "burnRateAlert": 0.10, "windowSec": 3600, "action": "pause" }, "manualKillSwitch": true, "logCompaction": true }, "codex.telemetry": { "logCompactionBeforeAfter": true, "logChargePerCompaction": true } }

manualKillSwitch打开后,界面上会有一个立即中断自动压缩的入口,循环起来能一键停。logCompactionBeforeAfter和logChargePerCompaction是排查的关键——没有这两个日志,你根本不知道每次压缩前后长度变了多少、扣了多少额度。

3.3 环境变量

export TAOTOKEN_API_KEY="sk-你的key"

配完先别急着开长会话,下一节先验证单次请求通不通。

4. 验证请求与循环是否终止

配置改完,分两步验证:先确认接入正常,再确认压缩循环收敛。

4.1 验证接入

curl -s https://taotoken.net/api/chat/completions \ -H "Authorization: Bearer $TAOTOKEN_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-5-codex", "messages": [{"role": "user", "content": "ping"}], "max_tokens": 16 }'

返回里有正常的choices就说明接入没问题。如果这里就报错,先去看 API Keys 和接入文档,别往下走。

4.2 复现并验证循环终止

用一个「压缩后长度不降」的坏例子先复现,再用护栏验证它被拦住:

import time class LoopGuard: def __init__(self, max_per_window=3, window_sec=300): self.max_per_window = max_per_window self.window_sec = window_sec self.timestamps = [] def allow(self): now = time.time() self.timestamps = [t for t in self.timestamps if now - t < self.window_sec] if len(self.timestamps) >= self.max_per_window: return False self.timestamps.append(now) return True def needs_compact(ctx_len, threshold, recently_compacted): if recently_compacted: return False return ctx_len > threshold def compact_bad(ctx_len): # 坏实现:压缩后反而变长,条件永不收敛 return ctx_len + 10 def compact_good(ctx_len): # 好实现:真正缩短 return int(ctx_len * 0.3) if __name__ == "__main__": guard = LoopGuard(max_per_window=3, window_sec=300) length = 1000 threshold = 800 recently = False count = 0 while needs_compact(length, threshold, recently): if not guard.allow(): print(f"第 {count+1} 次被护栏拦截,循环终止") break length = compact_good(length) recently = True count += 1 print(f"第 {count} 次压缩后长度: {length}") print("最终长度:", length, "压缩次数:", count)

跑一遍你会看到:压缩一次后长度降到 300,recently置真,条件不再满足,循环立刻退出。把compact_good换成compact_bad,护栏会在第 4 次拦截,循环同样终止——这就是护栏的价值:条件误判也拦得住。

4.3 看日志确认

打开logCompactionBeforeAfter后,日志里每次压缩会打印「压缩前长度 / 压缩后长度 / 是否扣费」。正常收敛的日志长这样:

[compact] before=9800 after=2940 shrunk=true charged=true [compact] cooldown active, skip

如果看到after >= before且charged=true反复出现,说明压缩没真缩短还在扣费,回到第 3 节把require_shrink打开。

5. 本篇常见错排查

5.1 改了配置但循环照旧

最常见的原因是配置没被加载。Codex App 有的版本读~/.codex/config.toml,有的读项目级.codex/config.toml,还有的读settings.json里的覆盖项。用 CC Switch 切换排查:

# 查看当前生效的配置来源 codex config show --effective # 用 CC Switch 切到干净配置,逐个加回 cc-switch list cc-switch use default

切到 default 后如果循环消失,说明是你自定义配置里的某项触发的,逐个加回auto_compaction字段定位。

5.2 压缩后长度没降反升

摘要模型可能把内容压得比原文还啰嗦,或者压缩后立刻又被新内容填回。对策是require_shrink = true,压缩没真缩短就标记失败、本次不计费、不立即重试。

5.3 额度熔断没触发

检查quota_burn_alert和quota_window_sec是否配对。如果窗口设得太长(比如 86400),消耗速率被摊薄,永远到不了阈值。建议 3600 秒窗口配 0.10 阈值,一小时烧超 10% 就暂停。

5.4 失败仍计费

压缩抛错但没标记「已尝试」,下次又触发,错误循环也烧额度。确保失败路径返回charged: false并打上已尝试标记。

5.5 手动急停找不到入口

manualKillSwitch没开,或者版本不支持。先升级到支持该字段的版本,再在设置里确认开关状态。

6. 把护栏配好,让自动压缩不再烧额度

回到这次故障本身:根因是压缩的终止条件在压缩后依然成立,甚至被压缩本身破坏,形成正反馈死循环,而每次循环都计费。修复思路就四条——压缩必须真正缩短上下文并加冷却标记,让循环收敛;加次数和频率护栏,条件误判也拦得住;加额度熔断,消耗过快就暂停自动操作;失败不计费,错误循环不重复烧。

我踩过的坑是只加了冷却期没加频率护栏,结果冷却一过又连压好几次,额度还是掉。后来把max_per_window和quota_burn_alert一起配上,才彻底稳住。如果你在长会话或 Agent 场景里跑 Codex,建议现在就把第 3 节的两份配置抄进去,再按第 4 节跑一遍验证。额度消耗曲线可以在控制台看,接入和 Key 的问题走 API Keys 和接入文档,长期编码任务用 Coding Plan 更省心。把收敛性当成自动化的前提、把熔断当成计费的底线,auto-compaction 就不会再变成额度蒸发机。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询