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
{ "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" }}| Champ | Notes |
|---|---|
model | ID épinglé : claude-fable-5-1, claude-opus-5, claude-sonnet-5, claude-haiku-4-5 |
max_tokens | Plafond dur de sortie ; stop_reason: max_tokens = tronqué |
system | Chaîne de niveau supérieur ou blocs de contenu ; stable → cachable |
messages | Alternance user/assistant ; le contenu est une chaîne ou un tableau de blocs (text, image, document, tool_use, tool_result, thinking) |
temperature / top_p | Ré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 |
effort | low / medium / high / xhigh |
tools / tool_choice | Voir tool use ci-dessous ; le choix forcé est un 400 sur Fable 5.1 |
output_config.format | JSON Schema pour la sortie structurée |
Anatomie d’une réponse
{ "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
| Valeur | Signification | Action |
|---|---|---|
end_turn | Terminé naturellement | Fini |
tool_use | Veut exécuter des outils | Exécuter, ajouter tool_result, rappeler |
max_tokens | Tronqué | Continuer ou augmenter le plafond ; ne jamais traiter comme complet |
stop_sequence | A atteint une chaîne d’arrêt | Fini (vérifiez stop_sequence) |
pause_turn | Long tour d’outil côté serveur mis en pause | Renvoyer pour continuer |
refusal | Refus de sécurité | Chemin de repli explicite ; ne réessayez pas aveuglément |
Appels minimaux
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)import Anthropic from '@anthropic-ai/sdk';
const client = new Anthropic();
const msg = await 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?' }],});console.log(msg.content[0].type === 'text' ? msg.content[0].text : '', msg.stop_reason);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.
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)const stream = client.messages.stream({ model: 'claude-sonnet-5', max_tokens: 1024, messages: [{ role: 'user', content: 'Write a haiku about latency.' }],});stream.on('text', (t) => process.stdout.write(t));const final = await stream.finalMessage();console.log(final.stop_reason, final.usage);Aller-retour du tool use
// 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)
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_sequencetool_choice
| Valeur | Comportement | Fable 5.1 |
|---|---|---|
{"type":"auto"} | Le modèle décide (défaut) | ✓ |
{"type":"any"} | Doit appeler un outil | 400 |
{"type":"tool","name":"x"} | Doit appeler l’outil x | 400 |
{"type":"none"} | Aucun outil ce tour | ✓ |
disable_parallel_tool_use: true | Un outil par tour | ✓ |
Sorties structurées
{ "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
{ "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_tokensconfirme les hits.
Message Batches
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 resultsfor 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
| HTTP | Type | Retry ? |
|---|---|---|
| 400 | invalid_request_error | Non — corrigez la requête (p. ex. tool_choice forcé sur Fable 5.1, budget_tokens sur Opus 5) |
| 401 | authentication_error | Non — clé |
| 403 | permission_error | Non — droit d’accès |
| 404 | not_found_error | Non — modèle/ressource |
| 413 | request_too_large | Non — réduire |
| 429 | rate_limit_error | Oui — backoff, respectez retry-after |
| 500 | api_error | Oui — backoff |
| 529 | overloaded_error | Oui — 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 |
|---|---|
| Vision | Bloc de contenu type: image avec une source de type base64 ou url |
Bloc de contenu type: document avec une source base64 ou url et media_type: application/pdf | |
| Files API | Uploadez une fois, référencez par file_id dans un bloc de contenu |
| Citations | Activez sur les documents pour obtenir les spans sources |
| Outils côté serveur | web_search, code_execution, text_editor, bash, memory, computer |
| Recherche d’outils | Outil tool_search plus defer_loading: true sur les outils de catalogue |
| Connecteur MCP | Tableau mcp_servers de niveau supérieur d’objets avec type: url, url et name |
| Context editing | Stratégies context_management qui effacent les anciens tool results côté serveur |
| Compaction | Résumé côté serveur préservant le fil narratif |
| Memory tool | Stockage persistant de type fichier entre sessions |
// 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 …) :
event: message_startdata: {"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_startdata: {"type":"content_block_start","index":0,"content_block":{"type":"thinking","thinking":""}}
event: content_block_deltadata: {"type":"content_block_delta","index":0,"delta":{"type":"thinking_delta","thinking":"Checking the order…"}}
event: content_block_deltadata: {"type":"content_block_delta","index":0,"delta":{"type":"signature_delta","signature":"Er8B…"}}
event: content_block_stopdata: {"type":"content_block_stop","index":0}
event: content_block_startdata: {"type":"content_block_start","index":1,"content_block":{"type":"text","text":""}}
event: content_block_deltadata: {"type":"content_block_delta","index":1,"delta":{"type":"text_delta","text":"Looking that up"}}
event: content_block_stopdata: {"type":"content_block_stop","index":1}
event: content_block_startdata: {"type":"content_block_start","index":2,"content_block":{"type":"tool_use","id":"toolu_01","name":"get_order","input":{}}}
event: content_block_deltadata: {"type":"content_block_delta","index":2,"delta":{"type":"input_json_delta","partial_json":"{\"order_id\":\"ORD-12345\"}"}}
event: content_block_stopdata: {"type":"content_block_stop","index":2}
event: message_deltadata: {"type":"message_delta","delta":{"stop_reason":"tool_use","stop_sequence":null},"usage":{"output_tokens":57}}
event: message_stopdata: {"type":"message_stop"}
event: ping (may arrive at any time; ignore)| Événement | Porte | Notes |
|---|---|---|
message_start | Message coquille, usage d’entrée | content est vide ; stop_reason null |
content_block_start | Type de bloc à un index | Un par bloc text/thinking/tool_use |
content_block_delta | text_delta, thinking_delta, signature_delta, input_json_delta | L’entrée d’outil arrive en JSON partiel — bufferisez par index et parsez au stop |
content_block_stop | Bloc terminé | – |
message_delta | stop_reason et usage de sortie cumulé | Branchez ici, pas sur la prose |
message_stop | Tour terminé | – |
ping | Keep-alive | Ignorer |
error | overloaded_error etc. en cours de flux | Gé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 :
{ "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.
// 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
import json, jsonschemafrom 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 dataLa 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.
{ "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)." } ]} ]}| Breakpoint | Contenu | TTL | Justification |
|---|---|---|---|
| 1 | Guide de style système | 5 min | Ne change jamais ; préfixe le plus profond |
| 2 | Définitions d’outils | 5 min | Stable dans toute l’application |
| 3 | Documents de session | 1 heure | Réutilisés toute la session ; le TTL plus long amortit l’écriture 2× |
| — | Question actuelle | aucun | Unique 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
create → in_progress ──► (per request: succeeded | errored | canceled | expired) └─► ended (all requests terminal; results retrievable) cancel ─► canceling ─► endedprocessing_status | Signification |
|---|---|
in_progress | Toujours en cours ; sondez request_counts |
canceling | Annulation demandée |
ended | Terminal ; récupérez le flux de résultats |
result.type par requête | Traitement |
|---|---|
succeeded | Utilisez result.message |
errored | Inspectez result.error ; peut re-soumettre cet élément |
canceled | Le batch a été annulé avant l’exécution |
expired | Non 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
import time, randomfrom 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")import Anthropic, { APIError } from '@anthropic-ai/sdk';
const client = new Anthropic();const RETRYABLE = new Set([429, 500, 502, 503, 529]);const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms));
async function callWithRetry(params: Anthropic.MessageCreateParamsNonStreaming) { for (let attempt = 0; attempt < 6; attempt++) { try { return await client.messages.create(params); } catch (err) { if (err instanceof APIError && (RETRYABLE.has(err.status ?? 0))) { const retryAfter = Number(err.headers?.['retry-after']) || Math.min(60, 2 ** attempt); await sleep((retryAfter + Math.random() * 0.5) * 1000); continue; } throw err; // 4xx client errors → fix the request } } throw new Error('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ête | Signification |
|---|---|
anthropic-ratelimit-requests-remaining | Budget RPM restant |
anthropic-ratelimit-input-tokens-remaining | Budget ITPM restant |
anthropic-ratelimit-output-tokens-remaining | Budget OTPM restant |
anthropic-ratelimit-*-reset | Quand chaque compartiment se recharge (RFC 3339) |
retry-after | Secondes à attendre après un 429/529 — respectez-le |
request-id | Journalisez-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.
# Idempotency: safe retries for side-effecting requestsclient.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
# Upload once, reference by file_id across many requestsf = 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 :
{ "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
// 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": [ … ] }// Compaction: summarise older turns while preserving the narrative{ "context_management": { "edits": [{ "type": "compact", "trigger": { "type": "input_tokens", "value": 150000 } }] } }| Stratégie | Supprime | Conserve | À utiliser quand |
|---|---|---|---|
Context editing (clear_tool_uses) | Anciens tool results verbeux | Les N derniers tool uses, tout le texte | Longues exécutions d’agent à forte densité d’outils |
Compaction (compact) | Anciens tours → résumé | Continuité narrative | Longues 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
{ "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)
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 Agents | Agent SDK (claude-agent-sdk) | |
|---|---|---|
| Qui exécute la boucle | Anthropic (boucle + sandbox hébergés) | Vous (votre infra) |
| Charge d’exploitation | Minimale | Vous gérez le scaling, le sandboxing, les secrets |
| Contrôle sur runtime/réseau/localité des données | Limité | Complet |
| Tools/MCP/hooks/subagents | Configurés | Contrô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é » |
# Agent SDK sketch – you host the harnessfrom claude_agent_sdk import ClaudeAgentagent = 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çue | Réalité | Pourquoi cela compte à l’examen |
|---|---|---|
| « tool_result va dans un message assistant » | Il va dans un message user, associé par tool_use_id | Distracteur de mauvais rôle |
| « Parser le texte pour ‘done’ afin de terminer la boucle » | Branchez sur stop_reason | Anti-pattern de parsing de prose |
« Une troncature max_tokens est une réponse terminée » | C’est une troncature — continuez ou augmentez le plafond | Distracteur de silent-failure |
| « Réessayer chaque erreur » | Seulement 429/5xx/529 avec backoff ; corrigez les 4xx | Distracteur 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étier | Distracteur de sur-confiance |
| « Mettre en cache un bloc volatile tôt fait économiser » | Cela invalide tout cache plus profond ; le stable en premier | Distracteur 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 ?
- Contrôle de boucle — branchez sur
stop_reason; surtool_use, exécutez tous les blocstool_useparallèles en concurrence et renvoyez chaquetool_resultdans un seul message user. - Ordonner les effets de bord — pour l’outil de paiement, activez
disable_parallel_tool_usequand il doit être séquencé, et passez une clé d’idempotence pour qu’un appel réessayé soit sûr. - 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). - 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. - Erreurs venant des outils — résultats
is_errorstructurés avec category/retryable, jamais un succès vide. - Observabilité — journalisez
request-idetusagepar appel ; surveillezanthropic-ratelimit-*-remaininget 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