AI Cert Prep
Saisissez un mot-clé pour rechercher dans la documentation.

Annexes · Claude

Aide-mémoire API Claude

Anatomie des requêtes et réponses de la Messages API, événements de streaming, tool use, sorties structurées, thinking, caching, batches, erreurs et retries — avec Python et TypeScript.

Anatomie d’une requête

json
{
"model": "claude-opus-5",
"max_tokens": 2048,
"system": "You are a precise assistant. Answer only from <document>.",
"messages": [
{ "role": "user", "content": [
{ "type": "text", "text": "<document>…</document>", "cache_control": { "type": "ephemeral" } },
{ "type": "text", "text": "Summarise the termination clause." }
]}
],
"temperature": 0.2,
"stop_sequences": ["</answer>"],
"thinking": { "type": "adaptive" },
"effort": "high",
"tools": [],
"tool_choice": { "type": "auto" },
"metadata": { "user_id": "hashed-id" }
}
ChampNotes
modelID épinglé : claude-fable-5-1, claude-opus-5, claude-sonnet-5, claude-haiku-4-5
max_tokensPlafond dur de sortie ; stop_reason: max_tokens = tronqué
systemChaîne de niveau supérieur ou blocs de contenu ; stable → cachable
messagesAlternance user/assistant ; le contenu est une chaîne ou un tableau de blocs (text, image, document, tool_use, tool_result, thinking)
temperature / top_pRéglez l’un, pas les deux ; 0 réduit la variance, pas l’erreur
thinking{"type":"adaptive"} (modèles actuels) ; {"type":"enabled","budget_tokens":N} uniquement Haiku 4.5
effortlow / medium / high / xhigh
tools / tool_choiceVoir tool use ci-dessous ; le choix forcé est un 400 sur Fable 5.1
output_config.formatJSON Schema pour la sortie structurée

Anatomie d’une réponse

json
{
"id": "msg_01…",
"type": "message",
"role": "assistant",
"model": "claude-opus-5",
"content": [
{ "type": "thinking", "thinking": "…", "signature": "…" },
{ "type": "text", "text": "The notice period is 60 days." }
],
"stop_reason": "end_turn",
"stop_sequence": null,
"usage": {
"input_tokens": 1200,
"cache_creation_input_tokens": 0,
"cache_read_input_tokens": 18000,
"output_tokens": 42
}
}

stop_reason — branchez dessus à chaque fois

ValeurSignificationAction
end_turnTerminé naturellementFini
tool_useVeut exécuter des outilsExécuter, ajouter tool_result, rappeler
max_tokensTronquéContinuer ou augmenter le plafond ; ne jamais traiter comme complet
stop_sequenceA atteint une chaîne d’arrêtFini (vérifiez stop_sequence)
pause_turnLong tour d’outil côté serveur mis en pauseRenvoyer pour continuer
refusalRefus de sécuritéChemin de repli explicite ; ne réessayez pas aveuglément

Appels minimaux

python
from anthropic import Anthropic
client = Anthropic() # reads ANTHROPIC_API_KEY
msg = client.messages.create(
model="claude-sonnet-5",
max_tokens=1024,
system="You are a concise analyst.",
messages=[{"role": "user", "content": "Three risks of vendor lock-in?"}],
)
print(msg.content[0].text, msg.stop_reason, msg.usage)

Streaming (SSE)

Ordre des événements : message_start → (content_block_start → content_block_delta* → content_block_stop)* → message_delta (porte stop_reason, l’usage de sortie) → message_stop.

python
with client.messages.stream(
model="claude-sonnet-5", max_tokens=1024,
messages=[{"role": "user", "content": "Write a haiku about latency."}],
) as stream:
for text in stream.text_stream:
print(text, end="", flush=True)
final = stream.get_final_message()
print(final.stop_reason, final.usage)

Aller-retour du tool use

