批量出图最怕的场景一千张跑到第七百张进程挂了。重跑一遍不仅浪费两个小时 前面七百张的积分也白花了。这篇讲在该服务上给批量生图任务加断点续跑挂了能接着跑。一、核心任务状态要落盘不能只在内存内存里的results数组进程一死就没了。每完成一条就写一次const fs require(fs); // 用 JSONL一行一条追加写进程崩了也不会毁掉整个文件 // JSON 数组写到一半崩了文件就是坏的解析不出来 function appendDone(file, rec) { fs.appendFileSync(file, JSON.stringify(rec) \n); } function loadDone(file) { if (!fs.existsSync(file)) return new Set(); return new Set( fs.readFileSync(file, utf8).split(\n).filter(Boolean) .map((l) { try { return JSON.parse(l).key; } catch { return null; } }) .filter(Boolean) ); }用 JSONL 不用 JSON 数组——追加写是原子的崩在半路最多损失最后一行 而 JSON 数组写到一半崩了整个文件都解析不出来。二、每条任务要有稳定的 key续跑靠 key 判断这条做过没有。key 必须由输入决定不能用随机数或时间戳const crypto require(crypto); const keyOf (item) crypto.createHash(md5) .update(item.sku | item.prompt | item.model).digest(hex).slice(0, 12);把model也放进 key换了模型就该重跑不然续跑时会把旧模型的结果当成已完成。三、完整的续跑实现async function runBatch(items, { workers 16, doneFile done.jsonl } {}) { const done loadDone(doneFile); const todo items.filter((it) !done.has(keyOf(it))); console.log(总共 ${items.length} 条已完成 ${done.size}本次要跑 ${todo.length}); if (!todo.length) return; let next 0, ok 0, fail 0; await Promise.all(Array.from({ length: workers }, async () { while (next todo.length) { const it todo[next]; const key keyOf(it); try { const r await fetch(https://API_HOST/v1/chat/completions, { method: POST, headers: { Authorization: Bearer ${process.env.TTQ_API_KEY}, Content-Type: application/json, }, body: JSON.stringify({ model: it.model, messages: [{ role: user, content: it.prompt }] }), signal: AbortSignal.timeout(120_000), }); if (!r.ok) throw new Error(HTTP r.status); const data await r.json(); appendDone(doneFile, { key, sku: it.sku, model: it.model, data, at: Date.now() }); ok; } catch (e) { // 失败的不写 done 文件 —— 下次续跑会自动重试它 fail; console.warn(失败, it.sku, e.message); } if ((ok fail) % 50 0) console.log(进度 ${ok fail}/${todo.length} 成功 ${ok} 失败 ${fail}); } })); console.log(本轮完成成功 ${ok}失败 ${fail}); }失败的不写 done 文件这样下次直接跑就是在重试失败的那些不用另写重试逻辑。 反复跑几次直到本次要跑 0为止。四、几个容易忽略的点进度要打印。跑两个小时一行日志没有没人知道是在跑还是卡死了。每 50 条打一次刚好。别把图片二进制写进 done 文件。只存 URL 或落地后的路径 否则 JSONL 文件几个 G加载done集合就要等半天。中断要能优雅退出let stopping false; process.on(SIGINT, () { stopping true; console.log(收到中断跑完手上的就停...); }); // 循环里if (stopping) break;直接 CtrlC 强杀正在飞的请求积分已经扣了但结果没落盘白花钱。五、模型与消耗模型代码定位消耗nano-banana2跑量首选最快最便宜8 积分/次nano-banana-pro终稿支持 1K/2K/4K10 积分/次gpt-image-2细节质感按次计费Veo 3.1 视频生成/Sora 2生视频120 / 130 积分/次跑量务必用nano-banana2——同样一千张比 Pro 级省两千积分挑中的再用 Pro 重出。六、小结断点续跑四条状态落盘用 JSONL 不用 JSON 数组、key 由输入内容决定、失败的不记账下次自动重试、中断要优雅退出别浪费在途请求。该服务并发高跑量任务才有意义模型全nano banana pro、gpt-image-2、Veo 3.1、Sora 2、价格便宜、出图快、稳定。接口服务甜甜圈APIdashengfenshen.cn