Уроки миграции с OpenAI на Anthropic: что мы не ожидали

2025-11-05 · Алексей Волков

Уроки миграции с OpenAI на Anthropic

В августе мы начали постепенный перевод части трафика с OpenAI на Anthropic — cost, latency, и набор фич, которых у нас не хватало. Через 6 недель примерно половина production запросов уехала на Claude. По дороге наступили на интересные грабли.

Что мотивировало

  • Prompt caching (у OpenAI на тот момент работал не для всех моделей)
  • Более длинный context window
  • Стабильно более предсказуемый output на structured tasks
  • ~30% экономия на сопоставимой модели

Технически: одна строка, но не совсем

Если вы уже через абстракцию (LangChain, LiteLLM, свой shim), смена модели — одна строка. Но дьявол в деталях.

1. Message format

OpenAI принимает system как роль внутри messages, Anthropic — отдельным параметром:

# OpenAI
messages = [{"role": "system", "content": "..."}, {"role": "user", "content": "..."}]

# Anthropic
response = client.messages.create(
    system="...",
    messages=[{"role": "user", "content": "..."}],
)

Любая библиотека, которая пишет «claim-agnostic», это знает. Но свои внутренние парсеры пришлось править.

2. Tool use

Формат schema отличается, и Claude строже к обязательным полям. У нас 30% инструментов слетело в ошибки валидации после миграции — пришлось вычищать опциональные поля, которые OpenAI молча игнорировал.

3. Stop sequences ведут себя по-разному

Claude останавливается на stop sequence и не включает его в output. GPT-4 включает. Нашли это на проде через расхождение в парсере.

Качественные различия

Задача Winner
Structured extraction (JSON) Claude 3 Sonnet
Instruction following в длинных промптах Claude
Creative generation (короткие) GPT-4
Многошаговое reasoning с tool use ≈ (зависит от схемы)
Русский язык GPT-4 (на тот момент)

Для наших задач (в основном extraction + classification + generation в корпоративном стиле) Claude выигрывал стабильно.

Что сломалось в тестах

  • Snapshot-тесты на точный текст — ожидаемо, пришлось переписать в семантические (embedding similarity + judge).
  • Regex-парсеры output — Claude чаще использует вместо -, разные кавычки, список с вместо *. Починили нормализатором на входе в парсер.
  • Temperature=0 всё равно не детерминирован — ни у одного провайдера. Если сильно зависите — кешируйте сами на уровне приложения.

Operational

  • Rate limits калибруются дольше. У OpenAI мы давно имели высокий tier, у Anthropic пришлось заново писать запрос в поддержку. Первые 2 недели жили на retry logic.
  • Latency немного стабильнее у Anthropic на europe-west регионах.
  • Streaming API — принципиально похожи, но SSE format разный. Наш frontend-парсер пришлось подправить.

Вывод

Миграция между ведущими провайдерами — проект на 4–8 недель для среднего product с 50–100 prompt-ами. Две трети времени — не код, а переписывание eval-сетов, парсеров и инструментов. Если закладываете в архитектуру «будем менять провайдера» с самого начала — сделайте абстракцию тонкой, но предусмотрите именно эти места.


← Ко всем постам