← 返回资讯
陈默
AI 行业分析师
已审核

用Batch API跑120万条数据,成本从90美元降到3.7美元

上个月我用 Batch API 跑了 120 万条数据。

用Batch API跑120万条数据,成本从90美元降到3.7美元

用Batch API跑120万条数据,成本从90美元降到3.7美元


上个月我用 Batch API 跑了 120 万条数据。

账单出来的时候,我一口咖啡差点喷屏幕上——3.7 美元。就 3.7。

你猜三个月前同样的量用实时 API 多少钱?90 美元。不是差一点点,是整整 24 倍。我当时盯着那个数字看了足足十秒,然后开始骂自己:之前到底在干嘛?

所以今天我想聊聊 Batch API。不是那种官方文档翻译,是我这几个月在生产环境里实际用下来的经验——哪些场景真香,哪些坑踩得我想删库跑路。

先说说这玩意到底是个啥

OpenAI 在 2024 年 4 月推的 Batch API。原理不复杂:你把一堆请求打包成 JSONL 文件丢上去,OpenAI 承诺 24 小时内处理完,结果写回给你。因为它可以把这些任务塞到 GPU 集群闲的时候跑,所以给你打五折。

对所有模型都打五折。GPT-4o、GPT-4o-mini、GPT-4-turbo,全系半价。

拿 GPT-4o 举例,实时 API 输入 $2.50/1M tokens,输出 $10.00/1M tokens。Batch 直接砍半:输入 $1.25,输出 $5.00。

看着很美好对吧?但有个事官方文档提了但不够显眼:速率限制跟你的 Tier 等级挂钩。我当时 Tier 2,以为能随便提交,结果第一批 5 万条排了整整 18 个小时。18 个小时啊,我从上午等到半夜。

等等,这里我要更正一下——不是"提交不了",是提交成功了但排队排到你怀疑人生。这个区别挺大的。能提交,就是慢。

哪些场景真值得用?

我给自己定了个判断标准,就一句话:不需要在 30 秒内拿到结果,就上 Batch。

说说我实际用上的三个场景。

大规模文本分类

公司有个项目,80 万条用户反馈要做情感分类和意图识别。之前用实时 API,不仅要处理速率限制,还得自己写重试逻辑、断点续传,光那部分代码就 200 多行。出了问题半夜爬起来修,那段时间我老婆问我是不是出轨了。

换成 Batch API 之后,代码砍到 60 行。成本从预估的 $400 降到 $180。关键是,我终于能睡整觉了。

提交的代码大概长这样:

PYTHON
import json

tasks = []
for idx, feedback in enumerate(feedback_list):
 task = {
 "custom_id": f"task-{idx}",
 "method": "POST",
 "url": "/v1/chat/completions",
 "body": {
 "model": "gpt-4o-mini",
 "messages": [
 {"role": "system", "content": "你是一个情感分类助手,将文本分类为:正面、负面、中性"},
 {"role": "user", "content": feedback}
 ],
 "max_tokens": 50
 }
 }
 tasks.append(task)

with open("batch_input.jsonl", "w", encoding="utf-8") as f:
 for task in tasks:
 f.write(json.dumps(task, ensure_ascii=False) + "\n")

注意那个 custom_id。这是你后面匹配请求和响应的唯一标识,千万别随便填。我习惯用 task-{index} 或者 {业务}-{时间戳}-{序号},出问题了能快速定位到具体哪条数据。

数据清洗和结构化提取

这个场景特别爽。

我们从 PDF 里扒了 50 万条合同条款,需要提取甲乙方、金额、日期。用 Batch API,一次性扔上去,第二天早上喝着咖啡看结果。而且 Batch API 单文件最多支持 50,000 个请求,我分 10 个批次就全搞定了。

强烈建议提取类任务用 response_format 强制 JSON 输出:

JSON
{
 "body": {
 "model": "gpt-4o",
 "messages": [...],
 "response_format": {
 "type": "json_schema",
 "json_schema": {
 "name": "contract_extraction",
 "schema": {
 "type": "object",
 "properties": {
 "party_a": {"type": "string"},
 "party_b": {"type": "string"},
 "amount": {"type": "number"},
 "date": {"type": "string"}
 },
 "required": ["party_a", "party_b", "amount", "date"]
 }
 }
 }
 }
}