json
// 1. Request with tools
{ "model": "claude-opus-5", "max_tokens": 1024,
"tools": [{
"name": "get_order",
"description": "Look up one order by ID. Use when the user references an order number. Returns status, items and total. Does not modify anything.",
"input_schema": { "type": "object",
"properties": { "order_id": { "type": "string", "description": "Order ID, e.g. ORD-12345" } },
"required": ["order_id"] },
"strict": true
}],
"messages": [{ "role": "user", "content": "Where is ORD-12345?" }] }
// 2. Response: stop_reason = "tool_use"
{ "content": [{ "type": "tool_use", "id": "toolu_01", "name": "get_order", "input": { "order_id": "ORD-12345" } }],
"stop_reason": "tool_use" }
// 3. Follow-up with tool_result (in a USER message)
{ "messages": [
{ "role": "user", "content": "Where is ORD-12345?" },
{ "role": "assistant", "content": [{ "type": "tool_use", "id": "toolu_01", "name": "get_order", "input": { "order_id": "ORD-12345" } }] },
{ "role": "user", "content": [{ "type": "tool_result", "tool_use_id": "toolu_01",
"content": "{\"status\":\"shipped\",\"eta\":\"2026-09-17\"}" }] }
] }
// Error result – structured, never empty-success
{ "type": "tool_result", "tool_use_id": "toolu_01", "is_error": true,
"content": "{\"category\":\"not_found\",\"retryable\":false,\"message\":\"No order ORD-12345\"}" }

La boucle agentique (Python)

python
def run(messages, tools, model="claude-opus-5"):
while True:
r = client.messages.create(model=model, max_tokens=4096, tools=tools, messages=messages)
messages.append({"role": "assistant", "content": r.content})
if r.stop_reason == "tool_use":
results = []
for block in r.content:
if block.type == "tool_use":
try:
out = TOOLS[block.name](**block.input)
results.append({"type": "tool_result", "tool_use_id": block.id, "content": json.dumps(out)})
except ToolError as e:
results.append({"type": "tool_result", "tool_use_id": block.id, "is_error": True,
"content": json.dumps({"category": e.category, "retryable": e.retryable, "message": str(e)})})
messages.append({"role": "user", "content": results})
continue
if r.stop_reason == "max_tokens":
messages.append({"role": "user", "content": "Continue."}); continue
if r.stop_reason == "pause_turn":
continue
if r.stop_reason == "refusal":
return handle_refusal(r)
return r # end_turn / stop_sequence

tool_choice

ValeurComportementFable 5.1
{"type":"auto"}Le modèle décide (défaut)✓
{"type":"any"}Doit appeler un outil400
{"type":"tool","name":"x"}Doit appeler l’outil x400
{"type":"none"}Aucun outil ce tour✓
disable_parallel_tool_use: trueUn outil par tour✓

Sorties structurées

json
{ "model": "claude-sonnet-5", "max_tokens": 1024,
"output_config": { "format": { "type": "json_schema", "schema": {
"type": "object",
"properties": {
"vendor": { "type": "string" },
"total": { "type": "number" },
"currency": { "type": "string", "enum": ["USD", "EUR", "GBP"] },
"line_items": { "type": "array", "items": { "type": "object",
"properties": { "sku": { "type": "string" }, "qty": { "type": "integer" } },
"required": ["sku", "qty"], "additionalProperties": false } }
},
"required": ["vendor", "total", "currency", "line_items"],
"additionalProperties": false } } },
"messages": [{ "role": "user", "content": [
{ "type": "document", "source": { "type": "base64", "media_type": "application/pdf", "data": "…" } },
{ "type": "text", "text": "Extract the invoice." } ] }] }

Validez toujours en aval et exécutez une boucle de validation-retry qui réinjecte l’erreur spécifique.

Prompt caching

