# Claude API: полный гайд для разработчиков

> Исчерпывающий туториал по Claude API: Messages API, tool use, adaptive thinking, prompt caching, vision, streaming, batches, MCP connector, structured outputs. Примеры на Python и TypeScript.
> Author: Roman Belov · Published: 2026-06-28 · Source: https://futurecraft.pro/ru/blog/claude-api-developer-guide/

Claude API — программный интерфейс для работы с моделями Anthropic. Один endpoint — Messages API — покрывает текст, изображения, tool use, streaming и extended thinking. Python SDK, TypeScript SDK, REST — три способа интеграции.

Эта статья — полный справочник. От первого запроса до продвинутых техник: prompt caching, batches, vision. Каждый пример — рабочий код, который можно скопировать и запустить.

## Что такое Claude API

### Messages API — единый endpoint

Вся работа с Claude проходит через один endpoint: `POST https://api.anthropic.com/v1/messages`. Вы отправляете массив сообщений с ролями `user` и `assistant`, модель генерирует следующее сообщение в диалоге.

Это не completions API в стиле GPT-3 — Messages API построен вокруг диалоговой структуры. Каждый запрос содержит полную историю разговора. Состояние между вызовами не хранится.

### Модели

Три модели текущего поколения:

| Модель | API ID | Контекст | Max output | Цена (input/output за MTok) |
|--------|--------|----------|------------|----------------------------|
| **Opus 4.8** | `claude-opus-4-8` | 1M | 128k | $5 / $25 |
| **Sonnet 4.6** | `claude-sonnet-4-6` | 1M | 128k | $3 / $15 |
| **Haiku 4.5** | `claude-haiku-4-5` | 200k | 64k | $1 / $5 |

**Opus 4.8** — флагман. Максимальная точность в коде, reasoning, агентных задачах. 1M токенов контекста, 128k output. Выбирайте для задач, где ошибка дорого стоит.

**Sonnet 4.6** — рабочая лошадка. Быстрый, умный, в 1.67 раза дешевле Opus по output ($15 vs $25). Подходит для 90% production-задач: чат-боты, генерация контента, анализ документов.

**Haiku 4.5** — скорость и экономия. Классификация, извлечение данных, модерация, массовая обработка. В 3 раза дешевле Sonnet по output ($5 vs $15).

### Отличие от Claude.ai

Claude.ai — чат-интерфейс. API дает то, чего в чате нет:

- **System prompts** — точная настройка поведения модели
- **Tool use** — модель вызывает ваши функции
- **Streaming** — потоковый вывод токен за токеном
- **Extended thinking** — доступ к цепочке рассуждений
- **Prompt caching** — кэширование повторяющегося контекста
- **Batches** — асинхронная пакетная обработка со скидкой 50%
- **Полный контроль** — temperature, stop sequences, metadata

## Быстрый старт

### Получение API-ключа

