Files
Genarrative/docs/technical/【技术方案】LLM累计额度结算-2026-09-05.md
suzmii 736a1b6ac6
Project CI / Repository checks (pull_request) Successful in 2m36s
Project CI / Frontend tests (pull_request) Successful in 3m15s
Project CI / Backend tests (pull_request) Successful in 7m35s
Project CI / Native shell tests (pull_request) Failing after 7m43s
接入 LLM Router 累计额度结算
按 Router used_quota 累计值与首次基线结算泥点
新增原子 checkpoint 事务及 llm_router_consume 钱包流水
同步额度查询校验、前端展示、生成绑定和技术文档
2026-09-06 00:36:31 +08:00

3.0 KiB

LLM 累计额度结算

目标与边界

使用 New API GET /api/user/{id}used_quota 累计值结算,不按 token 估价,不依赖单次响应 cost,不建立本地 WAL、余数 Map 或延迟队列。模型调用前同步并检查余额,成功响应后再同步;失败、断流或进程退出留下的消耗由下一次调用前同步补结算。不承诺无后续调用的闲置账号立即结清。

单位与算法

Router GET /api/statusdata.quota_per_unit 已实测为 500000,即每美元 500000 quota。美元数值直接乘 10 转泥点,不使用 USDExchangeRate。每泥点对应 50000 quota,采用整数运算。查询时验证单位不变,异常时拒绝结算,不静默改价。

pending = max(observed_used_quota - settled_quota, 0)
charged_points = min(pending / 50000, spendable_points)
next_settled_quota = settled_quota + charged_points * 50000

settled_quota 只推进实际扣费对应的部分。查询失败、钱包被冻结、扣款失败时不推进。重复或乱序快照不回退游标;Router 重置累计值需人工核对,不自动清零本地记录。

持久化与事务

新增私有表 llm_router_billing_checkpoint,主键沿用已认证用户的 llm_router_account.account_key,保存 router_user_idsettled_quota 和更新时间。账号 owner/route 来自现有本地映射,不信任客户端提交的 Router ID;同一映射更换 Router ID 时拒绝结算,需明确迁移。

首次同步在模型请求之前执行:若没有 checkpoint,以当前累计额度完整建立基线,不扣历史,不抹零。现有账号与首次接入账号均在使用前建立基线。

只有服务身份可以调用结算 procedure。事务内先检查人工冻结与退款欠款限制(包括尚不足一整点与首次初始化),再读取 checkpoint,计算可扣金额,更新钱包和流水,最后推进 checkpoint;三者同一事务提交。返回的剩余可消费余额扣除了退款占用与本次消费,模型调用前据此拒绝零可用余额。并发、重复快照和响应丢失后重试不能重复扣费。流水 ID 由账号与已结算额度区间构成,而非单次请求 ID。

新增流水来源 llm_router_consume,显示“LLM 调用消耗”,保持免费/会员/永久泥点消耗顺序、退款冻结和消费统计。历史资产来源流水不改写;旧资产生成扣费/退款协议不变。

验收

  • 首次基线、234 余量跨次保留、重复/乱序快照、余额不足、零余额、整数溢出均有定向测试。
  • Router 查询验证业务 success、用户 ID、非负整数 used_quota、quota_per_unit;错误不打印凭据或原始用户数据。
  • 模型请求前基线失败关闭;成功模型响应不因后置同步故障变为失败,下一次可重试。
  • schema 同步 migration、表目录、生成绑定,运行定向 Rust 测试、schema guard、编码检查、diff check。
  • 本次不部署、不迁移历史钱包流水、不修改外部 OpenAPI。