json
{ "system": [{ "type": "text", "text": "<20k-token style guide>", "cache_control": { "type": "ephemeral" } }],
"tools": [ … ],
"messages": [ … dynamic content last … ] }
  • Ordre : tools → system → messages ; les breakpoints de cache marquent la fin d’un préfixe stable.
  • Minimum ~1024 tokens (2048 sur Haiku 4.5). Jusqu’à 4 breakpoints.
  • TTL de 5 minutes par défaut (écriture 1,25×) ; option 1 heure (écriture 2×). Lectures 0,1×.
  • usage.cache_read_input_tokens confirme les hits.

Message Batches

python
batch = client.messages.batches.create(requests=[
{"custom_id": f"doc-{i}", "params": {"model": "claude-haiku-4-5", "max_tokens": 512,
"messages": [{"role": "user", "content": doc}]}} for i, doc in enumerate(docs)])
# poll batch.processing_status until "ended", then stream results
for res in client.messages.batches.results(batch.id):
if res.result.type == "succeeded": ...

50 % de réduction ; résultats sous 24 h ; succès/erreur par élément ; idéal pour « du jour au lendemain, le coût compte ».

Erreurs et retries

HTTPTypeRetry ?
400invalid_request_errorNon — corrigez la requête (p. ex. tool_choice forcé sur Fable 5.1, budget_tokens sur Opus 5)
401authentication_errorNon — clé
403permission_errorNon — droit d’accès
404not_found_errorNon — modèle/ressource
413request_too_largeNon — réduire
429rate_limit_errorOui — backoff, respectez retry-after
500api_errorOui — backoff
529overloaded_errorOui — backoff, envisagez un modèle de repli

Backoff exponentiel avec jitter ; clés d’idempotence pour les outils à effet de bord ; journalisez request_id depuis les en-têtes de réponse.

Autres entrées et fonctionnalités

FonctionnalitéForme
VisionBloc de contenu type: image avec une source de type base64 ou url
PDFBloc de contenu type: document avec une source base64 ou url et media_type: application/pdf
Files APIUploadez une fois, référencez par file_id dans un bloc de contenu
CitationsActivez sur les documents pour obtenir les spans sources
Outils côté serveurweb_search, code_execution, text_editor, bash, memory, computer
Recherche d’outilsOutil tool_search plus defer_loading: true sur les outils de catalogue
Connecteur MCPTableau mcp_servers de niveau supérieur d’objets avec type: url, url et name
Context editingStratégies context_management qui effacent les anciens tool results côté serveur
CompactionRésumé côté serveur préservant le fil narratif
Memory toolStockage persistant de type fichier entre sessions
json
// Vision and PDF content blocks
{ "type": "image", "source": { "type": "url", "url": "https://example.com/chart.png" } }
{ "type": "document", "source": { "type": "base64", "media_type": "application/pdf", "data": "…" } }
// MCP connector
{ "mcp_servers": [{ "type": "url", "url": "https://mcp.example.com/mcp", "name": "orders" }] }

Séquence complète d’événements de streaming

Le format filaire est SSE. Un tour complet avec un bloc de texte et un appel d’outil ressemble à ceci (deltas élidés marqués …) :

