批量推理任务完整指南

批量推理任务完整指南 · Batch Inference Complete Guide

批量推理任务完整指南(概念→实践→阿里云百炼实战)理解批量推理的核心价值,掌握阿里云百炼 BatchAPI 的完整使用流程

批量推理 vs 实时推理、适用场景、JSONL 数据准备、任务提交与监控、结果获取、成本优化、常见问题全覆盖

一、什么是批量推理(Batch Inference)

批量推理(Batch Inference)是指将大量独立的推理请求汇总成一个批次,统一提交给模型进行处理,在后台离线执行并异步返回结果的计算模式。

准备数据 上传文件 提交任务 后台处理 获取结果

批量推理的四大特征

异步处理 提交任务后立即返回,无需等待,后台排队处理

高吞吐 充分利用 GPU 并行计算能力,单位时间处理更多请求

低成本 阿里云百炼 BatchAPI 成本仅为实时调用的 50%

离线执行 不占用实时服务资源,适合非时效性任务

二、批量推理 vs 实时推理对比

维度批量推理(Batch)实时推理(Online)
响应方式异步,提交后后台处理同步,即时返回结果
延迟要求分钟级~小时级,可接受毫秒级~秒级,要求低延迟
成本低(约为实时的 50%)高(按实时调用计费)
吞吐量高(GPU 批量并行)受并发限制
数据规模大规模(千~百万条)小规模(单条或少量)
适用场景数据分析、内容生成、模型评估对话应用、实时推荐、在线客服
资源利用闲时调度,资源复用率高常驻服务,预留资源
容错性支持断点续传,失败可重试单次失败即返回错误

✅ 选择批量推理

需要处理数万条文本分类

批量生成营销文案

对历史数据做情感分析

模型效果批量评估测试

非工作时间的数据处理

❌ 选择实时推理

用户实时对话交互

在线推荐系统

实时风控判断

需要即时响应的 API

单次、少量、高频请求

三、适用场景与核心价值

3.1 典型应用场景

数据分析

对海量用户评论、反馈、日志进行情感分析、主题分类、实体提取,洞察业务趋势。

内容生成

批量生成商品描述、营销文案、翻译内容、代码注释,提升内容生产效率。

模型评估

使用标准测试集对模型进行批量评估,获取准确率、F1 分数等指标。

数据标注

利用模型预标注数据,人工复核修正,降低标注成本 70% 以上。

3.2 核心价值总结

批量推理的核心价值公式:更低的单位成本 × 更高的吞吐量 × 灵活的时间窗口 = 大规模 AI 处理的经济最优解。

四、阿里云百炼 BatchAPI 概览

阿里云百炼 BatchAPI 是专为大规模、非实时推理任务设计的异步处理服务,支持通过文件一次性提交海量请求,在后台离线处理。

阿里云百炼 BatchAPI 核心能力

文件提交 支持 JSONL 格式文件,每行一个独立请求

模型丰富 支持通义千问、Llama、DeepSeek 等主流大模型

成本优化 批量推理价格约为实时调用的 50%

状态追踪 提供任务创建、排队、运行、完成全流程状态查询

结果持久 处理完成后结果文件保存,支持下载和解析

4.1 使用前准备

  1. 注册阿里云账号并完成实名认证 阿里云官网
  2. 开通百炼(Model Studio)服务 在阿里云控制台搜索”百炼”或”Model Studio”,按指引开通
  3. 获取 API-Key 百炼控制台 → API-Key 管理 → 创建 API-Key 妥善保存 Key,不要硬编码在代码中提交到仓库
  4. 安装 Python SDK pip install alibabacloud-bailian20231229 或通过 OpenAI 兼容接口使用 openai 库

五、数据准备:JSONL 格式规范

JSONL(JSON Lines)是批量推理的标准输入格式,每行一个独立的 JSON 对象,表示一个推理请求。文件无需首尾括号,纯文本即可。

5.1 JSONL 格式示例

