Files
codex-app-server/docs/chv-muse-p11-gap.md
T
host 60c371d9d5 feat(chv): 对齐 Muse P11 用量计量与配额执行——四项计量 + 三档档位门 + 哈希链账本
- cloud/usage.py(新):TIER_LIMITS 三档提案(Free $0/Paid $19/Heavy $99,
  提案非定价,payload 带 proposal 标注)、TierStore(无档位行不受档位门
  约束)、UsageLedger(追加写哈希链,篡改可发现)、UsageMeter(token 读
  cost_records、消息读账本、并发/存储注入实测)、QuotaGate(纯准入判定,
  402,不碰在跑任务)、80% 提醒每维度每窗口一次
- cloud/server.py:_reject_if_over_quota 内加档位门(同一漏斗,双人门
  自动覆盖;查询异常 fail-closed 503)、GET /users/me/usage、
  GET/POST /admin/tiers、存储实测复用 _task_worktree_for_files
- docs/chv-muse-p11-gap.md:差距表(先写表再动手)+ 诚实边界(USD 路径
  30 秒轮询掐断与“不掐断”语义不一致是已知待决;并发即拒与设计文档
  排队不同;cloud 任务存储量不到;Heavy 消息/天提案未给,本轨定不限)
- 测试 22/22,全量 1889 OK(skipped=8);mypy 在 usage.py 上干净,
  server.py 5 错均为基线旧错(marker_tools/sub2api/_row_get,与本轨无关)
- 真机验证排队在 round-11 与 P4~P9 之后;不接真实计费
2026-11-06 22:42:30 +00:00