text
event: message_start
data: {"type":"message_start","message":{"id":"msg_01","role":"assistant","model":"claude-opus-5","content":[],"stop_reason":null,"usage":{"input_tokens":1200,"output_tokens":1}}}
event: content_block_start
data: {"type":"content_block_start","index":0,"content_block":{"type":"thinking","thinking":""}}
event: content_block_delta
data: {"type":"content_block_delta","index":0,"delta":{"type":"thinking_delta","thinking":"Checking the order…"}}
event: content_block_delta
data: {"type":"content_block_delta","index":0,"delta":{"type":"signature_delta","signature":"Er8B…"}}
event: content_block_stop
data: {"type":"content_block_stop","index":0}
event: content_block_start
data: {"type":"content_block_start","index":1,"content_block":{"type":"text","text":""}}
event: content_block_delta
data: {"type":"content_block_delta","index":1,"delta":{"type":"text_delta","text":"Looking that up"}}
event: content_block_stop
data: {"type":"content_block_stop","index":1}
event: content_block_start
data: {"type":"content_block_start","index":2,"content_block":{"type":"tool_use","id":"toolu_01","name":"get_order","input":{}}}
event: content_block_delta
data: {"type":"content_block_delta","index":2,"delta":{"type":"input_json_delta","partial_json":"{\"order_id\":\"ORD-12345\"}"}}
event: content_block_stop
data: {"type":"content_block_stop","index":2}
event: message_delta
data: {"type":"message_delta","delta":{"stop_reason":"tool_use","stop_sequence":null},"usage":{"output_tokens":57}}
event: message_stop
data: {"type":"message_stop"}
event: ping (may arrive at any time; ignore)
ÉvénementPorteNotes
message_startMessage coquille, usage d’entréecontent est vide ; stop_reason null
content_block_startType de bloc à un indexUn par bloc text/thinking/tool_use
content_block_deltatext_delta, thinking_delta, signature_delta, input_json_deltaL’entrée d’outil arrive en JSON partiel — bufferisez par index et parsez au stop
content_block_stopBloc terminé–
message_deltastop_reason et usage de sortie cumuléBranchez ici, pas sur la prose
message_stopTour terminé–
pingKeep-aliveIgnorer
erroroverloaded_error etc. en cours de fluxGérer comme l’erreur HTTP

Piège du streaming

L’entrée d’outil arrive en fragments input_json_delta ; ne faites jamais JSON.parse sur un fragment partiel. Accumulez partial_json par index de bloc et parsez seulement après content_block_stop. Le stop_reason faisant foi est sur message_delta.

tool_result avec images

Un outil peut renvoyer une image (p. ex. un graphique rendu) en plus du texte. Le content d’un tool_result accepte un tableau de blocs :

json
{ "role": "user", "content": [
{ "type": "tool_result", "tool_use_id": "toolu_09", "content": [
{ "type": "text", "text": "Chart rendered for Q3 revenue." },
{ "type": "image", "source": { "type": "base64", "media_type": "image/png", "data": "iVBORw0KGgo…" } }
] }
]}

Appels d’outils parallèles — aller-retour complet

Le modèle peut émettre plusieurs blocs tool_use en un tour. Exécutez-les en concurrence et renvoyez tous les blocs tool_result dans le message utilisateur unique suivant.

json
// Assistant turn: three parallel calls (stop_reason: tool_use)
{ "role": "assistant", "content": [
{ "type": "tool_use", "id": "toolu_a", "name": "get_weather", "input": { "city": "Paris" } },
{ "type": "tool_use", "id": "toolu_b", "name": "get_weather", "input": { "city": "Tokyo" } },
{ "type": "tool_use", "id": "toolu_c", "name": "get_fx", "input": { "pair": "EURJPY" } }
]}
// Your next user turn: all results, matched by tool_use_id, order-independent
{ "role": "user", "content": [
{ "type": "tool_result", "tool_use_id": "toolu_a", "content": "{\"c\":18}" },
{ "type": "tool_result", "tool_use_id": "toolu_b", "content": "{\"c\":26}" },
{ "type": "tool_result", "tool_use_id": "toolu_c", "content": "{\"rate\":171.2}" }
]}

Utilisez disable_parallel_tool_use: true dans tool_choice pour forcer un appel par tour lorsque les appels ont des effets de bord qui doivent être ordonnés.

Sortie structurée avec validation-retry