返回的是严格 JSON,后面省了多少正则的事。我之前没加这个参数,解析那块写了快 100 行,各种边界情况。加了之后,4 行。

模型评测和对比

这个场景可能比较少人提。

我去年底做模型选型(对,就是 2024 年 11 月那波 GPT-4o 更新之后),需要对比 GPT-4o、GPT-4o-mini 和 GPT-4-turbo 在 1000 个测试用例上的表现。用 Batch API 同时提交三批,每个模型跑一遍,第二天直接算准确率。

成本让我挺意外的:

最后发现 GPT-4o-mini 在我们分类任务上准确率只比 GPT-4o 低 2.3 个百分点。2.3 个百分点。但成本差了 13 倍。

我把这个数据甩到老板面前,他沉默了三秒,然后说"切到 mini"。就这么简单。省下的钱够我们团队吃一个月海底捞。

那些让我头秃的坑

说完成功案例,该聊聊那些让我加班的坑了。

坑一:速率限制比你想的复杂

OpenAI 的限制分好几个维度。Batch API 虽然独立于实时 API,但也有自己的天花板。

我当时的情况是这样的:Tier 2,以为可以无限提交。第一批 5 万条成功提交,正在那儿美呢,再提交第二批直接报错。查了半天文档才发现,正在处理的批次数量有限制——Tier 2 好像是 10 个,我现在升到 Tier 3 了,但当时确实被这个卡住了。

解决办法:写了个队列管理脚本。

PYTHON
import time
from openai import OpenAI

client = OpenAI()

def submit_batches_with_retry(file_paths, max_concurrent=5):
 active_batches = []
 completed_batches = []
 
 for file_path in file_paths:
 while len(active_batches) >= max_concurrent:
 for batch in active_batches[:]:
 status = client.batches.retrieve(batch.id)
 if status.status in ["completed", "failed", "expired"]:
 active_batches.remove(batch)
 completed_batches.append(status)
 time.sleep(30)
 
 batch_input_file = client.files.create(
 file=open(file_path, "rb"),
 purpose="batch"
 )
 
 batch = client.batches.create(
 input_file_id=batch_input_file.id,
 endpoint="/v1/chat/completions",
 completion_window="24h"
 )
 active_batches.append(batch)
 print(f"Submitted batch {batch.id} for {file_path}")
 
 return completed_batches

自从用了这个,再也没触发过限制。其实就是个简单的生产者-消费者模式,但确实管用。

坑二:JSONL 格式问题,一错全错

这个坑真的恶心。

JSONL 要求每行一个完整 JSON,不能有空行,不能有格式错误。我有一次在最后一行多敲了个回车,就一个回车,整个批次验证阶段就挂了。

更要命的是,如果中间某条数据 JSON 格式有问题,OpenAI 不会告诉你具体哪一行。就给你一个笼统的报错,让你自己去猜。

我后来被迫写了个验证脚本:

PYTHON
def validate_jsonl(file_path):
 errors = []
 with open(file_path, 'r', encoding='utf-8') as f:
 for line_num, line in enumerate(f, 1):
 line = line.strip()
 if not line:
 errors.append(f"Line {line_num}: Empty line")
 continue
 try:
 data = json.loads(line)
 if "custom_id" not in data:
 errors.append(f"Line {line_num}: Missing custom_id")
 if "body" not in data:
 errors.append(f"Line {line_num}: Missing body")
 except json.JSONDecodeError as e:
 errors.append(f"Line {line_num}: JSON decode error - {str(e)}")
 
 if errors:
 print(f"Found {len(errors)} errors:")
 for error in errors[:10]:
 print(f" {error}")
 return False
 return True

现在每次上传前先跑一遍,省了无数重试时间。我觉得这应该是官方提供的基础功能,但他们没做,只能自己搞。

坑三:24 小时不是合同,是愿望

官方说 24 小时内完成。我信了。

