Gemini 批量任务怎么把响应对回请求:key 字段的用法

2026-08-25

数据截至 2026-08,价格与限额以各官网为准。本文只讲计费与接入机制,不列具体价格数字。

先把结论摆这儿:Gemini Batch API 里,官方明文给出的、能让你把每条响应对回原始请求的机制只有一个——你自己定义的 key,响应会用同名 key 标注。不是靠数组下标,不是靠时间戳,也不是靠你提交时的行号。所以 key 该怎么取,是你在写第一行提交代码之前就要定下来的事,而不是等结果落盘之后再想办法补救。批量作业一次提交的条数往往超出人工逐条核对的范围,一旦对账逻辑建立在错误的假设上,返工成本远比重跑一次批量作业高。

先划清 Batch API 的边界,别用错了地方

官方页首有一条注意事项写得很直白:Batch API 目前仅适用于 generateContent API。这句话的实际含义是,你在别处用惯的那些接口——不管是别的调用形态还是别的能力入口——不能想当然地套到批量里来。如果你的业务链路里混了非 generateContent 的调用,那部分就不能打包进批量作业,得走另外的路径。这是设计批量流水线时第一个要做的分流判断。

Batch API 的定位是异步:官方把它描述为处理大批量请求的方式,给出了一个目标周转时间(具体时长以官方文档为准,多数情况下会更快完成),适用场景官方举的例子是数据预处理、跑评估这类不需要立即响应的大规模非紧急任务。计费上,Batch 属于官方列出的四种服务档之一,按低于标准档的价格计费,具体比例见官方定价页。

正因为它是异步的、按作业为单位交付的,「对账」才成为一个真问题。同步调用你发一条收一条,请求和响应天然一一对应;批量则是你丢进去一大包,过一段时间捞出来一大包,中间这层对应关系必须由数据本身携带。

key 到底是什么:官方给的那一句

事实很简单,也很重要:用户自定义的 key 用于把响应对回请求,响应会用同名 key 标注。

这句话里有两个要点值得拆开看。

第一,key 是用户自定义的。也就是说,它不是平台生成后返还给你的某个作业内序号,而是你在构造请求的时候自己写进去的字符串。这个性质决定了你可以把它和自己业务系统里的主键直接对齐——比如商品 ID、工单号、数据集里那条样本的行标识。key 一旦和业务主键同构,结果回来之后你连映射表都不用维护,直接拿 key 去落库就行。反过来,如果你图省事随手用了自增序号,那就必须在本地额外保存一份「序号 → 业务对象」的对照关系,多一份状态就多一个出错的地方。

第二,响应是用同名 key 标注的。这意味着你在结果里看到的是原样的 key,不需要做任何转换或反解析。所以 key 应当满足两个朴素的工程要求:在一个批次内不重复,以及在你的落库逻辑里可以直接当索引用。关于平台层面是否会对 key 的唯一性、字符集、长度做校验或拒绝,官方文档里没有找到相关说明,所以别指望平台帮你兜底——重复 key 会导致什么行为是没有官方明文的,稳妥做法是在提交之前自己在本地做一次去重校验。

内嵌提交:输出是 inlineResponse 对象列表

Batch API 有两种提交形态,第一种是内嵌请求(inline)。做法是直接把一组 GenerateContentRequest 对象放进 BatchGenerateContentRequest 里,官方给它标注的适用范围是总请求体较小的批次。

内嵌形态的输出结构是 inlineResponse 对象列表。对账时你要处理的就是这个列表,逐个取出其中携带的 key,再和你提交时那份请求集合做映射。

内嵌形态的好处是链路短,不涉及文件上传,调试期尤其省事——你想验证 key 的取法对不对、结果结构长什么样,用一个只含少量请求的内嵌批次去试,比一上来就走文件通道要快得多。但它的限制也摆在明面上:官方明确它适合总请求体较小的场景,请求体一大就该换形态了。

JSONL 提交:每行不一定都是结果,解析器必须分支处理

第二种形态是输入文件,格式是 JSONL:每行一个完整的 GenerateContentRequest。官方对这种形态给的定位是推荐用于较大请求集。

输出同样是 JSONL。这里有个细节,是整篇文章里我最希望你记住的一条:输出 JSONL 的每一行,是 GenerateContentResponse 或者状态对象

也就是说,你不能假设「输出文件有 N 行,就是 N 条成功的生成结果」。逐行解析的时候,你的代码必须先判断这一行到底是一条正常响应,还是一个状态对象,然后才决定往哪条分支走。如果解析器写成了「打开文件、逐行 json 解析、直接按生成结果的固定结构去取文本字段」这种一根筋的形态,那么读到状态对象那一行时取字段必然落空或抛异常;异常再往外一冒,整个循环就断在那儿,前面已经解析好的部分也跟着一起丢。

正确的写法有三个要点。第一,逐行独立处理,每一行套自己的错误捕获,单行失败不影响整体推进。第二,先按行的结构分流再取值,而不是先取值再看取没取到——因为官方明文说了输出行有两种形态,这就是设计中的正常分支,不是异常。第三,状态对象那一行也要落盘,连同它携带的 key 一起记进失败清单,这样后续重跑时才能精确定位到那几条,而不是把整个批次推倒重来。