1. Зарегистрируйтесь на [console.anthropic.com](https://console.anthropic.com)
2. Перейдите в **Settings → API Keys**
3. Создайте ключ (начинается с `sk-ant-`)
4. Пополните баланс в **Billing** (минимум $5)

Ключ привязан к workspace. Один workspace — один набор ключей, лимитов, биллинга.

### Установка SDK

**Python:**

```bash
pip install anthropic
```

**TypeScript:**

```bash
npm install @anthropic-ai/sdk
```

SDK берёт ключ из переменной окружения `ANTHROPIC_API_KEY`:

```bash
export ANTHROPIC_API_KEY="sk-ant-api03-..."
```

### Первый запрос

**Python:**

```python
import anthropic

client = anthropic.Anthropic()

message = client.messages.create(
    model="claude-sonnet-4-6",
    max_tokens=1024,
    messages=[
        {"role": "user", "content": "Объясни разницу между REST и GraphQL в трех предложениях."}
    ]
)

print(message.content[0].text)
```

**TypeScript:**

```typescript
import Anthropic from "@anthropic-ai/sdk";

const client = new Anthropic();

const message = await client.messages.create({
  model: "claude-sonnet-4-6",
  max_tokens: 1024,
  messages: [
    { role: "user", content: "Объясни разницу между REST и GraphQL в трех предложениях." }
  ]
});

console.log(message.content[0].text);
```

Ответ в `message.content` — массив контент-блоков. Для простого текста — один блок с `type: "text"`.

### Streaming — потоковый ответ

Streaming отдаёт токены по мере генерации — пользователь видит ответ сразу.

**Python:**

```python
import anthropic

client = anthropic.Anthropic()

with client.messages.stream(
    model="claude-sonnet-4-6",
    max_tokens=1024,
    messages=[
        {"role": "user", "content": "Напиши функцию бинарного поиска на Python."}
    ]
) as stream:
    for text in stream.text_stream:
        print(text, end="", flush=True)
```

**TypeScript:**

```typescript
import Anthropic from "@anthropic-ai/sdk";

const client = new Anthropic();

const stream = await client.messages.stream({
  model: "claude-sonnet-4-6",
  max_tokens: 1024,
  messages: [
    { role: "user", content: "Напиши функцию бинарного поиска на Python." }
  ]
});

for await (const event of stream) {
  if (event.type === "content_block_delta" && event.delta.type === "text_delta") {
    process.stdout.write(event.delta.text);
  }
}
```

## Messages API: полный разбор

### Параметры запроса

| Параметр | Тип | Обязательный | Описание |
|----------|-----|-------------|----------|
| `model` | string | Да | ID модели: `claude-opus-4-8`, `claude-sonnet-4-6`, `claude-haiku-4-5` |
| `messages` | array | Да | Массив сообщений с ролями `user` и `assistant` |
| `max_tokens` | integer | Да | Максимум токенов в ответе (Opus 4.8/Sonnet 4.6: до 128k, Haiku 4.5: до 64k) |
| `system` | string/array | Нет | System prompt — инструкции для модели |
| `temperature` | float | Нет | Случайность: 0.0-1.0 (default: 1.0). Ниже — детерминированнее |
| `top_p` | float | Нет | Nucleus sampling: 0.0-1.0. Альтернатива temperature |
| `top_k` | integer | Нет | Выборка из top-K токенов. Для продвинутых кейсов |
| `stop_sequences` | array | Нет | Строки, при генерации которых модель останавливается |
| `stream` | boolean | Нет | Потоковый вывод через Server-Sent Events |
| `metadata` | object | Нет | `user_id` для отслеживания и abuse detection |

### System prompt

System prompt задаёт роль и правила поведения. Передаётся отдельным параметром, не через `messages`.

**Python:**

```python
message = client.messages.create(
    model="claude-sonnet-4-6",
    max_tokens=1024,
    system="Ты — senior Python-разработчик. Отвечай кратко. Код — с типами и docstrings.",
    messages=[
        {"role": "user", "content": "Напиши декоратор для retry с exponential backoff."}
    ]
)
```

System prompt — строка или массив контент-блоков. Массив нужен для prompt caching:

```python
system=[
    {
        "type": "text",
        "text": "Ты — senior Python-разработчик. Отвечай кратко.",
        "cache_control": {"type": "ephemeral"}
    }
]
```

### Multi-turn conversations

Claude не хранит состояние между запросами. Для продолжения диалога отправляйте всю историю:

**Python:**

```python
messages = [
    {"role": "user", "content": "Что такое dependency injection?"},
    {"role": "assistant", "content": "Dependency injection — паттерн, при котором зависимости объекта передаются извне, а не создаются внутри..."},
    {"role": "user", "content": "Покажи пример на Python с dataclass."}
]

message = client.messages.create(
    model="claude-sonnet-4-6",
    max_tokens=1024,
    messages=messages
)
```

Каждое сообщение — объект с `role` (`user` или `assistant`) и `content`. Роли чередуются. Два подряд от одной роли — API объединит их.

### Stop sequences

Кастомные строки, при генерации которых модель останавливается:

```python
message = client.messages.create(
    model="claude-sonnet-4-6",
    max_tokens=1024,
    stop_sequences=["```", "END"],
    messages=[
        {"role": "user", "content": "Напиши JSON-конфигурацию для nginx."}
    ]
)

# message.stop_reason == "stop_sequence"
# message.stop_sequence == "```"
```

Полезно для structured output: модель генерирует до определенного маркера, вы парсите результат.

### Metadata

`user_id` в metadata нужен для отслеживания abuse. Добавляйте в production:

```python
message = client.messages.create(
    model="claude-sonnet-4-6",
    max_tokens=1024,
    metadata={"user_id": "user_abc123"},
    messages=[
        {"role": "user", "content": "Привет!"}
    ]
)
```

Используйте хеши или UUID, не передавайте email или реальные имена.

## Tool Use (Function Calling)

Tool use — способ вызывать ваши функции из Claude. Вы описываете инструменты через JSON Schema. Модель решает, когда и какой вызвать, формирует параметры. Вы исполняете вызов и возвращаете результат.

### Определение инструментов

Инструмент — это имя, описание и JSON Schema для входных параметров:

```python
tools = [
    {
        "name": "get_weather",
        "description": "Получить текущую погоду в указанном городе.",
        "input_schema": {
            "type": "object",
            "properties": {
                "city": {
                    "type": "string",
                    "description": "Название города, например: Москва"
                },
                "units": {
                    "type": "string",
                    "enum": ["celsius", "fahrenheit"],
                    "description": "Единицы измерения температуры"
                }
            },
            "required": ["city"]
        }
    }
]
```

Описания критичны. Claude читает их, чтобы решить — вызывать инструмент или нет. Чем точнее описание, тем правильнее выбор.

### Цикл tool_use → tool_result

Цикл выглядит так:

1. Вы отправляете запрос с `tools`
2. Claude возвращает блок `tool_use` с именем инструмента и параметрами
3. Вы исполняете вызов
4. Отправляете результат как `tool_result`
5. Claude формирует финальный ответ (или вызывает следующий инструмент)

### Полный пример: погодный бот

**Python:**

```python
import anthropic
import json

client = anthropic.Anthropic()