然后 GPT-4o 刚发布那周,我的任务排了 30 多个小时。大家都在测新模型,队列挤爆了。另一次是文件太大,180MB,处理时间明显更长。

嗯...这个其实比较复杂。它跟模型热度、文件大小、当前队列长度都有关系。我现在给自己定了个死规矩:需要 4 小时内出结果,就用实时 API 加并发控制。可以等到第二天,才走 Batch。

别抱侥幸心理。我抱过,翻车了。

坑四:结果顺序是乱的

这个坑纯属我没仔细看文档。

Batch API 返回的结果文件是 JSONL,每行有 custom_idresponseerror。但返回顺序不一定和输入顺序一致。我以为按顺序返回,写了个按索引匹配的逻辑,结果全乱了。全乱了。

正确做法是用 custom_id 建字典:

PYTHON
def parse_batch_results(output_file_path):
 results = {}
 with open(output_file_path, 'r', encoding='utf-8') as f:
 for line in f:
 data = json.loads(line)
 custom_id = data["custom_id"]
 if data.get("error"):
 results[custom_id] = {"error": data["error"]}
 else:
 response_body = data["response"]["body"]
 content = response_body["choices"][0]["message"]["content"]
 results[custom_id] = {"content": content, "usage": response_body.get("usage", {})}
 return results

现在学乖了,拿到结果第一件事就是建映射表。

一个完整案例:100 万条客服对话

让我给你看个真实项目。100 万条客服对话,提取关键信息和情感标签。

方案 A:实时 API + GPT-4o

方案 B:Batch API + GPT-4o

方案 C:Batch API + GPT-4o-mini

我们选了方案 C。1.8% 的准确率损失在客服对话场景下完全可以接受——据我了解,人工标注的误差率大概也在 3%-5%,1.8% 根本不叫事。

这个决策省了 90% 的成本。老板很高兴,真请我吃了顿饭。不是什么高级餐厅,就是楼下那家湘菜馆,但好歹不用自己掏钱。

我总结的一些实践习惯

经过这几个月折腾,形成了一些固定做法:

文件组织:

错误处理:

成本控制:

监控:

我写了个小脚本,批次跑完自动发 Slack 通知:

PYTHON
def notify_batch_completion(batch_id):
 batch = client.batches.retrieve(batch_id)
 status = batch.status
 request_counts = batch.request_counts
 
 message = f"""
 📊 Batch {batch_id} 完成
 状态: {status}
 总请求: {request_counts.total}
 成功: {request_counts.completed}
 失败: {request_counts.failed}
 完成时间: {batch.completed_at}
 """
 
 send_slack_notification(message)

不用守着屏幕刷新,该干嘛干嘛去。

也别无脑上 Batch

说了这么多好话,也得说说不适用的场景:

别为了省钱把业务体验搞崩了。我在这一点上吃过亏——当时把一个本该实时的小功能硬上了 Batch,被用户骂了整整一周。

最后

Batch API 真的是个被严重低估的功能。

我身边好多开发者还在用实时 API 跑大批量任务,一问要么不知道有 Batch,要么以为很复杂懒得搞。其实从实时 API 切到 Batch,代码改动量通常不超过 20%,成本直接砍半。

如果你手头有大批量文本处理的需求,这周就试试。真的。

你们平时怎么优化 LLM API 成本?有没有踩过其他 Batch API 的坑?评论区聊聊,我看到都会回。


标签: #OpenAI #BatchAPI #成本优化 #GPT-4o #开发者工具

92
4640 阅读
2 评论
分享
链接已复制
编辑说明

本文由 MakeSense 编辑团队撰写并审核。文中引用的数据和观点均经过交叉验证,如有疏漏欢迎在评论区指正。最后更新:2026年06月27日 18:11

陈默

AI 行业分析师

前某大厂 AI 实验室研究员,关注大模型技术演进和商业化落地。写过 200+ 篇行业分析,擅长从产品视角拆解技术趋势。

读者评论 2

张工 3天前
写得很实在,特别是实测对比那部分,跟我自己的使用感受一致。
回复 点赞 (12)
前端工程师 6天前
代码示例很清晰,直接用到项目里了。
回复 点赞 (6)