Протокол Claude Messages
Claude Messages — это нативный протокол диалогов Anthropic. Если ваш клиент уже разработан в соответствии со спецификациями Anthropic, просто замените Base URL и API Key на RouteAPI, чтобы использовать его напрямую без изменения структуры запросов.
Обзор протокола
Section titled “Обзор протокола”Claude Messages использует массив messages для представления многораундовых диалогов, независимое поле system для системных промптов и требует явного объявления max_tokens. По сравнению с форматом, совместимым с OpenAI, его структура блоков контента более унифицирована: текст, изображения, вызовы инструментов и результаты инструментов — все это разные значения type в одном массиве.
Применимые сценарии:
| Сценарий | Описание |
|---|---|
| Claude Code | Официальный агент кодирования Anthropic, распознает только /v1/messages |
| Anthropic SDK | Python / TypeScript SDK anthropic, просто измените base_url |
| Клиенты с нативным форматом сообщений | Приложения, уже организованные со структурой блоков контента |
| Расширенное мышление и кэширование промптов | Зависит от специфических возможностей Claude, таких как thinking, cache_control |
Если ваш клиент поддерживает только протокол OpenAI, используйте Chat Completions. RouteAPI выполнит необходимую адаптацию формата внутри, но отдавайте предпочтение протоколу, нативно поддерживаемому клиентом, для лучшей совместимости.
Детали конечной точки
Section titled “Детали конечной точки”POST /v1/messagesПолный адрес:
https://api.routeapi.ai/v1/messagesЗаголовки запросов поддерживают два метода аутентификации, оба используют один и тот же токен RouteAPI:
Authorization: Bearer sk-your-routeapi-tokenContent-Type: application/jsonx-api-key: sk-your-routeapi-tokenanthropic-version: 2023-06-01Content-Type: application/jsonx-api-key — это метод по умолчанию для Anthropic SDK. RouteAPI автоматически распознает его как токен на пути /v1/messages, поэтому официальный SDK не требует дополнительной конфигурации. anthropic-version передается вверх по потоку как есть, и официальный SDK автоматически включит его.
Формат запроса
Section titled “Формат запроса”| Поле | Тип | Обязательно | Описание |
|---|---|---|---|
model | string | Да | ID модели, должна быть доступной моделью для текущего аккаунта |
messages | array | Да | Список сообщений диалога, минимум одно, role должны чередоваться |
max_tokens | integer | Да | Максимальное количество выходных токенов, требуется протоколом Claude |
system | string/array | Нет | Системный промпт, независимое поле, не в messages |
temperature | number | Нет | Температура выборки, диапазон от 0 до 1 |
top_p | number | Нет | Параметр nucleus sampling |
top_k | integer | Нет | Выборка только из K наиболее вероятных токенов |
stream | boolean | Нет | Использовать ли потоковый вывод SSE |
stop_sequences | array | Нет | Пользовательские последовательности остановки |
tools | array | Нет | Список определений инструментов |
tool_choice | object | Нет | Стратегия выбора инструмента |
thinking | object | Нет | Конфигурация расширенного мышления, зависит от поддержки модели |
metadata | object | Нет | Метаданные запроса, специфичные для Claude |
max_tokens обязателен
Section titled “max_tokens обязателен”Это наиболее распространенная ловушка при миграции с OpenAI. max_tokens OpenAI использует лимит модели по умолчанию, когда опущен, протокол Claude не имеет значения по умолчанию, и upstream вернет invalid_request_error, если отсутствует.
{ "model": "claude-sonnet-4-5", "max_tokens": 1024, "messages": [{ "role": "user", "content": "Привет" }]}max_tokens — это выходной лимит, не включает входные токены и не является точным обязательством по длине: модель может закончить раньше (stop_reason: "end_turn") или быть обрезана точно на лимите (stop_reason: "max_tokens"). Для production устанавливайте на основе ожидаемой длины ответа с некоторым запасом и проверяйте stop_reason, чтобы определить, была ли обрезка.
system — это независимое поле
Section titled “system — это независимое поле”Протокол Claude не принимает сообщения с role: "system". Системные промпты должны быть размещены в поле верхнего уровня system, а массив messages может содержать только user и assistant.
Правильное использование:
{ "model": "claude-sonnet-4-5", "max_tokens": 1024, "system": "Вы строгий технический ассистент, держите ответы краткими.", "messages": [{ "role": "user", "content": "Объясните, что такое API-шлюз" }]}Неправильное использование (протокол Claude отклонит):
{ "messages": [ { "role": "system", "content": "Вы строгий технический ассистент." }, { "role": "user", "content": "Объясните, что такое API-шлюз" } ]}system также поддерживает форму массива для установки кэширования промптов на отдельных абзацах:
{ "system": [ { "type": "text", "text": "Вы ассистент по проверке кода." }, { "type": "text", "text": "Вот полный стандарт кодирования проекта...", "cache_control": { "type": "ephemeral" } } ]}metadata
Section titled “metadata”metadata несет мета-информацию запроса, в настоящее время имеет только одно поле user_id для обнаружения злоупотреблений upstream. Не размещайте здесь личную информацию, такую как email или номер телефона, рекомендуется передавать хэш-значения или внутренние ID.
{ "metadata": { "user_id": "a3f1c2d4e5b6" }}Структура сообщений
Section titled “Структура сообщений”Каждое сообщение в массиве messages содержит поля role и content. role может быть только user или assistant, должны чередоваться, и первое должно быть user.
content поддерживает две формы. Строка — это сокращение для одного текста:
{ "role": "user", "content": "Пожалуйста, представьте RouteAPI одним предложением" }Форма массива состоит из блоков контента, каждый различается по type:
| type | Расположение | Описание |
|---|---|---|
text | user / assistant | Обычный текстовый контент |
image | user | Входное изображение, поддерживает base64 и URL |
document | user | Входной документ, зависит от поддержки модели |
tool_use | assistant | Модель запрашивает вызов инструмента |
tool_result | user | Результат выполнения инструмента, возвращенный клиентом |
thinking | assistant | Блок контента расширенного мышления |
Мультимодальный контент
Section titled “Мультимодальный контент”Изображения передаются через поле source. Метод base64 также требует предоставления media_type:
{ "role": "user", "content": [ { "type": "image", "source": { "type": "base64", "media_type": "image/jpeg", "data": "/9j/4AAQSkZJRgABAQAAAQ..." } }, { "type": "text", "text": "Какие элементы управления на этом изображении?" } ]}Метод URL более лаконичен, но требует, чтобы адрес изображения был доступен upstream:
{ "role": "user", "content": [ { "type": "image", "source": { "type": "url", "url": "https://example.com/screenshot.png" } }, { "type": "text", "text": "Опишите макет этого интерфейса" } ]}Размещение текстовых блоков после блоков изображений обычно работает лучше. В один запрос можно включить несколько изображений, но это значительно увеличит входные токены, рекомендуется сначала сжать размер.
Вызов инструментов
Section titled “Вызов инструментов”Формат определения tools
Section titled “Формат определения tools”Определение инструментов Claude имеет плоскую структуру с полем схемы параметров, называемым input_schema:
{ "tools": [ { "name": "get_weather", "description": "Запросить текущую погоду для указанного города. Используйте полное название города на китайском.", "input_schema": { "type": "object", "properties": { "city": { "type": "string", "description": "Название города, например Пекин" }, "unit": { "type": "string", "enum": ["celsius", "fahrenheit"] } }, "required": ["city"] } } ]}По сравнению с вложенной структурой OpenAI, разница в том, что Claude не имеет внешней обертки type: "function", нет слоя вложенности function, и parameters переименован в input_schema:
{ "tools": [ { "type": "function", "function": { "name": "get_weather", "description": "Запросить текущую погоду для указанного города.", "parameters": { "type": "object", "properties": {} } } } ]}Качество description напрямую определяет, правильно ли модель выберет инструмент, рекомендуется четко указать назначение, формат параметров и граничные условия.
Опции tool_choice
Section titled “Опции tool_choice”| Формат | Поведение |
|---|---|
{ "type": "auto" } | Модель сама решает, вызывать ли инструменты, значение по умолчанию |
{ "type": "any" } | Должна вызвать инструмент, но модель выбирает какой |
{ "type": "tool", "name": "get_weather" } | Принудительно вызвать указанный инструмент |
{ "type": "none" } | Запретить вызовы инструментов |
Добавление "disable_parallel_tool_use": true может ограничить модель инициированием только одного вызова инструмента за раз.
Передача результата инструмента
Section titled “Передача результата инструмента”Вызов инструмента — это полный диалоговый цикл туда-обратно. После того, как модель вернет блок tool_use, вам нужно передать обратно исходное сообщение assistant вместе с результатами выполнения.
Первый шаг, модель возвращает запрос на вызов инструмента:
{ "role": "assistant", "content": [ { "type": "text", "text": "Позвольте мне проверить погоду в Пекине." }, { "type": "tool_use", "id": "toolu_01A09q90qw90lq917835lq9", "name": "get_weather", "input": { "city": "Пекин", "unit": "celsius" } } ]}Второй шаг, добавьте это сообщение assistant как есть в messages, затем добавьте сообщение user, несущее результат. tool_use_id должен точно соответствовать id из предыдущего шага:
{ "role": "user", "content": [ { "type": "tool_result", "tool_use_id": "toolu_01A09q90qw90lq917835lq9", "content": "Пекин, солнечно, температура 23 градуса Цельсия, влажность 45%." } ]}Когда выполнение инструмента не удается, используйте маркер is_error, чтобы сообщить модели, что нужно изменить стратегию, а не повторять попытку:
{ "type": "tool_result", "tool_use_id": "toolu_01A09q90qw90lq917835lq9", "content": "Тайм-аут службы погоды, данные не получены.", "is_error": true}Обратите внимание, что tool_result принадлежит роли user, протокол Claude не имеет независимой role: "tool", как OpenAI. Если модель возвращает несколько блоков tool_use за раз, все соответствующие tool_result должны быть размещены в массиве content одного сообщения user.
Формат ответа
Section titled “Формат ответа”Стандартный ответ
Section titled “Стандартный ответ”{ "id": "msg_01XFDUDYJgAACzvnptvVoYEL", "type": "message", "role": "assistant", "model": "claude-sonnet-4-5", "content": [ { "type": "text", "text": "RouteAPI — это API-шлюз, который единообразно управляет несколькими поставщиками моделей AI." } ], "stop_reason": "end_turn", "stop_sequence": null, "usage": { "input_tokens": 24, "output_tokens": 18 }}content всегда является массивом, даже с одним текстовым сегментом. Клиенты не должны предполагать, что content[0] — это текстовый блок; когда модель включает расширенное мышление или инициирует вызовы инструментов, первый блок может быть thinking или tool_use.
Значения stop_reason:
| Значение | Значение |
|---|---|
end_turn | Модель естественно завершила ответ |
max_tokens | Достигнут лимит max_tokens и обрезано |
stop_sequence | Попадание в последовательность в stop_sequences |
tool_use | Модель запрашивает вызов инструмента, ожидает результата |
Потоковый ответ
Section titled “Потоковый ответ”Установка stream: true возвращает SSE. Потоковый формат Claude значительно отличается от OpenAI: каждое событие имеет явное имя типа event:, и маркер конца — это событие message_stop, а не data: [DONE].
event: message_startdata: {"type":"message_start","message":{"id":"msg_01XFD","type":"message","role":"assistant","content":[],"model":"claude-sonnet-4-5","usage":{"input_tokens":24,"output_tokens":1}}}
event: content_block_startdata: {"type":"content_block_start","index":0,"content_block":{"type":"text","text":""}}
event: content_block_deltadata: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"RouteAPI"}}
event: content_block_deltadata: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":" это"}}
event: content_block_stopdata: {"type":"content_block_stop","index":0}
event: message_deltadata: {"type":"message_delta","delta":{"stop_reason":"end_turn","stop_sequence":null},"usage":{"output_tokens":18}}
event: message_stopdata: {"type":"message_stop"}Описания типов событий:
| Событие | Описание |
|---|---|
message_start | Начало сообщения, несет начальный usage (output_tokens еще не точен) |
content_block_start | Начинается блок контента, index идентифицирует позицию |
content_block_delta | Инкрементальный контент, текст использует text_delta, параметры инструмента используют input_json_delta |
content_block_stop | Текущий блок контента заканчивается |
message_delta | Инкремент на уровне сообщения, несет финальный stop_reason и накопительный output_tokens |
message_stop | Весь ответ заканчивается |
ping | Событие heartbeat, можно игнорировать |
error | Ошибка произошла в середине потока |
Параметры вызова инструмента возвращаются как строки JSON по частям, нужно конкатенировать все partial_json из input_json_delta перед парсингом:
event: content_block_deltadata: {"type":"content_block_delta","index":1,"delta":{"type":"input_json_delta","partial_json":"{\"city\":"}}
event: content_block_deltadata: {"type":"content_block_delta","index":1,"delta":{"type":"input_json_delta","partial_json":"\"Пекин\"}"}}Группируйте и накапливайте по index, не пытайтесь парсить промежуточные состояния во время конкатенации.
Поле usage
Section titled “Поле usage”| Поле | Описание |
|---|---|
input_tokens | Количество входных токенов, исключает часть попадания в кэш |
output_tokens | Количество выходных токенов |
cache_creation_input_tokens | Токены, записанные в кэш промпта |
cache_read_input_tokens | Токены, прочитанные из кэша промпта |
server_tool_use | Использование инструментов на стороне сервера, например web_search_requests |
В потоковом ответе output_tokens должен использовать значение в событии message_delta, значение в message_start — это начальный заполнитель. Биллинг основан на логах консоли, фактически поддерживаемые поля зависят от выбранной модели.
Сравнение с форматом OpenAI
Section titled “Сравнение с форматом OpenAI”Таблица соответствия параметров
Section titled “Таблица соответствия параметров”| Claude Messages | OpenAI Chat Completions | Разница |
|---|---|---|
model | model | Одинаково |
system (поле верхнего уровня) | messages[0] с role: "system" | Разное расположение, Claude отклоняет системные сообщения |
messages | messages | Claude разрешает только чередование user / assistant |
max_tokens | max_tokens / max_completion_tokens | Claude обязателен, OpenAI опционален |
stop_sequences | stop | Разное название |
temperature | temperature | Лимит Claude 1, лимит OpenAI 2 |
top_k | Нет эквивалента | OpenAI не поддерживает |
tools[].input_schema | tools[].function.parameters | Разная иерархия и название поля |
tool_choice: {"type":"any"} | tool_choice: "required" | Разный формат |
metadata.user_id | user | Разное расположение |
thinking | reasoning_effort | Разный метод управления |
| Нет эквивалента | n | Claude не поддерживает генерацию нескольких кандидатов |
| Нет эквивалента | frequency_penalty / presence_penalty | Claude не поддерживает |
| Нет эквивалента | response_format | Claude использует инструменты или промпты для ограничения структуры вывода |
Различия в структуре ответа:
| Элемент | Claude Messages | OpenAI Chat Completions |
|---|---|---|
| Контент верхнего уровня | Массив content | choices[0].message |
| Расположение текста | content[0].text | choices[0].message.content |
| Причина остановки | stop_reason | finish_reason |
| Вызовы инструментов | Блоки tool_use в content | message.tool_calls |
| Роль результата инструмента | Блок tool_result в сообщении user | Независимая role: "tool" |
| Использование входа | usage.input_tokens | usage.prompt_tokens |
| Использование выхода | usage.output_tokens | usage.completion_tokens |
| Поле итого | Нет, нужно суммировать вручную | usage.total_tokens |
| Конец потока | Событие message_stop | data: [DONE] |
Заметки по миграции
Section titled “Заметки по миграции”При миграции с OpenAI на Claude Messages проверяйте в таком порядке:
- Переместите системное сообщение из массива
messagesв поле верхнего уровняsystem. - Добавьте
max_tokens, это обязательно. - Подтвердите, что первое сообщение в
messages— этоuser, и роли строго чередуются без последовательных сообщений одной роли. - Удалите слои обертки
typeиfunctionиз определений инструментов, переименуйтеparametersвinput_schema. - Измените результаты инструментов с
role: "tool"на блокtool_resultв сообщенииuserи выровняйтеtool_use_id. - Если
temperatureизначально был больше1, отрегулируйте до диапазона значений Claude. - Измените парсинг ответа на итерацию по массиву
contentи диспетчеризацию поtype, не предполагайте фиксированные индексы. - Измените парсинг потока на диспетчеризацию по типу
event:, замените условие конца наmessage_stop.
Если стоимость рефакторинга высока, вы можете продолжать использовать протокол OpenAI для вызова моделей серии Claude, с RouteAPI, выполняющим преобразование формата. Компромисс в том, что некоторые специфичные для Claude возможности (например, полный контроль расширенного мышления, тонкозернистое кэширование промптов) не могут быть полностью выражены в формате OpenAI.
Полные примеры
Section titled “Полные примеры”Базовый диалог
Section titled “Базовый диалог”curl:
curl https://api.routeapi.ai/v1/messages \ -H "x-api-key: $ROUTEAPI_KEY" \ -H "anthropic-version: 2023-06-01" \ -H "Content-Type: application/json" \ -d '{ "model": "claude-sonnet-4-5", "max_tokens": 1024, "system": "Вы строгий технический ассистент, держите ответы краткими.", "messages": [ { "role": "user", "content": "Пожалуйста, представьте RouteAPI одним предложением" } ] }'Anthropic Python SDK, просто измените base_url:
import osfrom anthropic import Anthropic
client = Anthropic( api_key=os.environ["ROUTEAPI_KEY"], base_url="https://api.routeapi.ai",)
message = client.messages.create( model="claude-sonnet-4-5", max_tokens=1024, system="Вы строгий технический ассистент, держите ответы краткими.", messages=[ {"role": "user", "content": "Пожалуйста, представьте RouteAPI одним предложением"}, ],)
print(message.content[0].text)print(message.usage.input_tokens, message.usage.output_tokens)base_url нужен только домен, SDK автоматически добавит /v1/messages. Потоковые вызовы используют client.messages.stream():
with client.messages.stream( model="claude-sonnet-4-5", max_tokens=1024, messages=[{"role": "user", "content": "Объясните пошагово, что такое API-шлюз"}],) as stream: for text in stream.text_stream: print(text, end="", flush=True)
final = stream.get_final_message() print() print(final.stop_reason, final.usage.output_tokens)Понимание изображений
Section titled “Понимание изображений”curl, используя base64:
curl https://api.routeapi.ai/v1/messages \ -H "x-api-key: $ROUTEAPI_KEY" \ -H "anthropic-version: 2023-06-01" \ -H "Content-Type: application/json" \ -d '{ "model": "claude-sonnet-4-5", "max_tokens": 1024, "messages": [ { "role": "user", "content": [ { "type": "image", "source": { "type": "base64", "media_type": "image/jpeg", "data": "'"$(base64 -w 0 screenshot.jpg)"'" } }, { "type": "text", "text": "Какие элементы управления UI на этом изображении?" } ] } ] }'Python SDK:
import base64import osfrom anthropic import Anthropic
client = Anthropic( api_key=os.environ["ROUTEAPI_KEY"], base_url="https://api.routeapi.ai",)
with open("screenshot.jpg", "rb") as f: image_data = base64.standard_b64encode(f.read()).decode("utf-8")
message = client.messages.create( model="claude-sonnet-4-5", max_tokens=1024, messages=[ { "role": "user", "content": [ { "type": "image", "source": { "type": "base64", "media_type": "image/jpeg", "data": image_data, }, }, {"type": "text", "text": "Какие элементы управления UI на этом изображении?"}, ], } ],)
print(message.content[0].text)Вызов инструментов
Section titled “Вызов инструментов”Полный цикл на два раунда, включая передачу результата:
import jsonimport osfrom anthropic import Anthropic
client = Anthropic( api_key=os.environ["ROUTEAPI_KEY"], base_url="https://api.routeapi.ai",)
tools = [ { "name": "get_weather", "description": "Запросить текущую погоду для указанного города. Используйте полное название города на китайском.", "input_schema": { "type": "object", "properties": { "city": {"type": "string", "description": "Название города, например Пекин"}, "unit": {"type": "string", "enum": ["celsius", "fahrenheit"]}, }, "required": ["city"], }, }]
def get_weather(city: str, unit: str = "celsius") -> str: # Замените на реальный вызов службы погоды return f"{city}, солнечно, температура 23 градуса Цельсия, влажность 45%."
messages = [{"role": "user", "content": "Какая погода в Пекине сейчас?"}]
response = client.messages.create( model="claude-sonnet-4-5", max_tokens=1024, tools=tools, messages=messages,)
# Нужно выполнить инструмент и передать обратно только когда stop_reason это tool_useif response.stop_reason == "tool_use": # Исходное сообщение assistant должно быть добавлено обратно как есть, иначе tool_use_id не может выровняться messages.append({"role": "assistant", "content": response.content})
tool_results = [] for block in response.content: if block.type != "tool_use": continue try: result = get_weather(**block.input) is_error = False except Exception as exc: result = f"Не удалось выполнить инструмент: {exc}" is_error = True tool_results.append( { "type": "tool_result", "tool_use_id": block.id, "content": result, "is_error": is_error, } )
# Все результаты инструментов из одного раунда идут в одно сообщение user messages.append({"role": "user", "content": tool_results})
response = client.messages.create( model="claude-sonnet-4-5", max_tokens=1024, tools=tools, messages=messages, )
print(response.content[0].text)Соответствующий curl-запрос второго раунда:
curl https://api.routeapi.ai/v1/messages \ -H "x-api-key: $ROUTEAPI_KEY" \ -H "anthropic-version: 2023-06-01" \ -H "Content-Type: application/json" \ -d '{ "model": "claude-sonnet-4-5", "max_tokens": 1024, "tools": [ { "name": "get_weather", "description": "Запросить текущую погоду для указанного города.", "input_schema": { "type": "object", "properties": { "city": { "type": "string" } }, "required": ["city"] } } ], "messages": [ { "role": "user", "content": "Какая погода в Пекине сейчас?" }, { "role": "assistant", "content": [ { "type": "tool_use", "id": "toolu_01A09q90qw90lq917835lq9", "name": "get_weather", "input": { "city": "Пекин" } } ] }, { "role": "user", "content": [ { "type": "tool_result", "tool_use_id": "toolu_01A09q90qw90lq917835lq9", "content": "Пекин, солнечно, температура 23 градуса Цельсия, влажность 45%." } ] } ] }'Напоминания о совместимости
Section titled “Напоминания о совместимости”- Фактическая поддержка параметров зависит от выбранной модели и возможностей upstream-сервиса, опциональные возможности, такие как
thinking,cache_control,mcp_servers, должны быть проверены в тестовой среде в первую очередь. - Опциональные параметры, явно переданные как
0илиfalse, будут рассматриваться как явные настройки пользователя, не будут отброшены как значения по умолчанию. - В production-среде следует фиксировать ID модели, не полагаться на временные алиасы или отображаемые имена.
- Записывайте request ID, ID модели, код состояния и использование токенов для каждого запроса, чтобы облегчить устранение неполадок с задержкой и аномалиями стоимости.
- Ответы об ошибках следуют структуре
{"type": "error", "error": {...}}Claude, см. Обработка ошибок для деталей.