python
import json, jsonschema
from anthropic import Anthropic
client = Anthropic()
SCHEMA = { "type": "object",
"properties": { "vendor": {"type": "string"}, "total": {"type": "number"},
"currency": {"type": "string", "enum": ["USD","EUR","GBP"]} },
"required": ["vendor","total","currency"], "additionalProperties": False }
def extract(doc_text, max_attempts=3):
messages = [{"role": "user", "content": f"Extract the invoice.\n<doc>{doc_text}</doc>"}]
for attempt in range(max_attempts):
r = client.messages.create(
model="claude-sonnet-5", max_tokens=1024,
output_config={"format": {"type": "json_schema", "schema": SCHEMA}},
messages=messages)
text = r.content[0].text
try:
data = json.loads(text)
jsonschema.validate(data, SCHEMA) # schema + business rules
if data["total"] < 0:
raise ValueError("total must be non-negative")
return data
except (json.JSONDecodeError, jsonschema.ValidationError, ValueError) as e:
messages += [{"role": "assistant", "content": text},
{"role": "user", "content": f"That failed validation: {e}. Return corrected JSON only."}]
raise RuntimeError("extraction failed after retries") # escalate, never silently return bad data

La boucle réinjecte l’erreur spécifique et plafonne les tentatives, puis escalade — elle ne renvoie jamais d’objet vide ou non validé (anti-pattern de silent-failure).

Prompt caching — disposition multi-breakpoint

Jusqu’à quatre breakpoints. Ordonnez du plus stable au moins stable pour que le plus long préfixe possible reste en cache quand seule la fin change.

json
{
"system": [
{ "type": "text", "text": "<static company style guide, 15k tokens>", "cache_control": { "type": "ephemeral" } }
],
"tools": [
{ "name": "search_kb", "description": "…", "input_schema": { }, "cache_control": { "type": "ephemeral" } }
],
"messages": [
{ "role": "user", "content": [
{ "type": "text", "text": "<retrieved policy docs, changes per session>", "cache_control": { "type": "ephemeral", "ttl": "1h" } },
{ "type": "text", "text": "Now: the user's current question (never cached)." }
]}
]
}
BreakpointContenuTTLJustification
1Guide de style système5 minNe change jamais ; préfixe le plus profond
2Définitions d’outils5 minStable dans toute l’application
3Documents de session1 heureRéutilisés toute la session ; le TTL plus long amortit l’écriture 2×
—Question actuelleaucunUnique par requête

Règle : un breakpoint met en cache tout ce qui le précède. Placer un bloc volatile tôt invalide tout le caching plus profond.

États du cycle de vie d’un Batch

text
create → in_progress ──► (per request: succeeded | errored | canceled | expired)
└─► ended (all requests terminal; results retrievable)
cancel ─► canceling ─► ended
processing_statusSignification
in_progressToujours en cours ; sondez request_counts
cancelingAnnulation demandée
endedTerminal ; récupérez le flux de résultats
result.type par requêteTraitement
succeededUtilisez result.message
erroredInspectez result.error ; peut re-soumettre cet élément
canceledLe batch a été annulé avant l’exécution
expiredNon terminé dans la fenêtre de 24 h ; re-soumettre

Les résultats sont disponibles pendant 29 jours. Associez les éléments par custom_id ; ne présumez pas de l’ordre.

Gestion des erreurs avec backoff

python
import time, random
from anthropic import Anthropic, APIStatusError, RateLimitError, APIConnectionError
client = Anthropic()
RETRYABLE = {429, 500, 502, 503, 529}
def call_with_retry(**params):
for attempt in range(6):
try:
return client.messages.create(**params)
except RateLimitError as e:
wait = float(e.response.headers.get("retry-after", 0)) or min(60, 2 ** attempt)
time.sleep(wait + random.uniform(0, 0.5))
except APIStatusError as e:
if e.status_code in RETRYABLE:
time.sleep(min(60, 2 ** attempt) + random.uniform(0, 0.5))
else:
raise # 400/401/403/404/413 → fix, don't retry
except APIConnectionError:
time.sleep(min(60, 2 ** attempt) + random.uniform(0, 0.5))
raise RuntimeError("exhausted retries")

Les SDK réessaient automatiquement avec backoff ; une boucle personnalisée importe quand vous réglez le plafond, ajoutez du jitter, ou basculez vers un modèle de repli sur des 529 répétés.

