不要把第几行当作第几个任务
异步Message Batches结果不保证与提交顺序一致。应在提交前保存唯一custom_id与业务任务的对应关系,读取结果时再按这个ID回连;不能把结果文件第二行直接写给输入第二行的客户。
本篇处理结果归属与缺失清单,不提供自动发起收费批次的脚本。若你还没跑过单条请求,先在测试环境确认输入结构,再考虑批量。
先有清单,再提交批次
虚构输入清单是A、B、C三份不同文档。每个ID只对应一份固定版本;保存提交批次的标识、输入版本和预期条数,不把同一个ID复用于内容已经变化的新任务。
结果文件先返回B失败、再返回A成功,C尚未见到。这时能确认的是A/B有记录、C待核对;既不能给C补一个A的答案,也不能宣布三条都成功。
本站原创示意图:A、B、C不能按结果行号回填。虚构乱序结果;custom_id不是全局幂等保证。查看大图 ↗完整性、处理结果、内容验收分开
官方结果以JSONL逐行给出,每行有custom_id与result。先验证ID存在于预期集合且没重复,再按succeeded、errored、canceled、expired等分支处理;未知类型保留人工检查。
succeeded意味着这一条有消息结果,仍要检查停止原因与内容。自然结束也只进入事实/格式验收;批次处理结束更不等于每条请求成功。
运行乱序示例
将文末代码存为batch-result-reconcile.mjs,用Node.js 22或24 LTS执行:node batch-result-reconcile.mjs --demo。结果应先标B为review_errored,再标A为validate_content,missing为[C],complete为false。
顺序按输入结果保留,但归属由ID决定。该演示读取内置对象,不创建批次、不轮询、不下载真实结果文件。完整JSONL读取器还需逐行解析、处理大文件及保留错误行号。
发现缺失与重复,先停止入库
如果重复收到A,不能以“最后一条为准”覆盖先前结果;若见到从未提交的Z,也应隔离并核对批次标识和文件来源。函数对这两类情况直接抛错,避免悄悄串单。
少了C时先查看服务器真实处理状态和结果文件是否完整下载;本地清单少一行不等于服务器从未处理。没有查清就把整个批次重发,可能重复处理已经完成的A。
只为明确问题安排下一步
对请求参数错误,先修输入;对取消、到期或服务错误,保留原因,由上层在预算和授权范围内决定是否新建尝试。示例没有自动重试,也不把custom_id当成官方承诺的全局幂等键。
测试覆盖乱序、缺失、重复、未知ID、非自然结束及未知结果类型,所有输出publishable都为false。未进行真实批次调用、计费或大文件性能测量。
参考资料与核验范围
资料核对日期:2026年10月19日。以下为官方原始资料;产品入口和可用范围仍以当前账户为准。
1. [官方资料1](https://platform.claude.com/docs/en/build-with-claude/batch-processing)
TRY THIS / 可复制模板
把方法变成一次练习
// 本地对账器;不创建批次、不轮询、不重发请求。输入应为已解析的JSONL各行。
export function reconcileBatch(expectedIds,rows){
if(!Array.isArray(expectedIds)||!expectedIds.length||expectedIds.some(x=>typeof x!=='string'||!x)||new Set(expectedIds).size!==expectedIds.length||!Array.isArray(rows))throw Error('输入清单必须为非空、唯一ID');
const expected=new Set(expectedIds),seen=new Set(),items=[];
for(const row of rows){
if(!row||!expected.has(row.custom_id)||seen.has(row.custom_id))throw Error('未知或重复custom_id;停止入库');
seen.add(row.custom_id);const type=row.result?.type;
if(type==='succeeded'){
const msg=row.result.message,content=msg?.content;
const ready=msg?.stop_reason==='end_turn'&&Array.isArray(content)&&content.length>0&&content.every(x=>x?.type==='text'&&typeof x.text==='string');
items.push({id:row.custom_id,action:ready?'validate_content':'review_message',publishable:false});
}else if(['errored','canceled','expired'].includes(type))items.push({id:row.custom_id,action:'review_'+type,publishable:false});
else items.push({id:row.custom_id,action:'review_unknown_result',publishable:false});
}
return {items,missing:expectedIds.filter(x=>!seen.has(x)),complete:seen.size===expected.size};
}
if(process.argv.includes('--demo'))console.log(JSON.stringify(reconcileBatch(['A','B','C'],[{custom_id:'B',result:{type:'errored'}},{custom_id:'A',result:{type:'succeeded',message:{stop_reason:'end_turn',content:[{type:'text',text:'虚构结果'}]}}}])));第三方 AI 工具 · 邮箱注册 · 付费使用。模板为编辑设计,实际效果取决于输入与模型。