ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

【Bug已解决】Codex App 陷入 auto-compaction 无限循环并消耗约 30% 用量额度的排查与修复

【Bug已解决】Codex App 陷入 auto-compaction 无限循环并消耗约 30% 用量额度的排查与修复 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_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentAPI 基地址https://taotoken.net/api这个不加 UTM直接填进配置几个常用 deep link按需取用模型对话验证模型是否正常响应https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_contentmodel_chatutm_campaignrewriteCoding Plan长期编码 / Agent 场景https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding_planutm_campaignrewrite控制台看额度消耗曲线https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewriteAPI Keys 管理https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi_keysutm_campaignrewrite接入文档https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewriteClaudeCodeAnthropic 接入说明https://taotoken.net/claudecode-anthropic?utm_sourcetaotoken_aicg_blog_endutm_contentclaudecodeutm_campaignrewrite注意先把 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_KEYsk-你的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_window3, window_sec300): 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_window3, window_sec300) length 1000 threshold 800 recently False count 0 while needs_compact(length, threshold, recently): if not guard.allow(): print(f第 {count1} 次被护栏拦截循环终止) break length compact_good(length) recently True count 1 print(f第 {count} 次压缩后长度: {length}) print(最终长度:, length, 压缩次数:, count)跑一遍你会看到压缩一次后长度降到 300recently置真条件不再满足循环立刻退出。把compact_good换成compact_bad护栏会在第 4 次拦截循环同样终止——这就是护栏的价值条件误判也拦得住。4.3 看日志确认打开logCompactionBeforeAfter后日志里每次压缩会打印「压缩前长度 / 压缩后长度 / 是否扣费」。正常收敛的日志长这样[compact] before9800 after2940 shrunktrue chargedtrue [compact] cooldown active, skip如果看到after before且chargedtrue反复出现说明压缩没真缩短还在扣费回到第 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 就不会再变成额度蒸发机。
返回列表