tools = [
    {
        "name": "get_weather",
        "description": "Получить текущую погоду в указанном городе.",
        "input_schema": {
            "type": "object",
            "properties": {
                "city": {"type": "string", "description": "Город"},
                "units": {"type": "string", "enum": ["celsius", "fahrenheit"]}
            },
            "required": ["city"]
        }
    }
]

def get_weather(city: str, units: str = "celsius") -> dict:
    """Заглушка — в production здесь вызов реального API."""
    return {"city": city, "temperature": 22, "units": units, "condition": "облачно"}

# Шаг 1: отправляем запрос
messages = [{"role": "user", "content": "Какая погода в Москве?"}]

response = client.messages.create(
    model="claude-sonnet-4-6",
    max_tokens=1024,
    tools=tools,
    messages=messages
)

# Шаг 2: обрабатываем tool_use
if response.stop_reason == "tool_use":
    # Находим блок tool_use в ответе
    tool_block = next(b for b in response.content if b.type == "tool_use")

    # Исполняем вызов
    result = get_weather(**tool_block.input)

    # Шаг 3: возвращаем результат
    messages.append({"role": "assistant", "content": response.content})
    messages.append({
        "role": "user",
        "content": [
            {
                "type": "tool_result",
                "tool_use_id": tool_block.id,
                "content": json.dumps(result)
            }
        ]
    })

    # Шаг 4: получаем финальный ответ
    final_response = client.messages.create(
        model="claude-sonnet-4-6",
        max_tokens=1024,
        tools=tools,
        messages=messages
    )

    print(final_response.content[0].text)
```

**TypeScript:**

```typescript
import Anthropic from "@anthropic-ai/sdk";

const client = new Anthropic();

const tools: Anthropic.Tool[] = [
  {
    name: "get_weather",
    description: "Получить текущую погоду в указанном городе.",
    input_schema: {
      type: "object" as const,
      properties: {
        city: { type: "string", description: "Город" },
        units: { type: "string", enum: ["celsius", "fahrenheit"] }
      },
      required: ["city"]
    }
  }
];

function getWeather(city: string, units = "celsius") {
  return { city, temperature: 22, units, condition: "облачно" };
}

const messages: Anthropic.MessageParam[] = [
  { role: "user", content: "Какая погода в Москве?" }
];

const response = await client.messages.create({
  model: "claude-sonnet-4-6",
  max_tokens: 1024,
  tools,
  messages
});

if (response.stop_reason === "tool_use") {
  const toolBlock = response.content.find(
    (b): b is Anthropic.ToolUseBlock => b.type === "tool_use"
  )!;

  const result = getWeather(
    (toolBlock.input as any).city,
    (toolBlock.input as any).units
  );

  messages.push({ role: "assistant", content: response.content });
  messages.push({
    role: "user",
    content: [
      {
        type: "tool_result",
        tool_use_id: toolBlock.id,
        content: JSON.stringify(result)
      }
    ]
  });

  const finalResponse = await client.messages.create({
    model: "claude-sonnet-4-6",
    max_tokens: 1024,
    tools,
    messages
  });

  console.log((finalResponse.content[0] as Anthropic.TextBlock).text);
}
```

### tool_choice — контроль вызова инструментов

| Значение | Поведение |
|----------|-----------|
| `{"type": "auto"}` | Модель сама решает — вызывать или нет (default) |
| `{"type": "any"}` | Модель обязана вызвать хотя бы один инструмент |
| `{"type": "tool", "name": "get_weather"}` | Модель обязана вызвать конкретный инструмент |
| `{"type": "none"}` | Инструменты отключены |

```python
# Принудительный вызов конкретного инструмента
response = client.messages.create(
    model="claude-sonnet-4-6",
    max_tokens=1024,
    tools=tools,
    tool_choice={"type": "tool", "name": "get_weather"},
    messages=[{"role": "user", "content": "Расскажи о Москве."}]
)
```

Forced tool use (`type: "tool"`) удобен для structured output: опишите «инструмент» с нужной JSON-структурой — Claude заполнит её.

### Параллельные вызовы инструментов

Claude вызывает несколько инструментов за один ход. Ответ содержит несколько блоков `tool_use` — обработайте все и верните результаты:

```python
# Если Claude вызвал 3 инструмента — возвращаем 3 результата
tool_results = []
for block in response.content:
    if block.type == "tool_use":
        result = execute_tool(block.name, block.input)
        tool_results.append({
            "type": "tool_result",
            "tool_use_id": block.id,
            "content": json.dumps(result)
        })