{"custom_id": "req-001", "method": "POST", "url": "/v1/chat/completions", "body": {"model": "qwen-turbo", "messages": [{"role": "user", "content": "请用一句话总结:人工智能正在改变各个行业。"}]}}
{"custom_id": "req-002", "method": "POST", "url": "/v1/chat/completions", "body": {"model": "qwen-turbo", "messages": [{"role": "user", "content": "请翻译为英文:你好,世界。"}]}}
{"custom_id": "req-003", "method": "POST", "url": "/v1/chat/completions", "body": {"model": "qwen-turbo", "messages": [{"role": "user", "content": "判断情感倾向:这款产品太棒了,非常满意!"}]}}

5.2 JSONL 字段说明

字段类型必填说明
custom_idstring自定义请求标识,用于结果匹配
methodstringHTTP 方法,固定为 “POST”
urlstring接口路径,”/v1/chat/completions”
bodyobject请求体,与实时 API 参数一致
body.modelstring模型名称,如 qwen-turbo
body.messagesarray对话消息列表

5.3 数据准备代码

import json
from pathlib import Path


def prepare_batch_input(
    items: list[dict],
    output_path: str = "batch_input.jsonl",
    model: str = "qwen-turbo"
) -> str:
    """将数据列表转换为 JSONL 批量推理输入文件。

    Args:
        items: 每条包含自定义字段的数据字典
        output_path: 输出 JSONL 文件路径
        model: 使用的模型名称

    Returns:
        输出文件的绝对路径
    """
    output = Path(output_path)
    with output.open("w", encoding="utf-8") as f:
        for idx, item in enumerate(items, 1):
            record = {
                "custom_id": item.get("id", f"req-{idx:04d}"),
                "method": "POST",
                "url": "/v1/chat/completions",
                "body": {
                    "model": model,
                    "messages": [
                        {"role": "system", "content": item.get("system", "")},
                        {"role": "user", "content": item["prompt"]}
                    ] if item.get("system") else [
                        {"role": "user", "content": item["prompt"]}
                    ],
                    "max_tokens": item.get("max_tokens", 512),
                    "temperature": item.get("temperature", 0.7)
                }
            }
            f.write(json.dumps(record, ensure_ascii=False) + "\n")

    return str(output.resolve())


# 使用示例
data = [
    {"id": "sentiment-001", "prompt": "判断情感:产品质量很差,退货了。"},
    {"id": "sentiment-002", "prompt": "判断情感:物流很快,包装完好。"},
    {"id": "summary-001", "prompt": "总结:本文介绍了批量推理的优势和应用场景。"},
]

file_path = prepare_batch_input(data, model="qwen-turbo")
print(f"已生成批量推理输入文件: {file_path}")

5.4 数据校验

def validate_jsonl(file_path: str) -> tuple[int, list[str]]:
    """校验 JSONL 文件格式是否正确。

    Returns:
        (有效行数, 错误列表)
    """
    errors = []
    valid_count = 0

    with open(file_path, "r", encoding="utf-8") as f:
        for line_num, line in enumerate(f, 1):
            line = line.strip()
            if not line:
                continue
            try:
                obj = json.loads(line)
                required = ["custom_id", "method", "url", "body"]
                for field in required:
                    if field not in obj:
                        errors.append(f"第 {line_num} 行缺少字段: {field}")
                        break
                else:
                    valid_count += 1
            except json.JSONDecodeError as e:
                errors.append(f"第 {line_num} 行 JSON 解析错误: {e}")

    return valid_count, errors


count, errs = validate_jsonl("batch_input.jsonl")
print(f"有效请求: {count} 条")
if errs:
    for e in errs:
        print(f"错误: {e}")

六、提交批量推理任务(完整代码)

6.1 使用 OpenAI 兼容接口(推荐)

import os
from pathlib import Path
from openai import OpenAI