En-têtes de limite de débit et idempotence

En-têteSignification
anthropic-ratelimit-requests-remainingBudget RPM restant
anthropic-ratelimit-input-tokens-remainingBudget ITPM restant
anthropic-ratelimit-output-tokens-remainingBudget OTPM restant
anthropic-ratelimit-*-resetQuand chaque compartiment se recharge (RFC 3339)
retry-afterSecondes à attendre après un 429/529 — respectez-le
request-idJournalisez-le pour chaque requête ; incluez-le dans les tickets de support

Réduisez proactivement le débit quand un en-tête *-remaining approche de zéro plutôt que d’attendre le 429.

python
# Idempotency: safe retries for side-effecting requests
client.messages.create(**params, extra_headers={"idempotency-key": f"charge-{order_id}"})

Réutilisez la même clé sur les retries pour qu’une livraison en double ne double pas la facturation. Concevez les outils à effet de bord de la même façon (acceptez un argument de clé d’idempotence).

Files API et citations

python
# Upload once, reference by file_id across many requests
f = client.files.upload(file=("contract.pdf", open("contract.pdf","rb"), "application/pdf"))
r = client.messages.create(
model="claude-sonnet-5", max_tokens=1024,
messages=[{"role": "user", "content": [
{"type": "document", "source": {"type": "file", "file_id": f.id},
"citations": {"enabled": True}},
{"type": "text", "text": "What is the termination notice period? Cite the clause."}
]}])

Avec les citations activées, les blocs de texte portent un tableau citations de spans sources :

json
{ "type": "text", "text": "The notice period is 60 days.",
"citations": [{ "type": "page_location", "cited_text": "…sixty (60) days…",
"document_index": 0, "start_page_number": 4, "end_page_number": 4 }] }

Les citations activent le test de provenance : chaque affirmation renvoie à un span que vous pouvez ouvrir.

Formes de requête pour context editing et compaction

json
// Context editing: clear stale tool results server-side, keep the turn valid
{ "model": "claude-opus-5", "max_tokens": 4096,
"context_management": {
"edits": [{ "type": "clear_tool_uses", "trigger": { "type": "input_tokens", "value": 100000 },
"keep": { "type": "tool_uses", "value": 3 } }]
},
"messages": [ … ] }
json
// Compaction: summarise older turns while preserving the narrative
{ "context_management": {
"edits": [{ "type": "compact", "trigger": { "type": "input_tokens", "value": 150000 } }] } }
StratégieSupprimeConserveÀ utiliser quand
Context editing (clear_tool_uses)Anciens tool results verbeuxLes N derniers tool uses, tout le texteLongues exécutions d’agent à forte densité d’outils
Compaction (compact)Anciens tours → résuméContinuité narrativeLongues sessions conversationnelles

Les deux s’exécutent côté serveur, donc ils gardent valide l’historique en ajout seul de Fable 5.1 (un élagage côté client ne le ferait pas).

Memory tool

json
{ "model": "claude-opus-5", "max_tokens": 2048,
"tools": [{ "type": "memory_20250818", "name": "memory" }],
"messages": [{ "role": "user", "content": "Remember I prefer metric units, then convert 5 miles." }] }

Le modèle lit/écrit dans un stockage persistant de type fichier (via des appels à l’outil memory que vous exécutez sur votre stockage sous-jacent) qui survit à la compaction et aux nouvelles sessions. Utilisez-le pour des préférences et un état durables ; ne le bourrez pas dans le prompt système.

Connecteur MCP (côté serveur)

python
r = client.messages.create(
model="claude-opus-5", max_tokens=1024,
mcp_servers=[{ "type": "url", "url": "https://mcp.example.com/mcp", "name": "orders",
"authorization_token": user_scoped_token }],
extra_headers={"anthropic-beta": "mcp-client-2025-04-04"},
messages=[{"role": "user", "content": "Where is ORD-12345?"}])

