如果你的产品接了不止一家模型供应商,可以自问一个问题:
同一个生成任务,你能保证它只被扣过一次钱吗?
大部分人答不上来。因为接第二家供应商之后,真正难的不再是"调通接口",而是保证每一笔业务只花一次钱。
下面这 7 条,是踩过之后定下来的。
先分清四层,谁管什么
在讲规则之前先说分层。很多坑的根源是分层没想清楚——比如创作端能看见供应商名字,或者网关能碰用户的积分。
| 层 | 该管 | 不该管 |
|---|---|---|
| 创作端 | 公开模型、能力参数、参考素材、预计积分 | 供应商名、上游模型 ID、密钥 |
| 主站 API | 权限、能力校验、选路、计费 | 各家供应商的请求差异 |
| 自有网关 | 协议映射、提交与查询、能力清单、线路治理 | 用户、积分、素材权限 |
| 官方直连 | 网关提交前故障时的兜底 | 已被上游接受任务的二次提交 |
最后一行是整张表的重点:兜底可以,重复提交不行。
规则一 · 幂等键必须稳定
异步生成一次要等几十秒到几分钟。这段时间里,客户端会重试、网络会抖动、服务会重启、用户会狂点提交。
幂等键必须在创建任务时生成,重试时复用——不能每次重试新生成一个。
不然同一个任务会提交两次。轻则用户扣两次钱,重则你扣了一次而上游跑了两个任务,成本自己吃。
规则二 · 拿到任务 ID,先写库再回应
拿到 ID 的那一刻,钱已经出去了。
如果这时候没落库,服务一重启,你就不知道有这个任务在跑、不知道去哪轮询、用户的钱扣了东西拿不到。
同步写入成功,才算提交成功。
规则三 · 备用线路只在"拿到 ID 之前"才允许切
这一条最微妙。
| 失败类型 | 能不能切备用线路 |
|---|---|
| 连接失败(请求根本没出去) | 可以 |
| 提交后超时(请求到了,响应没回来) | 绝对不能 |
原因:你不知道上游到底接没接。 如果接了,你切备用线路重发,就是付两次钱。
规则落成一句话:拿到任务 ID 之后,无论发生什么,都不再切线路。
它把"可能付两次钱"的概率,从"每次超时都可能"降到零。
规则四 · 拿到 ID 后失败,只退款不重发
这条反直觉。直觉上失败了该重试——但在付费异步任务里,重试的代价可能是双倍成本。
所以:已经拿到任务 ID 的任务,失败就是失败,记录 + 原路退款,不创建第二个付费任务。
宁可这一次体验差一点,也不能让账目变得解释不清。
规则五 · 凭据精确匹配,不许跨租户兜底
支持 BYOK(用户自带密钥)的话,这条是红线。
系统通常有"个人 → 团队 → 平台"的凭据查找顺序。这里有个特别危险的默认行为:用户自己的密钥失效了,系统悄悄用平台额度顶上。
用户以为花的是自己的钱,其实花的是你的。
所以:
- 凭据只能精确匹配当前所有者
- 换线路时禁止跨凭据层——用户密钥失效就报错,不许偷偷换平台账号继续跑
- 三层凭据各自独立,互不兜底
这条同时保护两件事:你的钱,和用户对你账目的信任。
规则六 · 付款来源在创建时固化
一次生成要经历:选路 → 预占 → 执行 → 结算或退款。
如果这中间配置变了(团队预算调整、用户换了凭据、线路改了价),退款时就有一个问题:这笔钱退给谁?
所以:任务创建时把付款来源写死在任务记录上,退款只能回原账本或原付款人。
不固化,账目会跟着配置漂移,最后对不上。
规则七 · 密钥不进浏览器,前端不露供应商
两条:
密钥出现在前端,泄露只是时间问题,不是概率问题。
创作端不显示"这条走的是某某供应商的某某模型"——用户看到就会绕开你,直接去找上游。
所以创作端只给三样:公开模型名、能力参数、预计积分。
除了这 7 条,还有三件事
一、线路健康与熔断。 定时探测,连续失败到阈值进冷却,冷却结束自动恢复。没有这个,上游挂了你还在一直接单。
二、统一的任务取消。 用户点取消要触发三件事:退款、标记任务、通知网关去调上游取消。上游不支持取消时,日志里留下明确诊断,别让运维猜。
三、状态归一化。 每家上游的状态命名都不一样,内部只认五个:
QUEUED → RUNNING → SUCCEEDED
→ FAILED
→ CANCELED
外部差异全部在网关层吃掉。
最后
这 7 条看着像技术规则,其实指向同一件事:
别让同一笔业务,被花两次钱。
接第一家供应商的时候,你会觉得这是个技术问题。接第二家你会发现——这是个账目问题。