Files
litellm-config/docs/volc-quota-notes.md
T
vibe-code 4e98c14920 perf: 已软踢模型短路跳过 token_counter 预估
- _soft_evicted_block: 扫描 router 中 {model}-limit-reached 条目返回
  (usage, limit), 供 async_pre_call_hook 直接 429
- 省去注定被拒请求的 token_counter 调用(含多模态图片 URL 下载等昂贵路径)
- 与 _check_request_block 的 limit-reached 恒 429 双保险
- stub 验证: 已踢/未踢/不存在三场景符合预期
2026-08-13 09:04:30 +00:00

26 KiB
Raw Blame History

火山免费额度机制速查(VolcEngine Quota Notes)

日期:2026-08-12 ~ 2026-08-13 用途:后续限额/防扣费设计、排障、消耗任务的事实基础,避免重复推导 来源:

  • merge-auto-consume.md §1(2026-08-11 用户确认的业务细则)
  • 2026-08-12 ~ 13 对话澄清 + 生产日志观察(deepseek-v4-flash-ga-260731)
  • 2026-08-13 网页口径校准(真实初始余额 B0=2,242,178;API 结算滞后 Δ≈14 分钟)

代码现状:v6 = Snapshot-Offset(limit = balance_at_sync + usage_at_sync − SMALL_BUFFER),2026-08-12 实施。 v2/v5 (usage + balance − BUFFER) 已废弃,见 §7.4/§7.5。 v6 reserve 改进(token_counter + max_tokens)2026-08-13 实施,见 §7.6。 ⚠️ v6 的「B0 常数/永远不扣费」论证有前提缺陷,见 §4.2 与 §7.5 修正。


1. 业务模型(用户确认)

1.1 日返还循环

每天任意时刻    真实用户 + 消耗任务 的消耗 = 当日采集量(火山实时统计,每模型单独计算)
每天 23:20 CST  消耗任务补齐缺口(可选)
次日 00:00 起   火山按昨日采集量发放资源包(不定时,可能延迟到上午)

1.2 硬规则

规则 值 影响
采集上限 2M token/模型/天 消耗超过 2M 的部分不返还(白烧);所有模型统一
发放额度 = 昨日采集量(≤2M) 消耗 X → 次日返还 X,用多少补多少,延迟一天到账
发放时间 不定时 次日 0:00 至发放时点存在余额空窗
资源包有效期 30 天 过期额度实打实消失,失去"启动资金"
消耗顺序 FIFO(先消耗获得时间早的包) 队列头部 = 最早获得 = 最早过期
余额耗尽 请求不失败,按真实价格扣费 全部防护的终极目标:避免扣费
首次开通 每模型赠送 2M 启动资金的种子(无对应前日采集的免费午餐)

1.3 账目守恒

余额_{N+1} = 余额_N − 消耗_N + 返还_{N+1}
           = 余额_N − 消耗_N + 采集_N
           = 余额_N                    (消耗即采集,≤2M 内全额返还)

推论:

  • 使用不损失额度(用多少补多少,仅延迟一天)
  • 过期才损失额度(额度蒸发 = 启动资金永久减少)
  • 消耗任务的价值 = 防止"启动资金"被 30 天过期侵蚀
  • 真实用量 ≥ 66.7k/天(= 2M/30 天)时,赠送包被自然消耗;低于此值必须靠消耗任务在最后 9 天窗口洗掉

2. 扣费判定(用户确认口径,最重要)

用户原话:是否扣费只取决于当下有多少免费额度。超出免费额度的请求,都会被扣费。采集量只决定明日发放,与今日是否扣费无关。

扣费判定:消耗 ≤ 当下余额(免费额度) → 免费,余额扣减
         消耗 > 当下余额              → 超出部分按真实价格扣费(无返还、不退)
今日采集量(≤2M) → 只决定明日返还的包大小,与今日是否扣费无关