def create_batch_job(
    input_file: str,
    api_key: str | None = None,
    base_url: str = "https://dashscope.aliyuncs.com/compatible-mode/v1"
) -> dict:
    """创建阿里云百炼批量推理任务。

    Args:
        input_file: JSONL 输入文件路径
        api_key: 百炼 API-Key,默认从环境变量 DASHSCOPE_API_KEY 读取
        base_url: 百炼兼容接口地址

    Returns:
        任务信息字典,包含 batch_id
    """
    api_key = api_key or os.getenv("DASHSCOPE_API_KEY")
    if not api_key:
        raise ValueError("请设置 API-Key:环境变量 DASHSCOPE_API_KEY 或参数传入")

    client = OpenAI(api_key=api_key, base_url=base_url)

    # 1. 上传输入文件
    with open(input_file, "rb") as f:
        file_obj = client.files.create(file=f, purpose="batch")
    print(f"文件上传成功: {file_obj.id}")

    # 2. 创建批量任务
    batch = client.batches.create(
        input_file_id=file_obj.id,
        endpoint="/v1/chat/completions",
        completion_window="24h"  # 任务超时时间
    )
    print(f"批量任务创建成功: {batch.id}")
    print(f"状态: {batch.status}")

    return {
        "batch_id": batch.id,
        "input_file_id": file_obj.id,
        "status": batch.status
    }


# 使用
job = create_batch_job("batch_input.jsonl")

6.2 使用阿里云官方 SDK

import os
from alibabacloud_bailian20231229.client import Client as BailianClient
from alibabacloud_tea_openapi import models as open_api_models


def create_batch_job_sdk(
    input_file_url: str,
    access_key_id: str | None = None,
    access_key_secret: str | None = None,
    model: str = "qwen-turbo"
) -> str:
    """使用阿里云 SDK 创建批量推理任务。

    Args:
        input_file_url: 已上传到 OSS 的输入文件 URL
        access_key_id: 阿里云 AccessKey ID
        access_key_secret: 阿里云 AccessKey Secret
        model: 模型名称

    Returns:
        任务 ID
    """
    access_key_id = access_key_id or os.getenv("ALIBABA_CLOUD_ACCESS_KEY_ID")
    access_key_secret = access_key_secret or os.getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET")

    config = open_api_models.Config(
        access_key_id=access_key_id,
        access_key_secret=access_key_secret
    )
    config.endpoint = "bailian.cn-beijing.aliyuncs.com"
    client = BailianClient(config)

    # 创建批量任务
    response = client.create_batch_inference_job(
        model=model,
        input_file_url=input_file_url,
        output_file_prefix="batch-output/"
    )

    job_id = response.body.job_id
    print(f"任务创建成功: {job_id}")
    return job_id

七、任务监控与状态管理

7.1 任务状态流转

validating in_progress completed
文件校验中          正在处理          已完成
failed (异常终止) expired (超时) cancelled (已取消)

7.2 状态查询代码

import time
from openai import OpenAI


def wait_for_batch_completion(
    batch_id: str,
    api_key: str | None = None,
    poll_interval: int = 30,
    max_wait: int = 86400
) -> dict:
    """轮询等待批量任务完成。

    Args:
        batch_id: 批量任务 ID
        api_key: API-Key
        poll_interval: 轮询间隔(秒)
        max_wait: 最大等待时间(秒)

    Returns:
        最终任务状态信息
    """
    client = OpenAI(
        api_key=api_key or os.getenv("DASHSCOPE_API_KEY"),
        base_url="https://dashscope.aliyuncs.com/compatible-mode/v1"
    )

    start_time = time.time()
    while True:
        batch = client.batches.retrieve(batch_id)
        elapsed = int(time.time() - start_time)

        print(f"[{elapsed}s] 状态: {batch.status} | "
              f"完成: {batch.request_counts.completed}/"
              f"{batch.request_counts.total} | "
              f"失败: {batch.request_counts.failed}")

        if batch.status in ("completed", "failed", "expired", "cancelled"):
            return {
                "status": batch.status,
                "batch": batch,
                "elapsed_seconds": elapsed
            }

        if elapsed > max_wait:
            raise TimeoutError(f"任务等待超时(>{max_wait}秒)")

        time.sleep(poll_interval)