messages.append({"role": "assistant", "content": response.content})
messages.append({"role": "user", "content": tool_results})
```

Отключить параллельные вызовы: `tool_choice: {"type": "auto", "disable_parallel_tool_use": true}`.

## Extended Thinking

Extended thinking показывает, как Claude рассуждает перед ответом. Модель разбирает задачу по шагам, проверяет гипотезы, выявляет ошибки — и только потом отвечает.

### Когда включать

- Сложная математика и логические задачи
- Многошаговый анализ кода
- Задачи с неочевидным решением
- Любая ситуация, где «подумать дольше» = «ответить лучше»

### Параметры

С выходом Claude 4.6 Anthropic перешла на **adaptive thinking** — модель сама решает, сколько думать. Параметр `budget_tokens` **deprecated** начиная с Opus 4.6 и Sonnet 4.6; на Opus 4.7, Opus 4.8 и новее его передача возвращает `400 Bad Request`.

Новый синтаксис — `thinking: {type: "adaptive"}` с управлением глубиной через `output_config.effort`:

```python
response = client.messages.create(
    model="claude-sonnet-4-6",
    max_tokens=16000,
    thinking={"type": "adaptive"},
    output_config={"effort": "high"},  # low | medium | high (default) | max
    messages=[
        {"role": "user", "content": "Докажи, что существует бесконечно много простых чисел p, где p mod 4 == 3."}
    ]
)
```

`effort` — мягкий сигнал, не жёсткий лимит токенов. Жёсткая граница — `max_tokens`. При `low/medium` Claude может пропустить thinking на простых задачах; при `max` — всегда думает глубоко.

### Чтение thinking-блоков

Ответ содержит `thinking` и `text` блоки:

```python
for block in response.content:
    if block.type == "thinking":
        print(f"Размышления: {block.thinking}")
    elif block.type == "text":
        print(f"Ответ: {block.text}")
```

В моделях Opus 4.7+ и Opus 4.8 thinking по умолчанию скрыт (`display: "omitted"`) — блок возвращается с пустым полем `thinking`. На более ранних моделях Claude 4 дефолт — `display: "summarized"` (краткая выжимка рассуждений). Управляйте отображением явно: `thinking: {type: "adaptive", display: "summarized"}` вернёт суть рассуждений, `display: "omitted"` скроет их и снизит latency при streaming. Биллинг — за полные thinking-токены независимо от значения `display`.

### Streaming с extended thinking

**Python:**

```python
with client.messages.stream(
    model="claude-sonnet-4-6",
    max_tokens=16000,
    thinking={"type": "adaptive"},
    output_config={"effort": "high"},
    messages=[{"role": "user", "content": "Реализуй красно-черное дерево на Python."}],
) as stream:
    for event in stream:
        if event.type == "content_block_delta":
            if event.delta.type == "thinking_delta":
                print(event.delta.thinking, end="", flush=True)
            elif event.delta.type == "text_delta":
                print(event.delta.text, end="", flush=True)
```

### Ограничения и совместимость

- На Opus 4.7, Opus 4.8 и новее параметры `temperature`, `top_p` и `top_k` не принимаются при значениях, отличных от дефолтных (400)
- Нельзя prefill (начинать ответ assistant-сообщением)
- Для тяжёлых задач с `effort: max` рекомендуется `max_tokens` не менее 64k
- Для тяжёлых задач рекомендуется Batches API

### Стоимость

Thinking-токены тарифицируются как output-токены. Стоимость зависит от реально израсходованного объёма (не от `effort` — это лишь подсказка). Следите за `usage.output_tokens_details.thinking_tokens`, а не только `usage.output_tokens`.

Опция `display: "omitted"` скрывает thinking-блоки в ответе и снижает latency, но **не стоимость** — токены всё равно биллятся.

## Prompt Caching

Prompt caching сохраняет обработанные токены между запросами. Cache read стоит 10% от базовой цены — экономия в 10 раз на повторяющемся контексте.

### Как работает

Помечаете блоки маркером `cache_control`. Anthropic кэширует результат. Следующий запрос с тем же контентом — cache hit, токены не перечитываются.

Кэш иерархический: `tools` → `system` → `messages`. Изменение на любом уровне инвалидирует его и все последующие.

### TTL: два варианта

| TTL | Cache write | Cache read | Когда использовать |
|-----|------------|------------|-------------------|
| 5 минут (default) | 1.25x от базовой цены | 0.1x от базовой цены | Частые запросы (чаще раза в 5 минут) |
| 1 час | 2x от базовой цены | 0.1x от базовой цены | Агентные задачи, длинные сессии |

### Минимальный размер для кэширования

| Модель | Минимум токенов |
|--------|----------------|
| Opus 4.8 | 1024 |
| Sonnet 4.6 | 1024 |
| Haiku 4.5 | 4096 |

Контент короче минимума не кэшируется — запрос обработается без ошибок, просто без кэша.

### Пример: кэширование большого документа

**Python:**

```python
import anthropic

client = anthropic.Anthropic()

# Большой документ — кэшируем в system prompt
long_document = open("technical_spec.md").read()

response = client.messages.create(
    model="claude-sonnet-4-6",
    max_tokens=1024,
    system=[
        {
            "type": "text",
            "text": "Ты — технический аналитик. Отвечай на вопросы по документу ниже.",
        },
        {
            "type": "text",
            "text": long_document,
            "cache_control": {"type": "ephemeral"}  # 5-минутный кэш
        }
    ],
    messages=[
        {"role": "user", "content": "Какие нефункциональные требования описаны в документе?"}
    ]
)

