Travailler avec les messages
Mis à jour le 29 juillet 2026
Objectifs
- Comprendre le format de l’API Messages
- Manipuler les objets de réponse
- Construire un chatbot multi-tours
Le format des messages
Toute interaction avec Claude, du prompt isolé au chatbot complet, emprunte le même chemin : une liste de messages transmise à client.messages.create(). Chaque message est un dictionnaire à deux clés, et ce sont les deux seules requises.
role:"user"(vos messages) ou"assistant"(messages de Claude)content: le texte du message
Un échange minimal ne contient donc qu’une entrée, celle de votre question :
messages = [
{"role": "user", "content": "Quels sont les ingrédients du Dr. Pepper ?"}
]
Le point qui surprend le plus les débutants est que l’API ne conserve aucune mémoire d’un appel à l’autre. Pour que Claude tienne compte de ce qui a déjà été dit, vous devez lui renvoyer l’historique complet à chaque requête : c’est votre code qui porte la mémoire de la conversation, pas le serveur. Une conversation multi-tours prend donc la forme d’une liste qui s’allonge.
messages = [
{"role": "user", "content": "Bonjour Claude !"},
{"role": "assistant", "content": "Bonjour ! Comment puis-je vous aider ?"},
{"role": "user", "content": "Raconte-moi un fait sur les furets."},
]
Les messages doivent toujours alterner entre user et assistant.
Inspecter la réponse
L’appel ne renvoie pas du texte brut mais un objet Message, dont le texte n’est qu’une propriété parmi d’autres. Prenons une requête simple pour l’examiner :
response = client.messages.create(
model="claude-sonnet-5",
max_tokens=1000,
messages=[
{"role": "user", "content": "Traduis bonjour en anglais. Réponds en un seul mot."}
]
)
| Propriété | Description |
|---|---|
content | Le contenu généré (liste de blocs) |
id | Identifiant unique du message |
model | Le modèle utilisé |
stop_reason | Raison de l’arrêt (end_turn, max_tokens, stop_sequence) |
usage | Tokens d’entrée (input_tokens) et de sortie (output_tokens) |
Deux de ces propriétés méritent votre attention en production. Le stop_reason vous dit si Claude a terminé son propos de lui-même ou s’il a été coupé par le plafond de tokens, ce qui change tout lorsque vous parsez la sortie. Et usage vous donne la consommation réelle de chaque appel, seule base fiable pour estimer un coût avant de passer à l’échelle. Le texte lui-même s’obtient en descendant dans le premier bloc de contenu :
print(response.content[0].text)
Erreurs courantes
Deux contraintes de format provoquent la quasi-totalité des erreurs de débutant, et toutes deux découlent de la même règle d’alternance. La première : la liste doit toujours commencer par un message user. Une conversation qui démarre sur une réplique de l’assistant n’a pas de sens du point de vue de l’API, puisque personne n’a rien demandé.
# ERREUR : commence par assistant
messages = [
{"role": "assistant", "content": "Bonjour !"}
]
La seconde : deux messages consécutifs portant le même rôle provoquent une erreur. Si votre utilisateur envoie deux phrases coup sur coup, votre code doit les fusionner en un seul message plutôt que de les empiler.
# ERREUR : deux messages user consecutifs
messages = [
{"role": "user", "content": "Salut !"},
{"role": "user", "content": "Ça va ?"}
]
Techniques avancées avec les messages
Mettre des mots dans la bouche de Claude
Rien ne vous oblige à terminer la liste par un message user. En ajoutant un message assistant en dernière position, vous forcez Claude à poursuivre à partir de ce texte au lieu de repartir de zéro. C’est le moyen le plus direct d’imposer un format de sortie : commencez la réponse par { et vous obtiendrez du JSON, commencez par un vers et vous obtiendrez la suite du poème.
response = client.messages.create(
model="claude-sonnet-5",
max_tokens=500,
messages=[
{"role": "user", "content": "Génère un beau haiku"},
{"role": "assistant", "content": "l'air frais de montagne"}
]
)
# Claude completera le haiku a partir de cette premiere ligne
print("l'air frais de montagne" + response.content[0].text)
Notez la reconstitution manuelle à la dernière ligne : la réponse ne contient que la suite, pas l’amorce que vous avez fournie.
Prompting few-shot
La même mécanique permet de montrer plutôt que d’expliquer. En glissant dans l’historique quelques paires question-réponse fictives, vous démontrez le format attendu sans écrire la moindre consigne. Ici, trois exemples suffisent à obtenir une classification de sentiment en un mot majuscule, sans jamais avoir demandé « réponds en un mot majuscule ».
response = client.messages.create(
model="claude-sonnet-5",
max_tokens=500,
messages=[
{"role": "user", "content": "Ce produit est nul, je deteste !"},
{"role": "assistant", "content": "NEGATIF"},
{"role": "user", "content": "J'adore ce produit, il est genial !"},
{"role": "assistant", "content": "POSITIF"},
{"role": "user", "content": "Le nouveau modèle est vraiment top !"},
]
)
print(response.content[0].text) # POSITIF
Exercice : construire un chatbot
Tout ce qui précède se rassemble dans un seul exercice : un chatbot en ligne de commande qui garde le fil de la discussion. La boucle est toujours la même — vous maintenez une liste pour l’historique, vous demandez une entrée utilisateur avec input(), vous envoyez l’historique complet à Claude, vous affichez la réponse, vous ajoutez cette réponse à l’historique, et vous répétez tant que l’utilisateur ne demande pas à sortir. Sans la cinquième étape, votre chatbot oubliera chaque phrase aussitôt prononcée.
Voir la solution
from anthropic import Anthropic
client = Anthropic()
conversation_history = []
while True:
user_input = input("Vous : ")
if user_input.lower() == "quit":
print("Conversation terminee.")
break
conversation_history.append({"role": "user", "content": user_input})
response = client.messages.create(
model="claude-sonnet-5",
messages=conversation_history,
max_tokens=500
)
assistant_response = response.content[0].text
print(f"Claude : {assistant_response}")
conversation_history.append({"role": "assistant", "content": assistant_response})