Главная / Статьи

Перенос цикла чата с OpenAI в Claude на Python: одна страница изменений

Переместите цикл обработки сообщений без состояния в API Messages от Anthropic: параметр верхнего уровня, обязательное значение max_tokens, блоки содержимого и строгая смена ролей.

Издатель: REACTAPP.TOP4 мин чтения867 слов

Источники: Part 3 — Call the Claude API in Python (same messages, system on the side)

Любой, кто писал цикл чатов для OpenAI, может перенести его на Claude от Anthropic, сделав примерно одну страницу изменений. Оба сервиса используют одинаковую структуру: список messages с указанием ролей, который отправляется полностью при каждом вызове, а ответ добавляется позже. Изменяется лишь один элемент, и важно точно знать, какой именно.

Тот же список, но другая система снаружи

API Messages от Claude принимает тот же формат разговора, который вам уже знаком: список словарей с полями role и content; сервер не сохраняет состояние, и данные отправляются полностью каждый раз. Названия и значение ролей идентичны — user и assistant.

Единственное структурное отличие заключается в том, что system не является сообщением. OpenAI размещает инструкции в нулевой строке списка, тогда как Anthropic перемещает их в параметр верхнего уровня system=, который сопровождает список. Сам список содержит только реальный диалог — по очереди вводимые пользователем и ассистентом сообщения.

import anthropic

client = anthropic.Anthropic()  # key from env

resp = client.messages.create(
    model="claude-sonnet-4-5",
    max_tokens=300,
    system="You are a concise Python assistant.",
    messages=[
        {
            "role": "user",
            "content": "Why is system a parameter "
                       "here, not a message?",
        },
    ],
)
print(resp.content[0].text)

До того, как всё это заработает, возникают два небольших различия:

  • max_tokens должен быть установлен. OpenAI позволяет отправлять запросы без ограничения по объему вывода. Claude требует указания этого параметра при каждом вызове, что немного раздражает, но помогает контролировать затраты, поскольку запрос невозможен до тех пор, пока не будет определено максимальное время ответа.
  • Ответы приходят в виде блоков контента. Вместо одной строки вы получаете список, причем обычный текст находится по адресу resp.content[0].text. Сегодня это кажется избыточным, но позже вызовы инструментов также приходят в такой же структуре, поэтому это имеет смысл.

Настройка и один ход

Установите SDK с помощью команды pip install anthropic, затем экспортируйте ключ из консоли Anthropic (он начинается с sk-ant-) под именем ANTHROPIC_API_KEY. Клиент сам его загрузит.

Если разместить его рядом с вызовом OpenAI, то общая разница составит четыре изменения. Метод client.chat.completions.create заменяется на client.messages.create, строка системы перемещается в поле system=, появляется обязательный лимит на вывод, а текст читается из content[0].text вместо choices[0].message.content. Примечание к SDK: текущая версия пакета anthropic не принимает параметр temperature= для метода messages.create, поэтому старые примеры, содержащие его, будут выдавать ошибки. Применяется стандартная стратегия выбора элементов, и это подходит для этих примеров.

Количество токенов указывается в полях resp.usage.input_tokens и resp.usage.output_tokens. Это один и тот же счетчик с незначительно отличающимися именами полей, поэтому стоит записывать их каждый раз.

Цикл истории в Claude

Вот функция ask() из версии OpenAI, адаптированная для использования здесь. Логика добавления данных и повторной отправки осталась прежней. Изменился только вызов в середине кода.

msgs = []

def ask(text: str) -> str:
    msgs.append(
        {"role": "user", "content": text}
    )
    resp = client.messages.create(
        model="claude-sonnet-4-5",
        max_tokens=300,
        system="You are a concise assistant.",
        messages=msgs,  # full history
    )
    reply = resp.content[0].text
    msgs.append(
        {"role": "assistant", "content": reply}
    )
    return reply

print(ask("Define a context window."))
print(ask("Now for a five-year-old."))
print(ask("Which answer was shorter?"))

Третий вопрос в диалоге по-прежнему подтверждает наличие цикла, поскольку он сработает только в том случае, если оба предыдущих ответа находятся в msgs и пересылаются заново. Здесь также применима та же экономика: количество входных токенов увеличивается с каждым разом, поэтому длинные диалоги стоят дороже за один вызов.

Claude также более придирчив к порядку: сообщения должны чередоваться между пользователем и ассистентом. Два сообщения от пользователя подряд, что легко происходит при повторной попытке выполнения неудачного запроса, заставляют Claude отклонить вызов, в то время как OpenAI это терпит. Считайте это бесплатным правилом проверки для того, кто управляет историей диалогов.

Запуск

Скрипт выполняет один раунд диалога, затем трехраундовый диалог, фиксируя количество входных и выходных токенов после каждого вызова. Он прерывается с ясным сообщением, если отсутствует ANTHROPIC_API_KEY. Кроме того, он отправляет реальные, оплачиваемые запросы, поэтому его не следует использовать в процессах CI.

pip install -r requirements.txt
export ANTHROPIC_API_KEY="sk-ant..."
python examples/part03_claude.py

Почему адаптация прошла легко и что вы получаете

Это были незначительные изменения, поскольку оба провайдера используют один и тот же интерфейс: безстатический конечный пункт, принимающий всю информацию о диалоге при каждом вызове и отвечающий один раз. Различия касаются деталей реализации, таких как местоположение системного промпта, формат объекта ответа и необходимые параметры. LangChain абстрагирует этот уровень сложностей, а его классы ChatOpenAI и ChatAnthropic предназначены для устранения тех четырех строк кода, которые раньше приходилось писать вручную. Понимание того, что скрывает эта абстракция, определяет разницу между её использованием и оказанием в зависимости от неё.

Существует также практическая выгода. Два поставщика, работающих в рамках одной структуры ask(), позволяют проводить тестирование моделей в режиме A/B, переключаться на альтернативный API в случае сбоев или направлять простые запросы к одной модели, а сложные — к другой, при этом не изменяя код приложения. API Gemini от Google использует ту же структуру общения, но с переименованным элементом: ассистент теперь называется model.