# Проверяем кэш
usage = response.usage
print(f"Cache write: {usage.cache_creation_input_tokens}")
print(f"Cache read: {usage.cache_read_input_tokens}")
print(f"Uncached: {usage.input_tokens}")
```

Второй запрос с тем же system prompt — cache hit. `cache_read_input_tokens` будет ненулевым.

### 1-часовой кэш

```python
system=[
    {
        "type": "text",
        "text": long_document,
        "cache_control": {"type": "ephemeral", "ttl": "1h"}
    }
]
```

Используйте 1-часовой кэш в агентных сценариях, где между запросами больше 5 минут.

### Автоматическое кэширование

Автоматического кэширования по умолчанию нет — без `cache_control` запрос не кэшируется. Для multi-turn conversations есть упрощённый вариант: `cache_control` верхнего уровня в запросе — API сам ставит breakpoint на последний кэшируемый блок и сдвигает его по мере роста истории.

## Vision (мультимодальность)

Claude принимает изображения в сообщениях. Два способа: base64 и URL.

### Поддерживаемые форматы

JPEG, PNG, GIF, WebP. Максимальный размер — 10 МБ на изображение через API (на Amazon Bedrock и Google Cloud — 5 МБ).

### Base64

**Python:**

```python
import anthropic
import base64

client = anthropic.Anthropic()

with open("screenshot.png", "rb") as f:
    image_data = base64.standard_b64encode(f.read()).decode("utf-8")

message = client.messages.create(
    model="claude-sonnet-4-6",
    max_tokens=1024,
    messages=[
        {
            "role": "user",
            "content": [
                {
                    "type": "image",
                    "source": {
                        "type": "base64",
                        "media_type": "image/png",
                        "data": image_data
                    }
                },
                {
                    "type": "text",
                    "text": "Проанализируй этот UI-скриншот. Найди проблемы с UX."
                }
            ]
        }
    ]
)

print(message.content[0].text)
```

### URL

```python
message = client.messages.create(
    model="claude-sonnet-4-6",
    max_tokens=1024,
    messages=[
        {
            "role": "user",
            "content": [
                {
                    "type": "image",
                    "source": {
                        "type": "url",
                        "url": "https://example.com/diagram.png"
                    }
                },
                {
                    "type": "text",
                    "text": "Опиши эту архитектурную диаграмму."
                }
            ]
        }
    ]
)
```

### Подсчет токенов для изображений

Стоимость зависит от размера. Изображение масштабируется автоматически — Claude не обрабатывает пиксели 1:1. Примерные значения:

- Мелкое изображение (до 384x384): ~170 токенов
- Среднее (до 1024x1024): ~1600 токенов
- Большое (до 2048x2048): ~6400 токенов

Для точного подсчёта используйте Token Counting API — отдельный endpoint, который считает токены без генерации ответа.

### TypeScript-пример: анализ скриншота

```typescript
import Anthropic from "@anthropic-ai/sdk";
import { readFileSync } from "fs";

const client = new Anthropic();

const imageData = readFileSync("screenshot.png").toString("base64");

const message = await client.messages.create({
  model: "claude-sonnet-4-6",
  max_tokens: 1024,
  messages: [
    {
      role: "user",
      content: [
        {
          type: "image",
          source: {
            type: "base64",
            media_type: "image/png",
            data: imageData
          }
        },
        {
          type: "text",
          text: "Что изображено на скриншоте? Перечисли все UI-элементы."
        }
      ]
    }
  ]
});

console.log(message.content[0].text);
```

## Streaming

### Server-Sent Events

При `stream: true` ответ приходит как поток SSE-событий. Основные типы:

| Событие | Описание |
|---------|----------|
| `message_start` | Начало сообщения, содержит метаданные |
| `content_block_start` | Начало контент-блока (text, tool_use, thinking) |
| `content_block_delta` | Дельта контента: `text_delta`, `thinking_delta`, `input_json_delta` |
| `content_block_stop` | Конец контент-блока |
| `message_delta` | Финальные данные: `stop_reason`, `usage` |
| `message_stop` | Конец сообщения |

### Python SDK — stream helper

```python
import anthropic

client = anthropic.Anthropic()

with client.messages.stream(
    model="claude-sonnet-4-6",
    max_tokens=1024,
    messages=[{"role": "user", "content": "Напиши async HTTP-клиент на Python."}]
) as stream:
    for text in stream.text_stream:
        print(text, end="", flush=True)

# После завершения — доступ к полному сообщению
final_message = stream.get_final_message()
print(f"\nТокены: {final_message.usage.input_tokens} in / {final_message.usage.output_tokens} out")
```

`stream.text_stream` отдаёт только текст. Для низкоуровневого доступа к событиям — `stream.events`.

### TypeScript SDK — stream

```typescript
import Anthropic from "@anthropic-ai/sdk";

const client = new Anthropic();

const stream = await client.messages.stream({
  model: "claude-sonnet-4-6",
  max_tokens: 1024,
  messages: [{ role: "user", content: "Напиши async HTTP-клиент на TypeScript." }]
});

for await (const event of stream) {
  if (event.type === "content_block_delta" && event.delta.type === "text_delta") {
    process.stdout.write(event.delta.text);
  }
}