L’infrastructure d’Anthropic se connecte au serveur MCP distant pour vous — aucun harnais client local. Passez un token au périmètre de l’utilisateur pour que les outils agissent en tant qu’utilisateur final, pas en tant que super-utilisateur partagé.

Managed Agents vs Agent SDK

Managed AgentsAgent SDK (claude-agent-sdk)
Qui exécute la boucleAnthropic (boucle + sandbox hébergés)Vous (votre infra)
Charge d’exploitationMinimaleVous gérez le scaling, le sandboxing, les secrets
Contrôle sur runtime/réseau/localité des donnéesLimitéComplet
Tools/MCP/hooks/subagentsConfigurésContrôle programmatique complet
Choisir quand« moindre charge opérationnelle », « ne veut pas héberger »« contrôler le runtime », « les données doivent rester dans notre VPC », « harnais personnalisé »
python
# Agent SDK sketch – you host the harness
from claude_agent_sdk import ClaudeAgent
agent = ClaudeAgent(model="claude-opus-5", tools=[...], mcp_servers=[...],
permission_mode="acceptEdits")
result = agent.run("Refactor the auth module and run the tests.")

Idées reçues courantes

Idée reçueRéalitéPourquoi cela compte à l’examen
« tool_result va dans un message assistant »Il va dans un message user, associé par tool_use_idDistracteur de mauvais rôle
« Parser le texte pour ‘done’ afin de terminer la boucle »Branchez sur stop_reasonAnti-pattern de parsing de prose
« Une troncature max_tokens est une réponse terminée »C’est une troncature — continuez ou augmentez le plafondDistracteur de silent-failure
« Réessayer chaque erreur »Seulement 429/5xx/529 avec backoff ; corrigez les 4xxDistracteur de tempête de retries
« Sortie structurée signifie qu’on peut sauter la validation »Validez quand même + réessayez les règles métierDistracteur de sur-confiance
« Mettre en cache un bloc volatile tôt fait économiser »Cela invalide tout cache plus profond ; le stable en premierDistracteur de disposition de cache
« Un résultat vide convient quand un outil ne trouve rien »Renvoyez un is_error/not_found structuréAnti-pattern de silent empty-success

Analyse de scénario

Une équipe exécute un agent qui appelle 3–4 outils par tour (certains parallèles), sur Opus 5, dans une longue session qui atteint parfois des 529 et dépasse 150k tokens. Les outils de paiement ne doivent pas double-facturer. À quoi ressemble une implémentation correcte ?

  1. Contrôle de boucle — branchez sur stop_reason ; sur tool_use, exécutez tous les blocs tool_use parallèles en concurrence et renvoyez chaque tool_result dans un seul message user.
  2. Ordonner les effets de bord — pour l’outil de paiement, activez disable_parallel_tool_use quand il doit être séquencé, et passez une clé d’idempotence pour qu’un appel réessayé soit sûr.
  3. Gestion des 529 — backoff exponentiel avec jitter respectant retry-after ; après des 529 répétés, basculez vers un modèle plus récent ou équivalent (Opus 5 → Fable 5.1 est sûr vers le haut ; jamais vers un modèle plus ancien en milieu de session si les blocs de thinking comptent).
  4. Croissance du contexte — configurez le context editing clear_tool_uses à ~100k tokens d’entrée en conservant les 3 derniers tool uses ; côté serveur pour que l’historique reste valide.
  5. Erreurs venant des outils — résultats is_error structurés avec category/retryable, jamais un succès vide.
  6. Observabilité — journalisez request-id et usage par appel ; surveillez anthropic-ratelimit-*-remaining et réduisez le débit avant le 429.

Alternatives rejetées : parser la prose pour s’arrêter (parsing de prose), réessayer les 400 (tempête de retries), élaguer l’historique côté client (casse l’ajout seul), et « j’ai fini » auto-déclaré comme sortie de boucle (recours à l’auto-déclaration).

Dernière mise à jour le 18 sept. 2026