注:2026-08-12 §5 曾基于滞后快照推断"当日超余额 137,828 tokens 已扣费", 后经 §4.2 网页口径校准推翻(滞后快照高估真实剩余约 50 万,实际无扣费)。 判扣费必须用网页真实初始余额 B0 或滞后收敛时点的 U+B 反推,不能用任意时点快照。


3. 关键概念区分(反复踩坑处)

概念 含义 量级特征 与扣费的关系
采集量(今日) 今日实时消耗统计(火山网页"今日采集量") 单日流量,0~2M 只决定明日返还
余额(免费额度) 资源包 AvailableAmount 存量之和 累计存量,守恒 ≈2M(种子) 扣费唯一依据
采集配额 2M 今日最大采集量 每模型固定 超了白烧(不返还),未必当场扣费
返还(T+1) 昨日采集量 → 新资源包 单日流量延迟一天 补回余额,不抵今日扣费

核心:余额是累计存量(守恒),采集量是单日流量。限额设计必须用存量(余额), 绝不能用单日流量(昨日采集量)当上限——那会把可用性锁死在水位以下(见 §7.2 v3)。


4. 余额 API 的滞后问题

4.1 2026-08-12 观测

  • 非实时,延迟未知(用户明确)。实测:2026-08-12 13:36:01 同步快照 Bal=1,558,307, 此后 usage 从 1,241,821 涨到 1,696,135(13:36→13:38)期间 Bal 纹丝不动。
  • 观察结论:AvailableAmount 不随今日消耗实时递减(至少滞后 20 万级)。

4.2 2026-08-13 网页口径校准(Δ≈14 分钟)

  • 网页确认今日真实初始余额 B0 = 2,242,178(采集量 0 时点读数,可信;次日 0:00 前无新包入账)
  • 11:08 同步:Bal = 2,242,063 ≈ B0(API 尚未扣减当日用量,差 115 为残差)
  • 11:22 同步:Bal = 1,726,526 = B0 − 515,652(= 11:08 时点累计用量,精确吻合)
  • 结论:结算滞后 Δ ≈ 14 分钟,即 Bal(t) ≈ B0 − Usage(t−Δ);滞后是周期收敛的 (2026-08-12 14:59:51 曾收敛:B=596,869 = 2,293,004 − 1,696,135 分毫不差)
  • 滞后量(未结算消耗)随消费速度增长:实测 149,616(14 分钟突发消费),全天平均口径仅 ~1 万

核心修正:balance_at_sync + usage_at_sync ≠ B0 常数

设 Δ_settled = 同步时点 API 已结算的今日消耗(= B0 − Bal_sync)
   L        = 滞后窗口内未结算消耗 = Usage_at_sync − Δ_settled ≥ 0

limit(v6) = Bal_sync + Usage_at_sync − SMALL_BUFFER
          = B0 + L − SMALL_BUFFER
  • 滞后收敛时(L=0):反推值 = B0 ✓(08-12 14:59:51 场景)
  • 滞后未收敛时(L>0):反推值 = B0 + L,虚高随消费增长(08-13 11:22:2,391,794 vs 真实 2,242,178,虚高 149,616)
  • 「守恒池」只在滞后收敛时成立;日常撞墙/恢复判定用的全是未收敛快照,故 v6 限额常态虚高 L

5. 症状对照:Today > BalAtSync

以下日志为 v2/v5 时代历史示例(字段 Rem/StableLimit 已不存在于 v6 代码), 保留用于说明「Today > 余额」这一常见误判。

历史日志示例(13:36-13:38,deepseek-v4-flash-ga-260731):

DEBUG: Checking deepseek-v4-flash-ga-260731 (Bal: 1,558,307, Rem: 1,258,307) (余额/剩余)
📊 [Usage] deepseek-v4-flash-ga-260731:ba0383d3: +38083 (Today: 1,279,904 | Rem: 1,558,307 | Limit: 2,000,000)

v6 当前日志格式:

📊 [Usage] deepseek-v4-flash-ga-260731:ba0383d3: +98 (Today: 1,696,233 | BalAtSync: 596,869 | Limit: 2,000,000)

字段口径(v6):

字段 含义 计算
Bal 火山 API 余额(ListResourcePackages AvailableAmount 之和) 同步快照
BalAtSync 同步时写入 Redis 的余额快照 = Bal
UsageAtSync 同步时写入 Redis 的累计消耗 Redis daily_usage 求和
Today 本地今日采集量 Redis daily_usage 逐请求累加
Limit 生效限额 min(2M, balance_at_sync + usage_at_sync − SMALL_BUFFER)

Today > BalAtSync 的原因: Today 是单日流量,BalAtSync 是存量余额快照,两套口径本就不该比较。 火山网页「今日采集量 1,696,135/2,000,000」对应 Today 与 2M 配额,与余额无关。

扣费结论(2026-08-13 修正): 判扣费必须用网页真实初始余额 B0 或滞后收敛时点的 U+B 反推。 13:36 滞后快照(B=1,558,307)高估真实剩余约 50 万,不能用于判扣费。 14:59:51 恰好收敛(B0=2,293,004 可信),今日消耗 1,696,135 < B0 → 当日无扣费。


6. Redis key 与日志口径(速查)

6.1 Redis key

key 读写方 说明
daily_usage:{北京日期}:{model}:{hash} LiteLLM 成功回调 INCR + 消耗任务 INCR 当日采集量,TTL 48h,北京 00:00 滚动
limit:dynamic:volcengine:balance_at_sync:{model} 守护线程写 同步时点 API AvailableAmount 快照
limit:dynamic:volcengine:usage_at_sync:{model} 守护线程写 同步时点 Redis 累计消耗快照
consume:last_run:{日期} 消耗任务 23:20 当日去重,TTL 48h

6.2 关键日志

  • 🚫 [PRE-BLOCK] {model} daily limit reached ({usage}+{reserve}/{limit}):请求前闸门 429
  • 📊 [Usage] {model}:{hash}: +{tokens} (Today: X | BalAtSync: Y | Limit: Z):成功记账
  • 🚫 [BLOCK] {model} Limit Hit: {current}/{limit}:后置闸门触发,soft_evict

7. 限额方案演进

7.1 v2(原式,已被 v5/v6 继承和替代):stable_limit = usage + balance − BUFFER

  • 意图:usage + balance = 当日初始免费池(守恒 B0),消耗中恒定 → 限额恒定
  • 数学正确性:B 滞后收敛后确实满足 B = B0 − U (14:59:51:U=1,696,135 + B=596,869 = B0=2,293,004,与理论剩余 597,004 几乎一致)。 同步时 stable = B0 − BUFFER,usage 累计撞线 = 用到剩 BUFFER 停止 —— 语义正确
  • 残留风险(非公式错误,是快照时点问题):同步时 B 若滞后(13:36 高估真实剩余 约 50 万)→ stable 虚高 → 多放行滞后窗口内消耗。由 BUFFER + 低余额加密同步 (600s/300s) + burst 触发覆盖;收敛后池回到守恒值
  • 已废弃:BUFFER 盲猜无法解决高强度击穿/低强度浪费(§7.5)

7.2 v3(尝试后否决,已回滚):昨日锚 effective = min(2M, 昨日采集量 × 系数)

  • 思路:把「返还 = 昨日采集量」当作「今日可采上限」
  • 否决理由(用户反例):余额 2M、昨日用量 10K → 今日最多 10K?荒谬。 错误在于把单日流量(昨日采集量)当存量(余额) 用;昨日少用不损余额(守恒), 今日可用 = min(2M, 余额) ≈ 2M,与昨日无关
  • 附:系数 <1.0 还会引发「今日少采 → 明日返还更少」的日复一日递减陷阱(0.9 系数 5 日降至 66%)

7.3 v4(2026-08-12 实施后当天发现错误,已回退):冻结快照

stable_limit = max(0, balance − BUFFER)        # ❌ 错误:把「剩余余额」当「从 0 累计的上限」
  • 设计初衷:余额 API 滞后 → 冻结快照防池膨胀
  • 错误实证(15:09:51 生产日志):Bal=596,869(真实剩余 59.7 万), stable = 296,869,usage=1,696,135 ≥ 296,869 → [PRE-BLOCK] … (1,696,135+16/296,869) 误拦。 账上有真实免费额度却全拦 —— 因为 stable 应是「今日累计上限」(= B0 − BUFFER), 而 balance 是「剩余」(= B0 − U);U>0 时两者相差 U,直接替换导致低估、误拦
  • 仅在同步时点 U=0(日初/刚返还)巧合正确
  • 教训:「剩余额度」不能当「累计限额」用;两者通过 usage + balance 桥接

7.4 v5(2026-08-12,已废弃):恢复 v2 骨架

stable_limit = max(0, usage + balance − BUFFER)
  • 与 v2(§7.1)公式相同,区别在论证与认识:v2 数学正确(B 收敛后 = B0 − U), 不需要冻结快照;v4 的「剩余当上限」误拦由「usage + balance 桥接」天然避免
  • 实测验证(14:59:51 同步 + 15:09:51 生产日志):U=1,696,135、B=596,869 → stable=1,993,004 → 今日剩余可用 296,869(账上 59.7 万用到剩 30 万 BUFFER) ✓
  • 防扣费语义不变:usage 撞 stable = 剩余恰为 BUFFER 时停,扣费结构性杜绝; balance < BUFFER(U=0)→ stable=0 → 全拦
  • 已废弃原因:BUFFER 盲猜滞后窗口消耗,高强度击穿低强度浪费。被 §7.5 v6 Snapshot-Offset 替代。

7.5 v6(Snapshot-Offset,当前方案)

limit = balance_at_sync + usage_at_sync − SMALL_BUFFER(5 万)
     = B0 + L − SMALL_BUFFER   # L = 同步时点滞后窗口未结算消耗(§4.2)

⚠️ 2026-08-13 修正:原论证称 = B0 − SMALL_BUFFER(B0 常数、精确抵消),前提「同步时点 balance + usage = B0」在滞后未收敛时不成立——实际 = B0 + L(§4.2 实测 L=149,616)。

核心思路

用同步时点快照 balance_at_sync + usage_at_sync 计算累计限额:滞后收敛时(L=0)恰为 当日初始免费池 B0;滞后未收敛时虚高 L。两次同步间快照不变,实时 current_usage 撞线 只抵消同步之后窗口内的增量消耗,不抵消同步时点已存在的滞后 L。

limit 是累计限额(从 0 到上限),不是剩余余额。_check_request_block 用 current_usage + reserve >= limit 撞线。

与旧方案的核心区别

# 旧 v5: 盲猜滞后窗口消耗
limit = usage + balance - BUFFER(30 万)

# 新 v6: 快照 + 实时 usage 撞固定线
limit = balance_at_sync + usage_at_sync - SMALL_BUFFER(5 万) = B0 + L - SMALL_BUFFER
  • v5 的 BUFFER 猜的是「两次同步之间 + 滞后窗口内的消耗」——猜不准,高强度击穿低强度浪费
  • v6 的 SMALL_BUFFER(5 万)只覆盖非消耗性余额跳降(资源包过期) + 少量未结算残差, 不覆盖滞后窗口消耗 L。L > 5 万时限额虚高进入扣费区(08-13 实测 L=149,616 > 5 万)。

不变量(带 L 的精确形式)

撞线 usage + reserve >= B0 + L - SMALL_BUFFER 时:usage = B0 + L − SMALL_BUFFER − reserve, 真实剩余 = B0 − usage = SMALL_BUFFER + reserve − L。

  • L=0(滞后收敛):剩余 = SMALL_BUFFER + reserve > 0 → 永远不扣费 ✓(原论证仅此情形成立)
  • L > SMALL_BUFFER + reserve:剩余 < 0 → 已扣费(超扣下界,取决于 reserve;保守写 L > SMALL_BUFFER)
  • 今日为何安全(08-13):真实 B0=2,242,178 > 静态 2M → min(2M, B0+L−S) 被静态 2M 压住, 撞线发生在 usage=2M,真实剩余 242,178 > 0——是静态墙兜底,不是 v6 不变量。 若某日 B0 < 2M(包变小),静态墙失效,L > SMALL_BUFFER 即可能超扣。

执行流程

同步线程(每 5~30min / 突发触发):

  1. 拉 API → 取到 balance
  2. 读 Redis 取 current_usage
  3. 写入 balance_at_sync = balance, usage_at_sync = current_usage (TTL 24h)
  4. 不再写入 remaining/stable_limit 旧键

请求闸门:

  1. 读 balance_at_sync + usage_at_sync
  2. limit = balance_at_sync + usage_at_sync - SMALL_BUFFER
  3. usage + reserve >= limit → 429

与旧方案对比

场景 旧 v5(BUFFER 30 万) 新 v6(Snapshot-Offset)
高强度消耗 滞后窗口内消耗 > 30 万 → 击穿扣费 撞 B0+L 线:L ≤ 5 万安全,L > 5 万虚高
低强度消耗 浪费 30 万额度(实际只用了 5 万) 只留 5 万 SMALL_BUFFER
新包到账 下次同步 stable limit 跳升 下次同步 B0 跳升,一致
包过期 余额跳降,limit 偏高至下次同步 同左,但 SMALL_BUFFER=5 万风险窗口小

遗留风险

  1. 同步时点滞后 L 无法抵消(当前首要风险):API 结算滞后 Δ≈14 分钟, L = 窗口内消耗,随消费速度增长(实测 149,616 > SMALL_BUFFER 5 万)。v6 限额常态虚高 L, 仅被静态 2M 墙 + min() 兜住(今日 B0=2,242,178 > 2M)。若 B0 < 2M 或 L 放大,有 放行进扣费区风险。缓解方向:SMALL_BUFFER 与最近消费联动 / 缩短低余额同步 + 突发检测 / 直接以网页口径 B0 校验限额。
  2. 多包同时过期 > SMALL_BUFFER:概率低,无法绝对防御。可调大 SMALL_BUFFER 或监控包过期时间。

7.6 2M 采集上限击穿事件与 reserve 改进

事件回顾(2026-08-12 20:53)

20:38:26  Today: 1,696,233  (delta +98)
20:53:13  Today: 1,872,804  (delta +176,571) ← 单请求消耗 176k
20:53:18  突发触发 (+176,669 > 100,000)
20:53:35  同步完成: Usage=2,050,826 ← 已超 2M!
23:18:59  下次同步: Bal=242,178 (API 滞后修正,B0 回归 2,293,004)
火山网页: 今日采集量 2,000,000/2,000,000 ← 2M 撑满

2M 采集上限确实撞了,usage 超出 50,826 token。但 B0=2,293,004 > usage,未扣费。

根因:reserve 估算不足 + 并发击穿

旧 reserve 逻辑:

reserve = data.get("max_tokens") or PREBLOCK_RESERVE_TOKENS(50000)
# 闸门: usage + reserve >= effective_limit → 429

deepseek-v4-flash 单请求实际消耗 176,571 token(远超 reserve 50k):

请求放行时: usage=1,800,000, reserve=50,000 → 1,850,000 < 2M → PASS
请求完成后: usage=1,976,571 (单请求吃了 176k)
下一个请求: usage=1,976,571, reserve=50,000 → 2,026,571 >= 2M → BLOCK
# 但此时已经超了 2M

并发请求放大问题:多个请求同时通过闸门(都读到 usage < 2M),各自实际消耗叠加, 放行时 usage 相加不超限,完成后集体超限。

B0 守恒假设的再次验证

20:33  B0 = 596,869 + 1,696,135 = 2,293,004
20:53  B0 = 596,771 + 2,050,826 = 2,647,597 ← 虚高(API 未结算 17.6 万消耗)
23:18  B0 = 242,178 + 2,050,826 = 2,293,004 ← API 修正后回归初值

B0 守恒 在 API 滞后收敛后成立,但同步快照期间虚高(§4.2 的 L 量)。

reserve 改进:token_counter 精确估算输入

# 旧: 盲猜
reserve = data.get("max_tokens") or 50000

# 新: 精确输入 + 声明输出
input_tokens = litellm.token_counter(model=data["model"], messages=data["messages"])
# 未知模型 fallback cl100k_base,对中文偏高(更安全)
max_out = data.get("max_tokens") or PREBLOCK_RESERVE_TOKENS
reserve = input_tokens + max_out

为什么中文偏高更安全:DeepSeek 自有 tokenizer 对中文 ~1.5 token/字, cl100k_base ~2-3 token/字。估算偏高 = 预留更多 = 提前撞线 = 更保守。

效果:输入 200k 的请求,旧 reserve 只有 50k,新 reserve 250k+,在 usage=1.8M 时拦住 (而非放行后超 2M)。⚠️ 该阈值仅对 input ≥ 150k 成立(由 1.8M + input + 50k ≥ 2M 反推); 小输入请求(如 126k)仍会在 usage<2M 时放行,封口实际依赖后置 soft_evict(见下节)。

后置闸门兜底

# _record_usage 里,写完 usage 后:
if current_total >= effective_limit:
    soft_evict_from_router(raw_model, api_base)  # 改名踢出,后续全 429

前置闸门挡不住已放行的请求,但 _record_usage 完成记账后检查超限, 立即 soft_evict,后续请求全部 429。

双重防线总结

防线 机制 挡什么 挡不住什么
前置 reserve = token_counter(messages) + max_tokens 预估超限的请求 实际消耗 > reserve 的请求
后置 current_total >= effective_limit → soft_evict 超限后的后续请求 已发出正在执行的请求

遗留解决(2026-08-13 短文本实证):max_tokens 未声明时前置仍可能预估过低;更隐蔽的是 思考模型 —— deepseek-v4-flash(ga-260731) 默认思考模式,usage.total_tokens 含 reasoning tokens 且不受 max_tokens 限制(max_tokens 只约束正文 completion)。实测「彩虹有几种颜色」: reserve = 18输入 + 16声明 = 34,实际消耗 +167/+150(正文 ~16 + 思考 ~120-130)。 修复:输出侧取 max(声明 max_tokens, THINKING_RESERVE_FLOOR=4096) 兜底思考开销。 大输入请求仍偏保守(cl100k_base 中文 2-3 token/字高估),方向安全。

软踢预警线(2026-08-13)

后置 soft_evict 触发点从"撞有效墙"提前为两个预警线(volc_limit_sync.py should_soft_evict, env SOFT_EVICT_USAGE_LIMIT=1950000 / SOFT_EVICT_BALANCE_LIMIT=100000 可调):

预警线 触发条件 目的
用量墙 usage ≥ min(effective_limit, 1.95M) 避开贴 2M 边的灰色地带(大请求 429/小请求放行 + reserve 低估/并发击穿余量)
余额墙 balance_at_sync < 10万 且 usage > 0 免费额度见底提前摘除;usage>0 防小额初始池(B0<10万)模型误杀永久死锁

恢复对称(should_restore_model):usage < min(effective_limit, 1.95M) 且 (余额 ≥ 10万 或 usage == 0) 才恢复——否则踢了立即被下一轮同步恢复成踢/恢复抖动。 恢复函数读 balance_at_sync 与 usage 同源(快照口径)。restore_blocked_models / restore_model_if_healthy / startup_audit 同口径。

前置联动:_check_request_block 对已软踢条目(*-limit-reached)恒 429,防踢后 1.95M~2M 区间小请求放行 → 路由原名缺失 → 400 路由错误。 短路优化:async_pre_call_hook 在 token_counter 之前先查 _soft_evicted_block—— 已软踢模型直接 429,跳过 token 预估(含多模态图片 URL 下载等昂贵路径),与 _check_request_block 的 limit-reached 恒 429 双保险。

代价:用量墙让 1.95M~2M 的 5 万采集放弃(明日返还少 5 万 ≈2.5%),换取贴边期容错; 余额墙对正常大池模型无影响(余额 ≥10万 不触发)。数字用例验证 2026-08-13:9/9 场景 (踢/不踢/死锁防护/防抖动/动态墙取 min)全部符合预期。


8. 排障速查

  1. 看到 Today > BalAtSync 不是异常,先分清口径(§5);判扣费优先看网页真实初始余额 B0(或滞后 收敛时点的 U+B 反推,§4.2),不要用任意时点快照(13:36 滞后快照曾误判「超余额扣费」,实际无扣费)
  2. 余额显示不动 ≠ 余额充足,是 API 滞后(Δ≈14 分钟,§4.2);v6 的 balance_at_sync + usage_at_sync 在滞后未收敛时 = B0 + L(虚高),「实时抵消」仅覆盖同步后窗口,L 不抵消(§7.5)
  3. 判断「还能免费采多少」:保守用 网页 B0 − 今日已采;v6 公式在滞后收敛时等价,未收敛时偏高 L
  4. 消耗任务 23:20 直连 Ark 绕过 LiteLLM 闸门(防自杀),其自身 clamp:plan ≤ 2M − usage、 plan ≤ b0_est − SMALL_BUFFER − usage(b0_est = 拉包时刻 余额+usage,守恒口径与 v6 闸门同构, 2026-08-13 已实施),停止条件三闭环
  5. 余额滞后不改变 B0 守恒;30 天过期蒸发是余额下跌的另一来源, 与消耗扣减区分(如 08-12 余额 1,558,307 → 596,869,其中约 50 万为滞后修正 + 过期)
  6. 2M 采集上限可被单请求 + 并发击穿(§7.6):reserve 估算不足时已放行的请求继续消耗, soft_evict 后置兜底只能挡后续。修复:reserve = litellm.token_counter(messages) + max_tokens (输出侧另加 THINKING_RESERVE_FLOOR 兜底思考模型 reasoning tokens,§7.6)
  7. B0 守恒仅在 API 滞后收敛后成立;同步快照期间 balance + usage = B0 + L(虚高,§4.2), 不是真实 B0。判扣费用网页真实初始余额或收敛时点反推

