经一把 key 跑 OpenAI Batch 风格的 JSONL 任务:聚合站保住了哪些步骤
面向需要在聚合站上跑批量 JSONL 任务的开发者:讲清提交前如何组织请求行、批处理返回中不同后端(Claude、GPT、DeepSeek、Qwen、GLM、Kimi)常见的字段与错误差异,以及聚合站把响应归一化并把错误映射回原始行时应关注的关键点。
为什么批处理在聚合站上值得单独讲
批处理(Batch)适合「量大、可等待、对延迟不敏感」的任务,比如离线打标、批量摘要、评测集跑分。
自己直连多个厂商时,你需要为每一家写一套 JSONL 格式、一套上传流程、一套结果解析。用聚合站做这件事的价值不在于「便宜」,而在于把差异收敛在一层:一个 API key、一套鉴权、一套请求体,后端换模型时客户端代码不用重写。
本文只讲流程和概念,不涉及具体价格与配额。
第一步:先把 JSONL 写对
批处理文件的本质是「一行一个请求」。聚合站通常沿用 OpenAI Batch 风格的 JSONL 结构,每行至少包含:
custom_id:你自己定义的行标识,必须唯一,它是后面把结果映射回原始行的唯一依据。method与url:一般是POST加对话补全路径。body:真正的请求体,含model、messages等字段。
写文件时有几个容易翻车的点:
- 一行必须是完整 JSON,行内不能换行;长 prompt 用
\n转义而不是真换行。 - 文件编码统一 UTF-8,末尾不要留多余空行。
custom_id不要用自增整数,建议带上业务前缀,例如task-2024-tag-000123,避免和别的批次混在一起时冲突。- 同一个文件里可以混用不同
model,这是聚合站相对单厂商直连的一个实际便利:一次提交,按行路由到不同后端。
提交与轮询:状态机是一样的
批处理是异步的,典型生命周期是:创建批次 → 上传/内联文件 → 得到批次 ID → 轮询状态 → 完成后下载结果文件。
聚合站通常会把不同厂商的状态名归一化成少数几个(例如进行中、已完成、失败、已取消、部分完成),这样你的轮询逻辑不用为每家写分支。
注意「部分完成」这个状态:它意味着有行成功了、有行失败了。不要把它当整体失败处理,否则会白跑一次。
各后端的结果差异出现在哪
即使输入格式统一,后端返回的细节并不一致。实际会遇到的差异主要有几类:
1. 响应体结构
GPT 系与多数兼容 OpenAI 协议的后端,对话结果在 choices[0].message.content;Claude 原生返回是 content 块数组;部分国产模型在 reasoning_content 之类的字段里放思维链。聚合站做归一化的重点之一,就是把「正文」抽到统一位置,让你按同一个路径取值。
2. token 用量字段
有的后端给 prompttokens / completiontokens,有的给 inputtokens / outputtokens,有的还区分缓存命中与未命中的输入。归一化后一般会同时给出总量与明细,明细字段缺失时不要假设它一定是 0,可能只是该后端没上报。
3. 结束原因
stop、length、content_filter、工具调用等在不同厂商的命名和粒度不同。如果你的下游逻辑依赖「是否被截断」,要基于归一化后的结束原因判断,而不是原始字符串。
4. 错误粒度
这是批处理最关键的一环。失败可能发生在三个层级:
- 整批失败:鉴权、余额、文件格式问题,整批直接拒掉,没有逐行结果。
- 单行失败:某一行模型名不存在、参数非法、上下文超长。
- 行内成功但内容异常:模型拒答、被安全策略拦截,HTTP 层面是成功。
第三类最容易被漏掉,它不会出现在错误列表里,只会出现在结果行里。
归一化响应:把错误映射回原始行
这是聚合站真正替你省事的地方。归一化后的结果文件应当满足:每一行都能按 custom_id 唯一对应到输入文件的某一行,且行数可核对。
具体要关注三件事:
完整性:结果行数应等于输入行数。如果有行既不在成功列表也不在错误列表,那就是丢了,必须能检测出来。做法是拿输入文件的 custom_id 集合减去输出文件的集合,差集就是缺失行。
错误对象的结构统一:单行错误应被包成一个统一的错误对象,包含错误类型、可读消息、以及该行自己的 custom_id。这样你可以按错误类型分组重试,而不是逐行读文案。
可重试性标记:区分「重试有意义」(限流、超时、后端临时不可用)和「重试无意义」(参数错误、内容被策略拒绝)。归一化层如果给出这个标记,你的重试脚本就能只重跑真正该重跑的行——把失败行抽出来,重新拼成一个小 JSONL,再提交一批。
一个稳妥的工程习惯
把批处理当成一次可重放的数据处理任务:
- 输入文件保存到对象存储,带版本号。
- 记录批次 ID 与输入文件的对应关系。
- 下载结果后先在本地做集合校验,再做业务解析。
- 只对可重试的错误行做二次提交,并给重试批次单独的
custom_id前缀,避免和原批次混淆。
这样即便中途换了后端模型,整条链路也不需要改,换的只是 JSONL 行里的 model 字段。
计费上的一个提醒
聚合站按官方价乘一个系数计费,贡献 key 的一方按另一个系数返 USDC。批处理任务量大,跑之前先估算 token 总量,跑完用用量字段核对,比事后对账省事。具体系数以站点公示为准,不要按「折扣价」的心理预期去规划预算。