1. Anatomie d'une requête Messages API
Un seul appel porte tout : client.messages.create(). Il n'y a pas de session à
ouvrir ni quoi que ce soit à fermer.
| Champ | Obligatoire | Rôle |
|---|---|---|
model |
oui | Le modèle Claude qui répond à la requête |
messages |
oui | L'historique de conversation que vous envoyez, du plus ancien au plus récent |
max_tokens |
oui | Plafond du nombre de tokens que la réponse peut contenir |
system |
non | Instructions permanentes, hors de la conversation |
tools |
non | Les schémas des outils que le modèle peut appeler — conçus en D2.1 |
tool_choice |
non | Décide si le modèle peut répondre en prose ou doit appeler un outil — développé en D4.3 |
temperature |
non | Le degré d'aléatoire dans la sélection des tokens, de 0 à 1 |
Deux d'entre eux sont assez souvent mal compris pour mériter d'être énoncés clairement.
max_tokens est une limite de sécurité, pas une cible. Réglez-le à 1000 et
Claude s'arrête après 1000 tokens même s'il avait encore quelque chose à dire ;
il ne cherche jamais à atteindre ce nombre. Il borne la sortie, pas la requête
que vous envoyez.
system est un voisin de messages, pas l'un d'eux. Tous les autres champs de
cette liste sont des voisins aussi : tools et tool_choice se placent au
premier niveau du corps de la requête, jamais à l'intérieur d'un message.
{
"model": "claude-sonnet-5",
"max_tokens": 1024,
"system": "Tu es un assistant spécialisé en analyse financière. Réponds de manière concise.",
"messages": [
{ "role": "user", "content": "Quel est le chiffre d'affaires de l'entreprise X au T3 ?" }
]
}
system se place à côté de messages, jamais dedans. Glisser les instructions
permanentes en premier message user est l'erreur de forme la plus courante sur
ce point d'entrée, et elle change le poids que le modèle leur accorde.
Les identifiants exacts de modèles ne sont pas matière d'examen. temperature
non plus. Le guide ne la nomme jamais et aucun task statement n'en dépend ; la
ligne est là pour que vous reconnaissiez le champ dans un corps de requête qu'on
vous présente, pas pour l'apprendre par cœur.
À tester — les questions d'auto-évaluation de cette page.
2. Rôles, tours de parole et API sans état
messages est une liste de dictionnaires, chacun avec un role et un content.
Il y a deux rôles :
| Rôle | Qui l'écrit |
|---|---|
user |
Vous — ce que vous envoyez à Claude |
assistant |
Claude — ce qu'il a généré |
Une conversation alterne entre les deux : tour user, tour assistant, tour user.
L'API ne stocke rien. Chaque requête est indépendante et ne garde aucune mémoire des précédentes. Demandez « Qu'est-ce que l'informatique quantique ? », obtenez une réponse, puis envoyez « Écris une autre phrase » sans rien d'autre : le modèle n'a aucune idée de ce qu'on lui demande de prolonger, donc il écrit une phrase sur tout autre chose.
La conversation multi-tours est donc un travail que vous faites, pas un service
que l'API rend : envoyer le premier message user, ajouter la réponse de Claude à
votre liste comme message assistant, ajouter la relance comme message user,
et renvoyer la liste entière.
"messages": [
{ "role": "user", "content": "Définis l'informatique quantique en une phrase" },
{ "role": "assistant", "content": "L'informatique quantique exploite des états quantiques pour traiter l'information." },
{ "role": "user", "content": "Écris une autre phrase" }
]
C'est uniquement parce que les deux premiers tours sont encore dans le tableau que « Écris une autre phrase » veut dire quelque chose au troisième.
Tout ce qui doit encore être visible au cinquième tour doit se trouver dans le tableau que vous envoyez au cinquième tour. C'est la règle derrière la plupart des bugs « le modèle a oublié », et elle vaut pour les résultats d'outils exactement comme pour le texte. C'est la même règle qui impose de passer explicitement son contexte à un subagent lancé, puisqu'il n'hérite de rien.
N'envoyer que le dernier message en s'attendant à ce que les tours précédents soient encore là. Rien n'expire côté serveur, parce que rien n'y est stocké ; reconstruire l'historique est le travail de l'appelant, à chaque requête.
Teste-toi sur cette section
Q3 A stateless API and a vanished tool_result
Scenario: A build pipeline drives Claude across several requests. From one request to the next, a tool_result produced earlier is no longer visible to the model.
Question: What is the most likely cause?
A) The API keeps no state, and the caller did not resend the earlier tool_result blocks.
B) A tool_result block expires server-side soon after it is submitted, so later requests miss it.
C) The max_tokens value is too low to hold the tool_result, so the oldest blocks drop first.
D) A session_id parameter is missing, so the server cannot attach this request to the earlier turn.
Answer: the API is stateless and the history was not resent
Why: nothing carries over between requests. Every call must ship the complete messages array, tool results included; rebuilding that history is the caller's job. The same rule is what forces you to pass context explicitly to a spawned subagent.
Why the others are wrong:
- Nothing expires server-side, because nothing is stored there.
- That budget bounds the output length, not the size of the history you send; and no block is dropped for you, because none is kept for you.
- The Messages API has no such parameter.
3. Les blocs de contenu
Le content d'une réponse n'est jamais une simple chaîne mais une liste de
blocs, chacun avec son type. Lire une réponse comme du texte ne marche que
parce que vous avez indexé cette liste : message.content[0].text est le texte
du premier bloc, pas celui du message. Les deux sens ne sont pas symétriques :
une requête accepte l'une ou l'autre forme, une chaîne simple ou une liste de
blocs.
| Type de bloc | Sens | Contenu |
|---|---|---|
text |
dans les deux sens | De la prose, à lire par l'utilisateur |
tool_use |
de l'assistant | Le nom de l'outil et les arguments choisis par le modèle |
tool_result |
de vous, dans un message user |
La sortie de l'outil que vous avez exécuté |
Une seule réponse d'assistant porte couramment plusieurs blocs à la fois, par
exemple un bloc texte qui explique ce que le modèle s'apprête à faire plus un
bloc tool_use qui le demande. Un bloc tool_result porte trois champs :
tool_use_id, qui correspond à l'id du bloc tool_use auquel il répond ;
content, la sortie de la fonction sérialisée en chaîne ; et is_error, un
booléen. Il repart dans un message user, accompagné de l'historique complet.
{ "role": "assistant", "content": [
{ "type": "text", "text": "Je consulte la commande." },
{ "type": "tool_use", "id": "toolu_01X", "name": "lookup_order",
"input": { "order_id": "ORD-67890" } }
]},
{ "role": "user", "content": [
{ "type": "tool_result", "tool_use_id": "toolu_01X",
"content": "{ \"status\": \"shipped\", \"total\": 42.90 }",
"is_error": false }
]}
Entre ces deux messages, c'est votre code qui a exécuté lookup_order. Le
modèle n'exécute jamais rien ; il émet une demande structurée et attend.
N'ajouter que le texte affiché et laisser tomber le bloc tool_use. C'est
l'appariement avec tool_result dont dépend la requête suivante : l'historique
devient impossible à traiter. Ajoutez tout response.content, et gardez les
schémas d'outils d'origine sur la requête de suivi même si aucun autre appel
n'est attendu.
Un outil se déclare en JSON Schema, en trois parties : name, description — ce
que fait l'outil, quand l'utiliser, ce qu'il retourne — et input_schema pour
les arguments. Voilà l'anatomie, et elle s'arrête là. Savoir quelle formulation
fait choisir au modèle le bon outil parmi plusieurs voisins relève de la conception
d'outils, développée en D2.1 ; se servir d'un schéma pour forcer une charge
utile conforme est la sortie structurée, développée en D4.3.
Toutes les questions de la banque sur 2.1
4. Le champ stop_reason
Quand le modèle s'arrête, la réponse dit pourquoi dans stop_reason, et c'est
ce champ, pas la présence d'un bloc texte, qui vous dit si une réponse est
terminée.
| Valeur | Ce qui a mis fin à la génération |
|---|---|
end_turn |
Le modèle est arrivé à une fin naturelle et n'avait rien à ajouter |
max_tokens |
Le budget de sortie que vous avez fixé s'est épuisé en cours de génération |
stop_sequence |
L'une de vos chaînes stop_sequences est apparue |
tool_use |
Le modèle appelle l'un de vos outils et attend que vous l'exécutiez |
pause_turn |
Une boucle d'outil serveur a atteint sa limite d'itérations, 10 par requête par défaut |
refusal |
Le modèle a refusé pour des raisons de politique de sécurité |
model_context_window_exceeded |
La réponse a rempli la fenêtre de contexte du modèle |
stop_details vaut null pour toutes les valeurs sauf refusal, où il porte la
catégorie de politique concernée. model_context_window_exceeded est la plus
récente : Sonnet 4.5 et les modèles plus récents la renvoient sans en-tête beta,
et les modèles antérieurs ont besoin de l'en-tête
model-context-window-exceeded-2025-08-26 pour l'activer.
{
"id": "msg_01ABC",
"role": "assistant",
"content": [
{ "type": "text", "text": "Je consulte la commande." },
{ "type": "tool_use", "id": "toolu_01X", "name": "lookup_order",
"input": { "order_id": "ORD-67890" } }
],
"stop_reason": "tool_use",
"stop_details": null
}
Cette réponse porte du texte et une demande d'outil, et c'est précisément pourquoi la présence de texte ne peut pas servir de signal de fin.
end_turn est la seule valeur qui signifie terminé. Une réponse avec
stop_reason: "max_tokens" porte un bloc texte comme n'importe quelle autre,
mais elle a été coupée en pleine phrase. Un code d'affichage qui teste la
présence de texte au lieu de lire stop_reason présentera un brouillon tronqué
comme un texte fini. C'est le distracteur classique sur ce champ.
Deux valeurs méritent d'être distinguées même si une seule paire est testée. Une
réponse qui attend votre outil dit toujours tool_use, et vous la poursuivez
en envoyant des blocs tool_result. pause_turn concerne un outil serveur à
court d'itérations, et vous la poursuivez en renvoyant le contenu de l'assistant
tel quel.
Le guide ne vous demande jamais que de distinguer tool_use de end_turn.
C'est cette paire qui pilote la boucle, et c'est tout ce que teste TS 1.1.
Transformer cette vérification en boucle — appeler, inspecter stop_reason,
exécuter, ajouter, rappeler — est la boucle agentique, développée en D1.1. D0
s'arrête à nommer le signal.
Teste-toi sur cette section
Q1 A stop_reason your display code ignores
Scenario: A drafting agent produces long documents. Your display code shows the API response to the user whenever content holds a text block. On one long document the response cuts off mid-sentence, yet the code still presents it as the finished draft. Inspecting the response, stop_reason is "max_tokens".
Question: What does that value mean, and what should the display code check before treating a response as final?
A) The response is complete; "max_tokens" caps the size of the request being sent, not the length of the output.
B) The token budget ran out mid-generation; only "end_turn" means the response is complete.
C) The model refused to continue for safety reasons, so nothing should be shown and the attempt should be escalated for human review instead.
D) A server-side tool call was interrupted mid-turn, so resending the same request unchanged lets the paused turn pick up where it left off.
Answer: "max_tokens" means truncated output, not completion
Why: stop_reason distinguishes several reasons generation stopped, and only "end_turn" marks a natural, complete stopping point. "max_tokens" means the output budget ran out mid-generation — the presence of a text block in content says nothing about whether that text is the whole answer. Scope note: among stop reasons, the exam guide names only the pair that drives the agentic loop, and that loop is what the task this sheet prepares tests. "max_tokens", "refusal" and "pause_turn" come from the API reference — D0 prerequisite material, not extra exam surface.
Why the others are wrong:
- Reverses cause and effect: the output token budget is exactly why this value was returned.
- Describes
"refusal", a distinct value with a distinct cause. Nothing was refused here, so withholding the draft and escalating would stall a run that only needs continuing. - Describes
"pause_turn", which concerns an interrupted server tool, not an exhausted token budget. Nothing is paused waiting to resume, so an identical resend would simply regenerate the same truncated draft.
5. Les system prompts
Le paramètre system est une simple chaîne passée à côté de messages, et il se
place hors de la conversation : ce n'est pas un tour, il n'a pas de rôle, et
ce n'est pas quelque chose à quoi le modèle répond. Il façonne comment Claude
répond, pas *ce qu'*il répond.
Prenez l'exemple d'un tuteur de maths. Sans system prompt, « Comment résoudre 5x + 2 = 3 pour x ? » donne aussitôt une solution complète pas à pas, ce qui est correct et inutile pour enseigner. Avec :
Tu es un tuteur de maths patient.
Ne réponds pas directement aux questions de l'élève.
Guide-le vers la solution, étape par étape.
la même question revient en « Quelle serait selon toi une bonne première étape pour isoler x ? ». Même modèle, même tour user ; c'est l'instruction permanente qui a changé le comportement.
Voilà à quoi sert un system prompt : assigner un rôle, et le modèle répond comme quelqu'un dans ce rôle le ferait ; énoncer des contraintes durables, et elles tiennent à tous les tours sans être répétées. Elles tiennent parce que vous les renvoyez à chaque requête, pas parce que l'API s'en souvient, et vous les payez à chaque fois.
Mettre le system prompt en premier message user. Il se lit alors comme du
contexte conversationnel plutôt que comme une instruction permanente, et il
entre en concurrence avec les tours qui l'entourent au lieu de les surplomber.
Une instruction system porte plus loin que n'importe quelle formulation à l'échelle d'un tour, assez loin pour qu'un seul mot-clé absolu prenne le pas sur des descriptions d'outils par ailleurs bien écrites. « Vérifie toujours l'identité du client » fera appeler l'outil le plus proche sémantiquement à chaque tour. Comment cette instruction prend le pas, et comment la formuler autrement, est développé en D2.1.
Une note pratique : l'API n'accepte pas system=None, donc une fonction de chat
réutilisable ne doit ajouter la clé que si un prompt a réellement été fourni.
Toutes les questions de la banque sur 2.1
6. La fenêtre de contexte
La fenêtre de contexte est le budget de tokens fini dans lequel chaque requête doit tenir, et les deux faits précédents le retournent contre vous : l'API ne garde aucun état, donc chaque requête embarque l'historique entier. L'historique n'est donc pas gratuit. C'est un coût récurrent qui croît de façon monotone avec la conversation, sauf si vous y faites quelque chose.
Ce qui s'accumule, à chaque requête :
| Ce que vous renvoyez | Pourquoi c'est là |
|---|---|
Le prompt system |
Il tient d'un tour à l'autre parce que vous le renvoyez, pas parce qu'il est stocké |
Chaque tour user et assistant passé |
Rien n'est stocké côté serveur |
Chaque bloc tool_use et tool_result |
Les enlever casse l'appariement |
| Les schémas d'outils complets | Obligatoires à chaque requête, même quand aucun appel ne suit |
Les résultats d'outils sont la part qu'on sous-estime. Un outil qui retourne un gros enregistrement met cet enregistrement dans l'historique pour tout le reste de l'exécution, et vous le payez à chaque tour suivant, pas une seule fois.
lookup_order → 42 champs retournés, 5 utiles pour l'éligibilité au retour
order_id, status, total, items, return_eligible ← utiles
warehouse_route, carrier_scac, pick_wave, audit_flags,
promo_ledger, tax_jurisdiction, … (37 autres) ← poids mort ici
3 commandes consultées = 126 champs renvoyés à chaque tour suivant, 15 le méritent.
Le guide nomme trois modes de défaillance d'une fenêtre pleine, et D0 va exactement aussi loin que les nommer : lost-in-the-middle, où un élément enfoui au milieu d'un long contexte passe inaperçu alors que le début et la fin sont lus de façon fiable ; l'accumulation, la croissance montrée ci-dessus ; et la progressive summarization, où comprimer l'historique pour faire de la place sacrifie d'abord les nombres, les dates et les détails précis.
Reconnaissez ici les trois modes de défaillance ; les remèdes ne sont pas ceux de D0. Élaguer les sorties d'outils verbeuses, extraire des faits structurés et ordonner l'entrée selon la position sont développés en D5.1. Le retrieval et le chunking ne sont pas la réponse attendue par le guide. Il les met hors périmètre, et ce corpus aussi.
Toutes les questions de la banque sur 5.1
7. Questions d'auto-évaluation
Q2 Which part of a tool definition decides the routing
Scenario: An HR assistant exposes search_policy_doc ("Retrieves policy content") and search_payroll_record ("Retrieves payroll data"). Both accept employee identifiers of a similar shape, and the model routinely picks the wrong one.
Question: Which part of a tool definition drives that choice?
A) The position of each tool in the request's tools array.
B) The tool name, which outranks the rest of the definition.
C) The description, which is the primary selection mechanism.
D) The size of the input_schema: the model prefers the simplest one.
Answer: the description
Why: the model selects a tool by reading the definitions, and the description is where the meaning lives. Two one-line descriptions give it nothing to tell the tools apart, so misrouting becomes structural. The fix is to state input formats, example requests, edge cases and the boundary with neighboring tools.
Why the others are wrong:
- Declaration order is not a selection criterion.
- The name helps, but it cannot carry input formats, edge cases or usage boundaries.
- The schema describes the arguments once a tool has been chosen; it does not decide which tool to choose.
Q4 A standing rule that drags a tool onto every turn
Scenario: A greenhouse operations assistant opens its system prompt with "Every answer must reflect the current sensor readings." Logs show read_sensors firing on each turn, including when a grower asks how a cultivar name should be spelled in the planting log. Each tool definition spells out its inputs and where its job ends.
Question: What stops the needless calls?
A) Add few-shot examples of clerical turns answered without any sensor call, so the assistant can copy the pattern on the next spelling question.
B) Have the client drop read_sensors from the tools array whenever it judges the incoming question clerical.
C) Name the conditions that call for a reading, and retire the absolute sentence.
D) Cache the last reading for ten minutes so repeat turns reuse it instead of calling out again.
Answer: state when a reading is required, and drop the blanket rule
Why: "every answer" reads as a permanent trigger, and the model binds it to whichever tool the wording resembles most. A rule phrased with no exceptions outranks the tool definitions, however careful they are — which is why the scenario tells you those definitions are sound. Spelling out the situations that genuinely need a reading removes the trigger while keeping the check where it earns its cost.
Why the others are wrong:
- Examples cannot outvote a rule that still admits no exception; the model treats them as odd cases rather than as the boundary.
- Moves the decision to the caller, which must now classify a question before the model has read it, and leaves the misleading rule in place.
- Cheaper calls, identical behavior: a spelling question still travels through the sensor path, and a stale value is the wrong answer where readings matter.
Q5 A triage loop that never writes its answer
Scenario: A ticket-triage client runs its own loop: send the request, run whatever tool comes back, append the result, send again. To stop the assistant from chatting while work remains, the client sets tool_choice to the value that guarantees a call — and it sets it on every request of the loop. Runs now end only when the client's iteration cap trips, and no written answer ever arrives.
Question: Why does the run never reach one?
A) That value binds the first request alone; the later ones revert to the automatic setting, so the assistant keeps reaching for lookup_account long after the answer is available.
B) Each tool result is appended as a user turn, and a request whose last turn came from the user can only be answered by another call.
C) Repeating that value on every request removes the assistant's only way of finishing, which is a turn that ends in text; the client should guarantee the opening call and leave the remaining requests on the automatic setting.
D) The iteration cap sits below the number of declared tools, so the client cuts the run short before classify_ticket and its neighbours have all been tried.
Answer: the setting also forbids the text-only turn a run has to end on
Why: tool_choice: "any" promises exactly one thing, on every request that carries it: a tool will be called. Sent once, that pins the opening step. Sent on every request, it also outlaws the closing one, since a turn answering in prose is no longer permitted. Handing the later requests back to tool_choice: "auto" restores the choice between calling again and answering.
Why the others are wrong:
- The field is read afresh on each request; it neither expires nor decays, and the loop here restates it every time.
- A user turn carrying a tool result can be answered in prose just as readily as with another call — that is precisely what the automatic setting permits.
- The cap is what halts a run that would otherwise not halt at all; raising it buys further calls, never a written answer.
Q6 A mandatory field the source cannot fill
Scenario: An invoice extraction tool lists discount_pct among its required properties, yet plenty of invoices grant no discount. Reviewers keep finding discount figures that appear nowhere on the invoice.
Question: What is happening, and what removes the cause?
A) The API validates the payload against the schema and rejects the call as invalid, so nothing reaches the pipeline.
B) The model invents a figure to satisfy the constraint; make the property nullable and no longer mandatory.
C) The model leaves the property empty, and the pipeline should read an empty value as absent.
D) The model skips the tool call whenever a mandatory property has no source value.
Answer: the model invents a figure, so make the property nullable and optional
Why: a mandatory property leaves the model choosing between breaking the schema and making something up, and it makes something up. Declaring it "type": ["number", "null"] and taking it out of required gives the model an honest way to report absence.
Why the others are wrong:
- Validation covers schema conformance, never truthfulness: an invented number conforms perfectly.
- Nothing empties the property while the schema still demands a value.
- The tool does get called; it is the payload that is wrong.