Back
NimBuild AI

NimBuild AI

AI 扣完积分后生成失败,应该怎么退款?

AI 扣完积分后生成失败,应该怎么退款?

AI 生成请求常常必须先消耗积分,再开始外部工作。这带来一个尴尬窗口:用户已经付费,但提供商还没有返回可用结果。

NimBuild 用显式补偿对象处理这个窗口。

补偿模式

文档中的流程是:

const compensation = createCreditCompensation({
  userId,
  amount: creditsNeeded,
  reason: "credit_adjustment_refund",
  referenceId,
});

try {
  await doExternalWork();
  compensation.settle();
} catch (error) {
  await compensation.compensate();
  throw error;
}

顺序是有意的:

  1. 检查是否可负担。
  2. 在数据库事务中扣除积分。
  3. 准备补偿。
  4. 调用提供商。
  5. 外部工作成功后结算。
  6. 失败后通过账本退款。

补偿路径不会原地改余额,而是生成可审计退款事件。

文档示例使用通用的 credit_adjustment_refund 演示这个 helper。AI 工具工作流还定义了更具体的 ai_generation_refund,用于把 AI 提供商补偿和其他积分调整区分开。

什么算失败?

对生产 AI 工作流来说,失败不只是 HTTP 500:

  • 提供商超时
  • 认证或额度错误
  • 无效提供商响应
  • 缺少必需输出字段
  • 内容不安全或不可用
  • 存储失败导致必需资产无法读取

上线前要明确哪些失败可退款。NimBuild 的补偿模式适用于“积分已扣除、外部工作随后失败”的操作。

账本原因为什么重要

NimBuild 用 ai_generation 表示消耗,用 ai_generation_refund 表示提供商失败补偿。这些名称让客服能区分:

  • 正常使用扣费
  • 失败生成退款
  • 人工客服调整
  • 通用产品退款

如果缺少这些区分,每次余额恢复看起来都一样,后续做风控、单位经济模型和提供商可靠性分析都会更困难。

生成历史是承诺的一部分

AI 工作区会记录生成历史。用户应能查看自己请求了什么、结果是什么;失败时能看到错误,成功时能找回工作流结果。

历史也提供运营上下文。偶尔一次超时,和影响大量客户的区域性提供商故障,应该有不同响应。

避免两个诱人的捷径

捷径一:提供商成功后再扣积分

这避免了退款,但会让用户在没有足够余额时也能无限启动外部工作,也会在两个请求并发读取同一余额时产生竞态。

捷径二:捕获所有错误后悄悄恢复余额

这看起来用户体验友好,却丢失了账务上下文。之后没人知道退款来自提供商失败、客服政策,还是系统 Bug。

NimBuild 的方式更严格:原子扣除,然后显式补偿。

测试你自己的工作流

  1. 余额不足的请求是否会启动外部工作?
  2. 两个并发请求是否会被阻止重复消费同一积分?
  3. 重试逻辑执行两次时,补偿是否幂等?
  4. 超时是否准确退回扣除金额?
  5. 失败操作是否出现在历史里?
  6. 管理员账本能否看到原始扣费和退款?
  7. 重试与退款是否使用同一个稳定引用?

失败后的用户体验

告诉用户发生了什么,以及恢复了什么:

  • 生成失败。
  • 已扣除积分已退回。
  • 可以重试。
  • 如果再次失败,可以联系支持排查。

不要隐藏失败,同时让积分消失。付费 AI 产品的信任来自可见的对账。

补偿流把提供商失败从余额 Bug 变成可审计的客户恢复流程。