Skip to content

rate-limit-proxy Phase 2:依真實 budget 主動排程(delay / 佇列 / block) #7

Description

@kiki830621

Priority: P3(backlog — 探索性,非本週;先讓 v1.7.0 的 Phase 1 觀測資料累積後再評估)

Problem

rate-limit-proxy.py(v1.5.0 交付)目前是 Phase 1 純觀測:透明轉發 + 擷取真實 anthropic-ratelimit-* header、token usage、請求 model 寫進 rate-state.jsonl,但不做任何主動干預。所有 guard 仍是風險降低(pacing-guard 的 deny/sleep 只看 launch 次數 heuristic、nudge 只提醒)——沒有任何機制依真實 budget 在請求送出前主動 delay / 佇列 / 擋下。

本 issue 是 #1(Phase 1,已交付 v1.5.0)明確 defer 的 Phase 2:把 proxy 從被動觀測升級成主動排程,讓「保證不撞牆」從 heuristic 逼近真正可達成。

Type

feature(大型、探索性)。很可能是 Spectra 級——新增主動排程是對外行為改變、有死鎖/公平性/正確性的長期維護意涵,diagnose 階段應據此評估 Complexity,並可能拆成多個子問題。

Expected

proxy 在轉發請求「之前」讀最近一筆同桶的 rate-state.jsonl 快照,依真實 remaining/reset 做主動排程,理想能力(diagnose 階段再定案優先序):

  1. Budget-aware delay:某桶 remaining 逼近 0 且 reset 還有 N 秒 → 主動延遲送出到 budget 回填,而非讓請求撞 429 再由 Claude Code retry。
  2. 佇列/序列化:同桶並發請求(尤其一個 Workflow 內部 fan-out 的數十個 subagent)排隊而非同時打,攤平 burst。
  3. Hard block(最後手段):budget 真的乾了 → 擋下並回一個明確錯誤/等待,而非讓它撞牆。

Actual(現狀 — Phase 1 邊界)

Impact / 待 diagnose 釐清的關鍵風險

  • 死鎖:proxy 若在請求路徑上同步等待 budget 回填,可能卡住整個 Claude Code 主迴圈(proxy 是所有 API 流量的必經點)。需要 timeout / 逃生閥設計。
  • 不公平排程:多個並發 session 共用同一 account bucket(使用者本機常 3-4 並發),proxy 如何在它們之間公平分配 budget?先到先服務 vs. 加權?
  • 正確性 vs. fail-open:Phase 1 的鐵律是 fail-open(proxy 掛掉不影響轉發)。Phase 2 主動 delay/block 與 fail-open 天生張力——排程邏輯出錯不能把使用者鎖死。預設應可一鍵退回純觀測(如 env flag RATE_LIMIT_PROXY_SCHEDULE=0)。
  • 前提驗證(gating precondition):Phase 1 defer Phase 2 的理由是「先靠 Phase 1 資料驗證『真實 header 可見度』本身有沒有用」。diagnose 第一步應先看 v1.7.0 累積的 rate-state.jsonl——真實 header 是否夠即時/準確到足以支撐主動排程決策;若 header 落後現實太多,Phase 2 的前提就不成立。
  • 範疇:只涵蓋「經過 proxy 的流量」——claude.ai 網頁版等其他管道仍在視野外,Phase 2 一樣不保證帳號級絕對不撞牆(誠實邊界不變)。

References

Current Status

Phase: needs-fix(v1 hold 條件經 31 天 live 證據證偽;disposition 待定)
Last updated: 2026-08-17 by idd-comment → idd-update

Key Decisions

  • 2026-08-17:v1 rejected-aware hold 已證偽。31 天 / 410,716 筆 record,sched_held_ms > 0 為 0 筆;同期 1,194 筆 rejected 快照(68 個窗)全部落在 90s cap 之外(reset − now 最小值 3.9 min)。非樣本不足,是結構上不可達。
  • 2026-08-17:根因為「兩個時鐘」—— rl_unified_reset 是 5h/7d 配額窗邊界、不是 retry-after;rejected 窗靠 utilization 衰退解除,不靠 reset 到達。窗長的原始經驗判斷(1–10 min)是對的,錯的是 hold 綁的欄位。
  • 2026-08-17:retry-after 從未被 proxy 擷取,且尚未證實 Anthropic 429 會回傳它 —— 不得當成已知替代方案。
  • 2026-07-16:live gating 啟用(daemon 帶 RATE_LIMIT_PROXY_SCHEDULE=1 + settings.json env 持久化)。
  • 2026-07-12:re-diagnosis —— 舊 blocker 全解除、gating precondition PASS 帶範疇轉折(帳號級訊號非桶級);Complexity=Spectra。spectra change rejected-aware-admission-hold 已 apply 並歸檔。

Scope Changes

  • v1 的「有界 hold」機制不再是可行的交付路徑;resolve_sched_hold_cap() 的 240s 設計上限與實測 reset − now 分佈(p5 = 42.8 min)差距數量級,調參無法補救。

Blocking

Commits

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions