Compare commits

...

2 Commits

Author SHA1 Message Date
vibe-code 65c52442c8 docs: 补充 §7.7 2M 击穿事件 + reserve 改进 + 排障速查更新
- 新增 §7.7: 2M 采集上限击穿事件(20:53 实测)与 reserve 改进
  (token_counter + max_tokens + 后置 soft_evict)
- §8 排障速查: 补充 2M 击穿(§7.7)、B0 守恒前提(§4.2)
- §9 待确认: 新增 reserve 精度验证项,标记 2M 击穿修复完成
- 文件头日期更新
2026-08-13 04:16:52 +00:00
vibe-code 9f8c6dc44c docs: 修正 v6 限额「B0 常数」论证缺陷,记录 08-13 余额滞后实测与 512 亿笔误 2026-08-13 03:55:40 +00:00
2 changed files with 179 additions and 29 deletions
+5
View File
@@ -157,6 +157,11 @@ daily_usage ≥ 2M → 停(采集上限,再烧不返还
## 四、限额设计(防扣费)——v2 简化逻辑(v5 修订见 §4.5,2026-08-12 已实施) ## 四、限额设计(防扣费)——v2 简化逻辑(v5 修订见 §4.5,2026-08-12 已实施)
> ⚠️ 现行版本为 v6 Snapshot-Offset,见 `volc-quota-notes.md` §7.6。2026-08-13 实测修正了
> 其「B0 常数」论证缺陷:`limit = balance_at_sync + usage_at_sync − SMALL_BUFFER = B0 + L − SMALL_BUFFER`,
> L = 同步时点 API 结算滞后窗口内未结算消耗(实测 Δ≈14 分钟、L=149,616 > SMALL_BUFFER 5 万),
> 今日由静态 2M 墙兜底(B0=2,242,178 > 2M),B0 < 2M 时无兜底、有超扣风险。本节 v2/v5 仅作沿革参考。
### 4.1 为什么废弃 Peak-Buffer 峰值状态机(存量场景产物) ### 4.1 为什么废弃 Peak-Buffer 峰值状态机(存量场景产物)
旧逻辑 `stable_limit = PeakPool × 0.85`(PeakPool = usage + balance,首锁/门控重锁/DROP 状态机维护)有两个问题: 旧逻辑 `stable_limit = PeakPool × 0.85`(PeakPool = usage + balance,首锁/门控重锁/DROP 状态机维护)有两个问题:
+174 -29
View File
@@ -1,14 +1,16 @@
# 火山免费额度机制速查(VolcEngine Quota Notes) # 火山免费额度机制速查(VolcEngine Quota Notes)
> 日期:2026-08-12 > 日期:2026-08-12(2026-08-13 增补:网页口径校准 + v6 滞后击穿实测 §4.2/§7.6 + 2M 击穿事件 §7.7)
> 用途:后续限额/防扣费设计、排障、消耗任务的事实基础,避免重复推导 > 用途:后续限额/防扣费设计、排障、消耗任务的事实基础,避免重复推导
> 来源: > 来源:
> - `merge-auto-consume.md` §1(2026-08-11 用户确认的业务细则) > - `merge-auto-consume.md` §1(2026-08-11 用户确认的业务细则)
> - 2026-08-12 对话澄清(扣费口径、余额 API 滞后、限额方案演进) > - 2026-08-12 对话澄清(扣费口径、余额 API 滞后、限额方案演进)
> - 2026-08-12 生产日志观察(deepseek-v4-flash-ga-260731) > - 2026-08-12 生产日志观察(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`), > 代码现状:v6 = Snapshot-Offset(`limit = balance_at_sync + usage_at_sync − SMALL_BUFFER`),
> 2026-08-12 修改;BUFFER 盲猜法已废弃,见 §7.6。 > 2026-08-12 修改;BUFFER 盲猜法已废弃,见 §7.6。
> ⚠️ 2026-08-13:§7.6 的「B0 常数/永远不扣费」论证有前提缺陷,见 §4.2 与 §7.6 修正。
--- ---
@@ -81,16 +83,37 @@
--- ---
## 4. 余额 API 的滞后问题(2026-08-12) ## 4. 余额 API 的滞后问题
### 4.1 2026-08-12 观测
- **非实时,延迟未知**(用户明确)。实测:2026-08-12 13:36:01 同步快照 Bal=1,558,307, - **非实时,延迟未知**(用户明确)。实测:2026-08-12 13:36:01 同步快照 Bal=1,558,307,
此后 usage 从 1,241,821 涨到 1,696,135(13:36→13:38)期间 Bal 纹丝不动。 此后 usage 从 1,241,821 涨到 1,696,135(13:36→13:38)期间 Bal 纹丝不动。
- 观察结论:AvailableAmount 不随今日消耗实时递减(至少滞后 20 万级,上限未知)。 - 观察结论:AvailableAmount 不随今日消耗实时递减(至少滞后 20 万级)。
- **后果**:一切「余额随消耗实时递减」的公式(v2 的 `usage + balance` 池恒定假设)在
滞后下失效——usage 涨而 balance 不动 → 池膨胀 → 限额虚高 → 放行到扣费区。 ### 4.2 2026-08-13 网页口径校准(Δ≈14 分钟,本次谬误的根源)
- 最终对策:Snapshot-Offset(§7.6),不再依赖 `usage + balance` 守恒,而是用同步时点
的快照 `balance_at_sync + usage_at_sync = B0` 直接计算累计限额,两次同步间常数, - 网页确认今日真实初始余额 **B0 = 2,242,178**(采集量 0 时点读数,可信;次日 0:00 前无新包入账)
实时 usage 自然抵消滞后窗口内的消耗。 - 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
--- ---
@@ -123,6 +146,10 @@ Set Redis limit:dynamic:volcengine:remaining:deepseek-v4-flash-ga-260731 = 1,258
此前「超余额 13.8 万已扣费」的推断基于 13:36 滞后快照(B=1,558,307 高估真实剩余 此前「超余额 13.8 万已扣费」的推断基于 13:36 滞后快照(B=1,558,307 高估真实剩余
约 50 万),不成立。判定扣费要用「守恒初始池」(U+B 反推)而非任意时点快照。 约 50 万),不成立。判定扣费要用「守恒初始池」(U+B 反推)而非任意时点快照。
⚠️ **2026-08-13 修正(§4.2)**:U+B 反推的「守恒池」仅在**滞后收敛时点**成立——
该例 14:59:51 恰好收敛(B0=2,293,004 可信,差 135)。滞后未收敛时 U+B = B0 + L 虚高,
不能当 B0 用。判扣费正确口径 = **网页真实初始余额 B0**(或收敛时点的 U+B 反推)。
--- ---
## 6. Redis key 与日志口径(速查) ## 6. Redis key 与日志口径(速查)
@@ -199,36 +226,50 @@ stable_limit = max(0, usage + balance − BUFFER)
balance < BUFFER(U=0)→ stable=0 → 全拦 balance < BUFFER(U=0)→ stable=0 → 全拦
- **已废弃原因**:BUFFER 盲猜滞后窗口消耗,高强度击穿低强度浪费。被 §7.6 Snapshot-Offset 替代。 - **已废弃原因**:BUFFER 盲猜滞后窗口消耗,高强度击穿低强度浪费。被 §7.6 Snapshot-Offset 替代。
### 7.6 v6(Snapshot-Offset,2026-08-12 当前):宝塔防扣费 ### 7.6 v6(Snapshot-Offset,2026-08-12 实施;2026-08-13 修正论证缺陷):宝塔防扣费
``` ```
limit = balance_at_sync + usage_at_sync − SMALL_BUFFER(5万) limit = balance_at_sync + usage_at_sync − SMALL_BUFFER(5万)
= B0 − SMALL_BUFFER = 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 + usage = 当日初始免费池 B0(B0 是常数,一天内不变)**。 用同步时点快照 `balance_at_sync + usage_at_sync` 计算累计限额:**滞后收敛时(L=0)恰为
当日初始免费池 B0**;滞后未收敛时虚高 L。两次同步间快照不变,实时 `current_usage` 撞线
只抵消同步**之后**窗口内的增量消耗,**不抵消同步时点已存在的滞后 L**。
**limit 是累计限额(从 0 到上限),不是剩余余额**。`_check_request_block` 用 `current_usage + reserve >= limit` 撞线,`current_usage` 自然抵消了滞后窗口内的增量消耗。 **limit 是累计限额(从 0 到上限),不是剩余余额**。`_check_request_block` 用 `current_usage + reserve >= limit` 撞线。
#### 为什么不需要 BUFFER #### 为什么 SMALL_BUFFER 不能覆盖全部滞后
旧方案用 BUFFER 猜「滞后窗口内消耗了多少」——猜不准。新方案直接记录同步时点的 `balance_at_sync` 和 `usage_at_sync`,闸门读实时 `current_usage` 去减,精确抵消: 旧方案用 BUFFER(30 万)猜「滞后窗口内消耗了多少」——猜不准。新方案改为快照 + 实时 usage:
``` ```
# 旧:盲猜 # 旧:盲猜
limit = usage + balance - BUFFER(300k) limit = usage + balance - BUFFER(300k)
# 新:精确抵消 # 新:快照
limit = balance_at_sync + usage_at_sync - SMALL_BUFFER(5k) limit = balance_at_sync + usage_at_sync - SMALL_BUFFER(5k) = B0 + L - SMALL_BUFFER
``` ```
**SMALL_BUFFER(5 万)只覆盖非消耗性余额跳降**(资源包过期、API 未结算残差),不是消耗量。 **SMALL_BUFFER(5 万)覆盖「非消耗性余额跳降」(资源包过期) + 少量未结算残差**——
滞后未结算消耗 L 只兜 5 万:**L > 5 万时限额虚高进入扣费区**(08-13 实测 L=149,616 > 5 万,
击穿条件成立;当日幸被静态 2M 兜底,见「不变量」)。
#### 不变量 #### 不变量(带 L 的精确形式)
**永远不扣费**:`usage + reserve >= B0 - SMALL_BUFFER` 撞线时,剩余实时余额 = `B0 - usage` = `B0 - (B0 - SMALL_BUFFER - reserve)` = `SMALL_BUFFER + reserve` > 0。扣费在余额归零时才发生,结构性杜绝。 撞线 `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:剩余可 < 0 → **已进扣费区** ✗
- **今日为何安全(08-13)**:真实 B0=2,242,178 > 静态 2M → `min(2M, B0+L−S)` 被静态 2M 压住,
撞线发生在 usage=2M,真实剩余 242,178 > 0——是**静态墙**兜底,不是 v6 不变量。
**若某日 B0 < 2M(包变小),静态墙失效,L > SMALL_BUFFER 即可能超扣。**
#### 执行流程 #### 执行流程
@@ -247,34 +288,138 @@ limit = balance_at_sync + usage_at_sync - SMALL_BUFFER(5k)
| 场景 | 旧(BUFFER 30万) | 新(Snapshot-Offset) | | 场景 | 旧(BUFFER 30万) | 新(Snapshot-Offset) |
|---|---|---| |---|---|---|
| 高强度消耗 | 滞后窗口内消耗 > 30 万 → 击穿扣费 | 实时 usage 撞 B0 线,不放行 | | 高强度消耗 | 滞后窗口内消耗 > 30 万 → 击穿扣费 | 撞 B0+L 线:L ≤ 5 万安全,L > 5 万虚高 |
| 低强度消耗 | 浪费 30 万额度(实际只用了 5 万) | 只留 5 万 SMALL_BUFFER | | 低强度消耗 | 浪费 30 万额度(实际只用了 5 万) | 只留 5 万 SMALL_BUFFER |
| 新包到账 | 下次同步 stable limit 跳升 | 下次同步 B0 跳升,一致 | | 新包到账 | 下次同步 stable limit 跳升 | 下次同步 B0 跳升,一致 |
| 包过期 | 余额跳降,limit 偏高至下次同步 | 同左,但 SMALL_BUFFER=5 万风险窗口小 | | 包过期 | 余额跳降,limit 偏高至下次同步 | 同左,但 SMALL_BUFFER=5 万风险窗口小 |
#### 遗留风险 #### 遗留风险
1. **同步时点 B0 本身滞后**:API 的 AvailableAmount 在同步时点就已经滞后了 L0。这个量无法通过抵消消除。唯一缓解:同步频率(低余额 5min) + 突发检测。 1. **同步时点滞后 L 无法抵消(08-13 实测,当前首要风险)**: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 或监控包过期时间。 2. **多包同时过期 > SMALL_BUFFER**:概率低,无法绝对防御。可调大 SMALL_BUFFER 或监控包过期时间。
### 7.7 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 逻辑:
```python
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 精确估算输入
```python
# 旧:盲猜
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)。
#### 后置闸门兜底
```python
# _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` | 超限后的后续请求 | 已发出正在执行的请求 |
**遗留**:若 `max_tokens` 未声明且模型输出无上限,前置仍可能预估过低。
此时只有后置 `soft_evict` 兜底(挡后续不挡本次)。
--- ---
## 8. 排障速查(今日结论汇总) ## 8. 排障速查(今日结论汇总)
1. 看到 Today > BalAtSync 不是异常,先分清口径(§5);判扣费用「守恒初始池」(BalAtSync+UsageAtSync 反推), 1. 看到 Today > BalAtSync 不是异常,先分清口径(§5);判扣费优先看**网页真实初始余额 B0**(或滞后
不要用任意时点快照(13:36 滞后快照曾误判「超余额扣费」,实际无扣费) 收敛时点的 U+B 反推,§4.2),不要用任意时点快照(13:36 滞后快照曾误判「超余额扣费」,实际无扣费)
2. 余额显示不动 ≠ 余额充足,是 API 滞后;Snapshot-Offset 不再依赖 BUFFER 覆盖,而是直接用 2. 余额显示不动 ≠ 余额充足,是 API 滞后(Δ≈14 分钟,§4.2);v6 的 `balance_at_sync + usage_at_sync`
`balance_at_sync + usage_at_sync - current_usage` 实时抵消 在滞后未收敛时 = B0 + L(虚高),「实时抵消」仅覆盖同步后窗口,L 不抵消(§7.6)
3. 判断「还能免费采多少」:`balance_at_sync + usage_at_sync - SMALL_BUFFER - 今日已采`(v6) 3. 判断「还能免费采多少」:保守用 `网页 B0 − 今日已采`;v6 公式在滞后收敛时等价,未收敛时偏高 L
4. 消耗任务 23:00 直连 Ark 绕过 LiteLLM 闸门(防自杀),其自身 clamp:`plan ≤ 2M − usage`、 4. 消耗任务 23:00 直连 Ark 绕过 LiteLLM 闸门(防自杀),其自身 clamp:`plan ≤ 2M − usage`、
`plan ≤ 余额快照 − SMALL_BUFFER`,停止条件三闭环 `plan ≤ 余额快照 − SMALL_BUFFER`,停止条件三闭环
5. 余额滞后不改变 B0 守恒;30 天过期蒸发是余额下跌的另一来源, 5. 余额滞后不改变 B0 守恒;30 天过期蒸发是余额下跌的另一来源,
与消耗扣减区分(如 08-12 余额 1,558,307 → 596,869,其中约 50 万为滞后修正 + 过期)## 9. 待确认/风险清单 与消耗扣减区分(如 08-12 余额 1,558,307 → 596,869,其中约 50 万为滞后修正 + 过期)
6. 2M 采集上限可被单请求 + 并发击穿(§7.7):reserve 估算不足时已放行的请求继续消耗,
`soft_evict` 后置兜底只能挡后续。修复:reserve = `litellm.token_counter(messages)` + `max_tokens`
7. B0 守恒仅在 API 滞后收敛后成立;同步快照期间 `balance + usage = B0 + L`(虚高,§4.2),
不是真实 B0。判扣费用网页真实初始余额或收敛时点反推
- [ ] 余额 API 滞后上限实测(决定是否需要进一步压缩 SMALL_BUFFER 或保留 5 万) ---
## 9. 待确认/风险清单
- [~] 余额 API 滞后实测(2026-08-13:Δ≈14 分钟,滞后量 = 窗口内消费,实测 L=149,616 > SMALL_BUFFER
5 万;上限随消费速度变化,建议 SMALL_BUFFER 与消费联动或改用网页 B0 校验,§7.6 遗留风险 1)
- [x] v4 误拦发现与回退(2026-08-12 15:09 生产日志实证,§7.3) - [x] v4 误拦发现与回退(2026-08-12 15:09 生产日志实证,§7.3)
- [x] v5 废弃(2026-08-12,§7.5;被 v6 Snapshot-Offset 替代) - [x] v5 废弃(2026-08-12,§7.5;被 v6 Snapshot-Offset 替代)
- [x] v6 实施(2026-08-12,§7.6;待生产主机重启生效) - [x] v6 实施(2026-08-12,§7.6;待生产主机重启生效)
- [x] 2M 采集上限击穿修复(2026-08-12 20:53 实测 §7.7:reserve 改用 token_counter + max_tokens,
后置 soft_evict 兜底;23:18 重启后 PRE-BLOCK 生效)
- [ ] 消耗任务口径同步收紧(已改为 vls.SMALL_BUFFER,确认与闸门一致) - [ ] 消耗任务口径同步收紧(已改为 vls.SMALL_BUFFER,确认与闸门一致)
- [ ] 空窗期(0:00–返还到账)行为观察:v6 下 B0 自动收紧,确认无异常误杀 - [ ] 空窗期(0:00–返还到账)行为观察:v6 下 B0 自动收紧,确认无异常误杀
- [ ] 数据库泄漏:512 亿 token 消耗记录(08-12 一天 deepseek-v4 烧了 512 亿)导致 Redis 内存膨胀;触发监控/清理机制 - [ ] 数据库泄漏:08-12 一天 deepseek-v4 消耗记录量级约 5 万条(此处曾误记「512 亿」,从未烧过该量级,
为口径笔误)导致 Redis 内存膨胀;触发监控/清理机制
- [ ] v6 无静态 2M 兜底时的安全:08-13 幸由 B0(2,242,178) > 2M 静态墙兜底;若某日 B0 < 2M,
滞后 L > SMALL_BUFFER 时 v6 限额虚高 → 可能放行到余额 0 → 扣费(§7.6 遗留风险 1)
- [ ] reserve 精度验证:token_counter fallback cl100k_base 对中文偏高,需实测确认偏差范围
(DeepSeek 自有 tokenizer ~1.5 token/字 vs cl100k_base ~2-3 token/字,§7.7)