const finalMessage = await stream.finalMessage();
console.log(`\nТокены: ${finalMessage.usage.input_tokens} in / ${finalMessage.usage.output_tokens} out`);
```

### Streaming с tool use

При streaming параметры инструмента приходят как `input_json_delta` — фрагменты JSON:

```python
with client.messages.stream(
    model="claude-sonnet-4-6",
    max_tokens=1024,
    tools=tools,
    messages=[{"role": "user", "content": "Какая погода в Москве?"}]
) as stream:
    for event in stream:
        if event.type == "content_block_start" and event.content_block.type == "tool_use":
            print(f"Вызов инструмента: {event.content_block.name}")
        elif event.type == "content_block_delta":
            if event.delta.type == "input_json_delta":
                print(f"  параметры: {event.delta.partial_json}", end="")
            elif event.delta.type == "text_delta":
                print(event.delta.text, end="", flush=True)
```

Собирайте `partial_json` до `content_block_stop`, затем парсите полный JSON.

## Batches API

Batches API — асинхронная пакетная обработка со скидкой 50%. Запросы не держат HTTP-соединение: отправили пакет, забрали результаты когда готово.

### Когда использовать

- Массовая обработка: классификация тысяч документов
- Eval-пайплайны: прогон тестов по бенчмарку
- Генерация контента: пакетное создание описаний
- Любые задачи, не критичные по latency

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

**Python:**

```python
import anthropic

client = anthropic.Anthropic()

batch = client.messages.batches.create(
    requests=[
        {
            "custom_id": "request-1",
            "params": {
                "model": "claude-sonnet-4-6",
                "max_tokens": 1024,
                "messages": [
                    {"role": "user", "content": "Классифицируй тикет: 'Не работает оплата картой'. Категории: billing, technical, feature_request."}
                ]
            }
        },
        {
            "custom_id": "request-2",
            "params": {
                "model": "claude-sonnet-4-6",
                "max_tokens": 1024,
                "messages": [
                    {"role": "user", "content": "Классифицируй тикет: 'Хочу экспорт в PDF'. Категории: billing, technical, feature_request."}
                ]
            }
        }
    ]
)

print(f"Batch ID: {batch.id}")
print(f"Статус: {batch.processing_status}")
```

### Получение результатов

```python
# Проверяем статус
batch = client.messages.batches.retrieve(batch.id)

if batch.processing_status == "ended":
    # Читаем результаты
    for entry in client.messages.batches.results(batch.id):
        if entry.result.type == "succeeded":
            print(f"{entry.custom_id}: {entry.result.message.content[0].text}")
        else:
            print(f"{entry.custom_id}: ошибка — {entry.result.type}")
```

### TypeScript

```typescript
import Anthropic from "@anthropic-ai/sdk";

const client = new Anthropic();

const batch = await client.messages.batches.create({
  requests: [
    {
      custom_id: "request-1",
      params: {
        model: "claude-sonnet-4-6",
        max_tokens: 1024,
        messages: [
          { role: "user", content: "Суммаризируй: 'Claude API поддерживает streaming, tool use и prompt caching.'" }
        ]
      }
    }
  ]
});

console.log(`Batch ID: ${batch.id}`);

// Позже — получение результатов
const results = await client.messages.batches.results(batch.id);
for await (const entry of results) {
  if (entry.result.type === "succeeded") {
    console.log(`${entry.custom_id}: ${(entry.result.message.content[0] as Anthropic.TextBlock).text}`);
  }
}
```

### Extended output в Batches

Batches API поддерживает увеличенный output: до 300k токенов для Opus 4.8, Opus 4.7, Opus 4.6 и Sonnet 4.6 с beta-заголовком `output-300k-2026-03-24`. Для генерации длинных документов, книг, отчётов.

## Цены и оптимизация

### Таблица цен

| Модель | Input | Output | Cache write (5m) | Cache read | Cache write (1h) |
|--------|-------|--------|------------------|------------|------------------|
| Opus 4.8 | $5 | $25 | $6.25 (1.25x) | $0.50 (0.1x) | $10 (2x) |
| Sonnet 4.6 | $3 | $15 | $3.75 (1.25x) | $0.30 (0.1x) | $6 (2x) |
| Haiku 4.5 | $1 | $5 | $1.25 (1.25x) | $0.10 (0.1x) | $2 (2x) |

Batches API — 50% скидка на все модели.

Все цены за миллион токенов (MTok).

### Стратегии оптимизации

**1. Выбирайте модель под задачу**

Не гоняйте Opus 4.8 на классификации — Haiku справится за 1/25 стоимости. Sonnet закрывает большинство production-задач. Opus берите когда ошибка дорого стоит.

**2. Prompt caching для повторяющегося контекста**

Если system prompt и контекстные документы одинаковы — кэшируйте. Cache read обходится в 10% от базовой цены. При 100k токенов кэшированного контекста:

- Без кэша: 100k * $3/MTok = $0.30 за запрос (Sonnet)
- С кэшем: 100k * $0.30/MTok = $0.03 за запрос
- Экономия: 90% на input-токенах

**3. Batches для некритичных задач**

Eval-пайплайны, массовая классификация, генерация датасетов — latency не критична. Batches API даёт 50% скидку.

**4. Streaming для UX**

Streaming на токенах не экономит, но первый символ появляется через секунды — пользователь не ждёт. Для чат-интерфейсов — обязательно.

**5. Контролируйте max_tokens**

Не ставьте `max_tokens: 128000`, если ответ занимает 200 токенов. Claude остановится раньше лимита, но завышенное значение замедляет обработку.

**6. Мониторинг usage**

Каждый ответ содержит `usage`:

```json
{
  "input_tokens": 2095,
  "output_tokens": 503,
  "cache_creation_input_tokens": 0,
  "cache_read_input_tokens": 2000
}
```

Логируйте. Считайте стоимость по формуле:

```
cost = (input_tokens * input_price + output_tokens * output_price
        + cache_creation * write_price + cache_read * read_price) / 1_000_000
