diff --git a/research-wiki/designs/est-tokens-decoupling.md b/research-wiki/designs/est-tokens-decoupling.md new file mode 100644 index 0000000..d2eddd1 --- /dev/null +++ b/research-wiki/designs/est-tokens-decoupling.md @@ -0,0 +1,23 @@ +--- +type: design +node_id: design:est-tokens-decoupling +title: "est_tokens 解耦: 拆分限流预扣与遥测用量兜底(issue #2)" +date: 2026-07-30 +--- + +# est_tokens 解耦: 拆分限流预扣与遥测用量兜底(issue #2) + +全文见 [2026-07-30-est-tokens-decoupling-design.md](2026-07-30-est-tokens-decoupling-design.md)。缘起是 Gitea issue #2(下游 CHSAnalyzer 提出)。 + +- **缘起**: `SourceConfig.est_tokens` 被派了两份对"保守"定义相反的差事——TPM 入场预扣(多押金 = 安全)与 usage 帧缺失时的遥测用量兜底(计费没有安全方向)。CHS `config.py:55` 自己就把它定义为"须 ≥ 最坏情形 token"的**上界**,而库把遥测拆 `prompt`/`completion` 两列后又将整个估值塞进单价更贵的 `completion`(`openai_compat.py:146`),形成双重系统性高估:实测算例 26 倍。 +- **第二个症状**: `types.py:125` 的 `tpm > 0 ⇒ est_tokens > 0` 把供应商配额(可从配额页抄)与库的实现细节(无人能正确取值)绑死,下游删掉猜测项后 `tpm` 只能填 0,被迫在自己配置模型里加校验绕开。 +- **方案(两个决策点,人类审批)**: ① usage 不可得时记 `0/0` + `usage_source` 新增 `unavailable` + cost 记 NULL;② `est_tokens` 未填时由库派生 `tpm // 60`,字段降为可选调优覆盖(不删不改名,迁移兼容)。 +- **值域三态各有生产者**: `measured`(正常)、`estimated`(打捞路径——收到 usage 帧但流被截断,数字真实而可信度降级)、`unavailable`(用量不可得)。故 `estimated` 不是空值域,历史行亦读兼容。 +- **被否决 · usage 兜底记 0 但沿用 `estimated`**: cost 会算出 `0.0`,"免费"与"未知"在数据上不可区分,账目缺口无法量化。 +- **被否决 · 保留 est 兜底只修 prompt/completion 分配比例**: 比例是又一个没有正确取值的魔数,且未触及"拿上界当实测"的根因,仍高估约 9 倍。 +- **被否决 · 固定默认常量(如 1000)**: 与配额规模无关,在途上限随规模乱飘(`tpm=6000` 只剩 6 个在途、`tpm=600000` 放行 600 个)。派生值尺度无关且语义可文档化("一次调用约占一秒钟的配额份额")。 +- **被否决 · issue 原建议的遥测 p90 自估**: `TelemetryRecorder` 是纯只写端口,自估需新增读接口并强制所有后端(含 `none`)实现,公共 API 扩张远大于它要省掉的一个可选字段,且无实测证据表明派生默认值不够用。**注**: 初稿曾以"把两条方向相反的降级铁律焊在一起"为主论据,经独立审查撤回——p90 可在遥测读失败时回退纯派生值,限流侧仍能 fail-closed。 +- **被否决 · 派生逻辑取全局与单源 tpm 的较紧者**: 需改三处 `QuotaGate` 装配,且它修的是一个**既有**缺口(单源 `tpm=0` 而全局 `tpm>0` 时预扣为 0),属任务外,建议另开 issue。 +- **有意放弃的迁移保留项**: `migrations/chsanalyzer.md:151` 曾把"usage 缺失按 est 估算"列为保留(理由"保守计量")。本设计推翻:CHS 只记单个 `total_tokens` 不存在分配问题,而"保守"在计费语境只有错误一个方向。反静默的原始意图仍保留——被放弃的只是"编一个数字"这个手段。 +- **独立审查(两轮)抓出的两处实质缺陷**: ① 只改失败侧结算不够,`retry.py:338`/`embedding.py:271` 的**成功侧**取自同一返回值,改前 `actual` 恰等于预扣量使 `delta==0`,不同改则成功调用押金被整笔退回,对"从不回 usage 帧的网关源"构成系统性 TPM 失效;② OCR 行原判为"假陈述"是错的——`types.py:51` 与 `ocr.py:9` 明示 OCR 的 0 token 属**事实**,且改标 `unavailable` 会灌水本方案赖以成立的缺口度量,已剔出。 +- **待办**: 经 `writing-plans` 出实施计划;发版须同步 CHANGELOG 与 wiki(缺口查询口径须带 `AND cache_hit = false`),并回帖 issue #2。 diff --git a/research-wiki/graph/edges.json b/research-wiki/graph/edges.json index 2a9ed65..c77bcca 100644 --- a/research-wiki/graph/edges.json +++ b/research-wiki/graph/edges.json @@ -100,6 +100,11 @@ "id": "design:settings-invariants-round-2", "label": "GatewaySettings 装配校验补齐(第二轮)", "type": "design" + }, + { + "id": "design:est-tokens-decoupling", + "label": "est_tokens 解耦: 拆分限流预扣与遥测用量兜底(issue #2)", + "type": "design" } ], "links": [ @@ -165,6 +170,13 @@ "relation": "implements", "evidence": "T0-T14 逐节实现设计 §4-§10/§15", "added": "2026-07-22T09:33:05.357964+00:00" + }, + { + "source": "design:est-tokens-decoupling", + "target": "design:m1-core-design", + "relation": "refines", + "evidence": "精化 M1 冻结的 est_tokens 双职责语义: 保留 TPM 预扣、推翻 usage 缺失按 est 兜底(m1-core-design.md:59,222),改记 0/0 + unavailable + cost NULL", + "added": "2026-07-30T09:33:55.383401+00:00" } ] } \ No newline at end of file diff --git a/research-wiki/index.md b/research-wiki/index.md index 153618d..428acea 100644 --- a/research-wiki/index.md +++ b/research-wiki/index.md @@ -1,15 +1,17 @@ # Research Wiki 索引 -> 自动生成,更新时间:2026-07-30 04:44 UTC +> 自动生成,更新时间:2026-07-30 09:33 UTC -## design (14) +## design (16) - [2026-07-20-m1-core-design](designs/2026-07-20-m1-core-design.md) `design:2026-07-20-m1-core-design` - [2026-07-20-m2-distributed-design](designs/2026-07-20-m2-distributed-design.md) `design:2026-07-20-m2-distributed-design` - [2026-07-21-m25-resilience-design](designs/2026-07-21-m25-resilience-design.md) `design:2026-07-21-m25-resilience-design` - [2026-07-21-m3-ocr-design](designs/2026-07-21-m3-ocr-design.md) `design:2026-07-21-m3-ocr-design` - [2026-07-22-m4-migration-design](designs/2026-07-22-m4-migration-design.md) `design:2026-07-22-m4-migration-design` - [2026-07-29-settings-invariant-guards-design](designs/2026-07-29-settings-invariant-guards-design.md) `design:2026-07-29-settings-invariant-guards-design` +- [2026-07-30-est-tokens-decoupling-design](designs/2026-07-30-est-tokens-decoupling-design.md) `design:2026-07-30-est-tokens-decoupling-design` - [2026-07-30-settings-invariants-round-2-design](designs/2026-07-30-settings-invariants-round-2-design.md) `design:2026-07-30-settings-invariants-round-2-design` +- [est_tokens 解耦: 拆分限流预扣与遥测用量兜底(issue #2)](designs/est-tokens-decoupling.md) `design:est-tokens-decoupling` - [GatewaySettings 装配校验补齐(第二轮)](designs/settings-invariants-round-2.md) `design:settings-invariants-round-2` - [GatewaySettings 跨字段不变量守卫的生效范围](designs/settings-invariant-guards.md) `design:settings-invariant-guards` - [M1 核心里程碑设计:公共签名冻结与治理栈落地](designs/m1-core-design.md) `design:m1-core-design` diff --git a/research-wiki/log.md b/research-wiki/log.md index 9797c82..45fa3b3 100644 --- a/research-wiki/log.md +++ b/research-wiki/log.md @@ -50,3 +50,7 @@ - [2026-07-30 03:44 UTC] 重建索引: 36 篇页面 - [2026-07-30 04:44 UTC] 新增 design: GatewaySettings 装配校验补齐(第二轮) (design:settings-invariants-round-2) - [2026-07-30 04:44 UTC] 重建索引: 38 篇页面 +- [2026-07-30 09:32 UTC] 新增 design: est_tokens 解耦: 拆分限流预扣与遥测用量兜底(issue #2) (design:est-tokens-decoupling) +- [2026-07-30 09:33 UTC] 重建索引: 40 篇页面 +- [2026-07-30 09:33 UTC] 新增边: design:est-tokens-decoupling --refines--> design:m1-core-design +- [2026-07-30 09:33 UTC] 重建索引: 40 篇页面