9. 待确认/风险清单

  • [~] 余额 API 滞后实测(2026-08-13:Δ≈14 分钟,滞后量 = 窗口内消费,实测 L=149,616 > SMALL_BUFFER 5 万;上限随消费速度变化,建议 SMALL_BUFFER 与消费联动或改用网页 B0 校验,§7.5 遗留风险 1)
  • v4 误拦发现与回退(2026-08-12 15:09 生产日志实证,§7.3)
  • v5 废弃(2026-08-12,§7.4;被 v6 Snapshot-Offset 替代)
  • v6 实施(2026-08-12,§7.5;2026-08-13 生产验证)
  • 2M 采集上限击穿修复(2026-08-12 20:53 实测 §7.6:reserve 改用 token_counter + max_tokens, 后置 soft_evict 兜底;23:18 重启后 PRE-BLOCK 生效)
  • 消耗任务口径同步收紧(2026-08-13 代码确认:clamp2 用 vls.SMALL_BUFFER,与闸门同源一致)
  • consume clamp2 余额快照口径修复待观测(2026-08-13 已实施守恒口径,待 1-2 个结算周期观测 🔴 深夜 L 实际是否 < SMALL_BUFFER;若触发"守恒墙已耗尽"且当日有扣费疑点 → 叠加 A' 收敛重取)
  • 视觉图片请求 reserve 估算口径未校准(2026-08-13 调查):LiteLLM token_counter 对 OpenAI 格式 image_url 按 OpenAI 口径计(detail low/auto = 85 token/图,high = 下载图按 tile 85+170×tiles, 失败 fallback 255),与火山 Ark doubao 视觉模型实际计费公式不一致;官方仅提供 Token 计算器 工具(未公开公式),FAQ 只说"图片计入输入 token"。闸门调用未传 use_default_image_token_count → high-detail 图会在 hook 内同步下载(阻塞)。偏差量级 = 每图几十几百 token,相对 THINKING_RESERVE_FLOOR(4096) 小;方向待实测(Token 计算器对照或发图看 usage),有图流量时关注
  • 软踢预警线(2026-08-13 实施,§7.6):后置踢出提前为 用量墙 1.95M / 余额墙 10万(usage>0), 恢复对称 + 前置 limit-reached 恒 429;9/9 数字用例验证
  • 空窗期(0:00~返还到账)行为观察:v6 下 B0 自动收紧,确认无异常误杀
  • 数据库泄漏:08-12 一天 deepseek-v4 消耗记录量级约 5 万条导致 Redis 内存膨胀;触发监控/清理机制
  • v6 无静态 2M 兜底时的安全:08-13 幸由 B0(2,242,178) > 2M 静态墙兜底;若某日 B0 < 2M, 滞后 L > SMALL_BUFFER 时 v6 限额虚高 → 可能放行到余额 0 → 扣费(§7.5 遗留风险 1)
  • reserve 精度验证(2026-08-13 短文本实证:估算 34 vs 实际 167 的根因 = 思考模型 reasoning tokens 不计入 reserve,已用 THINKING_RESERVE_FLOOR 兜底; token_counter cl100k_base 对中文偏高对输入侧仍是安全方向)
  • consume clamp2 余额快照口径不同步(2026-08-13 已实施方向 C 守恒口径):原 plan ≤ balance − SMALL_BUFFER 用拉包时刻余额快照 (volc_consume.py:455),不含 23:20→00:00 真实用户消耗; clamp1 用共享 daily_usage 实时精准,但余额维度两方不同步。§4.2 已实测结算滞后 Δ≈14 分钟 → 快照相对真实剩余虚高 L(随消费速度增长,实测 149,616 > SMALL_BUFFER 5 万),深夜真实消耗大时 两方合计可超烧余额。 实施内容(2026-08-13):_record_consumption 已将自身 INCR 进共享 daily_usage,每轮 _current_daily_usage 即"真实用户+自身"总消耗 → b0_est = balance + usage_now(= B0+L,与 v6 闸门同构),clamp2 改 plan ≤ b0_est − SMALL_BUFFER − usage_now、停止条件改 usage ≥ b0_est − SMALL_BUFFER(与 clamp1 同构)。零 API 成本,两方不同步结构性消除。 数字用例验证:L=0 时撞墙剩 SMALL_BUFFER(旧方案执行中用户消耗 100k → 超扣 100k); L>0 残余风险 = §7.5 遗留风险 1 同款(L > SMALL_BUFFER 时 b0_est 虚高,与闸门共享,非消耗任务独有)。 - 🅰 A' 可选叠加:执行前睡 ~15 分钟再拉一次取收敛 balance 算 b0_est(防 L 大,成本 8 次 API) - ❌ B 扣减 L 预估:v5 BUFFER 盲猜教训(L 实测浮动 15 倍),否决