est_tokens 应由库按实测自估,而不是让调用方填一个没有正确取值的常量 #2

Closed
opened 2026-07-30 14:13:18 +08:00 by iomgaa · 3 comments
Owner

问题

SourceConfig.est_tokens 是按 token 限流时的入场预扣量,由配置的人填。这个值没有正确取值,理由有三条,一条比一条硬。

一、正确值属于请求,不属于源。 同一个模型账号,一次短文本分类几百 token,一次带图的表格提取上万。下游项目 CHSAnalyzer 的上一版给三个 VLM 源一律填了 4000,因为它没有别的办法。

二、它没有安全方向。 填大了压吞吐,而且从外面看不出来(表现为「怎么这么慢」);填小了撞 429,进而触发熔断。一个参数如果连「往哪个方向填错更安全」都答不上来,配置的人就没有任何决策依据。

三、它还有第二个身份。 _settle 在模型没返回 usage 帧时,会把这个预估值当成实际用量写进遥测。也就是说一个拍脑袋的常量会污染计费数据,而这条路径是静默的——没有人会发现遥测里那一行的 token 数是编的。

为什么库比调用方更适合解决它

TelemetryRecorder 每次调用都落了实测 token 数,permit 事后本来就按实测 settle。也就是说库手里已经有算这个值需要的全部数据,而调用方手里没有。

按 Ousterhout 在《A Philosophy of Software Design》8.2 节的说法,这正是配置参数不该存在的典型形态:

"Before exporting a configuration parameter, ask yourself: 'will users (or higher-level modules) be able to determine a better value than we can determine here?'"

这里的答案明确是「不能」。他给的替代方案也正好对应——那节举的例子是传输协议的重传间隔,与其让人填,不如让协议自己测量成功请求的响应时间再推算,这样还能随运行条件自动调整,而配置参数 "can easily become out of date"。

一个可能的方向(仅供参考,具体怎么做由你们定)

按 scope + model 维度,从近期遥测记录里取一个分位数(比如 p90)当预扣量,冷启动没有历史时退到一个保守常量。这样它随负载自动调整,也不会再有「用途内部差异很大」的问题。

需要考虑的几点:冷启动窗口多长、要不要按 model 而不只按 scope 分桶、以及遥测后端是 none 时怎么退化。

对下游的影响

一旦库能自估,SourceConfig.est_tokens 就可以从必填降为可选或直接移除,_validate_gatestpm > 0 强制 est_tokens > 0 那条约束也能跟着去掉。

现在这条约束正在下游造成一个陷阱:CHSAnalyzer 新版已经把 est_tokens 从配置面移除(判定它没有正确取值),结果账号级的 tpm 就再也不能填非 0——运维照着供应商配额页填一个真实数字,配置层通过、装配期才炸。下游只能在自己的配置模型里加一条校验把 tpm 限死为 0,并解释为什么。这是个绕路,不是解法。

参考:下游的裁决记录在 CHSAnalyzer 的 research-wiki/design/0006-config-key-audit.md 决策三。

不提 PR 的原因

这件事的正确解法在库内部(遥测数据、限流器、settle 路径三者的交界),由熟悉这几处的人做更合适。下游先在自己那边绕过,等库这边有结论再跟进。

