/
Вопросы и ответы
/
Промт-инжиниринг
/

Что такое Batch API и когда его использовать

Что такое Batch API и когда его использовать

9 часов назад

Никита Вихров

Ответы

0

Что такое Batch API и когда его использовать

Обычный API — запрос за запросом. Ждёшь ответ, делаешь следующий запрос. Если нужно обработать 10 000 отзывов — это долго и дорого. Batch API принимает все запросы сразу, обрабатывает асинхронно и отдаёт результаты когда готово. В два раза дешевле.


Когда подходит Batch API

  • Обработка большого объёма данных: классификация отзывов, анализ документов, генерация описаний
  • Задачи без жёстких требований к времени ответа
  • Ночные джобы, аналитика, подготовка данных
  • Экономия бюджета: Batch API стоит 50% от обычного

Когда не подходит:

  • Нужен ответ в реальном времени (чат, API-эндпоинт)
  • Результат нужен немедленно для следующего шага

Создание batch-запроса

from anthropic import Anthropic
import json

client = Anthropic()

# Данные для обработки
reviews = [
    {"id": "rev_001", "text": "Отличный продукт, пользуюсь каждый день"},
    {"id": "rev_002", "text": "Доставка задержалась на неделю, очень расстроен"},
    {"id": "rev_003", "text": "Нормально, ничего особенного"},
    # ... ещё тысячи отзывов
]

# Формируем запросы для batch
requests = []
for review in reviews:
    requests.append({
        "custom_id": review["id"],  # твой идентификатор — вернётся с результатом
        "params": {
            "model": "claude-haiku-4-5",
            "max_tokens": 64,
            "system": "Отвечай только JSON. {\"sentiment\": \"positive|negative|neutral\", \"score\": 1-10}",
            "messages": [{
                "role": "user",
                "content": f"Отзыв: {review['text']}"
            }]
        }
    })

# Отправляем batch
batch = client.messages.batches.create(requests=requests)
print(f"Batch создан: {batch.id}")
print(f"Статус: {batch.processing_status}")
# → Batch создан: msgbatch_01HkcTjaV5uDC8jWR4ZsDV8d
# → Статус: in_progress

Проверка статуса и получение результатов

import time


def wait_for_batch(batch_id: str, poll_interval: int = 60) -> object:
    """Ждёт завершения batch и возвращает результаты"""

    while True:
        batch = client.messages.batches.retrieve(batch_id)
        print(f"Статус: {batch.processing_status} | "
              f"Обработано: {batch.request_counts.succeeded}/{batch.request_counts.processing + batch.request_counts.succeeded}")

        if batch.processing_status == "ended":
            return batch

        time.sleep(poll_interval)  # проверяем раз в минуту


def process_batch_results(batch_id: str) -> dict:
    """Собирает результаты в словарь {custom_id: result}"""
    results = {}

    for result in client.messages.batches.results(batch_id):
        if result.result.type == "succeeded":
            try:
                text = result.result.message.content[0].text
                text = text.replace("```json", "").replace("```", "").strip()
                results[result.custom_id] = json.loads(text)
            except Exception as e:
                results[result.custom_id] = {"error": f"parse_error: {e}"}

        elif result.result.type == "errored":
            results[result.custom_id] = {
                "error": result.result.error.type
            }

    return results


# Полный пайплайн
batch = client.messages.batches.create(requests=requests)
print(f"Отправлено {len(requests)} запросов")

# Ждём (batch обрабатывается до 24 часов, обычно быстрее)
completed_batch = wait_for_batch(batch.id)

results = process_batch_results(batch.id)

# Выводим статистику
succeeded = sum(1 for r in results.values() if "error" not in r)
print(f"\nОбработано: {succeeded}/{len(results)}")

# Объединяем с оригинальными данными
for review in reviews:
    review["analysis"] = results.get(review["id"], {"error": "not_found"})
    print(f"{review['id']}: {review['analysis']}")

Сравнение стоимости: обычный vs Batch API

def estimate_cost(
    n_requests: int,
    avg_input_tokens: int,
    avg_output_tokens: int,
    model: str = "claude-haiku-4-5"
) -> dict:

    # Цены за 1M токенов
    prices = {
        "claude-haiku-4-5": {"input": 0.25, "output": 1.25},
        "claude-sonnet-4-5": {"input": 3.0, "output": 15.0},
        "claude-opus-4-5":   {"input": 15.0, "output": 75.0},
    }

    p = prices[model]
    total_input = n_requests * avg_input_tokens
    total_output = n_requests * avg_output_tokens

    regular_cost = (total_input * p["input"] + total_output * p["output"]) / 1_000_000
    batch_cost = regular_cost * 0.5  # batch API в 2x дешевле

    return {
        "regular_api": f"${regular_cost:.2f}",
        "batch_api": f"${batch_cost:.2f}",
        "savings": f"${regular_cost - batch_cost:.2f}",
    }


# 50 000 отзывов, ~100 токенов на вход, ~50 на выход
costs = estimate_cost(50_000, 100, 50, "claude-haiku-4-5")
print(costs)
# → {'regular_api': '$4.69', 'batch_api': '$2.34', 'savings': '$2.34'}

Пакетная обработка с сохранением прогресса

import sqlite3


def batch_with_resume(items: list, batch_size: int = 1000, db_path: str = "batch_progress.db"):
    """Обрабатывает с возможностью продолжить после сбоя"""

    conn = sqlite3.connect(db_path)
    conn.execute("CREATE TABLE IF NOT EXISTS results (id TEXT PRIMARY KEY, result TEXT)")
    conn.commit()

    # Фильтруем уже обработанные
    processed = {row[0] for row in conn.execute("SELECT id FROM results")}
    pending = [item for item in items if item["id"] not in processed]

    print(f"Всего: {len(items)}, уже обработано: {len(processed)}, осталось: {len(pending)}")

    # Разбиваем на батчи по batch_size
    for i in range(0, len(pending), batch_size):
        chunk = pending[i:i + batch_size]
        print(f"\nБатч {i//batch_size + 1}: {len(chunk)} запросов")

        requests = [make_request(item) for item in chunk]
        batch = client.messages.batches.create(requests=requests)
        completed = wait_for_batch(batch.id)
        results = process_batch_results(batch.id)

        # Сохраняем в БД
        conn.executemany(
            "INSERT OR REPLACE INTO results VALUES (?, ?)",
            [(id_, json.dumps(result)) for id_, result in results.items()]
        )
        conn.commit()
        print(f"Сохранено {len(results)} результатов")

    conn.close()

На курсе «ИИ для разработчиков» на Хекслете разбирают как строить экономичные AI-пайплайны: выбор API, кэширование, мониторинг расходов в продакшне.

9 часов назад

Никита Вихров

+7 800 100 22 47

бесплатно по РФ

+7 495 085 21 62

бесплатно по Москве

108813 г. Москва, вн.тер.г. поселение Московский,
г. Московский, ул. Солнечная, д. 3А, стр. 1, помещ. 20Б/3
ОГРН 1217300010476
ИНН 7325174845