Travailler avec les messages
Sur cette page
Le paramètre messages est le cœur de l’API : c’est la conversation que vous envoyez, et la réponse de Claude en est le tour suivant. Cette leçon détaille son format, l’objet que renvoie l’API, les règles d’enchaînement des tours, ce qui a changé depuis 2024 pour le « préremplissage », et la technique des exemples par l’historique. Vous construisez à la fin un petit chatbot en ligne de commande.
Le format des messages
messages est une liste de dictionnaires, un par tour de parole. Chacun a deux clés : role, qui vaut "user" (vous) ou "assistant" (Claude), et content, le contenu du tour. content est soit une chaîne, soit une liste de blocs typés ({"type": "text", "text": "…"}, une image, un document…) ; une chaîne équivaut à un unique bloc text. La leçon 6 utilise la forme en liste pour joindre des images.
messages = [
{"role": "user", "content": "Bonjour Claude ! Comment vas-tu ?"},
{"role": "assistant", "content": "Très bien, merci. Que puis-je faire pour vous ?"},
{"role": "user", "content": "Un fait amusant sur les furets ?"},
]
Beaucoup d’usages n’ont pas besoin d’historique : une liste d’un seul message user est parfaitement normale. Quand il y a un historique, Claude le voit en entier à chaque requête : l’API ne garde aucune mémoire entre deux appels, c’est votre code qui renvoie la conversation.
L’objet réponse
response = client.messages.create(
model="claude-opus-5-5",
max_tokens=2048,
messages=[{"role": "user", "content": "Traduis « hello » en français. Réponds par un seul mot."}],
)
print(response)
L’API renvoie un objet Message dont voici les champs utiles :
content: la liste des blocs produits. Pour une réponse simple, un bloctext, éventuellement précédé d’un blocthinking; avec des outils, des blocstool_use. C’est le champ qu’on lit, par type.stop_reason: pourquoi la génération s’est arrêtée."end_turn"(fin naturelle),"max_tokens"(plafond atteint),"stop_sequence"(séquence d’arrêt rencontrée, leçon 4),"tool_use"(demande d’outil),"pause_turn"(tour long mis en pause),"refusal"(intervention des classificateurs de sécurité),"model_context_window_exceeded"(fenêtre de contexte dépassée).stop_sequence: la séquence d’arrêt rencontrée, le cas échéant.usage: les jetons facturés,input_tokensetoutput_tokens(plus les jetons de cache quand vous en utilisez).id,type(toujours"message"),role(toujours"assistant"),model.
Le carnet le résumait d’une phrase qui reste vraie : si vous ne retenez qu’une chose, c’est que content contient ce que le modèle a produit. Ajoutez-y aujourd’hui : on le lit par type de bloc.
def text_of(response):
"""Concatène les blocs de texte d'une réponse."""
return "".join(block.text for block in response.content if block.type == "text")
print(text_of(response)) # Bonjour
Les règles d’enchaînement
Les modèles sont entraînés sur des tours alternés user / assistant, et une conversation commence par un message user. Deux erreurs du carnet de 2024 ont changé de nature. Deux messages consécutifs du même rôle ne provoquent plus d’erreur : l’API les fusionne en un seul tour. Et une liste qui se termine par un message assistant n’est plus une astuce mais une erreur sur les modèles actuels, comme l’explique la section suivante.
Le préremplissage, et ce qui le remplace
Le carnet montrait une technique appelée « mettre des mots dans la bouche de Claude » : terminer la liste par un message assistant partiel, que Claude continue. Avec {"role": "assistant", "content": "calming mountain air"} après la demande d’un haïku, Claude 3 Haiku écrivait la suite du premier vers. Cette technique, le préremplissage, renvoie aujourd’hui une erreur 400 (invalid_request_error) sur Claude 4.6 et tous les modèles plus récents, donc sur toute la gamme actuelle. Ce qu’elle permettait se fait autrement :
- Imposer un début : le demander dans la consigne. « Écris un haïku dont le premier vers est exactement “l’air frais de montagne”. »
- Imposer un format : l’exiger dans la consigne, ou, pour du JSON, utiliser les sorties structurées (un schéma que la réponse doit respecter).
- Couper un préambule : « Réponds directement, sans phrase d’introduction », dans le prompt système.
response = client.messages.create(
model="claude-opus-5-5",
max_tokens=2048,
messages=[{"role": "user",
"content": "Écris un haïku dont le premier vers est exactement « l'air frais de montagne ». N'écris que le haïku."}],
)
print(text_of(response))
Des exemples par l’historique
L’historique sert aussi à montrer à Claude ce que vous attendez : c’est le prompting par exemples (few-shot). Le carnet voulait classer le sentiment de messages sur des cornichons. Demandé tel quel, Claude rendait une analyse détaillée en plusieurs paragraphes ; pour traiter des milliers de messages, on veut un seul mot. On fournit donc quelques échanges d’exemple, des vrais tours complets, avant le message à classer :
response = client.messages.create(
model="claude-opus-5-5",
max_tokens=2048,
messages=[
{"role": "user", "content": "Avis impopulaire : les cornichons, c'est dégoûtant."},
{"role": "assistant", "content": "NEGATIF"},
{"role": "user", "content": "Mon amour des cornichons va trop loin : j'ai acheté une bouée en forme de cornichon."},
{"role": "assistant", "content": "POSITIF"},
{"role": "user", "content": "Sérieusement, qui peut manger un cornichon ? Immonde !"},
{"role": "assistant", "content": "NEGATIF"},
{"role": "user", "content": "Je viens de goûter les nouveaux cornichons épicés de @PickleCo, mes papilles dansent ! 🌶️🥒"},
],
)
print(text_of(response)) # POSITIF
Ce n’est pas du préremplissage : chaque exemple est un tour terminé, et la liste finit par un message user. Pour un format strictement contrôlé à grande échelle, la consigne explicite et les sorties structurées restent plus sûres que les seuls exemples.
À vous de jouer
Traduire. Écrivez une fonction translate(word, language) qui renvoie la traduction d’un mot, et uniquement le mot : translate("chicken", "italien") doit renvoyer pollo, sans phrase autour.
Voir une solution
def translate(word, language):
response = client.messages.create(
model="claude-opus-5-5",
max_tokens=1024,
messages=[{"role": "user",
"content": f"Traduis le mot « {word} » en {language}. Réponds uniquement par le mot traduit, sans rien d'autre."}],
)
return text_of(response).strip()
Un chatbot en ligne de commande. Gardez une liste conversation ; à chaque tour, lisez la saisie de l’utilisateur avec input(), ajoutez-la comme message user, envoyez toute la liste, affichez le texte de la réponse, puis ajoutez la réponse comme message assistant. Prévoyez un mot pour quitter.
Voir une solution
conversation = []
while True:
user_input = input("Vous : ")
if user_input.lower() in ("quit", "quitter"):
print("Fin de la conversation.")
break
conversation.append({"role": "user", "content": user_input})
response = client.messages.create(
model="claude-opus-5-5",
max_tokens=4096,
messages=conversation,
)
print(f"Claude : {text_of(response)}")
# on renvoie le tour de Claude tel quel, blocs de réflexion compris (recommandé par la documentation)
conversation.append({"role": "assistant", "content": response.content})
Renvoyer response.content plutôt que le seul texte est ce que la documentation recommande : hors appel d’outil, retirer les blocs de réflexion des tours précédents est permis, mais renvoyer chaque tour tel quel et n’ajouter qu’à la fin de la liste garde la réflexion valide et le cache efficace.
Sources officielles : 02_messages_format.ipynb (anthropics/courses, archivé) · Create a Message (référence de l’API : messages, fusion des tours consécutifs,
stop_reason) · Errors (préremplissage refusé depuis Claude 4.6) · Thinking (documentation Claude : blocs de réflexion d’un tour à l’autre)
Points clés à retenir
messagesest une liste de tours{"role", "content"};contentest une chaîne ou une liste de blocs typés.- L’API ne garde aucune mémoire : votre code renvoie l’historique à chaque appel.
- La réponse se lit dans
content, par type de bloc ;stop_reasondit pourquoi elle s’est arrêtée ;usagedit ce que vous payez. - Deux tours consécutifs du même rôle sont fusionnés ; une liste qui finit par un message
assistant(préremplissage) renvoie 400 depuis Claude 4.6. - Les exemples par l’historique restent valables ; pour imposer un début ou un format, on le demande, ou on utilise les sorties structurées.