Перенесення циклу чату з OpenAI до Claude на Python: одна сторінка змін
Перемістіть цикл обробки повідомлень без стану до API Messages від Anthropic: параметр системи верхнього рівня, обов’язкова кількість tokenів max_tokens, блоки контенту та сувора зміна ролей.
Джерела: 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.