【Bug已解决】Codex App entered an infinite auto-compaction loop and consumed ~30% of my usage limit 解决方案
【Bug已解决】Codex App entered an infinite auto-compaction loop and consumed ~30% of my usage limit 解决方案原始报错线索Codex App entered an infinite auto-compaction loop and consumed ~30% of my usage limit应用陷入「无限自动压缩」循环反复触发压缩短时间内耗掉了约 30% 的用量额度。一、现象长什么样应用有个「自动压缩」功能本意是「上下文快满时压缩一次」。结果压缩被反复触发几秒内十几二十次每次压缩都消耗用量额度调用模型做摘要短时间耗尽约 30% 月度额度手动停止前根本停不下来查日志发现压缩后「是否还需要压缩」的判断仍为 True于是又压永动机。 根因是压缩的「终止条件」在压缩后依然成立甚至被压缩本身破坏形成正反馈死循环且每次循环都计费。二、背景自动化循环的「收敛性」任何自动触发操作都必须满足执行后触发条件应趋向「不再满足」。否则就是失控循环。常见触发条件上下文长度 阈值 → 压缩文件未保存 → 保存未同步 → 同步。 若操作本身不改变或不充分改变触发条件循环就停不下来。更糟的是若每次操作都计费/耗资源就会变成「烧钱永动机」。三、为什么无限循环根因3.1 终止条件被操作本身破坏压缩后上下文没真正变短可能摘要比原文还长或又立刻追加新内容长度仍超阈值 → 再压。3.2 缺少循环次数 / 频率上限没有「单位时间最多压 N 次」「连续压 M 次就停」的护栏。3.3 没有额度熔断循环消耗额度时没有「额度消耗过快就暂停」的熔断机制。3.4 压缩失败仍计入触发压缩抛错但没标记「已尝试」下次又触发错误循环也烧额度。四、最小可运行复现终止条件永不假下面演示「操作后条件仍为真」如何成死循环def needs_compact_bad(ctx_len, threshold): return ctx_len threshold def compact_bad(ctx_len): # 错误压缩后长度没降摘要反而更啰嗦或立即又被填回 return ctx_len 10 # 越压越长 if __name__ __main__: length 1000 threshold 800 count 0 while needs_compact_bad(length, threshold): length compact_bad(length) # 越压越长 count 1 if count 100: print(失控循环! 已压, count, 次) break print(最终长度:, length)compact_bad让长度不降反升循环永不终止——正是烧额度死循环的核心。五、解决方案一确保操作使条件收敛压缩必须真正缩短上下文且压缩后标记「已压缩」避免立即再触发def compact(ctx, threshold): 返回压缩后的上下文与『是否真缩短了』。 new_ctx summarize(ctx) # 真实摘要应显著变短 shortened len(new_ctx) len(ctx) return new_ctx, shortened def needs_compact(ctx_len, threshold, recently_compacted): if recently_compacted: return False # 刚压过给冷却期 return ctx_len threshold if __name__ __main__: # 正确压缩后显著变短 冷却标记循环收敛 length 1000 recently False for _ in range(5): if needs_compact(length, 800, recently): length int(length * 0.3) # 真正缩短 recently True else: break print(收敛后长度:, length, recently:, recently)压缩真正缩短 冷却标记循环收敛解决 3.1。六、解决方案二循环护栏次数 频率上限无论条件如何强制限制「单位时间压缩次数」import time class LoopGuard: def __init__(self, max_per_window3, window_sec60): self.max_per_window max_per_window self.window_sec window_sec self.timestamps [] def allow(self) - bool: 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 if __name__ __main__: guard LoopGuard(max_per_window3, window_sec60) for i in range(10): if guard.allow(): print(f第 {i1} 次压缩执行) else: print(f第 {i1} 次被护栏拦截频率超限) break护栏保证即使条件误判为真60 秒内也最多压 3 次杜绝烧额度解决 3.2。七、解决方案三额度熔断消耗过快即暂停压缩消耗额度需监控「额度消耗速率」过快则熔断暂停class QuotaCircuitBreaker: def __init__(self, limit, window_sec3600, burn_rate_alert0.1): self.limit limit self.used 0 self.window_sec window_sec self.burn_rate_alert burn_rate_alert # 1 小时内耗超 10% 即告警 self.tripped False def charge(self, amount): if self.tripped: raise RuntimeError(熔断已触发暂停自动压缩) self.used amount if self.used / self.limit self.burn_rate_alert: self.tripped True print(f[熔断] 额度消耗过快{self.used/self.limit:.0%}暂停自动压缩) if __name__ __main__: cb QuotaCircuitBreaker(limit100, window_sec3600, burn_rate_alert0.1) for i in range(20): try: cb.charge(3) # 每次压缩扣 3 额度 except RuntimeError as e: print(停止:, e) # 约 30 额度30%时熔断 break额度消耗达 30% 阈值即熔断自动压缩暂停不再烧钱解决 3.3呼应第 97/110 篇。八、解决方案四失败标记 不重复计费压缩失败要标记「已尝试」避免错误循环重复计费且失败不计为成功消耗def try_compact_with_meter(ctx, meter): try: new_ctx, ok compact(ctx, 800) if not ok: # 没真正缩短标记失败本次不计费且不立即重试 return ctx, {charged: False, reason: 未缩短} meter.charge(1) # 仅在真正压缩成功时计费 return new_ctx, {charged: True} except Exception: # 失败不计费、标记已尝试防错误循环烧额度 return ctx, {charged: False, reason: 异常} if __name__ __main__: meter QuotaCircuitBreaker(limit100) ctx x * 1000 for _ in range(5): ctx, info try_compact_with_meter(ctx, meter) print(info)失败不重复计费 成功才扣额度错误循环也不再烧钱。九、排查清单「自动压缩无限循环烧额度」按下面排查压缩是否真收敛压缩后长度是否真正下降第五节有无冷却/连续次数上限第六节护栏额度有无熔断消耗过快是否暂停第七节呼应第97/110篇频率护栏单位时间最多几次第六节失败是否计费错误循环是否重复扣额度日志是否记录每次压缩的『压缩前后长度 扣费』是否有手动急停用户能否立即中断循环终止条件是否被操作破坏第五节。十、小结「应用进入无限自动压缩循环耗尽额度」的根因是压缩终止条件在压缩后依然成立甚至被压缩破坏形成正反馈死循环且每次循环都计费。通用修复收敛性压缩必须真正缩短上下文 冷却标记循环必须收敛第五节频率护栏次数/时间窗口上限条件误判也拦得住第六节额度熔断消耗达阈值即暂停自动操作不再烧钱第七节呼应第 97/110 篇失败不计费仅成功压缩扣额度错误循环不重复烧。 一句话任何「自动触发 消耗资源」的操作都必须满足「执行后条件收敛」并有次数护栏与额度熔断两道保险。把收敛性当成自动化的前提、把熔断当成计费的底线自动压缩就再也不会变成「30% 额度蒸发机」——这与第 97 篇配额重置、第 110 篇幂等记账、第 120 篇配额恢复共同体现「自动操作必须可控、可熔断、可计量」。