生图API 怎么避免重复计费加一层结果缓存和请求去重nano-banana-pro 实践有个月对账我发现出图次数比预期高了三成。查下来原因很蠢同样的提示词被重复调用了。重复是怎么产生的三个来源都很常见一、用户手抖。生成按钮点了没反应其实在跑用户又点一次两个一模一样的请求同时打出去。二、重试打偏。网络超时触发重试但服务端其实已经生成成功了只是响应没回来。重试等于又生成一张。三、批量任务重跑。跑到一半挂了重跑的时候没有断点前面成功的那些又生成了一遍。三种加起来浪费的比例相当可观。解法一个 key 管两件事给每次请求算一个内容指纹用它同时做缓存和去重。constcryptorequire(crypto);functionfingerprint({model,prompt,aspect,size,refImage}){returncrypto.createHash(sha256).update(JSON.stringify({model,prompt,aspect,size,refImage:refImage||})).digest(hex);}关键是把所有影响出图结果的参数都算进去。漏掉aspect_ratio的话同一句提示词出方图和长图会撞成同一个 key取到错的缓存。缓存 在途去重constcachenewMap();// fp - 结果constinflightnewMap();// fp - 正在跑的 PromiseasyncfunctiongenImage(params){constfpfingerprint(params);// 1) 已经生成过直接返回if(cache.has(fp))returncache.get(fp);// 2) 正在生成中复用同一个 Promise —— 这一步挡住了「用户连点两次」if(inflight.has(fp))returninflight.get(fp);constpcallGenApi(params).then((r){cache.set(fp,r);returnr;}).finally(()inflight.delete(fp));inflight.set(fp,p);returnp;}inflight这层比缓存还重要。缓存只能挡住已经跑完的重复而用户连点、并发重试制造的是同时发出的重复——那时候缓存里什么都没有只有在途表能挡住。什么时候不该走缓存得留一个绕过缓存的口子否则用户会困惑“我就是想换一张为什么每次都给我同一张。”asyncfunctiongenImage(params,{noCachefalse}{}){if(noCache)returncallGenApi(params);// ...}界面上的「重新生成」按钮走noCache: true正常路径走缓存。持久化内存 Map 重启就没了批量任务重跑的场景挡不住。生产上建议落到 Redis 或数据库key指纹value图片地址 生成时间TTL按图床的链接有效期定别缓存得比链接还久甜甜圈APIdashengfenshen.cn返回的是图片链接如果你把图转存到了自己的存储缓存里存自己的地址就不用担心过期问题。上线之后重复调用基本归零那三成的额外消耗省下来了。顺带的好处是用户体验变好了连点两次不会等两倍时间第二次直接复用第一次的结果秒返回。这类优化投入很小——核心逻辑就上面那二十来行——但账单和体验都能立刻看到变化属于性价比很高的一类活。接口服务甜甜圈APIdashengfenshen.cn