```

## Structured Outputs

С 2026 года нативный JSON mode доступен через `output_config.format` — GA без beta-заголовков. Это альтернатива forced tool use: API гарантирует валидный JSON по заданной схеме.

```python
import anthropic
import json

client = anthropic.Anthropic()

response = client.messages.create(
    model="claude-sonnet-4-6",
    max_tokens=1024,
    output_config={
        "format": {
            "type": "json_schema",
            "schema": {
                "type": "object",
                "properties": {
                    "name": {"type": "string"},
                    "age": {"type": "integer"},
                    "role": {"type": "string"}
                },
                "required": ["name", "age", "role"],
                "additionalProperties": False
            }
        }
    },
    messages=[
        {"role": "user", "content": "Иван Петров, 35 лет, senior developer"}
    ]
)

data = json.loads(response.content[0].text)
# {"name": "Иван Петров", "age": 35, "role": "senior developer"}
```

**Когда что выбирать:**

- `output_config.format` — проще, не требует инструментального контекста, подходит для большинства случаев
- Forced tool use — если уже используете инструменты в том же запросе или нужна более сложная логика роутинга

## MCP Connector

MCP Connector позволяет подключать **remote MCP серверы** прямо из Messages API — без отдельного MCP-клиента. Полезно для агентных сценариев: модель сама вызывает инструменты из ваших MCP-сервисов.

Требует beta-заголовок `mcp-client-2025-11-20`.

```python
import anthropic

client = anthropic.Anthropic()

response = client.beta.messages.create(
    model="claude-sonnet-4-6",
    max_tokens=1024,
    mcp_servers=[
        {
            "type": "url",
            "url": "https://your-mcp-server.example.com/mcp",
            "name": "my-tools",
            "authorization_token": "YOUR_TOKEN"  # опционально
        }
    ],
    tools=[
        {
            "type": "mcp_toolset",
            "mcp_server_name": "my-tools"  # имя сервера из mcp_servers
        }
    ],
    messages=[
        {"role": "user", "content": "Выполни задачу, используя доступные инструменты."}
    ],
    betas=["mcp-client-2025-11-20"]
)
```

Конфигурация инструментов находится в массиве `tools` в виде объекта `mcp_toolset`. Можно ограничить набор инструментов через allowlist/denylist:

```python
tools=[
    {
        "type": "mcp_toolset",
        "mcp_server_name": "my-tools",
        "default_config": {"enabled": False},   # allowlist: отключаем всё по умолчанию
        "configs": {
            "tool1": {"enabled": True},          # включаем только нужные
            "tool2": {"enabled": True}
        }
    }
]
```

**Важные ограничения:**
- Поддерживаются только MCP **tool calls** — prompts и resources не поддерживаются
- Доступен на Claude API, Claude Platform on AWS и Microsoft Foundry; Bedrock и Google Cloud — не поддерживаются
- Результаты инструментов приходят из внешней системы — применяйте те же меры доверия, что и к user input (риск indirect prompt injection)

## Best Practices

### Error handling и retries

Стандартные HTTP-коды. Основные ошибки:

| Код | Причина | Действие |
|-----|---------|----------|
| 400 | Невалидный запрос | Проверьте параметры |
| 401 | Неверный API-ключ | Проверьте `ANTHROPIC_API_KEY` |
| 429 | Rate limit | Retry с exponential backoff |
| 529 | API перегружен | Retry через 30-60 секунд |
| 500 | Серверная ошибка | Retry с backoff |

Python SDK делает retry автоматически:

```python
# SDK автоматически ретраит 429 и 5xx
client = anthropic.Anthropic(
    max_retries=3,  # default: 2
    timeout=60.0     # секунды
)
```

Для кастомной логики:

```python
import time
from anthropic import RateLimitError, APIStatusError

def call_with_retry(client, max_retries=3, **kwargs):
    for attempt in range(max_retries):
        try:
            return client.messages.create(**kwargs)
        except RateLimitError:
            wait = 2 ** attempt  # 1s, 2s, 4s
            print(f"Rate limit, жду {wait}s...")
            time.sleep(wait)
        except APIStatusError as e:
            if e.status_code >= 500:
                time.sleep(2 ** attempt)
                continue
            raise
    raise Exception("Все попытки исчерпаны")