# 使用
result = wait_for_batch_completion(job["batch_id"], poll_interval=30)
print(f"最终状态: {result['status']}")

7.3 任务列表面板

def list_batch_jobs(api_key: str | None = None, limit: int = 10) -> list[dict]:
    """列出最近的批量任务。"""
    client = OpenAI(
        api_key=api_key or os.getenv("DASHSCOPE_API_KEY"),
        base_url="https://dashscope.aliyuncs.com/compatible-mode/v1"
    )

    batches = client.batches.list(limit=limit)
    jobs = []
    for batch in batches.data:
        jobs.append({
            "id": batch.id,
            "status": batch.status,
            "created_at": batch.created_at,
            "completed": batch.request_counts.completed,
            "failed": batch.request_counts.failed,
            "total": batch.request_counts.total
        })
    return jobs


# 打印任务列表
for job in list_batch_jobs():
    print(f"{job['id'][:20]:20s} | {job['status']:12s} | "
          f"{job['completed']:>4d}/{job['total']:<4d} | "
          f"失败: {job['failed']}")

八、结果获取与后处理

8.1 下载结果文件

def download_batch_results(batch_id: str, api_key: str | None = None) -> list[dict]:
    """下载并解析批量推理结果。

    Returns:
        结果记录列表,每条包含 custom_id 和模型输出
    """
    client = OpenAI(
        api_key=api_key or os.getenv("DASHSCOPE_API_KEY"),
        base_url="https://dashscope.aliyuncs.com/compatible-mode/v1"
    )

    # 获取任务信息
    batch = client.batches.retrieve(batch_id)
    if batch.status != "completed":
        raise ValueError(f"任务未完成,当前状态: {batch.status}")

    # 下载输出文件
    output_file = client.files.content(batch.output_file_id)
    content = output_file.read().decode("utf-8")

    # 解析 JSONL
    results = []
    for line in content.strip().split("\n"):
        if not line.strip():
            continue
        obj = json.loads(line)
        results.append({
            "custom_id": obj.get("custom_id"),
            "status_code": obj.get("status_code"),
            "response": obj.get("response", {}),
            "error": obj.get("error")
        })

    return results


# 使用
results = download_batch_results(job["batch_id"])

# 查看结果
for r in results[:3]:
    cid = r["custom_id"]
    if r["error"]:
        print(f"[{cid}] 错误: {r['error']}")
    else:
        content = r["response"]["body"]["choices"][0]["message"]["content"]
        print(f"[{cid}] 结果: {content[:100]}...")

8.2 结果与输入匹配

def merge_results_with_input(
    input_items: list[dict],
    results: list[dict]
) -> list[dict]:
    """将批量推理结果与原始输入数据合并。

    Returns:
        合并后的记录列表
    """
    result_map = {r["custom_id"]: r for r in results}
    merged = []

    for item in input_items:
        cid = item.get("id", "")
        result = result_map.get(cid, {})

        merged.append({
            **item,
            "status_code": result.get("status_code"),
            "output": result.get("response", {})
                .get("body", {})
                .get("choices", [{}])[0]
                .get("message", {})
                .get("content", ""),
            "error": result.get("error")
        })

    return merged


# 保存为 CSV
import csv

merged = merge_results_with_input(data, results)
with open("batch_results.csv", "w", newline="", encoding="utf-8-sig") as f:
    writer = csv.DictWriter(f, fieldnames=["id", "prompt", "output", "error"])
    writer.writeheader()
    for row in merged:
        writer.writerow({
            "id": row["id"],
            "prompt": row["prompt"],
            "output": row["output"],
            "error": row["error"] or ""
        })

print("结果已保存到 batch_results.csv")

九、成本优化与最佳实践

9.1 成本控制策略

批量推理降本五法

1. 批量代替实时 非紧急任务统一走 BatchAPI,成本降低约 50%

2. 选择合适模型 简单任务用 qwen-turbo(快且便宜),复杂任务再用 qwen-max

3. 限制输出长度 设置合理的 max_tokens,避免模型过度生成浪费 Token