## 问题 `SourceConfig.est_tokens` 是按 token 限流时的入场预扣量,由配置的人填。这个值**没有正确取值**,理由有三条,一条比一条硬。 **一、正确值属于请求,不属于源。** 同一个模型账号,一次短文本分类几百 token,一次带图的表格提取上万。下游项目 CHSAnalyzer 的上一版给三个 VLM 源一律填了 4000,因为它没有别的办法。 **二、它没有安全方向。** 填大了压吞吐,而且从外面看不出来(表现为「怎么这么慢」);填小了撞 429,进而触发熔断。一个参数如果连「往哪个方向填错更安全」都答不上来,配置的人就没有任何决策依据。 **三、它还有第二个身份。** `_settle` 在模型没返回 usage 帧时,会把这个预估值当成实际用量写进遥测。也就是说一个拍脑袋的常量会**污染计费数据**,而这条路径是静默的——没有人会发现遥测里那一行的 token 数是编的。 ## 为什么库比调用方更适合解决它 `TelemetryRecorder` 每次调用都落了实测 token 数,permit 事后本来就按实测 settle。也就是说**库手里已经有算这个值需要的全部数据**,而调用方手里没有。 按 Ousterhout 在《A Philosophy of Software Design》8.2 节的说法,这正是配置参数不该存在的典型形态: > "Before exporting a configuration parameter, ask yourself: 'will users (or higher-level modules) be able to determine a better value than we can determine here?'" 这里的答案明确是「不能」。他给的替代方案也正好对应——那节举的例子是传输协议的重传间隔,与其让人填,不如让协议自己测量成功请求的响应时间再推算,这样还能随运行条件自动调整,而配置参数 "can easily become out of date"。 ## 一个可能的方向(仅供参考,具体怎么做由你们定) 按 scope + model 维度,从近期遥测记录里取一个分位数(比如 p90)当预扣量,冷启动没有历史时退到一个保守常量。这样它随负载自动调整,也不会再有「用途内部差异很大」的问题。 需要考虑的几点:冷启动窗口多长、要不要按 model 而不只按 scope 分桶、以及遥测后端是 `none` 时怎么退化。 ## 对下游的影响 一旦库能自估,`SourceConfig.est_tokens` 就可以从必填降为可选或直接移除,`_validate_gates` 里 `tpm > 0` 强制 `est_tokens > 0` 那条约束也能跟着去掉。 **现在这条约束正在下游造成一个陷阱**:CHSAnalyzer 新版已经把 `est_tokens` 从配置面移除(判定它没有正确取值),结果账号级的 `tpm` 就再也不能填非 0——运维照着供应商配额页填一个真实数字,配置层通过、装配期才炸。下游只能在自己的配置模型里加一条校验把 `tpm` 限死为 0,并解释为什么。这是个绕路,不是解法。 参考:下游的裁决记录在 CHSAnalyzer 的 `research-wiki/design/0006-config-key-audit.md` 决策三。 ## 不提 PR 的原因 这件事的正确解法在库内部(遥测数据、限流器、settle 路径三者的交界),由熟悉这几处的人做更合适。下游先在自己那边绕过,等库这边有结论再跟进。
Author
Owner

感谢这份 issue,论证比一般的功能请求扎实得多,尤其是把「下游正在被卡住」的具体形态写清楚了。我们核对了库内代码,核心诉求接受,但有两条理由我们认为归因偏了,建议的解法也不打算照做。逐条回应。

一、我们认同的

1. 那条硬约束确实是库的边界缺陷,这是最有说服力的一条。

types.py:125tpm > 0 ⇒ est_tokens > 0 把两个本不该耦合的概念绑死了:tpm 是供应商的真实配额(运维照配额页抄一个数就行),est_tokens 是库的实现细节(入场预扣多少)。库要求用户为了填前者必须先猜出后者,这是把内部实现泄漏进了配置面。你们删掉猜测项后 tpm 只能填 0、配置层过而装配期炸 —— 这个陷阱由库负责,不该由下游加校验绕开。

2. est_tokens 兼任两个职责,违反单一职责,而且第二个职责实现得比 issue 说的还糙。

transports/openai_compat.py:146,usage 帧缺失时的兜底:

return 0, source.est_tokens, "estimated"

它把 prompt_tokens 记成 0,把整个限流预扣量塞进 completion_tokens。prompt token 绝不可能是 0,这个分配本身就是错的,cost 会照着这个错数算出来。一个限流押金常量根本不该出现在计费路径上 —— 这一点与限流完全无关,是独立的 bug。

二、我们认为归因偏了的

理由二「它没有安全方向」不成立。 预扣量填大就是安全方向:多押金 → 提前触顶 → 压吞吐,但不会撞 429、不会连带熔断;填小才危险。这是限流预扣的常识。issue 真正想说的是「填大的代价不易观测(只表现为变慢)」—— 这是可观测性问题,不是「无从决策」。区分这两者会影响解法选择:一个没有安全方向的参数必须消灭,一个有安全方向但默认值难定的参数,给个保守默认值就够了。