```

### Безопасность API-ключей

- Храните ключ в переменных окружения, не в коде
- Используйте разные ключи для dev/staging/production
- Ротируйте ключи регулярно
- Один workspace — один проект (изоляция биллинга и лимитов)
- В CI/CD — через секреты (GitHub Secrets, Vault)

```python
# Правильно
client = anthropic.Anthropic()  # берёт из ANTHROPIC_API_KEY

# Неправильно
client = anthropic.Anthropic(api_key="sk-ant-api03-...")  # ключ в коде
```

### Structured output (JSON mode)

Рекомендуемый способ — `output_config.format` (описан в [отдельной секции](#structured-outputs) выше). Для совместимости с более старым кодом или сложных случаев — два классических подхода:

**Подход 1: Инструкция в промпте + prefill**

```python
message = client.messages.create(
    model="claude-sonnet-4-6",
    max_tokens=1024,
    messages=[
        {"role": "user", "content": "Извлеки данные из текста: 'Иван Петров, 35 лет, senior developer'. Верни JSON с полями name, age, role."},
        {"role": "assistant", "content": "{"}  # prefill — Claude продолжит JSON
    ]
)
# Результат начинается с { — парсите как JSON
result = "{" + message.content[0].text
```

**Подход 2: Forced tool use**

```python
tools = [{
    "name": "extract_person",
    "description": "Извлечь данные о человеке.",
    "input_schema": {
        "type": "object",
        "properties": {
            "name": {"type": "string"},
            "age": {"type": "integer"},
            "role": {"type": "string"}
        },
        "required": ["name", "age", "role"]
    }
}]

message = client.messages.create(
    model="claude-sonnet-4-6",
    max_tokens=1024,
    tools=tools,
    tool_choice={"type": "tool", "name": "extract_person"},
    messages=[
        {"role": "user", "content": "Иван Петров, 35 лет, senior developer"}
    ]
)

# Гарантированный JSON в tool_use.input
tool_block = next(b for b in message.content if b.type == "tool_use")
person = tool_block.input  # {"name": "Иван Петров", "age": 35, "role": "senior developer"}
```

Forced tool use надёжнее — Claude всегда вернёт валидный JSON по схеме.

### Rate limits

Лимиты зависят от модели и плана. Основные метрики:

- **Requests per minute (RPM)** — количество запросов в минуту
- **Tokens per minute (TPM)** — количество токенов в минуту
- **Tokens per day (TPD)** — суточный лимит

Заголовки ответа содержат текущие лимиты:

```
anthropic-ratelimit-requests-limit: 1000
anthropic-ratelimit-requests-remaining: 999
anthropic-ratelimit-requests-reset: 2026-04-09T12:00:00Z
anthropic-ratelimit-tokens-limit: 80000
anthropic-ratelimit-tokens-remaining: 79500
```

Стратегии:

1. **Exponential backoff** при 429 — SDK делает это автоматически
2. **Пакетная обработка** через Batches API — не попадает под RPM/TPM лимиты
3. **Несколько workspace'ов** для изоляции лимитов между проектами
4. **Cache hits не считаются** в rate limits — ещё один повод кэшировать

### Logging и monitoring

Каждый ответ содержит `id` (`msg_...`) и `usage`. Логируйте оба значения:

```python
import logging

logger = logging.getLogger("claude_api")

response = client.messages.create(
    model="claude-sonnet-4-6",
    max_tokens=1024,
    messages=[{"role": "user", "content": "Привет"}]
)

logger.info(
    "API call",
    extra={
        "message_id": response.id,
        "model": response.model,
        "input_tokens": response.usage.input_tokens,
        "output_tokens": response.usage.output_tokens,
        "stop_reason": response.stop_reason,
    }
)
```

В production подключайте observability: Langfuse, LangSmith или собственный pipeline. Трекайте latency, стоимость на запрос, распределение по моделям.

## Итого

Claude API — один endpoint, три модели, набор мощных фичей.

**Для старта**: установите SDK, получите ключ, отправьте первый запрос. Sonnet 4.6 подойдёт для 90% задач.

**Для production**: prompt caching на повторяющемся контексте, streaming для UX, Haiku для массовых операций, Batches для eval-пайплайнов.

**Для сложных задач**: adaptive thinking (`thinking: {type: "adaptive"}` + `output_config.effort`) для reasoning, tool use для интеграции с внешними системами, MCP Connector для подключения к remote MCP-серверам напрямую из API, Opus 4.8 когда точность критична.

**Structured outputs**: `output_config.format` — нативный JSON mode без beta-заголовков, GA с 2026 года.

Вся документация — [docs.anthropic.com](https://docs.anthropic.com). SDK: [anthropic-sdk-python](https://github.com/anthropics/anthropic-sdk-python), [anthropic-sdk-typescript](https://github.com/anthropics/anthropic-sdk-typescript). Примеры и рецепты — [anthropic-cookbook](https://github.com/anthropics/anthropic-cookbook).