4. 合并相似请求 将相同 system prompt 的请求合并,减少重复计算

5. 失败重试机制 只重试失败的请求,避免全量重新提交

9.2 最佳实践清单

□ 数据准备
  - 提前校验 JSONL 格式,避免上传后校验失败
  - 单文件请求数控制在合理范围(建议 1万~10万条)
  - 单条请求不要太长,避免超时

□ 任务提交
  - 避开业务高峰期提交,减少排队时间
  - 大任务拆分为多个小批量,降低单点失败风险
  - 记录 batch_id,便于后续查询和跟踪

□ 监控管理
  - 设置合理的轮询间隔(30~60 秒),避免频繁调用
  - 任务完成后及时下载结果,释放存储
  - 定期检查失败请求,分析失败原因

□ 结果处理
  - 解析结果时做好异常处理,兼容格式变化
  - 将结果与输入通过 custom_id 精确匹配
  - 对失败请求单独记录,便于后续重试

9.3 失败重试流程

def retry_failed_requests(
    input_file: str,
    results: list[dict],
    api_key: str | None = None
) -> dict | None:
    """提取失败的请求,生成新的重试批次。

    Returns:
        新任务信息,如果没有失败则返回 None
    """
    failed_ids = {r["custom_id"] for r in results if r["error"]}
    if not failed_ids:
        print("没有失败请求,无需重试")
        return None

    # 读取原始输入
    retry_items = []
    with open(input_file, "r", encoding="utf-8") as f:
        for line in f:
            obj = json.loads(line.strip())
            if obj["custom_id"] in failed_ids:
                retry_items.append(obj)

    # 写入重试文件
    retry_file = input_file.replace(".jsonl", "_retry.jsonl")
    with open(retry_file, "w", encoding="utf-8") as f:
        for item in retry_items:
            f.write(json.dumps(item, ensure_ascii=False) + "\n")

    print(f"提取 {len(retry_items)} 条失败请求,写入 {retry_file}")
    return create_batch_job(retry_file, api_key)


# 使用
retry_job = retry_failed_requests("batch_input.jsonl", results)

十、常见问题与排查

问题现象原因解决方案
文件上传失败 JSONL 格式错误、文件过大(超过 100MB)、编码非 UTF-8 使用 validate_jsonl() 预校验,大文件拆分为多个,确认 UTF-8 编码
任务状态 validating 很久 文件正在格式校验,或请求数量过大 等待即可,通常几分钟内完成;如超 30 分钟联系客服
部分请求失败 个别请求参数非法、超长、或模型不支持 下载结果查看 error 字段,修复后单独重试失败项
任务状态 failed 文件格式严重错误、权限不足、模型不可用 检查 API-Key 权限、模型是否已开通、JSONL 格式
结果与输入不匹配 custom_id 重复或格式不一致 确保 custom_id 全局唯一,建议使用 UUID 或业务 ID
任务排队时间长 高峰期提交,资源紧张 避开高峰(工作日上午 10~12 点),或夜间提交
结果文件无法下载 结果文件已过期(通常保留 7 天) 任务完成后尽快下载,设置自动下载脚本
Token 消耗超预期 max_tokens 设置过大,或输入内容过长 优化 prompt 长度,设置合理的 max_tokens 上限

完整工作流代码模板

"""
阿里云百炼批量推理完整工作流模板
一步完成:数据准备 → 提交任务 → 监控 → 获取结果 → 保存 CSV
"""

import os
import json
import time
import csv
from pathlib import Path
from openai import OpenAI

API_KEY = os.getenv("DASHSCOPE_API_KEY")
BASE_URL = "https://dashscope.aliyuncs.com/compatible-mode/v1"
MODEL = "qwen-turbo"