「静默污染计费数据」说过头了。 usage_source 是 18 个冻结遥测字段之一,telemetry/postgres.py 的表里有这一列,估算行明确标着 estimated。下游做成本汇总时 WHERE usage_source = 'measured' 即可过滤。准确的说法是「数字是编的,但带着标记、可被发现」,而不是「没人会发现」。当然这不改变第一部分第 2 条的结论——编出来的数字本身就不该存在。

理由一的幅度有限。 settle 是多退少补的(backends/memory/limiter.py:52,delta = actual - est),所以 est 填不准只在请求 in-flight 期间失真,结算后自动找平。长流式请求会明显一些,但不是「填错就一直错」。

三、为什么不采用「遥测 p90 自估」

方向上讲得通(数据确实在库手里),但落到本库的架构上有三处硬障碍:

障碍 具体是什么
端口性质要改变 TelemetryRecorder(ports.py:247)是纯只写端口,只有 record_llm_call。要取分位数就得给它加查询接口,并强制所有后端实现。把一个观测端口改造成读写存储端口,公共 API 的扩张远大于它想省掉的那一个 int 字段
撞依赖契约 import-linter 契约里 telemetrybackends 同层且互不依赖,limiter 读不到遥测。预估器只能落在 middleware 层,是一个新子系统
降级绕回原点 遥测后端为 none 时没有历史,冷启动同理,两者都必须退到一个保守常量 —— 那个常量的取值问题跟今天完全一样

为消除一个可选 int 字段而引入带分桶、分位数、冷启动窗口和降级路径的自适应子系统,在本库的 YAGNI 纪律下过不了关。

四、我们准备怎么做

拆成两步,收益的绝大部分在第一步,且第一步不需要任何新子系统:

第一步 · 拆掉两个身份 + 解绑约束

其一,usage 缺失时不再拿限流押金去编计费数字:记 0 并由 usage_source 表明用量不可得,宁可算不出成本,也不算错成本。这直接消灭第一部分第 2 条。

其二,去掉 tpm > 0 ⇒ est_tokens > 0,由库内提供文档化的保守默认预扣量,est_tokens 从必填降为「想调优才填」的可选项。这直接解掉你们遇到的陷阱:运维可以正常填真实 TPM,不必再在下游配置模型里把 tpm 限死为 0。

第二步 · 暂不启动

遥测驱动的自适应预估留到出现实测证据(默认预扣量在真实负载下明显不合适)之后再评估。

具体方案会先走本库的 brainstorming 流程出 2-3 个备选并经人类确认,定稿后在本 issue 更新。届时 est_tokens 的公共语义变化会按迁移兼容约束处理(字段保留、降为可选,不删不改名)。

五、给下游的临时建议

你们目前在自己配置模型里把 tpm 限死为 0 的绕法,在第一步发版前请保留。发版后可以直接删掉那条校验并填真实 TPM,est_tokens 不填即可 —— 我们会在 CHANGELOG 和 wiki 里标明这个行为变更。

再次感谢,prompt_tokens 记 0 那个 bug 是顺着这份 issue 才翻出来的。

