Protocole Claude Messages
Claude Messages est le protocole de conversation natif d’Anthropic. Si votre client est déjà développé selon les spécifications Anthropic, il suffit de remplacer l’URL de base et la clé API par RouteAPI pour l’utiliser directement sans modifier la structure des requêtes.
Vue d’ensemble du protocole
Section intitulée « Vue d’ensemble du protocole »Claude Messages utilise un tableau messages pour représenter les conversations multi-tours, un champ system indépendant pour les prompts système, et nécessite une déclaration explicite de max_tokens. Comparé au format compatible OpenAI, sa structure de bloc de contenu est plus unifiée : texte, images, appels d’outils et résultats d’outils sont tous des valeurs type différentes dans le même tableau.
Scénarios applicables :
| Scénario | Description |
|---|---|
| Claude Code | Agent de codage officiel d’Anthropic, reconnaît uniquement /v1/messages |
| Anthropic SDK | SDK Python / TypeScript anthropic, il suffit de changer base_url |
| Clients au format de message natif | Applications déjà organisées avec la structure de bloc de contenu |
| Pensée étendue & cache de prompt | Dépend des capacités spécifiques à Claude comme thinking, cache_control |
Si votre client ne prend en charge que le protocole OpenAI, veuillez utiliser Chat Completions. RouteAPI effectuera l’adaptation de format nécessaire en interne, mais privilégiez le protocole nativement pris en charge par le client pour une meilleure compatibilité.
Détails du point de terminaison
Section intitulée « Détails du point de terminaison »POST /v1/messagesAdresse complète :
https://api.routeapi.ai/v1/messagesLes en-têtes de requête prennent en charge deux méthodes d’authentification, toutes deux utilisant le même jeton 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 est la méthode par défaut pour le SDK Anthropic. RouteAPI le reconnaît automatiquement comme jeton sur le chemin /v1/messages, donc le SDK officiel ne nécessite aucune configuration supplémentaire. anthropic-version est transmis tel quel en amont, et le SDK officiel l’inclura automatiquement.
Format de requête
Section intitulée « Format de requête »| Champ | Type | Requis | Description |
|---|---|---|---|
model | string | Oui | ID du modèle, doit être un modèle disponible pour le compte actuel |
messages | array | Oui | Liste des messages de conversation, au moins un, les role doivent alterner |
max_tokens | integer | Oui | Nombre maximum de tokens de sortie, requis par le protocole Claude |
system | string/array | Non | Prompt système, champ indépendant, pas dans messages |
temperature | number | Non | Température d’échantillonnage, plage 0 à 1 |
top_p | number | Non | Paramètre nucleus sampling |
top_k | integer | Non | Échantillonner uniquement à partir des K tokens les plus probables |
stream | boolean | Non | Utiliser ou non la sortie en streaming SSE |
stop_sequences | array | Non | Séquences d’arrêt personnalisées |
tools | array | Non | Liste de définitions d’outils |
tool_choice | object | Non | Stratégie de sélection d’outil |
thinking | object | Non | Configuration de pensée étendue, dépend du support du modèle |
metadata | object | Non | Métadonnées de requête, spécifique à Claude |
max_tokens est obligatoire
Section intitulée « max_tokens est obligatoire »C’est le piège le plus courant lors de la migration depuis OpenAI. Le max_tokens d’OpenAI utilise la limite par défaut du modèle lorsqu’il est omis, le protocole Claude n’a pas de valeur par défaut, et l’amont retournera invalid_request_error s’il manque.
{ "model": "claude-sonnet-4-5", "max_tokens": 1024, "messages": [{ "role": "user", "content": "Bonjour" }]}max_tokens est une limite de sortie, n’inclut pas les tokens d’entrée, et n’est pas un engagement de longueur exacte : le modèle peut finir plus tôt (stop_reason: "end_turn") ou être tronqué exactement à la limite (stop_reason: "max_tokens"). Pour la production, définissez en fonction de la longueur de réponse attendue avec une certaine marge, et vérifiez stop_reason pour déterminer si tronqué.
system est un champ indépendant
Section intitulée « system est un champ indépendant »Le protocole Claude n’accepte pas les messages avec role: "system". Les prompts système doivent être placés dans le champ system de niveau supérieur, et le tableau messages ne peut contenir que user et assistant.
Utilisation correcte :
{ "model": "claude-sonnet-4-5", "max_tokens": 1024, "system": "Vous êtes un assistant technique rigoureux, gardez les réponses concises.", "messages": [{ "role": "user", "content": "Expliquez ce qu'est une passerelle API" }]}Utilisation incorrecte (le protocole Claude rejettera) :
{ "messages": [ { "role": "system", "content": "Vous êtes un assistant technique rigoureux." }, { "role": "user", "content": "Expliquez ce qu'est une passerelle API" } ]}system prend également en charge la forme de tableau pour définir le cache de prompt sur différents paragraphes individuellement :
{ "system": [ { "type": "text", "text": "Vous êtes un assistant de révision de code." }, { "type": "text", "text": "Voici la norme de codage complète du projet...", "cache_control": { "type": "ephemeral" } } ]}metadata
Section intitulée « metadata »metadata transporte les méta-informations de requête, n’a actuellement qu’un champ user_id pour la détection d’abus en amont. Ne placez pas d’informations personnellement identifiables comme l’e-mail ou le numéro de téléphone ici, il est recommandé de transmettre des valeurs de hachage ou des ID internes.
{ "metadata": { "user_id": "a3f1c2d4e5b6" }}Structure des messages
Section intitulée « Structure des messages »Chaque message dans le tableau messages contient les champs role et content. role ne peut être que user ou assistant, doit alterner, et le premier doit être user.
content prend en charge deux formes. La chaîne est une abréviation pour un texte unique :
{ "role": "user", "content": "Veuillez présenter RouteAPI en une phrase" }La forme de tableau est composée de blocs de contenu, chacun distingué par type :
| type | Emplacement | Description |
|---|---|---|
text | user / assistant | Contenu texte brut |
image | user | Entrée d’image, prend en charge base64 et URL |
document | user | Entrée de document, dépend du support du modèle |
tool_use | assistant | Le modèle demande à appeler un outil |
tool_result | user | Résultat d’exécution d’outil retourné par le client |
thinking | assistant | Bloc de contenu de pensée étendue |
Contenu multimodal
Section intitulée « Contenu multimodal »Les images sont transmises via le champ source. La méthode base64 nécessite également de fournir media_type :
{ "role": "user", "content": [ { "type": "image", "source": { "type": "base64", "media_type": "image/jpeg", "data": "/9j/4AAQSkZJRgABAQAAAQ..." } }, { "type": "text", "text": "Quels contrôles sont dans cette image ?" } ]}La méthode URL est plus concise mais nécessite que l’adresse de l’image soit accessible par l’amont :
{ "role": "user", "content": [ { "type": "image", "source": { "type": "url", "url": "https://example.com/screenshot.png" } }, { "type": "text", "text": "Décrivez la mise en page de cette interface" } ]}Placer les blocs de texte après les blocs d’image fonctionne généralement mieux. Plusieurs images peuvent être incluses dans une requête, mais cela augmentera considérablement les tokens d’entrée, il est recommandé de compresser la taille d’abord.
Appel d’outils
Section intitulée « Appel d’outils »Format de définition des tools
Section intitulée « Format de définition des tools »La définition d’outil de Claude est une structure plate avec un champ de schéma de paramètre appelé input_schema :
{ "tools": [ { "name": "get_weather", "description": "Interroger la météo actuelle pour une ville spécifiée. Utilisez le nom complet de la ville en chinois.", "input_schema": { "type": "object", "properties": { "city": { "type": "string", "description": "Nom de la ville, par ex. Pékin" }, "unit": { "type": "string", "enum": ["celsius", "fahrenheit"] } }, "required": ["city"] } } ]}Comparé à la structure imbriquée d’OpenAI, la différence est que Claude n’a pas d’enveloppe externe type: "function", pas de couche d’imbrication function, et parameters est renommé en input_schema :
{ "tools": [ { "type": "function", "function": { "name": "get_weather", "description": "Interroger la météo actuelle pour une ville spécifiée.", "parameters": { "type": "object", "properties": {} } } } ]}La qualité de description détermine directement si le modèle choisira correctement l’outil, il est recommandé d’indiquer clairement l’objectif, le format des paramètres et les conditions limites.
Options de tool_choice
Section intitulée « Options de tool_choice »| Format | Comportement |
|---|---|
{ "type": "auto" } | Le modèle décide d’appeler ou non des outils, valeur par défaut |
{ "type": "any" } | Doit appeler un outil, mais le modèle choisit lequel |
{ "type": "tool", "name": "get_weather" } | Forcer l’appel de l’outil spécifié |
{ "type": "none" } | Interdire les appels d’outils |
L’ajout de "disable_parallel_tool_use": true peut limiter le modèle à initier un seul appel d’outil à la fois.
Transmission du résultat de l’outil
Section intitulée « Transmission du résultat de l’outil »L’appel d’outil est un aller-retour conversationnel complet. Après que le modèle retourne un bloc tool_use, vous devez retransmettre le message assistant original avec les résultats d’exécution.
Première étape, le modèle retourne une demande d’appel d’outil :
{ "role": "assistant", "content": [ { "type": "text", "text": "Laissez-moi vérifier la météo à Pékin." }, { "type": "tool_use", "id": "toolu_01A09q90qw90lq917835lq9", "name": "get_weather", "input": { "city": "Pékin", "unit": "celsius" } } ]}Deuxième étape, ajoutez ce message assistant tel quel à messages, puis ajoutez un message user portant le résultat. tool_use_id doit correspondre exactement à l’id de l’étape précédente :
{ "role": "user", "content": [ { "type": "tool_result", "tool_use_id": "toolu_01A09q90qw90lq917835lq9", "content": "Pékin, ensoleillé, température 23 celsius, humidité 45%." } ]}Lorsque l’exécution de l’outil échoue, utilisez le marqueur is_error pour faire savoir au modèle qu’il doit changer de stratégie plutôt que de réessayer :
{ "type": "tool_result", "tool_use_id": "toolu_01A09q90qw90lq917835lq9", "content": "Délai d'attente du service météo, aucune donnée récupérée.", "is_error": true}Notez que tool_result appartient au rôle user, le protocole Claude n’a pas de role: "tool" indépendant comme OpenAI. Si le modèle retourne plusieurs blocs tool_use en une fois, tous les tool_result correspondants doivent être placés dans le tableau content du même message user.
Format de réponse
Section intitulée « Format de réponse »Réponse standard
Section intitulée « Réponse standard »{ "id": "msg_01XFDUDYJgAACzvnptvVoYEL", "type": "message", "role": "assistant", "model": "claude-sonnet-4-5", "content": [ { "type": "text", "text": "RouteAPI est une passerelle API qui gère uniformément plusieurs fournisseurs de modèles AI." } ], "stop_reason": "end_turn", "stop_sequence": null, "usage": { "input_tokens": 24, "output_tokens": 18 }}content est toujours un tableau, même avec un seul segment de texte. Les clients ne doivent pas supposer que content[0] est un bloc de texte ; lorsque le modèle active la pensée étendue ou initie des appels d’outils, le premier bloc peut être thinking ou tool_use.
Valeurs de stop_reason :
| Valeur | Signification |
|---|---|
end_turn | Le modèle a terminé naturellement la réponse |
max_tokens | A atteint la limite max_tokens et a été tronqué |
stop_sequence | A atteint une séquence dans stop_sequences |
tool_use | Le modèle demande à appeler un outil, en attente du résultat |
Réponse en streaming
Section intitulée « Réponse en streaming »La définition de stream: true retourne SSE. Le format de streaming de Claude diffère considérablement d’OpenAI : chaque événement a un nom de type event: explicite, et le marqueur de fin est un événement message_stop, pas 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":" est une"}}
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"}Descriptions des types d’événements :
| Événement | Description |
|---|---|
message_start | Début du message, porte l’usage initial (output_tokens pas encore précis) |
content_block_start | Un bloc de contenu commence, index identifie la position |
content_block_delta | Contenu incrémental, le texte utilise text_delta, les paramètres d’outil utilisent input_json_delta |
content_block_stop | Le bloc de contenu actuel se termine |
message_delta | Incrément au niveau du message, porte le stop_reason final et output_tokens cumulatif |
message_stop | La réponse entière se termine |
ping | Événement de pulsation, peut être ignoré |
error | Erreur survenue au milieu du flux |
Les paramètres d’appel d’outil sont retournés sous forme de chaînes JSON pièce par pièce, il faut concaténer tous les partial_json d’input_json_delta avant l’analyse :
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":"\"Pékin\"}"}}Regrouper et accumuler par index, ne pas essayer d’analyser les états intermédiaires pendant la concaténation.
Champ usage
Section intitulée « Champ usage »| Champ | Description |
|---|---|
input_tokens | Nombre de tokens d’entrée, exclut la partie d’accès au cache |
output_tokens | Nombre de tokens de sortie |
cache_creation_input_tokens | Tokens écrits dans le cache de prompt |
cache_read_input_tokens | Tokens lus depuis le cache de prompt |
server_tool_use | Utilisation d’outils côté serveur, par ex. web_search_requests |
Dans la réponse en streaming, output_tokens doit utiliser la valeur dans l’événement message_delta, celle dans message_start est un espace réservé initial. La facturation est basée sur les journaux de la console, les champs réellement pris en charge dépendent du modèle sélectionné.
Comparaison avec le format OpenAI
Section intitulée « Comparaison avec le format OpenAI »Tableau de correspondance des paramètres
Section intitulée « Tableau de correspondance des paramètres »| Claude Messages | OpenAI Chat Completions | Différence |
|---|---|---|
model | model | Identique |
system (champ de niveau supérieur) | messages[0] avec role: "system" | Emplacement différent, Claude rejette les messages système |
messages | messages | Claude n’autorise que l’alternance user / assistant |
max_tokens | max_tokens / max_completion_tokens | Claude obligatoire, OpenAI optionnel |
stop_sequences | stop | Nom différent |
temperature | temperature | Limite Claude 1, limite OpenAI 2 |
top_k | Pas d’équivalent | OpenAI ne prend pas en charge |
tools[].input_schema | tools[].function.parameters | Hiérarchie et nom de champ différents |
tool_choice: {"type":"any"} | tool_choice: "required" | Format différent |
metadata.user_id | user | Emplacement différent |
thinking | reasoning_effort | Méthode de contrôle différente |
| Pas d’équivalent | n | Claude ne prend pas en charge la génération de plusieurs candidats |
| Pas d’équivalent | frequency_penalty / presence_penalty | Claude ne prend pas en charge |
| Pas d’équivalent | response_format | Claude utilise des outils ou des prompts pour contraindre la structure de sortie |
Différences de structure de réponse :
| Élément | Claude Messages | OpenAI Chat Completions |
|---|---|---|
| Contenu de niveau supérieur | Tableau content | choices[0].message |
| Emplacement du texte | content[0].text | choices[0].message.content |
| Raison d’arrêt | stop_reason | finish_reason |
| Appels d’outils | Blocs tool_use dans content | message.tool_calls |
| Rôle du résultat de l’outil | Bloc tool_result dans message user | role: "tool" indépendant |
| Utilisation d’entrée | usage.input_tokens | usage.prompt_tokens |
| Utilisation de sortie | usage.output_tokens | usage.completion_tokens |
| Champ total | Aucun, doit sommer manuellement | usage.total_tokens |
| Fin du streaming | Événement message_stop | data: [DONE] |
Notes de migration
Section intitulée « Notes de migration »Lors de la migration d’OpenAI vers Claude Messages, vérifiez dans cet ordre :
- Déplacez le message système du tableau
messagesvers le champsystemde niveau supérieur. - Ajoutez
max_tokens, c’est obligatoire. - Confirmez que le premier message dans
messagesestuser, et que les rôles alternent strictement sans messages consécutifs de même rôle. - Supprimez les couches d’enveloppe
typeetfunctiondes définitions d’outils, renommezparameterseninput_schema. - Changez les résultats d’outils de
role: "tool"à bloctool_resultdans messageuser, et aligneztool_use_id. - Si
temperatureétait à l’origine supérieur à1, ajustez-le à la plage de valeurs de Claude. - Changez l’analyse de réponse pour itérer à travers le tableau
contentet répartir partype, ne supposez pas d’indices fixes. - Changez l’analyse de streaming pour répartir par type
event:, remplacez la condition de fin parmessage_stop.
Si le coût de refonte est élevé, vous pouvez continuer à utiliser le protocole OpenAI pour appeler les modèles de la série Claude, avec RouteAPI effectuant la conversion de format. Le compromis est que certaines capacités spécifiques à Claude (comme le contrôle complet de la pensée étendue, le cache de prompt à grain fin) ne peuvent pas être entièrement exprimées dans le format OpenAI.
Exemples complets
Section intitulée « Exemples complets »Conversation de base
Section intitulée « Conversation de base »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": "Vous êtes un assistant technique rigoureux, gardez les réponses concises.", "messages": [ { "role": "user", "content": "Veuillez présenter RouteAPI en une phrase" } ] }'SDK Python Anthropic, changez simplement 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="Vous êtes un assistant technique rigoureux, gardez les réponses concises.", messages=[ {"role": "user", "content": "Veuillez présenter RouteAPI en une phrase"}, ],)
print(message.content[0].text)print(message.usage.input_tokens, message.usage.output_tokens)base_url n’a besoin que du domaine, le SDK ajoutera automatiquement /v1/messages. Les appels en streaming utilisent client.messages.stream() :
with client.messages.stream( model="claude-sonnet-4-5", max_tokens=1024, messages=[{"role": "user", "content": "Expliquez étape par étape ce qu'est une passerelle 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)Compréhension d’image
Section intitulée « Compréhension d’image »curl, en utilisant 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": "Quels contrôles UI sont dans cette image ?" } ] } ] }'SDK Python :
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": "Quels contrôles UI sont dans cette image ?"}, ], } ],)
print(message.content[0].text)Appel d’outils
Section intitulée « Appel d’outils »Aller-retour complet sur deux tours, incluant la transmission du résultat :
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": "Interroger la météo actuelle pour une ville spécifiée. Utilisez le nom complet de la ville en chinois.", "input_schema": { "type": "object", "properties": { "city": {"type": "string", "description": "Nom de la ville, par ex. Pékin"}, "unit": {"type": "string", "enum": ["celsius", "fahrenheit"]}, }, "required": ["city"], }, }]
def get_weather(city: str, unit: str = "celsius") -> str: # Remplacez par un appel de service météo réel return f"{city}, ensoleillé, température 23 celsius, humidité 45%."
messages = [{"role": "user", "content": "Quel temps fait-il à Pékin maintenant ?"}]
response = client.messages.create( model="claude-sonnet-4-5", max_tokens=1024, tools=tools, messages=messages,)
# N'a besoin d'exécuter l'outil et de retransmettre que lorsque stop_reason est tool_useif response.stop_reason == "tool_use": # Le message assistant original doit être rajouté tel quel, sinon tool_use_id ne peut pas s'aligner 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"Échec de l'exécution de l'outil : {exc}" is_error = True tool_results.append( { "type": "tool_result", "tool_use_id": block.id, "content": result, "is_error": is_error, } )
# Tous les résultats d'outils du même tour vont dans un message 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)Requête curl du deuxième tour correspondante :
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": "Interroger la météo actuelle pour une ville spécifiée.", "input_schema": { "type": "object", "properties": { "city": { "type": "string" } }, "required": ["city"] } } ], "messages": [ { "role": "user", "content": "Quel temps fait-il à Pékin maintenant ?" }, { "role": "assistant", "content": [ { "type": "tool_use", "id": "toolu_01A09q90qw90lq917835lq9", "name": "get_weather", "input": { "city": "Pékin" } } ] }, { "role": "user", "content": [ { "type": "tool_result", "tool_use_id": "toolu_01A09q90qw90lq917835lq9", "content": "Pékin, ensoleillé, température 23 celsius, humidité 45%." } ] } ] }'Rappels de compatibilité
Section intitulée « Rappels de compatibilité »- Le support réel des paramètres dépend du modèle sélectionné et des capacités du service en amont, les capacités optionnelles comme
thinking,cache_control,mcp_serversdoivent être vérifiées dans l’environnement de test en premier. - Les paramètres optionnels explicitement passés comme
0oufalseseront traités comme des paramètres explicites de l’utilisateur, ne seront pas jetés comme valeurs par défaut. - L’environnement de production devrait fixer l’ID du modèle, ne pas compter sur des alias temporaires ou des noms d’affichage.
- Enregistrez l’ID de requête, l’ID de modèle, le code d’état et l’utilisation de tokens pour chaque requête afin de faciliter le dépannage des anomalies de latence et de coût.
- Les réponses d’erreur suivent la structure
{"type": "error", "error": {...}}de Claude, voir Gestion des erreurs pour plus de détails.