def run_batch_pipeline(input_items: list[dict], output_csv: str = "results.csv") -> None:
    """批量推理完整流水线。"""

    # Step 1: 准备 JSONL
    jsonl_path = "batch_input.jsonl"
    with open(jsonl_path, "w", encoding="utf-8") as f:
        for idx, item in enumerate(input_items, 1):
            record = {
                "custom_id": item.get("id", f"req-{idx:04d}"),
                "method": "POST",
                "url": "/v1/chat/completions",
                "body": {
                    "model": MODEL,
                    "messages": [{"role": "user", "content": item["prompt"]}],
                    "max_tokens": item.get("max_tokens", 512)
                }
            }
            f.write(json.dumps(record, ensure_ascii=False) + "\n")
    print(f"✅ 已生成输入文件: {jsonl_path} ({len(input_items)} 条)")

    # Step 2: 创建任务
    client = OpenAI(api_key=API_KEY, base_url=BASE_URL)
    with open(jsonl_path, "rb") as f:
        file_obj = client.files.create(file=f, purpose="batch")
    batch = client.batches.create(
        input_file_id=file_obj.id,
        endpoint="/v1/chat/completions",
        completion_window="24h"
    )
    print(f"✅ 任务已创建: {batch.id}")

    # Step 3: 等待完成
    while True:
        batch = client.batches.retrieve(batch.id)
        print(f"⏳ 状态: {batch.status} | "
              f"完成: {batch.request_counts.completed}/{batch.request_counts.total}")
        if batch.status in ("completed", "failed", "expired"):
            break
        time.sleep(30)

    if batch.status != "completed":
        raise RuntimeError(f"任务未成功完成: {batch.status}")

    # Step 4: 下载结果
    output_file = client.files.content(batch.output_file_id)
    content = output_file.read().decode("utf-8")

    # Step 5: 解析并保存 CSV
    results = [json.loads(line) for line in content.strip().split("\n") if line.strip()]

    with open(output_csv, "w", newline="", encoding="utf-8-sig") as f:
        writer = csv.writer(f)
        writer.writerow(["id", "prompt", "output", "error"])
        for r in results:
            cid = r.get("custom_id", "")
            error = r.get("error", "")
            output = ""
            if not error:
                try:
                    output = r["response"]["body"]["choices"][0]["message"]["content"]
                except (KeyError, IndexError):
                    output = "[解析失败]"
            writer.writerow([cid, "", output, error or ""])

    print(f"✅ 结果已保存: {output_csv}")
    print(f"📊 总计: {len(results)} 条 | "
          f"成功: {sum(1 for r in results if not r.get('error'))} | "
          f"失败: {sum(1 for r in results if r.get('error'))}")


# 运行
if __name__ == "__main__":
    test_data = [
        {"id": "test-001", "prompt": "用一句话总结人工智能的发展趋势"},
        {"id": "test-002", "prompt": "将'Hello World'翻译为中文"},
    ]
    run_batch_pipeline(test_data, "batch_results.csv")
全流程总结:理解批量推理的异步离线特征 → 区分批量与实时的适用场景 → 按 JSONL 规范准备数据 → 通过 OpenAI 兼容接口提交任务 → 轮询监控任务状态 → 下载并解析结果文件 → 失败请求单独重试 → 选择合适的模型和参数控制成本。批量推理是大规模 AI 处理的降本利器。

Batch Inference Complete GuideConcept → Practice → Alibaba Cloud Bailian BatchAPI

Batch vs online inference, use cases, JSONL data prep, job submission & monitoring, result retrieval, cost optimization, troubleshooting

1 What is Batch Inference

Batch Inference aggregates large numbers of independent inference requests into a single batch, submitted to the model for background offline processing with asynchronous result delivery.

Four Key Characteristics

Asynchronous Submit and return immediately, process in background queue

High Throughput Fully utilize GPU parallel computing

Low Cost Alibaba Cloud BatchAPI costs ~50% of real-time calls

Offline Doesn't consume real-time service resources

2 Batch vs Online Inference

DimensionBatchOnline
ResponseAsync, backgroundSync, immediate
LatencyMinutes to hours OKMilliseconds to seconds
Cost~50% of real-timeFull real-time pricing
ScaleThousands to millionsSingle or small batches
Use CaseData analysis, generationChat, recommendation

3 Use Cases & Value

Data Analysis