感谢这份 issue,论证比一般的功能请求扎实得多,尤其是把「下游正在被卡住」的具体形态写清楚了。我们核对了库内代码,**核心诉求接受**,但有两条理由我们认为归因偏了,建议的解法也不打算照做。逐条回应。 ## 一、我们认同的 **1. 那条硬约束确实是库的边界缺陷,这是最有说服力的一条。** `types.py:125` 的 `tpm > 0 ⇒ est_tokens > 0` 把两个本不该耦合的概念绑死了:`tpm` 是供应商的真实配额(运维照配额页抄一个数就行),`est_tokens` 是库的实现细节(入场预扣多少)。库要求用户为了填前者必须先猜出后者,这是把内部实现泄漏进了配置面。你们删掉猜测项后 `tpm` 只能填 0、配置层过而装配期炸 —— 这个陷阱由库负责,不该由下游加校验绕开。 **2. `est_tokens` 兼任两个职责,违反单一职责,而且第二个职责实现得比 issue 说的还糙。** `transports/openai_compat.py:146`,usage 帧缺失时的兜底: ```python return 0, source.est_tokens, "estimated" ``` 它把 `prompt_tokens` 记成 0,把整个限流预扣量塞进 `completion_tokens`。prompt token 绝不可能是 0,这个分配本身就是错的,`cost` 会照着这个错数算出来。一个限流押金常量根本不该出现在计费路径上 —— 这一点与限流完全无关,是独立的 bug。 ## 二、我们认为归因偏了的 **理由二「它没有安全方向」不成立。** 预扣量填大就是安全方向:多押金 → 提前触顶 → 压吞吐,但不会撞 429、不会连带熔断;填小才危险。这是限流预扣的常识。issue 真正想说的是「填大的代价不易观测(只表现为变慢)」—— 这是可观测性问题,不是「无从决策」。区分这两者会影响解法选择:一个没有安全方向的参数必须消灭,一个有安全方向但默认值难定的参数,给个保守默认值就够了。 **「静默污染计费数据」说过头了。** `usage_source` 是 18 个冻结遥测字段之一,`telemetry/postgres.py` 的表里有这一列,估算行明确标着 `estimated`。下游做成本汇总时 `WHERE usage_source = 'measured'` 即可过滤。准确的说法是「数字是编的,但带着标记、可被发现」,而不是「没人会发现」。当然这不改变第一部分第 2 条的结论——编出来的数字本身就不该存在。 **理由一的幅度有限。** `settle` 是多退少补的(`backends/memory/limiter.py:52`,`delta = actual - est`),所以 `est` 填不准只在请求 in-flight 期间失真,结算后自动找平。长流式请求会明显一些,但不是「填错就一直错」。 ## 三、为什么不采用「遥测 p90 自估」 方向上讲得通(数据确实在库手里),但落到本库的架构上有三处硬障碍: | 障碍 | 具体是什么 | |---|---| | 端口性质要改变 | `TelemetryRecorder`(`ports.py:247`)是**纯只写**端口,只有 `record_llm_call`。要取分位数就得给它加查询接口,并强制所有后端实现。把一个观测端口改造成读写存储端口,公共 API 的扩张远大于它想省掉的那一个 int 字段 | | 撞依赖契约 | import-linter 契约里 `telemetry` 与 `backends` 同层且互不依赖,limiter 读不到遥测。预估器只能落在 middleware 层,是一个新子系统 | | 降级绕回原点 | 遥测后端为 `none` 时没有历史,冷启动同理,两者都必须退到一个保守常量 —— 那个常量的取值问题跟今天完全一样 | 为消除一个可选 int 字段而引入带分桶、分位数、冷启动窗口和降级路径的自适应子系统,在本库的 YAGNI 纪律下过不了关。 ## 四、我们准备怎么做 拆成两步,收益的绝大部分在第一步,且第一步不需要任何新子系统: **第一步 · 拆掉两个身份 + 解绑约束** 其一,usage 缺失时不再拿限流押金去编计费数字:记 0 并由 `usage_source` 表明用量不可得,宁可算不出成本,也不算错成本。这直接消灭第一部分第 2 条。 其二,去掉 `tpm > 0 ⇒ est_tokens > 0`,由库内提供文档化的保守默认预扣量,`est_tokens` 从必填降为「想调优才填」的可选项。这直接解掉你们遇到的陷阱:运维可以正常填真实 TPM,不必再在下游配置模型里把 `tpm` 限死为 0。 **第二步 · 暂不启动** 遥测驱动的自适应预估留到出现实测证据(默认预扣量在真实负载下明显不合适)之后再评估。 具体方案会先走本库的 brainstorming 流程出 2-3 个备选并经人类确认,定稿后在本 issue 更新。届时 `est_tokens` 的公共语义变化会按迁移兼容约束处理(字段保留、降为可选,不删不改名)。 ## 五、给下游的临时建议 你们目前在自己配置模型里把 `tpm` 限死为 0 的绕法,在第一步发版前请保留。发版后可以直接删掉那条校验并填真实 TPM,`est_tokens` 不填即可 —— 我们会在 CHANGELOG 和 wiki 里标明这个行为变更。 再次感谢,`prompt_tokens` 记 0 那个 bug 是顺着这份 issue 才翻出来的。
Author
Owner

已实现,方案与前一条回复一致

