图像任务超时后能不能重试?先把执行状态和验收结果分开
商品图换背景有一个容易误判的时刻:请求超时了,页面没有图片,用户准备再点一次"生成"。
但超时只说明客户端没有及时拿到响应。服务端可能没有接到请求,也可能已经开始处理,甚至已经产生结果。直接重发,有可能创建第二个任务,带来重复计费;直接标成失败,又会丢掉继续查找原结果的线索。
还有一种相反的误判:图片已经返回,背景也很好看,于是任务被标为"成功"。可瓶盖变了、标签串位了,这张图仍然不能交付。
解决这两类问题,第一步不是多加几个重试按钮,而是把两个问题分开:任务有没有执行完成?结果有没有通过验收?
创作说明:本文由 AI 生成。下文代码、时间线与检查结果均为合成设计示例,不是客户案例、生产故障统计,也不对应某个产品的现行接口。
执行完成,不等于验收通过
可以先用两组字段表示状态,而不是把所有信息压进一个 success。
| 维度 | 示例取值 | 回答的问题 |
|---|---|---|
执行观察 execution |
running / succeeded / failed / unknown |
最近取得的执行证据是什么? |
验收项 checks |
每项为 passed / rejected / unknown,缺失也视为待核对 |
商品与用途约束是否得到满足? |
执行证据为 succeeded,验收仍可能是 unknown 或 rejected。这不是状态冲突:前者描述处理过程,后者描述结果是否合格。
这里的执行 unknown 是观察方"暂时不知道"的状态,不是宣称服务端已经进入一个叫 unknown 的终态。查询接口断开,也不能把此前的 running 反向改写为"服务端失败"。应保留上一次已知状态、观察时间和本次查询错误。
用一段合成时间线看重试风险
假设一次生成包含以下过程,时间只是说明顺序,不是服务耗时承诺:
text
t0 客户端为一次业务操作生成 operation_id,保存输入引用与参数版本
t1 服务端受理,并创建 task_id
t2 返回响应途中网络断开,客户端没有取得 task_id
t3 客户端看到超时;原任务可能仍在运行
如果 t3 为了重试换一个全新的 operation_id,再发一次创建请求,服务端很可能把它当成第二次业务操作。
更稳的恢复顺序是:保留 t0 的操作标识,通过正常的只读查询或任务历史对账,尝试找回原任务。有明确的 task_id 时查询同一任务;没有时,能否按 operation_id 查询,取决于服务端是否提供这项合同,不能由前端自己假定。
如果平台没有查询入口,也没有承诺创建接口幂等,就应明确显示"结果待确认",而不是用自动重发掩盖信息缺失。
同一个"失败",下一步可能完全不同
| 已知情况 | 下一步 | 不该做什么 |
|---|---|---|
| 请求超时,服务端是否受理未知 | 只读对账,保留原操作标识 | 仅凭超时认定失败、换标识重复创建 |
| 执行已完成,但输出文件打不开 | 核对结果引用、文件与下载问题 | 立即再调用一次生成模型 |
| 图片可打开,但包装文字无法核对 | 补原图或商品资料,继续人工验收 | 把模型猜出的文字当作事实 |
| 图片已生成,但商品数量改变 | 记录质量拒收原因,调整素材或约束 | 把质量问题当临时网络错误循环重试 |
| 权威任务查询明确返回可重试的执行失败 | 在接口语义、授权与预算满足时评估重试 | 认为"有一个幂等键"就保证不会重复计费 |
重试请求和重新生成不是一回事。 重放同一操作的响应,是否允许重新执行已经失败的任务,是否会再次扣费,都应由接口合同解释。有些接口会缓存第一次失败响应,同一幂等键未必意味着再次生成;有些平台则根本没有幂等保障。
因此,"明确失败"只是评估重试的前提,不是自动执行许可。质量拒收后的再次生成通常是一次新的创作尝试,应保留原结果和退回原因,并按已有费用与操作授权处理。
先固定检查项,再判断能不能交付
以一张瓶装商品换背景为例,可以把验收项固定为:
quantity:商品数量;silhouette:外轮廓与比例;key_parts:瓶盖、泵头等关键结构;text_fact:标签、容量、型号与原始资料是否一致;material:颜色、透明度、磨砂感与必要高光;contact_shadow:落地关系、透视和阴影方向。
这个顺序先看商品事实,再看场景美观。这里的 passed 指完成了该项核对,不是模型自己给出的高置信度,也不是"缩略图没看出问题"。
多 SKU 时还需要把每个检查结果绑定到具体 SKU 和输入版本,不能用"三件都在"替代"三个包装分别正确"。如果输入标签模糊或缺少规格资料,相应字段应保持待核对;生成结果不能反过来证明商品事实。
用纯函数给出下一步建议
下面是一个可以独立运行的 JavaScript 示例。它只返回动作名称,不查询平台、不执行重试,也不创建任何收费任务。
js
const REQUIRED = [
'quantity', 'silhouette', 'key_parts',
'text_fact', 'material', 'contact_shadow',
];
function nextAction(t) {
if (t.execution === 'unknown') return 'reconcile';
if (t.execution === 'running') return 'wait';
if (t.execution === 'failed') {
return t.failureClass === 'transient' && t.retryContextReady === true
? 'retry_candidate'
: 'hold';
}
if (t.execution !== 'succeeded') return 'hold';
if (t.artifactVerified !== true) return 'verify_output';
const checks = t.checks ?? {};
if (REQUIRED.some(key => checks[key] === 'rejected')) return 'revise';
if (!REQUIRED.every(key => checks[key] === 'passed')) return 'review';
if (t.rightsConfirmed !== true) return 'hold';
return 'deliver';
}
retryContextReady 是为了缩短示例而设的前提字段:它要求调用方已经核实失败证据、接口重试/幂等语义、剩余次数、相关操作授权与费用预算。它不能因为前端生成了一个 UUID 就变成 true。即使返回 retry_candidate,也只是允许进一步评估,并没有执行重试。
artifactVerified 只表达结果文件与当前任务的对应关系、可读取性等检查已经完成。文件哈希能帮助锁定同一份文件,不能证明包装文字正确、素材有权使用或画面可交付,所以质量检查与 rightsConfirmed 仍然单独保留。
固定 REQUIRED 还有一个小作用:避免把空对象上的 every 当作全通过。少一项检查,得到的应该是 review,而不是绿色交付状态。实际业务如果允许某项"不适用",应先在该任务类型的验收合同中明确定义,不要临时删掉未通过的项。
这些字段也必须绑定到同一任务、同一输入/参数和输出版本。替换图片后不能复用旧图的验收结果;生产系统应重新验证输入和授权,不能把这个展示用纯函数当作安全或计费依据。
用边界样例检验,而不是只测绿色路径
可以先用合成对象检查下面几组行为:
| 输入 | 预期建议 |
|---|---|
| 执行未知,即便本地留下了全部通过的检查记录 | reconcile |
| 执行完成,文件尚未核对 | verify_output |
执行完成,检查对象为空或缺少 text_fact |
review |
| 执行完成,任一必需项明确拒收 | revise |
| 必需项全部通过,但素材权利未确认 | hold |
| 执行完成,文件、全部检查项与素材权利均确认 | deliver |
| 明确的临时执行失败,但重试前提未齐 | hold |
| 明确的临时执行失败,重试前提已齐 | retry_candidate,仍不执行 |
它们只能检验决策函数是否按设计分流,不能验证模型出图质量、服务端幂等、真实费用或任务成功率。后几项仍需要各自的真实合同与验证证据。
页面要告诉用户下一步,不只是一个颜色
执行完成、验收待确认时,可以展示:
text
任务:处理完成,可查看结果
商品验收:标签文字待核对
下一步:对照原图与商品资料检查,暂不进入交付
执行结果未知时则展示"查询原任务",而不是把"再次生成"包装成无风险的刷新。
跨页面恢复时保留操作标识、已知 task_id、输入/输出版本和最后观察时间即可。日志不要保存用户 token、临时签名链接或无必要的原图内容;展示错误也不应泄露这些信息。
最后,统计时把"执行成功数""质量通过数""结果待确认数"分开。不能只用拿到图片的次数计算交付成功率,也不能把查不到的任务当作失败后从分母里删掉。
unknown 的价值不是让状态枚举更复杂,而是给证据不足留一个真实的位置:原任务先对账,生成结果再验收。做到这两点,超时和质量问题才不会被同一个"重试"按钮混在一起。