Skip to content

Протокол Claude Messages

Claude Messages — это нативный протокол диалогов Anthropic. Если ваш клиент уже разработан в соответствии со спецификациями Anthropic, просто замените Base URL и API Key на RouteAPI, чтобы использовать его напрямую без изменения структуры запросов.

Claude Messages использует массив messages для представления многораундовых диалогов, независимое поле system для системных промптов и требует явного объявления max_tokens. По сравнению с форматом, совместимым с OpenAI, его структура блоков контента более унифицирована: текст, изображения, вызовы инструментов и результаты инструментов — все это разные значения type в одном массиве.

Применимые сценарии:

СценарийОписание
Claude CodeОфициальный агент кодирования Anthropic, распознает только /v1/messages
Anthropic SDKPython / TypeScript SDK anthropic, просто измените base_url
Клиенты с нативным форматом сообщенийПриложения, уже организованные со структурой блоков контента
Расширенное мышление и кэширование промптовЗависит от специфических возможностей Claude, таких как thinking, cache_control

Если ваш клиент поддерживает только протокол OpenAI, используйте Chat Completions. RouteAPI выполнит необходимую адаптацию формата внутри, но отдавайте предпочтение протоколу, нативно поддерживаемому клиентом, для лучшей совместимости.

POST /v1/messages

Полный адрес:

https://api.routeapi.ai/v1/messages

Заголовки запросов поддерживают два метода аутентификации, оба используют один и тот же токен RouteAPI:

Authorization: Bearer sk-your-routeapi-token
Content-Type: application/json
x-api-key: sk-your-routeapi-token
anthropic-version: 2023-06-01
Content-Type: application/json

x-api-key — это метод по умолчанию для Anthropic SDK. RouteAPI автоматически распознает его как токен на пути /v1/messages, поэтому официальный SDK не требует дополнительной конфигурации. anthropic-version передается вверх по потоку как есть, и официальный SDK автоматически включит его.

ПолеТипОбязательноОписание
modelstringДаID модели, должна быть доступной моделью для текущего аккаунта
messagesarrayДаСписок сообщений диалога, минимум одно, role должны чередоваться
max_tokensintegerДаМаксимальное количество выходных токенов, требуется протоколом Claude
systemstring/arrayНетСистемный промпт, независимое поле, не в messages
temperaturenumberНетТемпература выборки, диапазон от 0 до 1
top_pnumberНетПараметр nucleus sampling
top_kintegerНетВыборка только из K наиболее вероятных токенов
streambooleanНетИспользовать ли потоковый вывод SSE
stop_sequencesarrayНетПользовательские последовательности остановки
toolsarrayНетСписок определений инструментов
tool_choiceobjectНетСтратегия выбора инструмента
thinkingobjectНетКонфигурация расширенного мышления, зависит от поддержки модели
metadataobjectНетМетаданные запроса, специфичные для Claude

Это наиболее распространенная ловушка при миграции с 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 несет мета-информацию запроса, в настоящее время имеет только одно поле user_id для обнаружения злоупотреблений upstream. Не размещайте здесь личную информацию, такую как email или номер телефона, рекомендуется передавать хэш-значения или внутренние ID.

{
"metadata": {
"user_id": "a3f1c2d4e5b6"
}
}

Каждое сообщение в массиве messages содержит поля role и content. role может быть только user или assistant, должны чередоваться, и первое должно быть user.

content поддерживает две формы. Строка — это сокращение для одного текста:

{ "role": "user", "content": "Пожалуйста, представьте RouteAPI одним предложением" }

Форма массива состоит из блоков контента, каждый различается по type:

typeРасположениеОписание
textuser / assistantОбычный текстовый контент
imageuserВходное изображение, поддерживает base64 и URL
documentuserВходной документ, зависит от поддержки модели
tool_useassistantМодель запрашивает вызов инструмента
tool_resultuserРезультат выполнения инструмента, возвращенный клиентом
thinkingassistantБлок контента расширенного мышления

Мультимодальный контент

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": "Опишите макет этого интерфейса" }
]
}

Размещение текстовых блоков после блоков изображений обычно работает лучше. В один запрос можно включить несколько изображений, но это значительно увеличит входные токены, рекомендуется сначала сжать размер.

Определение инструментов 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 напрямую определяет, правильно ли модель выберет инструмент, рекомендуется четко указать назначение, формат параметров и граничные условия.

