Quand une nouvelle tentative est utile, et quand elle ne l’est pas
Très utile pour les erreurs de format, mais inutile lorsque des données manquent.
Quand une nouvelle tentative est utile, et quand elle ne l’est pas est une leçon Claude Architect gratuite sur CoddyKit. Ceci est la leçon 1 sur 4. Tu peux lire la leçon complète ci-dessous gratuitement — puis la pratiquer en direct dans le navigateur avec un éditeur de code intégré et un tuteur IA 24/7. Elle fait partie du parcours d'apprentissage Claude Architect, et ta progression se synchronise sur le web et l'application CoddyKit. Le cours Claude Architect comprend 4 leçons au total.
Certaines parties de cette leçon n'ont pas encore été traduites et s'affichent en anglais.
The Retry Reflex
When a model's output fails validation, the tempting move is to just try again. Sometimes that works beautifully. Sometimes it burns tokens and latency for nothing.
As an architect, your job is to know which failure you are looking at before you wire up a retry loop. Retry is a precision tool, not a blanket safety net.
This lesson draws a hard line: retry is excellent for format and structural errors, and useless when the needed information is simply absent from the source.
Two Very Different Failures
Extraction and structured-output pipelines fail in two fundamentally different ways:
- Format / structural / arithmetic errors — the answer exists in the source, but the model rendered it wrong: invalid JSON, a missing required field it actually had data for, a total that doesn't add up.
- Absent information — the source document simply does not contain the value. There is nothing to extract.
Retry-with-feedback can fix the first class. It cannot conjure data that was never there. Confusing the two is a classic anti-pattern.
What Retry-With-Feedback Actually Sends
A good retry is not "run the same prompt again and hope." It is a corrective retry. You send the model three things:
- The original source document
- The wrong output it produced
- The exact validation error that was raised
This gives the model the specific signal it needs to self-correct. Vague feedback like "that was wrong, try harder" performs far worse than handing it the precise validator message.
def retry_with_feedback(client, source_doc, bad_output, validation_error):
return client.messages.create(
model="claude-sonnet-4-5",
max_tokens=1024,
messages=[
{"role": "user", "content": (
"Extract the invoice fields as JSON.\n\n"
f"SOURCE DOCUMENT:\n{source_doc}\n\n"
f"YOUR PREVIOUS OUTPUT (rejected):\n{bad_output}\n\n"
f"VALIDATION ERROR:\n{validation_error}\n\n"
"Fix only what the error names. Return corrected JSON."
)}
],
)Format Errors: Retry's Sweet Spot
Format and structural problems are exactly where retry shines, because the correct answer is recoverable from the same input:
- Malformed JSON or a stray trailing comma
- A field present in the doc but missing from the output
- Wrong enum casing or an unexpected key
- Arithmetic that doesn't reconcile
On the second pass, armed with the validator's message, the model almost always lands the corrected structure. Even better: enforce structure up front with tool_use + a JSON Schema so most syntax errors never happen at all.
Catching Arithmetic With Self-Correction
Arithmetic discrepancies are a format-class error retry can fix — but first you have to detect them. The technique: extract both the value the model computes and the value the document states, then compare.
Pull out calculated_total (sum of line items) AND stated_total (the printed total). If they diverge, you have a concrete, retryable validation error to feed back — not a vague hunch.
from pydantic import BaseModel, model_validator
class Invoice(BaseModel):
line_items: list[float]
calculated_total: float
stated_total: float
@model_validator(mode="after")
def totals_match(self):
if round(self.calculated_total, 2) != round(self.stated_total, 2):
raise ValueError(
f"calculated_total {self.calculated_total} != "
f"stated_total {self.stated_total}"
)
return selfWhere Retry Hits a Wall
Now the hard limit. If a field is not present in the source, retrying does nothing useful. The model has two bad options on each loop:
- Return the same "missing" result — wasted latency and cost.
- Fabricate a plausible-looking value to satisfy the validator — far worse, because now you've injected a hallucination into trusted data.
Retry pressure on absent data actively encourages invention. No amount of re-prompting extracts a value that was never written down.
The Schema Trap: Required Fields
This is where a subtle schema design choice bites. Mark a field required ONLY if it is always present. Never require a field that may be absent — the model will fabricate it to satisfy the schema, and your retry loop will keep accepting garbage.
For optional data, make the field optional and let the model report absence honestly. A retry can't rescue a schema that forces invention.
tax_id_tool = {
"name": "extract_vendor",
"description": "Extract vendor fields from an invoice.",
"input_schema": {
"type": "object",
"properties": {
"vendor_name": {"type": "string"},
# tax_id is OFTEN absent -> optional, never required
"tax_id": {"type": ["string", "null"]},
},
# require ONLY the always-present field
"required": ["vendor_name"],
},
}Make Absence a First-Class Result
The fix for absent data is not retry — it is letting the model say so explicitly. Distinguish a genuine access/format failure (maybe retryable) from a valid empty result (no value exists — stop, don't loop).
Use an enum with an "other"/"not_present" value plus a free-text detail field. That keeps the schema extensible and gives downstream code a clean signal to skip the field instead of triggering a doomed retry.
{
"properties": {
"discount_status": {
"type": "string",
"enum": ["applied", "none", "not_present", "other"]
},
"discount_detail": { "type": "string" }
},
"required": ["discount_status"]
}Route by Error Type, Not by Reflex
Mature pipelines branch on why validation failed. Structured errors enable this; generic ones ("Operation failed") block it.
- validation / format / arithmetic mismatch → retry-with-feedback.
- transient (timeout, rate limit) → retry locally, the value still exists.
- absent data / valid empty result → record "not present" and move on. Do NOT retry.
This mirrors structured MCP errors: an errorCategory and an isRetryable flag let your loop make an intelligent routing decision instead of blindly looping.
def handle(result):
if result.error_category in ("validation", "arithmetic"):
return "retry_with_feedback" # answer is recoverable
if result.error_category == "transient" and result.is_retryable:
return "retry_local" # network/rate-limit blip
if result.error_category == "absent":
return "record_not_present" # NEVER retry absent data
return "escalate"Cap the Loop, But Don't Lean on the Cap
Even for genuinely retryable format errors, bound the loop. A retry budget of 2–3 attempts is plenty — if a corrective retry hasn't converged by then, the problem usually isn't format; it's missing data or an over-strict schema.
Treat the cap as a safety net, never the primary stop mechanism. The real terminator is "validation passed." If you find yourself relying on the cap to exit, that's a signal you're retrying something retry can't fix.
def extract(client, doc, validate, max_attempts=3):
out = first_pass(client, doc)
for _ in range(max_attempts):
try:
return validate(out) # PRIMARY stop: it's valid
except ValidationError as e:
out = retry_with_feedback(client, doc, out, str(e))
# SAFETY NET only -- not the intended exit path
raise RuntimeError("unresolved after retries; likely absent data")When Even a Good Retry Won't Save You
Two more honest limits an architect must respect:
- A fresh, independent reviewer beats same-session self-review. An author retains its own reasoning and won't challenge itself — so for a true second opinion, validate with a fresh instance, not another turn of the same context.
- Blocking, time-sensitive checks belong inline. The Batch API is 50% cheaper but has no latency SLA and a window up to 24h — great for overnight audits, wrong for a pre-merge or real-time validation gate.
Retry tuning can't compensate for the wrong validation architecture underneath it.
Quick Check: Retry or Not?
A structured-extraction pipeline pulls fields from supplier invoices. The Pydantic validator rejects an output because the tax_id field is empty. On inspection, this particular invoice has no tax ID printed anywhere on it. What is the correct architecture?
Recap: Retry Is a Scalpel, Not a Hammer
Key takeaways:
- Retry fixes format, structural, and arithmetic errors — send the original doc + the wrong output + the exact validation error.
- Retry can't fix absent data. If it's not in the source, looping only wastes cost and invites fabrication.
- Never require a possibly-absent field — make it optional and let the model report "not present."
- Route by error type: validation/arithmetic → retry-with-feedback; transient → retry locally; absent → record and stop.
- Cap attempts (2–3) as a safety net; the primary stop is "validation passed."
- For true second opinions use a fresh instance; keep blocking checks inline, not on the Batch API.
Apprends Python avec un tuteur IA — gratuit
Écris et exécute du vrai code dans ton navigateur, obtiens de l'aide instantanée d'un tuteur IA disponible 24h/24, et reprends là où tu t'es arrêté sur le web ou dans l'app.
- Cours
- 26
- Leçons
- 104
Questions Fréquemment Posées
La leçon « Quand une nouvelle tentative est utile, et quand elle ne l’est pas » est-elle gratuite ?
Oui — le texte complet de « Quand une nouvelle tentative est utile, et quand elle ne l’est pas » est gratuit à lire ici sur le web. Pour la pratiquer de manière interactive (un éditeur de code intégré et un tuteur IA 24/7) et déverrouiller le reste du cours Claude Architect, passe à CoddyKit PRO. Le cours Claude Architect comprend 4 leçons au total.
Qu'est-ce que j'apprendrai dans « Quand une nouvelle tentative est utile, et quand elle ne l’est pas » ?
Très utile pour les erreurs de format, mais inutile lorsque des données manquent. Tu pratiques Claude Architect avec du code pratique que tu exécutes directement dans le navigateur, et un tuteur IA 24/7 répond à tes questions au fur et à mesure que tu avances dans la leçon.
Dois-je avoir de l'expérience pour commencer Claude Architect ?
Aucune expérience préalable n'est requise. Claude Architect sur CoddyKit est structuré pour les débutants jusqu'aux apprenants avancés, donc tu peux commencer ici ou depuis le début et avancer à ton rythme. Ceci est la leçon 1 sur 4.
Combien de temps prend la leçon « Quand une nouvelle tentative est utile, et quand elle ne l’est pas » ?
La plupart des leçons CoddyKit prennent environ 5–10 minutes. Chacune est courte et interactive, tu progresses régulièrement et tu repiques exactement où tu t'es arrêté sur le web et l'app.
Peux-tu écrire et exécuter du code dans cette leçon Claude Architect ?
Oui. Chaque leçon Claude Architect inclut un éditeur de code intégré, tu écris et exécutes du vrai code directement dans ton navigateur et tu reçois des retours IA instantanés — aucune configuration locale requise.
Toutes les leçons de ce cours
- Quand une nouvelle tentative est utile, et quand elle ne l’est pas
- Requêtes de nouvelle tentative avec retour d’information
- Auto-correction
- Plusieurs passes et revue indépendante