Sentiment analysis, topic classification, entity extraction on massive review/feedback datasets.

Content Generation

Batch generate product descriptions, marketing copy, translations at scale.

Model Evaluation

Run benchmark tests to get accuracy, F1 scores across standard test sets.

Data Labeling

Use model for pre-labeling, human review correction, reduce costs 70%+.

4 Alibaba Cloud Bailian BatchAPI

Bailian BatchAPI Capabilities

File Upload JSONL format, one request per line

Model Support Qwen, Llama, DeepSeek and more

Cost Savings ~50% of real-time API pricing

Status Tracking Full lifecycle: created → queued → running → completed

Persistent Results Output files saved after completion

Prerequisites

  1. Alibaba Cloud account with real-name verification
  2. Activate Bailian (Model Studio) service
  3. Get API-Key from Bailian Console
  4. Install SDK: pip install openai

5 JSONL Data Preparation

{"custom_id": "req-001", "method": "POST", "url": "/v1/chat/completions", "body": {"model": "qwen-turbo", "messages": [{"role": "user", "content": "Summarize AI trends"}]}}
{"custom_id": "req-002", "method": "POST", "url": "/v1/chat/completions", "body": {"model": "qwen-turbo", "messages": [{"role": "user", "content": "Translate to Chinese"}]}}
FieldTypeRequiredDescription
custom_idstringYesCustom request identifier
methodstringYesHTTP method, always "POST"
urlstringYesAPI path
bodyobjectYesRequest body, same as real-time API

6 Submitting Batch Jobs

import os
from openai import OpenAI

client = OpenAI(
    api_key=os.getenv("DASHSCOPE_API_KEY"),
    base_url="https://dashscope.aliyuncs.com/compatible-mode/v1"
)

# Upload file
with open("batch_input.jsonl", "rb") as f:
    file_obj = client.files.create(file=f, purpose="batch")

# Create batch job
batch = client.batches.create(
    input_file_id=file_obj.id,
    endpoint="/v1/chat/completions",
    completion_window="24h"
)
print(f"Job created: {batch.id}")

7 Job Monitoring

Status Flow

validating in_progress completed
failed expired cancelled
def wait_for_completion(batch_id: str, poll_interval: int = 30):
    while True:
        batch = client.batches.retrieve(batch_id)
        print(f"Status: {batch.status} | "
              f"Done: {batch.request_counts.completed}/{batch.request_counts.total}")
        if batch.status in ("completed", "failed", "expired"):
            return batch
        time.sleep(poll_interval)

8 Result Retrieval

batch = client.batches.retrieve(batch_id)
output_file = client.files.content(batch.output_file_id)
content = output_file.read().decode("utf-8")

results = []
for line in content.strip().split("\n"):
    obj = json.loads(line)
    results.append({
        "custom_id": obj["custom_id"],
        "response": obj.get("response", {}),
        "error": obj.get("error")
    })

9 Cost Optimization

Five Cost Reduction Strategies

1. Batch over online Use BatchAPI for non-urgent tasks (~50% savings)

2. Right-size model Use qwen-turbo for simple tasks

3. Limit max_tokens Set reasonable output length caps

4. Merge requests Group similar prompts to reduce overhead

5. Retry failures only Don't resubmit entire batch

10 FAQ & Troubleshooting

IssueSolution
Upload failsValidate JSONL format, check file size < 100MB, ensure UTF-8
Stuck validatingWait normally; contact support if > 30 min
Partial failuresCheck error field in results, retry failed items only
Job failedVerify API-Key permissions, model availability, JSONL format
Long queue timeAvoid peak hours (10am-12pm), submit during off-hours
Result expiredDownload within 7 days; set up auto-download script
Unexpected token usageOptimize prompt length, set max_tokens limit
Summary: Understand async offline nature of batch inference → Distinguish batch vs online use cases → Prepare data in JSONL format → Submit via OpenAI-compatible API → Poll for completion → Download and parse results → Retry failed requests separately → Choose right model and parameters to control costs. Batch inference is a powerful cost-saving tool for large-scale AI processing.