MuseMVP
MuseMVP 文档
AI

积分与成本控制

积分账本、套餐与积分包、服务端拒绝、每日预算、退还与管理员恢复。

账本

每一次余额变化都是 D1 中只追加的 ledger 表里的一行,余额就是这些行的总和。每行的 ID 编码了用户、类型与来源,所以重试、队列重复投递或连续点击写入的是同一个 ID,会被直接忽略:

类型ID 格式写入时机
grant{user}:grant:{source}邮箱验证后的欢迎积分、已支付订单、经审计的管理员发放
reserve{user}:reserve:{task}生成请求发给提供方之前
release{user}:release:{task}预占的生成失败、被取消或被退还时,只写一次

成功的生成直接保留预占,完成时不会再扣一次。每行还带有 reason 与 ref,用量页 /app/usage 会把它们显示成可读的说明。新增任何写入方都必须设置这两个字段,冲正行要从被冲正的行复制它们。

不要在组件里或请求之前扣积分,也不要在队列重试中再扣一次。余额只能通过这些账本写入方改变。

售卖积分

可售目录位于 packages/config/src/index.ts:

条目kind默认值
Proplan,订阅每月 1,000 积分
Lifetimeplan,一次性10,000 积分
1,000 creditspack,一次性1,000 积分;设置 requiresPlan 可只卖给已有套餐的用户

积分包一定是一次性购买。积分数在结账时写入订单,之后修改目录也不会改变已有订单发放的数量。按 支付配置 填写各网关的商品 ID 并启用网关。

服务端拒绝

每次生成都在服务端计价,并在预占之前完成拒绝判断:

状态码代码触发条件
402余额与下一步余额低于价格;响应会说明下一步是购买套餐还是积分包
429too_many_active_tasks用户已有 ai.maxActiveTasks 个未完成任务
503capacity_reached今日预估上游花费加上本任务会超过 ai.dailyBudgetUsd

界面上禁用按钮不能替代这些检查。

每日预算

每个目录模型都带有预估的 costUsd。预占任务时会把预估值计入 UTC 零点以来的花费;这个上限由所有用户共享,用来防止滥用或定价失误导致账单失控。Mock 运行计为零成本。把 ai.dailyBudgetUsd 设为 0 可关闭上限,同时也请在提供方一侧设置限额。

毛利检查保证定价合理:只有当 积分 × 最便宜的积分单价 ≥ costUsd × (1 + ai.minGrossMargin) 时模型才有效。修改价格或积分包后请重新运行 pnpm test:unit。

失败与退还

  • 可重试的提供方失败最多再尝试 ai.maxAttempts(3)次,最终失败只退还一次积分。
  • 当 ai.refundOnContentRefusal 为 true 时,提供方的内容拒绝会退还积分。
  • 每 5 分钟的 cron 会回收过期租约,退还用尽尝试次数或超时的任务(视频为 30 分钟),并补发 outbox。

管理员恢复

管理员在 /app/admin 查看最近的 AI 任务,数据来自 GET /api/admin/ai_tasks:状态、尝试次数、错误类别、成本,以及积分是否已退还。POST /api/admin/ai-refund,正文 { "taskId": "..." },可为已结束的任务退还一次积分;再次调用会返回 409 not_refundable。通过 POST /api/admin/credits 发放的积分会记录审计。

验证

  1. 积分不足时发起生成,确认返回 402 且余额不变。
  2. 同时发起超过 ai.maxActiveTasks 个生成,确认返回 429。
  3. 在 Live 测试中调低 ai.dailyBudgetUsd,确认返回 503。
  4. 取消一个排队中的任务,或让一个 Live 任务失败,确认只有一行 release。重试与退还由 pnpm test:workers 覆盖。
  5. 以管理员身份对同一个已结束任务退还两次,确认第二次被拒绝。

本页目录