前几天写过一篇上下文缓存,讲的是实时对话怎么省输入的钱。这次说个更直接的开关:Kimi 的 Batch API。官方文档原话:相比实时 API 调用,可以节省 40% 的推理费用。40% 是什么概念,我之前在API 计费那篇里算过账,等于同样的活,账单直接打六折。
代价是你得等。Batch API 的玩法是把一批请求打包成一个文件传上去,服务器挑空闲算力异步跑,跑完给你一个结果文件。什么时候该用它?我的判断标准就一条:这个任务 12 小时后出结果也不耽误事,就该走这条路。
我自己会用的场景:
- 几千条客服对话做分类、打情绪标签
- 商品评论批量生成摘要
- 资料库整库翻译、润色
- 给自己的评测集跑基线分数
挂在用户请求路径上的活别用,用户等不起。还有一类是管理层明天就要看结果的报表,也别赌异步,老老实实走实时接口。
先说限制,免得白忙
我读文档的习惯是先翻限制,这里直接给你:
- 模型只有两个:kimi-k2.7-code 和 kimi-k2.6。旗舰 K3 暂不支持
- 参数锁死:temperature、top_p、n、presence_penalty、frequency_penalty 都不能写进请求,文档专门加粗提醒了这一条
- 文件必须是 .jsonl,不能为空,最大 100MB,一个组织最多存 1000 个 batch 文件
- 同一批次的所有请求必须用同一个模型
前两条最容易踩。平时习惯在 body 里写 temperature 的(比如我),打包前记得删干净。
四步跑通全流程
下面的流程假设你已经有一个能用的 API Key。还没开通过 Kimi API 的,先看这篇Kimi API 上手教程,把注册和充值搞定再回来。
第一步:把请求写成 JSONL 文件
JSONL 就是每行一个独立的 JSON。一行对应一次完整的对话请求,四个必填字段:
- custom_id:自己起的编号,用来对账,文件内不能重复
- method:固定写 POST
- url:固定写 /v1/chat/completions
- body:和普通 Chat Completions 请求体一样
{"custom_id": "request-1", "method": "POST", "url": "/v1/chat/completions", "body": {"model": "kimi-k2.6", "messages": [{"role": "system", "content": "你是一个文本分类助手"}, {"role": "user", "content": "请分类这段文本:人工智能正在改变世界"}]}}
一千条任务就是把这样的行生成一千行。用 Python 拼字符串循环写文件,几分钟的事。custom_id 建议起有意义的名字,比如日期加序号,后面拿结果对账能省不少劲。
第二步:上传文件
走文件接口,purpose 必须设成 batch:
curl https://api.moonshot.cn/v1/files \
-H "Authorization: Bearer $MOONSHOT_API_KEY" \
-F purpose="batch" \
-F file="@batch_requests.jsonl"
返回结果里拿 file_id,下一步要用。
第三步:创建批处理任务
curl https://api.moonshot.cn/v1/batches \
-H "Authorization: Bearer $MOONSHOT_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"input_file_id": "你的file_id",
"endpoint": "/v1/chat/completions",
"completion_window": "24h"
}'
completion_window 是完成期限,支持 12h、1d、3d 这种语义化写法,最小 12 小时,最大 7 天。文档建议数据量大就把窗口放宽,完成率会更高。
第四步:轮询状态,下载结果
任务一共 8 个状态:validating(校验中)、failed(校验失败)、in_progress(执行中)、finalizing(准备结果)、completed(完成)、expired(超时未完成)、cancelling(取消中)、cancelled(已取消)。
正常路径是 validating 到 in_progress,再到 finalizing 和 completed。轮询 GET /v1/batches/{batch_id} 就行,返回里的 request_counts 能看到进度,比如 300/1000。
跑完后 response 里有 output_file_id,下载它:
curl https://api.moonshot.cn/v1/files/{output_file_id}/content \
-H "Authorization: Bearer $MOONSHOT_API_KEY" \
-o results.jsonl
结果文件也是 JSONL,每行对应一个请求,custom_id 原样带回来了。取回答案就一层一层往下点:
result["response"]["body"]["choices"][0]["message"]["content"]
usage 字段照常给,每条请求花了多少 token,清清楚楚。看到 expired 别懵,那意思是没在 completion_window 里跑完,任务直接终止,把窗口调大重新提一次就行。
图片和视频也能塞进批次
批次里不只能发文本。图片有两种给法:小图直接 base64 编码塞进 JSONL,文档提醒 base64 会让体积膨胀约 33%,100MB 的上限要算在内;大图或者一张图要用很多次的,先走文件接口上传(purpose 设成 image),然后在 JSONL 里用 ms:// 加 file_id 的方式引用。视频同理,purpose 换成 video。
kimi-k2.6 本身支持图片和视频理解,所以批量给图片打标签、批量从视频里提取信息这类活,也能走 batch。做素材库整理的应该用得上。
跑挂了怎么办:失败、取消和重跑
异步任务最常见的两个坑,提前说。
第一是校验失败。任务卡在 validating 然后变成 failed,说明文件本身有问题,常见的就几种:某行 JSON 没写对、custom_id 重复了、一个文件里混了两个模型、body 里带了被锁死的参数。文件越大越容易混进一行坏数据,我现在的做法是生成之后先逐行过一遍再上传,本地校验比等到服务器报错省事。
第二是单条请求失败。整个任务 completed,不代表每条都成功,失败的会单独写进 error_file_id。靠 custom_id 把失败行挑出来,重新组一个小文件再补一轮就行。如果失败比例高,先停下来检查请求格式,别拿六折的价格硬刷错误。
另外两个管理接口顺带记一下:GET /v1/batches 能列出组织下所有任务;发现跑错了,对任务发取消请求,它会经过 cancelling 最后停在 cancelled,已经花掉的钱不退。
参数锁死这条,换个角度想也有好处:整批请求同一个模型、同一套固定参数,输出行为完全一致。拿它做评测基线,比实时接口少一个变量。
和上下文缓存怎么选
之前那篇上下文缓存,解决的是同一段长前缀反复用的问题,对话照常实时。Batch API 解决的是任务又多又不急,拿时间换六折。
两个不冲突。离线批量跑历史数据用 batch,在线服务挂缓存。我自己的顺序是:先把不急的任务全塞进 batch,剩下的实时请求再琢磨缓存。碰到报错分不清是自己的问题还是平台的问题,可以翻那篇API 报错排查。
最后给个实在建议:别上来就跑一万条。先拿 10 条数据全流程走一遍,确认参数没带错、结果格式对得上,再放大文件。异步任务的错误要等一轮才暴露,而窗口最小 12 小时,这个试错成本不值。
你手上有什么适合批处理的活?评论区聊聊。