Reflection: draft, critique, revise
One pass drafts, a separate adversarial pass critiques it against a stated rubric, and a third rewrites using that critique. The critic is a distinct call with its own instruction, because a model asked to check its own work inside one completion tends to agree with itself.
Fatti chiave
- Livello
- beginner
- Runtime
- Python • API OpenAI
- Pattern
- Flusso ispezionabile con confini di sistema visibili
- Interazione
- Sandbox live • Script
- Aggiornato
- 27 luglio 2026
Naviga questo esempio
Pattern
Relazioni con la doctrineQuali principi questo pattern esprime nel codice, e quali la sua forma predefinita infrange.Libreria
Sfoglia gli esempiRiapri la libreria completa per confrontare pattern vicini e percorsi collegati.Interazione
Esegui ora nel sandboxProva l'interazione direttamente nella superficie guidata di questo esempio.Sorgente
Apri codice completoLeggi l'implementazione reale, i punti evidenziati e i requisiti runtime.MCP
Chiama via MCPUsa la stessa risorsa dentro agenti, export deterministici e setup MCP.
Principi collegati
Relazioni con la doctrine
Queste sono forme di ragionamento: come un agente decide, quando agisce, cosa ricorda. Per comporre l'interfaccia intorno a esso, vedi l'AI Pattern Blueprint. Per trigger, pianificazione, caricamento del contesto e ripristino, vedi Agent runtime architecture.
Questo pattern esprime questi principi direttamente nel codice.
P7 · Stabilire fiducia attraverso l'ispezionabilità
The critique is a typed artifact rather than a hidden step: failing_rules and what_to_change are surfaced on every round, so a reader can see why a draft was rejected and judge whether the critic was right. A reflection loop that only prints the final text has thrown away the part worth inspecting.
reflection.py:Critique
La forma classica di questo pattern li viola per impostazione predefinita. Ognuno riporta la correzione.
P2 · Assicurarsi che il lavoro in background rimanga percepibile
Each round is two or three model calls, so a three-round loop can run for a long time behind a single silent call. Fix, applied here: emit the round number and phase before each call, so a loop that is still working is distinguishable from one that has stalled.
reflection.py:runP10 · Ottimizzare per la guida, non solo per l'inizio
The rubric is fixed at the start and the loop runs to acceptance or exhaustion. An operator watching a round fail for a reason they disagree with cannot amend the rubric or accept the draft as-is without restarting. Fix: check for an amended rubric or an operator override between rounds, and treat exhaustion as a decision point rather than a return.
reflection.py:run
Lo decide la tua implementazione, non il pattern. Nessun verdetto.
P5 · Sostituire la magia implicita con modelli mentali chiari
Whether the reader understands that the answer was revised, and how many times, is a wrapper decision. This implementation prints the arc; a version that returns only the accepted text presents a revised answer as if it were a first draft. The pattern does not decide which you get.
reflection.py:run
Le relazioni con la doctrine sono analisi di prima parte, non il risultato di una validazione. Esegui il validatore sul tuo codice per ottenere un verdetto con punteggio.
reflection.py
"""
Reflection: draft, critique, revise
The cheapest reliability win available. One model drafts, a second pass
critiques that draft against a stated rubric, and a third rewrites using the
critique. Loop until the score clears the bar or the budget runs out.
Draft -> Critique (scored) -> Revise -> ... -> Accept or exhaust
The critic is a separate call with a separate instruction, not the same call
asked to "check your work". A model grading its own output inside one
completion tends to agree with itself.
Two things worth knowing before you reach for this:
- It costs you a multiple of the latency and tokens of a single call. Spend
it where quality matters more than speed, not everywhere.
- Scores from a model cluster hard around the middle of whatever range you
give it. A 1 to 10 scale mostly returns 6, 7 and 8. Ask for specific
boolean checks instead when you need a real gate.
Run: python reflection.py
"""
from dotenv import load_dotenv
from openai import OpenAI
from pydantic import BaseModel, Field
load_dotenv()
client = OpenAI()
MODEL = "gpt-5.4-mini"
MAX_ROUNDS = 3
#: What "good" means here, stated once so the critic and the reviser cannot
#: drift apart. Editing this changes the gate for both.
RUBRIC = """
1. Every claim is supported by something in the brief. No invented specifics.
2. States what is NOT covered, rather than implying full coverage.
3. Concrete nouns and verbs. No filler adjectives.
4. Under 120 words.
""".strip()
class Critique(BaseModel):
"""Boolean checks, not a 1-to-10 score.
A model asked for a score returns the middle of the range almost
regardless of quality. Specific checks force it to commit to something
falsifiable, and they give the reviser an actionable list.
"""
passes_rubric: bool = Field(description="True only if every numbered rule holds.")
failing_rules: list[int] = Field(
description="Rule numbers that fail. Empty when passes_rubric is true."
)
what_to_change: str = Field(
description="Concrete instruction for the rewrite. Empty if nothing to change."
)
def draft(brief: str) -> str:
completion = client.chat.completions.create(
model=MODEL,
messages=[
{
"role": "system",
"content": "Write a short internal summary for the given brief.",
},
{"role": "user", "content": brief},
],
)
return completion.choices[0].message.content or ""
def critique(brief: str, text: str) -> Critique:
"""A separate call with an adversarial instruction, not self-review."""
completion = client.beta.chat.completions.parse(
model=MODEL,
messages=[
{
"role": "system",
"content": (
"You are reviewing someone else's draft against a rubric. "
"You did not write it and you gain nothing by approving it. "
"Mark a rule as failing whenever you are unsure it holds.\n\n"
"Rubric:\n" + RUBRIC
),
},
{"role": "user", "content": f"Brief:\n{brief}\n\nDraft:\n{text}"},
],
response_format=Critique,
)
return completion.choices[0].message.parsed
def revise(brief: str, text: str, verdict: Critique) -> str:
completion = client.chat.completions.create(
model=MODEL,
messages=[
{
"role": "system",
"content": (
"Rewrite the draft to fix exactly the listed problems. Change "
"nothing else. Do not add new claims.\n\nRubric:\n" + RUBRIC
),
},
{
"role": "user",
"content": (
f"Brief:\n{brief}\n\nDraft:\n{text}\n\n"
f"Failing rules: {verdict.failing_rules}\n"
f"Required change: {verdict.what_to_change}"
),
},
],
)
return completion.choices[0].message.content or ""
def run(brief: str) -> str:
print(" drafting...")
text = draft(brief)
print(f" draft: {text[:90]}...")
for round_number in range(1, MAX_ROUNDS + 1):
print(f" [{round_number}/{MAX_ROUNDS}] critiquing...")
verdict = critique(brief, text)
if verdict.passes_rubric:
print(f" ACCEPTED after {round_number} round(s)")
print(f" final: {text}")
return text
print(f" failing rules: {verdict.failing_rules}")
print(f" change : {verdict.what_to_change}")
text = revise(brief, text, verdict)
print(f" revised: {text[:90]}...")
# Budget exhausted is a real outcome. Return the best text with the
# honest caveat rather than presenting it as if it passed.
print(f" NOT ACCEPTED after {MAX_ROUNDS} rounds, returning last revision")
return text
if __name__ == "__main__":
brief = (
"Our nightly sync job failed twice this week. Both times it was the "
"same downstream timeout. We have not yet found why the timeout "
"started happening. Write the update for the team channel."
)
print(f"\n> {brief[:70]}...")
run(brief)
Principi correlati
- P2visibilityAssicurarsi che il lavoro in background rimanga percepibileQuando il sistema opera in modo asincrono o al di fuori del focus immediato dell'utente, dovrebbe fornire segnali persistenti e proporzionati che il lavoro sta continuando.Apri il principio →
- P5delegationSostituire la magia implicita con modelli mentali chiariIl prodotto dovrebbe aiutare gli utenti a comprendere cosa il sistema può fare, cosa sta facendo attualmente, cosa non può fare e quali condizioni governano il suo comportamento.Apri il principio →
- P7trustStabilire fiducia attraverso l'ispezionabilitàGli utenti dovrebbero essere in grado di esaminare come è stato prodotto un risultato quando la fiducia, la responsabilità o la qualità della decisione sono importanti.Apri il principio →
- P10delegationOttimizzare per la guida, non solo per l'inizioIl sistema dovrebbe supportare gli utenti non solo nell'avvio dei compiti, ma anche nella guida, nel perfezionamento, nella riprioritizzazione e nella correzione del lavoro mentre è in corso.Apri il principio →