实现完成在分支 feature/est-tokens-decoupling,版本号定为 1.0.3(patch:字段与环境键都没删没改名、端口签名未动、API 严格向后兼容,变的是遥测的数据口径)。尚未合并与发包,下面写明你们可以动手的时点。

设计与计划都过了独立审查与人类批准门,存档在主仓库 research-wiki/designs/2026-07-30-est-tokens-decoupling-design.mdresearch-wiki/plans/2026-07-30-est-tokens-decoupling-plan.md

最终落地的两条

一、est_tokens 不再兼任遥测用量兜底。 usage 帧缺失时库如实记 0/0,usage_source 取新值 unavailable,cost 落 NULL。不再拿"最坏情形上界"冒充实测值——你们指出的双重高估(上界当实测 + 整块塞进单价更贵的 completion)确实存在,实测算例是 26 倍。

值域现在三态,每个都有真实生产者:

含义 生产者 cost
measured usage 帧完整可信 正常路径;OCR 成功行 按 token 换算
estimated 有实测数字但可信度降级 打捞路径(收到 usage 帧但流被截断) 按 token 换算
unavailable 用量不可得 usage 帧缺失、失败尝试、终态失败 NULL

二、est_tokens 由必填降为可选调优覆盖,tpm > 0 ⇒ est_tokens > 0 的装配约束已删除。 不填时库按 max(1, tpm // 60) 派生——语义是"一次调用约占一秒钟的配额份额",尺度无关:tpm=6000tpm=600000 都收敛到约 60 个在途,而固定常量会让在途上限随配额规模乱飘。显式填值仍然优先,字段与 {SCOPE}__{PROVIDER}__{N}__EST_TOKENS 环境键保留不删不改名。

你们可以动手的时点

等 1.0.3 发到私有源之后,CHSAnalyzer 那边可以:

  • 删掉配置模型里把 tpm 限死为 0 的绕行校验,以及为它写的解释注释;
  • 按供应商配额页填真实 TPM,EST_TOKENS 留空即可。

升级时请复核一处:成本汇总若依赖"cost 非空"这个隐含假设,现在用量不可得的行 cost 是 NULL 了。SUM(cost) 会天然跳过它们(这正是选 NULL 而非 0.0 的原因——0.0 会让"免费"与"未知"在数据上不可区分)。账目缺口这样量化:

SELECT COUNT(*) FROM llm_calls
WHERE usage_source = 'unavailable' AND cache_hit = false;

AND cache_hit = false 不可省:缓存命中行没产生新调用,cost 是事实上的 0.0,本无缺口,不加限定会让度量偏高。

usage_source 分支的代码需要认识新值;历史库里既有的 estimated 行语义不变、照常可读。

两处与前一条回复不同的地方

OCR 那条我们撤回了。 前一条回复里我们说 ocr.py 把无 usage 的 OCR 调用标 measured 是"假陈述",准备一并改掉。独立审查推翻了这个判断:库既有立场是 OCR 的 0 token 属事实而非未知(types.pyUsage docstring 与 ocr.py 模块 docstring 都这么写),所以 measured 本就准确。而且改成 unavailable 会把本无缺口的 OCR 行灌进上面那个缺口度量,反而破坏了我们用来否决"记 0 但沿用 estimated"方案的核心理由。这条已从范围里剔除,OCR 一字未动。

实现期还抓出一个我们自己差点埋下的 bug。 把兜底改成 (0, 0) 之后,retry.pyembedding.py成功侧结算(actual = prompt + completion)会变成 0,而入场押的是预扣量,差额为负会把押金整笔退回——对"从不返回 usage 帧"的网关源等于 TPM 闸完全失效。设计初稿只处理了失败侧,是独立审查发现的。现在预扣与结算恒取同一个 effective_est_tokens(),成功侧与非 dead 瞬时失败侧差额都是 0。

未采纳遥测 p90 自估

理由收窄为一条:TelemetryRecorder 目前是纯只写端口,自估需要给它加查询接口并强制所有后端(含 none)实现,公共 API 的扩张远大于它要省掉的一个可选字段;而且尚无实测证据表明派生默认值不够用。等 tpm // 60 在真实负载下暴露问题再说。

前一条回复里我们还论证过"那会把两条方向相反的降级铁律焊在一起",这条撤回——审查指出 p90 完全可以在遥测读失败时回退到纯派生值,限流侧仍能 fail-closed,并非必然冲突。结论不变,理由改了。

一个已知限制,另开 issue 跟

单源 tpm == 0{SCOPE}__GLOBAL__TPM > 0 时,派生值为 0,全局 TPM 闸拿 0 预扣、入场保护形同虚设。这是既有行为(旧约束也只管单源 tpm > 0),本次没修——修它要把 GlobalLimits 注入 QuotaGate、改三处装配,属独立议题。如果你们的部署形态会踩到(只配全局 TPM 而不配单源 TPM),说一声,我们优先排。

合并发包后会在本 issue 补一条,届时再关闭。

## 已实现,方案与前一条回复一致 实现完成在分支 `feature/est-tokens-decoupling`,版本号定为 **1.0.3**(patch:字段与环境键都没删没改名、端口签名未动、API 严格向后兼容,变的是遥测的数据口径)。**尚未合并与发包**,下面写明你们可以动手的时点。 设计与计划都过了独立审查与人类批准门,存档在主仓库 `research-wiki/designs/2026-07-30-est-tokens-decoupling-design.md` 与 `research-wiki/plans/2026-07-30-est-tokens-decoupling-plan.md`。 ## 最终落地的两条 **一、`est_tokens` 不再兼任遥测用量兜底。** usage 帧缺失时库如实记 `0/0`,`usage_source` 取新值 `unavailable`,`cost` 落 NULL。不再拿"最坏情形上界"冒充实测值——你们指出的双重高估(上界当实测 + 整块塞进单价更贵的 completion)确实存在,实测算例是 26 倍。 值域现在三态,每个都有真实生产者: | 值 | 含义 | 生产者 | cost | |---|---|---|---| | `measured` | usage 帧完整可信 | 正常路径;OCR 成功行 | 按 token 换算 | | `estimated` | 有实测数字但可信度降级 | 打捞路径(收到 usage 帧但流被截断) | 按 token 换算 | | `unavailable` | 用量不可得 | usage 帧缺失、失败尝试、终态失败 | **NULL** | **二、`est_tokens` 由必填降为可选调优覆盖,`tpm > 0 ⇒ est_tokens > 0` 的装配约束已删除。** 不填时库按 `max(1, tpm // 60)` 派生——语义是"一次调用约占一秒钟的配额份额",尺度无关:`tpm=6000` 与 `tpm=600000` 都收敛到约 60 个在途,而固定常量会让在途上限随配额规模乱飘。显式填值仍然优先,字段与 `{SCOPE}__{PROVIDER}__{N}__EST_TOKENS` 环境键保留不删不改名。 ## 你们可以动手的时点 **等 1.0.3 发到私有源之后**,CHSAnalyzer 那边可以: - 删掉配置模型里把 `tpm` 限死为 0 的绕行校验,以及为它写的解释注释; - 按供应商配额页填真实 `TPM`,`EST_TOKENS` 留空即可。 **升级时请复核一处**:成本汇总若依赖"cost 非空"这个隐含假设,现在用量不可得的行 cost 是 NULL 了。`SUM(cost)` 会天然跳过它们(这正是选 NULL 而非 `0.0` 的原因——`0.0` 会让"免费"与"未知"在数据上不可区分)。账目缺口这样量化: ```sql SELECT COUNT(*) FROM llm_calls WHERE usage_source = 'unavailable' AND cache_hit = false; ``` `AND cache_hit = false` 不可省:缓存命中行没产生新调用,cost 是事实上的 `0.0`,本无缺口,不加限定会让度量偏高。 按 `usage_source` 分支的代码需要认识新值;历史库里既有的 `estimated` 行语义不变、照常可读。 ## 两处与前一条回复不同的地方 **OCR 那条我们撤回了。** 前一条回复里我们说 `ocr.py` 把无 usage 的 OCR 调用标 `measured` 是"假陈述",准备一并改掉。独立审查推翻了这个判断:库既有立场是 OCR 的 0 token 属**事实**而非未知(`types.py` 的 `Usage` docstring 与 `ocr.py` 模块 docstring 都这么写),所以 `measured` 本就准确。而且改成 `unavailable` 会把本无缺口的 OCR 行灌进上面那个缺口度量,反而破坏了我们用来否决"记 0 但沿用 estimated"方案的核心理由。这条已从范围里剔除,OCR 一字未动。 **实现期还抓出一个我们自己差点埋下的 bug。** 把兜底改成 `(0, 0)` 之后,`retry.py` 与 `embedding.py` 的**成功侧**结算(`actual = prompt + completion`)会变成 0,而入场押的是预扣量,差额为负会把押金整笔退回——对"从不返回 usage 帧"的网关源等于 TPM 闸完全失效。设计初稿只处理了失败侧,是独立审查发现的。现在预扣与结算恒取同一个 `effective_est_tokens()`,成功侧与非 dead 瞬时失败侧差额都是 0。 ## 未采纳遥测 p90 自估 理由收窄为一条:`TelemetryRecorder` 目前是**纯只写**端口,自估需要给它加查询接口并强制所有后端(含 `none`)实现,公共 API 的扩张远大于它要省掉的一个可选字段;而且尚无实测证据表明派生默认值不够用。等 `tpm // 60` 在真实负载下暴露问题再说。 前一条回复里我们还论证过"那会把两条方向相反的降级铁律焊在一起",这条**撤回**——审查指出 p90 完全可以在遥测读失败时回退到纯派生值,限流侧仍能 fail-closed,并非必然冲突。结论不变,理由改了。 ## 一个已知限制,另开 issue 跟 单源 `tpm == 0` 而 `{SCOPE}__GLOBAL__TPM > 0` 时,派生值为 0,全局 TPM 闸拿 0 预扣、入场保护形同虚设。这是**既有行为**(旧约束也只管单源 `tpm > 0`),本次没修——修它要把 `GlobalLimits` 注入 `QuotaGate`、改三处装配,属独立议题。如果你们的部署形态会踩到(只配全局 TPM 而不配单源 TPM),说一声,我们优先排。 合并发包后会在本 issue 补一条,届时再关闭。
Author
Owner

v1.0.3 已发布

已合并到 main(tag v1.0.3)并发到实验室私有源。合并结果上复验 make ci:605 passed,覆盖率 92%。

pip install --extra-index-url https://gitea.iomgaa.online/api/packages/iomgaa/pypi/simple/ \
    "polygateway[redis,postgres,structured]==1.0.3"

你们那边现在可以动手了:删掉配置模型里把 tpm 限死为 0 的绕行校验与相应注释,按供应商配额页填真实 TPM,EST_TOKENS 留空。

升级时记得复核成本汇总对"cost 非空"的隐含假设,缺口查询用 WHERE usage_source = 'unavailable' AND cache_hit = false(cache_hit 限定不可省,理由见上一条)。

wiki 的 指南-遥测与成本解释-治理行为参考-配置键参考-公共API 四页已同步新口径。

绕行校验删干净、跑通之后由你们关闭本 issue;若升级中撞到问题,直接在这里回。

## v1.0.3 已发布 已合并到 `main`(tag `v1.0.3`)并发到实验室私有源。合并结果上复验 `make ci`:605 passed,覆盖率 92%。 ```bash pip install --extra-index-url https://gitea.iomgaa.online/api/packages/iomgaa/pypi/simple/ \ "polygateway[redis,postgres,structured]==1.0.3" ``` 你们那边现在可以动手了:删掉配置模型里把 `tpm` 限死为 0 的绕行校验与相应注释,按供应商配额页填真实 `TPM`,`EST_TOKENS` 留空。 升级时记得复核成本汇总对"cost 非空"的隐含假设,缺口查询用 `WHERE usage_source = 'unavailable' AND cache_hit = false`(`cache_hit` 限定不可省,理由见上一条)。 wiki 的 `指南-遥测与成本`、`解释-治理行为`、`参考-配置键`、`参考-公共API` 四页已同步新口径。 绕行校验删干净、跑通之后由你们关闭本 issue;若升级中撞到问题,直接在这里回。
Sign in to join this conversation.
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: iomgaa/PolyGateway#2