139 lines
9.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 对齐 Muse P11 差距表:用量计量与配额执行
依据:`~/workspace/research_notes/meta-muse-ai-architecture-20261101-0755/report.md`
(官方帮助中心 / 新闻室 2026-09-08 为准;第三方转述只作参考,不得把
第三方转述的价格写成官方定价)。基线:`06c3e17`。先写表再动手。
## §1 要求(官方 + 本项目提案)
| # | 要求 | 来源 |
|---|---|---|
| 1 | 免费但有使用上限;达到上限可升级付费订阅,或等免费额度刷新 | 官方帮助中心(报告 §1.5/§6) |
| 2 | 免费额度具体数字、付费档价格未出现在首发新闻稿中;Power/Maximum 等价格是第三方转述的帮助中心内容,**不得写成官方定价** | 报告 §1.5、§5 表 |
| 3 | 三档档位与价格沿用本项目提案(用户 2026-11-06 委托“按市场定”,**未确认为定价**):Free $0 / Paid $19/月 / Heavy $99/月 | 本项目提案,见 §2 |
| 4 | 四项计量:token 数、消息数、并发任务数、存储量 | 本轨任务书 + 设计文档 §6.2 |
| 5 | 超限回 402;进行中回合不掐断,跑完才拦下一回合 | 本轨任务书 + 设计文档 §6.2 |
| 6 | 用量展示:用户可查当期用量与剩余额度;接近上限提醒一次(阈值默认 80%,本项目自定) | 本轨任务书 |
| 7 | 账本追加写、不可篡改;账本里绝不含对话原文、文件内容、密钥 | 本轨任务书 |
| 8 | 不接真实计费、不碰真实钱;计费对接(Stripe 等)待用户另行批准 | 本轨任务书 |
档位提案数值(**提案,非用户确认定价,非官方定价**):
| 档位 | 价格 | 消息/天 | tokens/月 | 并发任务 | 存储 |
|---|---|---|---|---|---|
| Free | $0 | 50 | 200,000 | 1 | 1 GB |
| Paid | $19/月 | 500 | 2,000,000 | 5 | 10 GB |
| Heavy | $99/月 | 不限(见 §5 第 4 条) | 20,000,000 | 20 | 100 GB |
## §2 现状(基线 06c3e17 已有,可复用)
- `cloud/cost.py:CostStore`:`cost_records(task_id, project_id, model,
tokens, usd_micros, source, day)`,`actual`(模型真实用量事件)与
`estimated`(字符估算)**永不混算**;价格表是 OpenAI 挂牌价,注释明言
“不是 relay 的账单”;`report_by_task/project/day`,**无 `report_by_user`**。
- `cloud/db.py`:`user_quotas(user_id PK, quota_usd_micros, updated_at)`。
- `cloud/server.py`:`_quota_status`(USD 月度配额,UTC 自然月)、
`_check_quota` / `_reject_if_over_quota`(超限回 402 `quota_exceeded`,
查询异常 fail-closed 回 503 `quota_check_unavailable`)、
`_check_unpriced_token_cap`(无价 token 上限,默认 20M/月,可配)、
`GET /users/me/quota`、`GET/POST /admin/quotas`。
准入点是建任务 / 分支 / 重试 / 续跑 / goal-resume,均为权限校验通过后、
写库之前;`_reject_if_over_quota_for_task` 双人门(触发人 + 原任务创建人)。
- 全仓 grep 确认:`cost_records` **没有任何 UPDATE / DELETE 路径**,
token 账本已是追加写;本轨不再为 token 另建一套账本(见 §3 第 2 条)。
缺失:消息数计量、并发任务门、存储门、三档档位表、用量展示端点、
80% 提醒、账本防篡改校验、`report_by_user`。
## §3 改动清单(本轨)
1. `cloud/usage.py`(新,标准库 + `cloud.db` / `cloud.cost` 的常量口径,
不引 server):
- `TIER_LIMITS`:三档提案数值,模块 docstring 与每个返回 payload
都标注 `proposal: true`(提案,非定价)。
- `TierStore`:`user_tiers(user_id PK, tier, updated_at)`,`get_tier` /
`set_tier`(非法档位名拒绝);**无档位行的用户不受档位门约束**,
只受既有 USD 配额约束——定档是管理员显式操作,不给存量用户
默认套 Free(否则上线即拦住所有人)。
- `UsageLedger`:`usage_ledger` 追加写哈希链账本(`prev_hash` +
`entry_hash = sha256(规范 JSON)`),`record` / `sum_kind` /
`verify_chain`(篡改返回首个坏行 id)。记消息、任务起止、存储
快照、提醒事件;**token 事件不重复记**(见第 2 条)。
- `UsageMeter`:月度 token(读 `cost_records` JOIN `tasks`,与
`_quota_status` 同款查询,actual / estimated 分开列)、当日消息
(读账本)、并发(注入 `running_task_count`,状态 `queued` /
`running`)、存储(注入 `storage_bytes`,磁盘实测)。
`summary(user_id)`:四项 used / limit / remaining + 提醒 + 账本
校验结果。
- `QuotaGate`:`check_task_admission`(token + 并发 + 存储)、
`check_message_admission`(消息/天 + token/月)。返回 None 放行,
否则否决 dict(`error: quota_exceeded` + `dimension` + `limit` /
`used` + 可读 `detail`);**纯准入判定,无任何终止在跑任务的
API**(进行中回合不掐断,见 §5 第 1 条)。
- `disk_usage_bytes(path)`:递归求和,不跟随符号链接,读错跳过
(尽力而为,调用方在 payload 里标明实测范围)。
- 提醒阈值默认 0.8,环境变量 `CLOUD_USAGE_WARN_RATIO` 可配;
每维度每窗口只记一次提醒(账本 `kind="warning"` 去重)。
2. token 计量口径:以模型方返回为准(`source="actual"`);无返回时
字符估算(`source="estimated"`,payload 标注为估算)。**档位门只计
actual**——与 `cost.py` 的 P1 决定一致(“Estimated rows are never
real spend and must not trip the cap”);estimated 只展示、不拦人。
但 payload 同时列出 estimated 数,让用户看得见估算占比。
3. `cloud/server.py` 接线(只动配额漏斗,不新增调用点):
- `_reject_if_over_quota` 内、USD 门与无价 cap 通过后,加档位门
(同一 `quota_user_id`);档位查询异常同样 fail-closed 回 503。
`_reject_if_over_quota_for_task` 不动(它已委托给前者,双人门
自动覆盖)。
- `GET /users/me/usage`(`_user_usage_self`):当期用量与剩余额度
+ USD 配额合并返回 + 提醒 + 账本校验结果。
- `GET/POST /admin/tiers`(`_admin_tiers_get/set`,`_is_global_admin`
同款鉴权):管理员定档 / 查档。
- 存储实测:复用 `_task_worktree_for_files` 逐任务解析本机 worktree
求和;cloud / cloud-hypervisor 任务不在本机,量不到,计 0 并在
payload 里标明 `storage_measured_tasks` / `storage_total_tasks`。
4. `cloud/tests/test_usage_quota.py`(新):档位数值钉住提案、非法档位
拒绝、无档位行不限档位门、token actual/estimated 口径、消息固定
UTC 日窗口、并发门、存储门与清理指引、账本哈希链与篡改发现、提醒
只发一次、准入纯判定不碰在跑任务、服务端点(`/users/me/usage`、
`/admin/tiers`、档位门 402)。
5. `docs/chv-production-readiness.md`:补 P11 一节。
## §4 真机待验证清单(未做,排队在 round-11 与 P4~P9 之后)
- 真实 token 回传下的月度累计与 402(actual 行累计、无价模型走
既有 cap 不走美元门)。
- 并发门在真实多任务下(建第 N+1 个任务被拒,在跑任务不受影响)。
- 存储门在真实 worktree 下(本机任务目录求和、cloud 任务不计入)。
- 80% 提醒在真实使用下只发一次、跨窗口重置。
- 账本在真实写入压力下的链完整性(并发写不丢链不断)。
## §5 诚实边界(本轨内不解决,写清不冒充)
1. **进行中回合的语义不一致(已知待决)**:设计文档 §6.2 与本轨
任务书说“进行中的回合不中途掐断”;但基线 `_check_quota` 的
docstring 与 worker 实现是另一套——Local popen 与 Docker 路径的
worker 轮询每 30 秒查一次 USD 配额,超限杀进程组/容器(掐断粒度
是轮次边界 + 30 秒检查)。本轨新增的四项档位门**只做准入**,
不改既有 USD 路径的轮询掐断;两处语义不一致是已知待决项,
不在本轨内悄悄统一,须由父级另行裁决。
2. **并发超限的处理与设计文档不同**:设计文档 §6.2 说并发超限
“新任务排队,不立即拒绝”;本轨任务书(父级指令)说“即拒并
给出可读原因”。本轨按任务书即拒,差异记在此。
3. **存储计量不完整**:cloud / cloud-hypervisor 任务的 worktree
不在本机,量不到;本机也只能按任务 worktree 求和,用户在
VM 内或其它路径的占用不在计量内。payload 标明实测范围,
不冒充全量。
4. **Heavy 消息/天提案未给**:提案原文只给了 Heavy 的 tokens /
并发 / 存储,未给消息/天。本轨定为不限,并标注为**本项目决定**,
不是提案原文、不是官方口径。
5. **窗口口径**:token 按 UTC 自然月(与既有 USD 配额一致);
消息按 UTC 自然日(固定窗口,非滑动);并发与存储是瞬时值。
固定窗口边界存在少量跨窗毛刺,文档写清,不冒充滑动窗口。
6. **价格与计费**:档位价格是助手提案(用户委托按市场定,未确认),
不是用户确认定价,更不是 Meta 官方定价;payload 与文档都带
`proposal` 标注。真实计费(Stripe/支付/开票/充值)不在本轨,
待用户另行批准;任何把“记账”等同于“收费”的说法都是错的。
7. **token 价格**:USD 换算沿用 `cost.py` 的 OpenAI 挂牌价表,
注释明言不是 relay 账单;无价模型(无价格表项)的 token 只
计量、不折算美元,仍走既有无价 token cap。