7.6 KiB
ADR-0172:不可变 Model Price Quote 与 Canonical Cost Settlement
- 状态:Accepted
- 日期:2026-07-27
- 关联:RFC D-12、D-13、D-157、D-160、D-161、D-162;ADR-0167、ADR-0168、ADR-0170、ADR-0171
背景
ADR-0170/0171 已经建立不可变 usage ledger 与 provider I/O 前的 Project quota reservation,但费用仍有三个未封闭的权威裂缝:
- provider 返回的
usage.costMicros既不可验证,也可能缺失、使用不同币种或被 adapter 错误换算; - model policy 的人工
maxCostMicros只能作为上限,不能证明调用使用了哪一版价格; - 价格变化后,如果只保存 model 和最终费用,就无法按历史 revision 重算、审计或精确重放;
- Cluster 多副本可能在一次调用期间观察到不同价格,本机重启也可能丢失当时的计价输入;
- 为价格目录、报价和结算各拆 package/service 会放大路由设备的 importer、制品与运维成本, 却没有独立部署或权限收益。
决策
1. Policy 必须选择一个精确、不可变的价格 revision
ModelInvocationPolicy.priceRevision 与 provider/model 共同选择
qinglong/model-price-catalog-entry@v1。条目固定:
USD;- 每百万 input/output token 的整数 micro-USD rate;
- publisher time 与内容摘要;
- provider、model、price revision 的精确身份。
启用单次费用上限或 Project cost quota 时,priceRevision 和精确 catalog entry 都是必需
条件;缺失必须在 provider I/O 前失败关闭。无费用约束且 priceRevision=null 的调用继续
允许 usage cost 为 nullable,不强迫所有部署启用计价。
当前 catalog 是有界、注入式 ModelPriceCatalogResolver。它冻结运行时消费契约,但不
宣称已经完成数据库 catalog、签名发布、激活、撤销或管理 UI;这些运营能力必须另行建立
权限和发布协议。
2. Provider I/O 前生成不可变最坏情况 Quote
Gateway 在 durable admission 前生成
qinglong/model-invocation-price-quote@v1。Quote 绑定 invocation、Project、model policy
revision、provider/model、price revision、币种、input/output rate、catalog digest、
token 上限与独立 digest。
费用预留按 maxTotalTokens 和 maxOutputTokens 中合法且最昂贵的 input/output 分配计算,
不能用调用方估计值。每个 token 维度分别向上取整到整数 micro-USD,并用 BigInt 做中间
运算;越界失败关闭。Quote 的 reservedCostMicros 同时是单次费用 fence 和 Project
cost quota reservation 的权威输入,替代人工复制的 reservation 数值。
3. Provider 报价不是账单权威
provider 返回 usage 后,Gateway 只信任经过既有上限校验的 input/output/total token。
usage.costMicros 无论是 null、错误还是任意值,都由 Quote 重新计算的 canonical cost
替换。返回给调用方、Completion、UsageLedger、QuotaSettlement 使用同一个 canonical
usage。
qinglong/model-invocation-price-settlement@v1 绑定 Quote digest、Completion digest、
精确 token、canonical cost、settled time 与独立 digest。usage 缺失时不得创建零费用
Settlement;unknown outcome 保留 Quote 和 quota reservation,但不伪造价格结算。
4. Quote 与 Settlement 进入既有原子事务
准入事务原子提交:
StepRun ready → running、Run/Event/Mutation;- ModelInvocationStart;
- PriceQuote;
- 可选 QuotaReservation。
完成事务原子提交:
- terminal StepRun/Run/Event/Mutation;
- Completion;
- 可选 UsageLedger;
- usage 存在时的 PriceSettlement;
- 可选 QuotaSettlement。
exact replay 必须复验全部预期事实;Quote/Settlement 缺失、意外存在、digest 或字段漂移均 失败关闭。持久化 JSON 的一致性比较按字段进行,不依赖 SQLite TEXT 或 PostgreSQL JSONB 的对象键顺序。
5. 9004 保持可选、append-only 和双方言一致
不改写 9001/9002/9003。新增:
- SQLite
9004-ai-model-pricing-snapshots; - PostgreSQL
pg-9004-ai-model-pricing-snapshots; - PriceQuote/PriceSettlement 两张双方言表。
PostgreSQL ql3_runtime 只有 SELECT, INSERT,没有 UPDATE, DELETE;其它业务角色与
PUBLIC 不获得权限。Quote 与 Settlement 不是第二状态机,StepRun 仍是执行权威。
6. 不新增 package、依赖或后台服务
价格 contract、resolver 和计算留在已有 @qinglong/ai/pricing;Gateway、coordinator、
repository 与 Profile 复用现有 AI package。没有新增 workspace package或第三方依赖。
AI 禁用时不解析 catalog、不执行 9001–9004、不加载 provider/credential/storage,也不 创建 timer。edge/standalone 只在显式启用 AI 时把该模块纳入制品;Cluster 可注入未来的 数据库/配置发布 resolver,但不能把价格管理 authority 放进常驻 runtime。
本 ADR 不开放 HTTP/MCP/UI,也不把静态 resolver 解释为产品级价格目录管理。
被否决方案
- 直接信任 provider
usage.costMicros:来源、币种、revision 与舍入不可验证。 - 只保存最终费用:价格变更后不能历史重算或证明计价输入。
- 调用完成后读取最新价格:一次调用可能跨 revision,Cluster 副本会产生不同结果。
- 用浮点美元计价:序列化与舍入会产生跨运行时漂移。
- 把 unknown usage 结算为零:允许预算绕过并伪造账单事实。
- 为 pricing 新建 workspace package/service:没有独立部署、依赖或权限边界,只会增加 路由器制品、lockfile importer 和 CI 成本。
- 立即把 catalog 管理写入 runtime 表:会把运营写 authority 与调用 runtime 合并。
当前验证
@qinglong/ai:70 项测试,69 pass、1 条 PostgreSQL 条件 skip;真实 PostgreSQL 18 migration/runtime 分角色集成另 1 pass;- SQLite 覆盖 exact revision、最坏情况 Quote、provider cost 覆盖、PriceSettlement、 quota 原子提交、exact replay,以及 priced unknown recovery 保留 Quote/全额 reservation 且不创建 Settlement;
- PostgreSQL 真实集成覆盖 JSONB 回读、9004 DDL/ACL、Quote/Completion/Ledger/ PriceSettlement/QuotaSettlement 同一事务;
- SQLite 9004 checksum 为
572e37d2f44df43a50b51a07c1b4b0bb87fbb22e9cafbd3421ec7ab250036951; PostgreSQL 9004 checksum 为d38b12c2640fdd9fe21dc43a4743fb3480c988fa0a87e210fd81074d87569d2f; - QL3 22 个 package importer 的完整拓扑 build/test 退出 0;dependency audit 覆盖
@qinglong/ai16 个源码文件且findings=[]; - edge-ai 为 4,363,769 bytes/512 files/41 loaded modules,standalone-ai 为 4,363,841 bytes/512 files/41 modules,均低于 5 MiB/640 files;disabled AI 只加载 1 个模块、RSS 增量 425,984 bytes,三种 Profile 的 storage/provider loader 均为 0;
- PostgreSQL 18.4 arm64 physical HA 在 timeline 1→2 promotion 前后精确复验八张
ql3_ai表、9001–9004 history/checksum 和八表 runtime append-only ACL;optionalAiFeatureSchemaSurvivesPromotion=true且总passed=true。
后续门禁
- 建立签名或受权限保护的 durable catalog publisher、activation/revoke 与配置 ceremony;
- 明确非 USD 币种、汇率 revision、cached-input/批处理等 provider-specific 计价维度;
- 增加 Quote/Settlement 数据行级 COMMIT-response-loss、promotion 与并发 revision 切换 fault,而不只验证 schema promotion;
- 完成认证、Project Policy、rate limit、低敏 audit 的 pricing/usage/quota API、CLI/UI;
- 完成不可变 rollup、coverage receipt、retention 和磁盘耗尽证据。