Le domaine 4 pèse 20 % — environ 12 questions sur 60. C'est celui où les candidats se sentent le plus à l'aise, et c'est précisément là qu'ils perdent des points : tout le monde « sait prompter », mais l'examen ne teste pas la capacité à écrire un bon prompt. Il teste si tu reconnais quand une instruction ne suffit pas — quand il faut un critère mesurable plutôt qu'un adjectif, un schéma plutôt que de la prose, une seconde instance plutôt qu'une auto-critique. Le fil rouge est le même qu'au domaine 1 : la garantie bat la probabilité.
Cette leçon suit les 5 task statements du guide officiel v1.0 (juillet 2026). Les scénarios d'examen qui tirent sur ce domaine sont Code Generation with Claude Code (scénario 2), Document Processing Pipeline (scénario 6) et Claude Code for Continuous Integration (scénario 5).
tool_use / tool_choice du domaine 2, isolement de session du domaine 3.La carte du domaine
Le domaine 4 est un continuum de contrôle : plus on descend, plus on impose de structure au modèle. 4.1 guide (prompt), 4.2 contraint (schéma), 4.3 fait raisonner (thinking), 4.4 déplace dans le temps (batch), 4.5 multiplie les regards (instances).
4.1 — Guider : critères explicites, few-shot, XML, system prompt
Question 3 du guide officiel, la plus emblématique : un pipeline de revue signale des changements de style mineurs comme « critiques » et rate une injection SQL. On ajoute « sois conservateur, ne signale que les vrais problèmes ». Ça ne marche pas. Pourquoi ?
Ce qu'il faut savoir
Les adjectifs ne se mesurent pas. « Conservateur », « rigoureux », « pertinent » demandent une interprétation, et l'interprétation varie d'un appel à l'autre. La réponse attendue est toujours des critères explicites : la liste de ce qui doit être signalé (avec le niveau de sévérité), la liste de ce qui ne doit pas l'être, le format attendu.
Few-shot avec le raisonnement. Des exemples de bonnes et de mauvaises revues ne suffisent pas s'ils ne disent pas pourquoi. Chaque exemple porte le raisonnement (« ceci est critical parce que… », « ceci n'est pas signalé parce que… »). C'est ce qui permet la généralisation aux cas non montrés.
Formulation positive. « Fais X » guide mieux que « ne fais pas Y ». Une liste de « ne pas » laisse ouvert tout ce qui n'est pas interdit. On décrit le comportement attendu, et on réserve le négatif aux exclusions précises (« ne pas signaler le formatage »).
Balises XML. Pour séparer sans ambiguïté les instructions, le contexte et les données à traiter : <instructions>, <code_to_review>, <previous_findings>. Sans délimitation, du code ou un document injecté peut être lu comme une consigne.
Cas limites explicités. Quand une transformation est ambiguë (un champ vide devient undefined ou une valeur par défaut ?), on tranche dans le prompt. Ne pas le faire, c'est laisser le modèle choisir différemment à chaque fois.
Le system prompt pour le style de travail. « Pose tes questions avant d'implémenter », « propose deux approches avant de coder » : un comportement qui doit persister sur toute la session va dans le system prompt (ou CLAUDE.md en Claude Code), pas dans chaque message.
SYSTEM = """<role>Relecteur de code senior. Tu poses tes questions de clarification AVANT de proposer une correction.</role>
<criteria>
Signaler en CRITICAL : injection SQL/XSS/commande, secrets en clair, contrôle d'accès contournable.
Signaler en HIGH : ressources non libérées, conditions de course, exceptions avalées.
Signaler en MEDIUM : logique dupliquée, validation d'entrée absente sur une API publique.
NE PAS signaler : formatage, nommage, ordre des imports, tout ce que couvre le linter.
Chaque finding : fichier, ligne, sévérité, preuve (chemin d'exploitation ou scénario d'échec).
</criteria>
<examples>
<example>
<code>query = "SELECT * FROM users WHERE id = " + user_id</code>
<finding severity="critical">Concaténation d'une entrée utilisateur dans une requête SQL. Preuve : user_id = "1 OR 1=1" renvoie toute la table. Correction : requête paramétrée.</finding>
</example>
<example>
<code>import os, sys # ordre non alphabétique</code>
<finding>AUCUN — ordre des imports = linter, hors périmètre.</finding>
</example>
</examples>
"""
messages = [{"role": "user", "content": f"<code_to_review>\n{diff}\n</code_to_review>"}]4.2 — Contraindre : sortie structurée, nullable, validation-retry
Le cœur technique du domaine, ancré sur le scénario 6 (pipeline documentaire). La question 11 du guide en est l'archétype : les factures sans numéro de bon de commande reçoivent un numéro plausible mais inventé. Que faire ?
Ce qu'il faut savoir
tool_use + JSON schema, jamais de parsing. Pour obtenir du structuré, on définit un outil dont input_schema est le schéma cible. Le modèle remplit le schéma ; l'application lit tool_use.input. Demander « réponds en JSON » puis parser du texte est fragile (préambules, backticks, champs manquants).
tool_choice garantit l'appel. Forcé sur un outil précis quand le schéma est connu ; any quand plusieurs schémas d'extraction existent (facture, contrat, bon de livraison) et que le modèle doit choisir selon le document.
Valider, puis réessayer avec le feedback. La sortie est validée programmatiquement (types, formats, contraintes métier). En cas d'échec, on renvoie le message d'erreur précis au modèle pour un retry ciblé (« total_ht doit être un nombre, reçu "1 250,00 €" »). Pas un retry aveugle, pas un abandon.
Nullable contre l'hallucination. C'est la question 11 : si le schéma exige une valeur, le modèle en fabrique une. La correction est un schéma strict où les champs optionnels sont explicitement nullable, avec une description qui dit de renvoyer null quand la valeur est absente. Les distracteurs : « instruction dans le prompt » (probabiliste), « post-traitement qui détecte le suspect » (trop tard, et comment distinguer un vrai PO d'un faux plausible ?), « validation sur la base des PO » (couplage inutile, et un PO inventé peut exister par hasard).
Enums pour la confiance. Un niveau de confiance en texte libre (« assez sûr », « probable ») est inexploitable. Un enum strict ("high" | "medium" | "low" | "not_found") permet un routage programmatique : low → revue humaine.
Distinguer les types de sections. Dans un document mixte (tableaux, prose, listes), une extraction unique aplatit tout. Le prompt dit comment traiter chaque type et le schéma a des champs distincts (tables[], narrative, line_items[]), et on découpe à la structure (une passe par section).
EXTRACT_INVOICE = {
"name": "extract_invoice",
"description": "Extrait les champs d'une facture. Renvoie null pour tout champ ABSENT du document ; n'invente jamais.",
"input_schema": {
"type": "object",
"properties": {
"invoice_number": {"type": "string"},
"po_number": {"type": ["string", "null"],
"description": "Numéro de bon de commande tel qu'écrit. null si aucun n'apparaît."},
"total_ht": {"type": "number", "description": "Nombre décimal, sans devise ni séparateur de milliers"},
"currency": {"type": "string", "enum": ["EUR", "USD", "GBP"]},
"confidence": {"type": "string", "enum": ["high", "medium", "low", "not_found"]},
"line_items": {"type": "array", "items": {"type": "object",
"properties": {"label": {"type": "string"}, "qty": {"type": "number"}, "unit_price": {"type": "number"}},
"required": ["label", "qty", "unit_price"]}},
},
"required": ["invoice_number", "po_number", "total_ht", "currency", "confidence", "line_items"],
"additionalProperties": False,
},
}
def extract(doc: str, max_retries: int = 2) -> dict:
messages = [{"role": "user", "content": f"<document>\n{doc}\n</document>"}]
for attempt in range(max_retries + 1):
resp = client.messages.create(
model=M, max_tokens=2048, tools=[EXTRACT_INVOICE],
tool_choice={"type": "tool", "name": "extract_invoice"}, # appel garanti
messages=messages,
)
block = next(b for b in resp.content if b.type == "tool_use")
errors = validate(block.input) # jsonschema + règles métier
if not errors:
return block.input
messages += [{"role": "assistant", "content": resp.content},
{"role": "user", "content": [{"type": "tool_result", "tool_use_id": block.id,
"content": f"Validation échouée : {errors}. Corrige UNIQUEMENT ces champs.",
"is_error": True}]}] # retry avec feedback ciblé
raise ExtractionError(errors)tool_use + schéma. Garanti → tool_choice. Absent → nullable + description. Confiance → enum. Invalide → retry avec le message d'erreur.4.3 — Raisonner : quand activer extended thinking
Section courte et de proportion. L'examen ne demande pas comment fonctionne le raisonnement étendu, mais quand il est justifié.
Extended thinking : oui ou non ?
| Activer | Ne pas activer | |
|---|---|---|
| Nature de la tâche | Raisonnement multi-étapes, dépendances entre fichiers, architecture, arbitrages | Formatage, extraction simple, classification évidente |
| Exemple | Impact d'un changement de schéma sur 12 modules, choix entre deux stratégies de migration | Renommer une variable, extraire une date, corriger une coquille |
| Avec tool_use | Raisonner sur les résultats d'outils entre deux appels (analyse → décision → appel) | Appel d'outil direct sans arbitrage |
| Audit | Rendre le raisonnement visible pour vérifier POURQUOI une décision a été prise | Seul le résultat compte |
| Coût / latence | Accepté quand l'erreur coûte plus cher que les tokens | Injustifié sur du volume simple |
| Signal d'examen | « complexe », « interdépendant », « pourquoi », « auditer » | « simple », « chaque document », « à grande échelle » |
Ce qu'il faut savoir
Quand. Pour un raisonnement complexe et multi-étapes : analyser les dépendances entre fichiers avant un refactoring, arbitrer entre deux approches, comprendre l'impact d'un changement de schéma. Pas pour une tâche simple où le coût et la latence supplémentaires ne sont pas justifiés.
Avec tool_use. Le raisonnement étendu combiné aux outils permet de réfléchir sur les résultats d'outils avant l'action suivante : lire trois fichiers, raisonner sur leurs interactions, puis décider quoi modifier.
Pour l'audit. Quand on doit comprendre pourquoi une décision a été prise (une recommandation d'architecture, une classification), le raisonnement visible permet de vérifier la logique, pas seulement le résultat.
4.4 — Différer : la Batch API
Scénario 6, volume et coût. Trois chiffres à connaître par cœur et une contre-indication.
Ce qu'il faut savoir
Les chiffres. 50 % de réduction sur le coût des tokens. Résultats sous 24 heures (souvent bien moins, mais 24 h est l'engagement). Chaque requête du lot porte un custom_id qui permet de la retrouver dans les résultats, parce que l'ordre de sortie n'est pas garanti.
Le flux. Constituer le lot → soumettre → interroger le statut (polling) → récupérer les résultats et les rapprocher par custom_id. Une entrée peut être succeeded ou errored individuellement.
Quand. Traitement de masse non urgent : indexer 50 000 documents d'archives, classifier un historique, générer des descriptions produit. Quand le traitement est attendu en temps réel (revue de PR, réponse client), c'est l'API standard.
Avec le prompt caching. Les requêtes d'un lot partagent souvent le même system prompt et le même schéma d'extraction : marqués avec cache_control, ils sont mis en cache et la remise batch s'applique par-dessus.
# Batch : un item par document, custom_id pour le rapprochement
requests = [
{"custom_id": f"doc-{d.id}",
"params": {"model": M, "max_tokens": 2048,
"system": [{"type": "text", "text": SYSTEM_EXTRACT,
"cache_control": {"type": "ephemeral"}}], # partagé → cache
"tools": [EXTRACT_INVOICE],
"tool_choice": {"type": "tool", "name": "extract_invoice"},
"messages": [{"role": "user", "content": d.text}]}}
for d in documents
]
batch = client.messages.batches.create(requests=requests)
while client.messages.batches.retrieve(batch.id).processing_status != "ended":
time.sleep(60) # polling
for r in client.messages.batches.results(batch.id):
if r.result.type == "succeeded":
store(r.custom_id, r.result.message) # rapprochement par custom_id
else:
requeue_or_flag(r.custom_id, r.result.error)custom_id.4.5 — Multiplier : la revue multi-instances
Question de méthode, à la frontière du domaine 3 (isolement de session). L'idée : une instance = une perspective, plus une instance qui consolide.
Ce qu'il faut savoir
Une instance par perspective. Sécurité, performance, maintenabilité : chaque instance reçoit le même diff avec des critères propres. Une seule instance qui « fait tout » dilue l'attention (cf. domaine 1, 1.6) et mélange les sévérités.
L'agrégation. Une instance dédiée consolide les findings, déduplique, priorise et arbitre les contradictions : quand la revue performance recommande d'inliner une fonction et la revue maintenabilité de l'extraire, l'agrégateur tranche selon le contexte (chemin critique ou non) au lieu de laisser deux conseils opposés sur la PR.
L'indépendance. L'instance qui a généré le code n'est jamais relectrice (cf. domaine 3, 3.6).
Réduire les faux positifs. Des critères explicites (4.1) et, surtout, des exemples de ce qu'il ne faut pas signaler : les faux positifs viennent presque toujours d'un périmètre non borné. Une revue qui signale l'ordre des imports n'a pas reçu l'instruction que c'est hors périmètre.
Les pièges transversaux du domaine 4
- Un mot entre guillemets est un signal. « Sois conservateur », « n'invente pas », « sois moins sensible » : l'énoncé te montre l'instruction probabiliste qui a échoué. La réponse la remplace par une structure.
- Le schéma est un contrat, pas une suggestion. Tout ce qui peut être imposé par le schéma (nullable, enum, required, additionalProperties) l'emporte sur une consigne.
- La proportion, encore. Extended thinking et multi-instances coûtent ; ils se justifient par la complexité ou le risque, pas par défaut.
- Le temps réel exclut le batch. Si quelqu'un attend, c'est l'API standard.
Checklist de la veille
<instructions>, <code_to_review>, <document> — séparer consignes et données.
- Cas ambigus (vide → undefined ou défaut ?) tranchés dans le prompt.
- Style de travail persistant (« pose tes questions d'abord ») → system prompt.
- Structuré → tool_use + input_schema ; jamais « réponds en JSON » + parsing.
- tool_choice forcé (schéma connu) ou any (plusieurs schémas).
- Champs absents → ["string", "null"] + description « null si absent » — pas une instruction, pas un post-traitement.
- Confiance → enum strict, routage programmatique (low → humain).
- Validation programmatique → retry avec le message d'erreur, jamais aveugle.
- Sections mixtes → champs distincts dans le schéma + une passe par section.
- Extended thinking : complexe, multi-étapes, audit, avec tool_use ; pas sur du simple à grande échelle.
- Batch API : -50 %, ≤ 24 h, custom_id, polling ; jamais pour du temps réel ; cumulable avec cache_control.
- Multi-instances : une par perspective + un agrégateur qui déduplique et arbitre ; jamais l'instance génératrice.
- Faux positifs → critères + exemples de ce qu'il ne faut pas signaler.Cinq questions-scénario originales, corrigées
Questions écrites par nAIvigate dans l'esprit de l'examen, sans reproduction d'items réels.
Question 1 — Scénario CI. Ta revue automatisée classe « variable non utilisée » en critical et laisse passer un token API en clair. Le prompt dit « concentre-toi sur ce qui compte vraiment ». Correction la plus efficace ?
A. Remplacer par « sois beaucoup plus strict sur la sécurité ». B. Définir des critères explicites par sévérité (secrets et injections = critical ; variables non utilisées = hors périmètre) avec des exemples annotés du raisonnement. C. Passer à un modèle plus grand. D. Activer extended thinking pour la revue.
📚Correction Q1
B. Un adjectif remplacé par un autre adjectif reste non mesurable (A). C et D ne définissent pas ce qui doit être signalé. Les critères explicites + few-shot avec raisonnement sont la réponse attendue.
Question 2 — Scénario documentaire. Un pipeline extrait des contrats. Les contrats sans date de fin reçoivent une date de fin plausible. Le prompt dit déjà « n'invente pas de dates ». Que faire ?
A. Répéter l'instruction en majuscules. B. Rendre end_date nullable dans le schéma avec la description « null si non mentionnée », et ajouter confidence en enum. C. Post-traiter pour rejeter les dates de fin postérieures à 2030. D. Comparer avec la base de contrats existante.
📚Correction Q2
B. Le schéma force l'invention si le champ est requis ; nullable donne une sortie légitime pour l'absence. A est probabiliste. C rejette selon une heuristique arbitraire et rate les dates inventées plausibles. D couple inutilement et ne détecte pas une date inventée qui coïncide.
Question 3 — Scénario documentaire. 80 000 rapports d'archive à classifier avant fin de mois, budget serré, aucun utilisateur n'attend. Approche ?
A. API standard avec concurrence maximale. B. Batch API avec custom_id par rapport, system prompt et schéma marqués cache_control, polling jusqu'à ended. C. Extended thinking pour améliorer la précision. D. Une instance par catégorie de rapport en temps réel.
📚Correction Q3
B. Volume, non-urgence, budget : le cas d'école de la Batch API (-50 %), cumulée avec le caching du contexte partagé. A paie plein tarif. C et D augmentent le coût sans raison.
Question 4 — Scénario CI. Une extraction structurée échoue 6 % du temps sur une validation de format (montant en chaîne au lieu de nombre). La logique actuelle réessaie la requête à l'identique jusqu'à 3 fois. Amélioration ?
A. Passer à 5 tentatives. B. Renvoyer au modèle le message d'erreur précis de la validation (« total_ht doit être un nombre, reçu "1 250,00 €" ») et ne demander la correction que de ce champ. C. Ajouter « renvoie des nombres » au system prompt. D. Convertir côté application toutes les chaînes en nombres.
📚Correction Q4
B. Le retry avec feedback ciblé est le pattern attendu. A répète l'aveugle. C aide mais n'est pas le mécanisme de récupération. D masque le problème et cassera sur des cas non prévus (« 1.250,00 » vs « 1,250.00 »).
Question 5 — Scénario CI. Ta revue multi-instances produit sur la même PR « inliner cette fonction (perf) » et « extraire cette fonction (lisibilité) ». Les développeurs ignorent désormais les deux. Que faire ?
A. Supprimer l'instance performance. B. Ajouter une instance d'agrégation qui déduplique, priorise et arbitre les contradictions selon le contexte (chemin critique ou non). C. Fusionner les deux perspectives dans une seule instance. D. Laisser les développeurs trancher, c'est leur rôle.
📚Correction Q5
B. L'agrégateur est le composant attendu du pattern multi-instances. A perd une perspective. C dilue l'attention. D est ce qui se passe déjà, et ça ne marche pas.
Quiz de validation
« Sois conservateur » ne fonctionne pas. Réponse attendue ?
📚Lexique du domaine 4 (déroulez)
Critères explicites — Liste mesurable de ce qui doit être signalé, ignoré et du format attendu, en remplacement d'un adjectif de comportement.
Few-shot avec raisonnement — Exemples annotés du pourquoi (pourquoi critical, pourquoi non signalé), condition de la généralisation.
Formulation positive — Décrire le comportement attendu (« fais X ») plutôt que l'interdit (« ne fais pas Y »).
Balises XML — Délimiteurs (<instructions>, <document>) séparant consignes, contexte et données pour éviter qu'un contenu injecté soit lu comme une instruction.
System prompt — Emplacement des comportements persistants sur toute la session (style de travail, rôle, critères).
Sortie structurée — Réponse conforme à un schéma, obtenue via tool_use avec input_schema.
input_schema — JSON schema d'un outil ; sert de contrat pour la sortie structurée.
tool_choice — auto / any / forcé ; any et forcé garantissent un appel d'outil, donc une sortie structurée.
Champ nullable — Type ["string", "null"] autorisant explicitement l'absence de valeur, contre l'hallucination.
Enum de confiance — Valeurs fermées (high / medium / low / not_found) permettant un routage programmatique.
additionalProperties: false — Contrainte interdisant les champs hors schéma.
Validation-et-retry — Vérification programmatique de la sortie, puis nouvel appel avec le message d'erreur précis.
Retry aveugle — Anti-pattern : rejouer la même requête sans feedback.
Extended thinking — Raisonnement étendu pour les tâches complexes multi-étapes ; rend la logique auditable ; combinable avec tool_use.
Batch API — Traitement asynchrone à -50 %, résultats sous 24 h, pour le volume non urgent.
custom_id — Identifiant par requête d'un lot, pour rapprocher les résultats (ordre non garanti).
Polling — Interrogation périodique du statut d'un lot jusqu'à ended.
Prompt caching (cache_control) — Mise en cache des préfixes partagés (system prompt, schéma) ; cumulable avec la remise batch.
Revue multi-instances — Une instance par perspective (sécurité, performance, maintenabilité) sur la même entrée.
Instance d'agrégation — Instance qui consolide, déduplique, priorise et arbitre les contradictions entre relectrices.
Indépendance de revue — L'instance génératrice n'est jamais relectrice (isolement de session).
Exemples de non-findings — Exemples de ce qu'il ne faut pas signaler, principal levier contre les faux positifs.
Pour aller plus loin
La dernière leçon est le Domaine 5 — Context Management & Reliability (15 %), qui reprend la mémoire persistante, le compactage, la dégradation gracieuse et l'attribution des sources — les questions de fiabilité qui complètent les schémas et les critères vus ici. Pour approfondir la sortie structurée en production, notre guide RAG en production montre les schémas d'extraction et de citation à l'échelle.
Si tu veux certifier une équipe entière — ou industrialiser un pipeline d'extraction structurée (schémas nullable, validation-retry, batch + caching) sur de vrais documents — c'est ce que nAIvigate Studio fait en Sprint.