key 在这里的价值就体现出来了:无论某一行是成功响应还是状态对象,你都能凭 key 知道它对应的是哪条业务数据。没有 key,一旦中间掺进了非结果行,你按行号做的映射立刻就全错位了。

顺带说一句:多模态输入可以在 JSONL 里引用其他已上传的文件。也就是说图片、音视频这类内容不必塞进 JSONL 正文,而是先上传再引用,JSONL 行本身仍然保持成一条结构化的请求描述。

提交链路:文件怎么上去,作业怎么建

JSONL 文件通过 File API 上传。走 REST 的话,官方给的是 resumable upload 协议,请求头写 X-Goog-Upload-Protocol: resumable。用可续传协议而不是一次性表单上传,对大文件场景是必要的——中断之后可以接着传,而不是从头再来。

作业创建这一层,三种形态官方给的调用签名分别是:

Python:  client.batches.create(model=..., src=..., config={'display_name': ...})
JS:      ai.batches.create({model, src, config: {displayName}})
REST:    POST /v1beta/models/{model}:batchGenerateContent

src 就是数据来源,内嵌形态和文件形态的差别主要落在这个参数上。config 里官方示例传的是展示名(Python 写 display_name,JS 写 displayName),具体用途与可选字段以官方文档为准。

展示名这个参数值得认真填。批量是异步交付的,提交和取结果之间隔着一段时间,这段时间里你手头能用来指代这个批次的信息越明确越好。建议把批次的业务含义和提交时间一起编进展示名里,别留空,也别一律写成 test。至于展示名在平台侧还会出现在哪些位置、有没有长度或字符集限制,官方文档里没有找到相关说明,所以别把它当成一个可以塞结构化信息、事后再解析回来的字段用——它就是给人看的一个标签。

批量有自己的一套限流和结算特性

对账做完不等于事情结束,还有两件事和批量强相关。

一是限流。官方明确 Batch API 的限流完全独立于非批量调用,另外还有四类约束:并发作业数、输入文件大小、文件存储总量、每模型的排队 token 数(这几项的具体数值见官方文档,都是会变的)。「独立」这两个字的实际后果是双向的——你的批量作业不会因为在线业务打满限流而排不上队,反过来你也不能拿在线业务的限流经验去推算批量能吃多少量。另外那条「文件存储总量」提醒了一件容易被忽略的事:上传上去的 JSONL 是占存储配额的,跑完的批次该清就清,不然攒着攒着就会顶到上限。顺带一提,Gemini 的限流是按项目应用而不是按 API 密钥应用,这一点在限流按项目还是按密钥里单独讲过。

二是结算延迟。官方列出的延迟里有一条特别点名了批量:结算流水线本身存在延迟,在这段时间里可能产生超额用量,而批量模式与 Agent 这类长时间运行的任务,尤其容易在系统停止之前继续消耗。翻译成运维语言就是:别指望支出上限能像闸刀一样在批量作业上瞬间生效。第一次跑大规模批量时,稳妥做法是先用小批次探路,把单条请求的 token 规模摸清楚,再按条数线性放大去估整批的量级,监控口径可以参考API 成本监控怎么搭这篇的通用做法。

最后:批量之前先想清楚这三件事

第一,key 的取法。提交之前就把它和业务主键对齐,别等结果回来了再补映射表。

第二,解析器的容错。输出 JSONL 里混着状态对象是官方明文说明的行为,不是异常,你的解析逻辑要把它当成正常分支来写,并把失败条目连 key 一起记下来。

第三,形态的选择。请求体小就用内嵌,请求集大官方推荐走 JSONL 文件,这个分界线官方给的是定性描述而不是一个具体数值,实际操作中可以在调试期用内嵌、生产期用文件。如果你还在纠结该用缓存省钱还是用批量省钱,缓存和 Batch 该用哪个这篇讲的是跨厂商的通用取舍思路。

最后还有一点关于错误处理的提醒,得先把话说清楚:官方那份错误码参考文档是按 Interactions API 给出的,批量作业失败时具体会返回哪一套错误码、码值与同步调用是不是一一对应,官方文档里没有找到明确说明。所以别照着同步调用的错误码去硬编码批量侧的分支判断,稳妥做法是先把状态对象那一行的原始内容整条留存下来,等真正拿到样本再决定怎么归类。

不过退避策略本身的思路是可以提前想明白的。官方对两个都是 429 的错误给出的处置完全不同:一个是超出每分钟、每秒的请求或 token 限制,官方推荐等待加指数退避重试;另一个是每日配额耗尽,退避多少次都没意义,只能等配额重置或者申请提额。这两者的区别在Gemini 的两种 429里拆得比较细。批量场景条数多、跑得久,如果把这两类混在一起统计,很容易得出「重试策略没用」的结论,而真实情况多半是策略用错了地方。

想系统学会用 AI?报名体系课或加入会员,照着学、照着用。