ФорматПоведение
{ "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.

{
"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Модель запрашивает вызов инструмента, ожидает результата

Установка stream: true возвращает SSE. Потоковый формат Claude значительно отличается от OpenAI: каждое событие имеет явное имя типа event:, и маркер конца — это событие message_stop, а не data: [DONE].

event: message_start
data: {"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_start
data: {"type":"content_block_start","index":0,"content_block":{"type":"text","text":""}}
event: content_block_delta
data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"RouteAPI"}}
event: content_block_delta
data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":" это"}}
event: content_block_stop
data: {"type":"content_block_stop","index":0}
event: message_delta
data: {"type":"message_delta","delta":{"stop_reason":"end_turn","stop_sequence":null},"usage":{"output_tokens":18}}
event: message_stop
data: {"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_delta
data: {"type":"content_block_delta","index":1,"delta":{"type":"input_json_delta","partial_json":"{\"city\":"}}
event: content_block_delta
data: {"type":"content_block_delta","index":1,"delta":{"type":"input_json_delta","partial_json":"\"Пекин\"}"}}

Группируйте и накапливайте по index, не пытайтесь парсить промежуточные состояния во время конкатенации.

ПолеОписание
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 MessagesOpenAI Chat CompletionsРазница
modelmodelОдинаково
system (поле верхнего уровня)messages[0] с role: "system"Разное расположение, Claude отклоняет системные сообщения
messagesmessagesClaude разрешает только чередование user / assistant
max_tokensmax_tokens / max_completion_tokensClaude обязателен, OpenAI опционален
stop_sequencesstopРазное название
temperaturetemperatureЛимит Claude 1, лимит OpenAI 2
top_kНет эквивалентаOpenAI не поддерживает
tools[].input_schematools[].function.parametersРазная иерархия и название поля
tool_choice: {"type":"any"}tool_choice: "required"Разный формат
metadata.user_iduserРазное расположение
thinkingreasoning_effortРазный метод управления
Нет эквивалентаnClaude не поддерживает генерацию нескольких кандидатов
Нет эквивалентаfrequency_penalty / presence_penaltyClaude не поддерживает
Нет эквивалентаresponse_formatClaude использует инструменты или промпты для ограничения структуры вывода

Различия в структуре ответа:

ЭлементClaude MessagesOpenAI Chat Completions
Контент верхнего уровняМассив contentchoices[0].message
Расположение текстаcontent[0].textchoices[0].message.content
Причина остановкиstop_reasonfinish_reason
Вызовы инструментовБлоки tool_use в contentmessage.tool_calls
Роль результата инструментаБлок tool_result в сообщении userНезависимая role: "tool"
Использование входаusage.input_tokensusage.prompt_tokens
Использование выходаusage.output_tokensusage.completion_tokens
Поле итогоНет, нужно суммировать вручнуюusage.total_tokens
Конец потокаСобытие message_stopdata: [DONE]

При миграции с OpenAI на Claude Messages проверяйте в таком порядке:

  1. Переместите системное сообщение из массива messages в поле верхнего уровня system.
  2. Добавьте max_tokens, это обязательно.
  3. Подтвердите, что первое сообщение в messages — это user, и роли строго чередуются без последовательных сообщений одной роли.
  4. Удалите слои обертки type и function из определений инструментов, переименуйте parameters в input_schema.
  5. Измените результаты инструментов с role: "tool" на блок tool_result в сообщении user и выровняйте tool_use_id.
  6. Если temperature изначально был больше 1, отрегулируйте до диапазона значений Claude.
  7. Измените парсинг ответа на итерацию по массиву content и диспетчеризацию по type, не предполагайте фиксированные индексы.
  8. Измените парсинг потока на диспетчеризацию по типу event:, замените условие конца на message_stop.

Если стоимость рефакторинга высока, вы можете продолжать использовать протокол OpenAI для вызова моделей серии Claude, с RouteAPI, выполняющим преобразование формата. Компромисс в том, что некоторые специфичные для Claude возможности (например, полный контроль расширенного мышления, тонкозернистое кэширование промптов) не могут быть полностью выражены в формате OpenAI.

curl:

Terminal window
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 os
from 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)

curl, используя base64:

Terminal window
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 base64
import os
from 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)

Полный цикл на два раунда, включая передачу результата:

import json
import os
from 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_use
if 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-запрос второго раунда:

Terminal window
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, см. Обработка ошибок для деталей.