Reflection: stesura, critica, revisione
Una prima passata produce una bozza, una passata avversariale separata la sottopone a critica sulla base di una rubric esplicita e una terza la riscrive utilizzando quella critica. Il critic è una chiamata distinta, con istruzioni proprie, perché un modello a cui viene chiesto di verificare il proprio lavoro all'interno di una singola completion tende ad assecondare se stesso.
Fatti chiave
- Livello
- Base
- Runtime
- Python • API OpenAI
- Pattern
- Flusso ispezionabile con confini di sistema visibili
- Interazione
- Sandbox live • Script
- Aggiornato
- 28 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à
La critica è un artefatto tipizzato, non un passaggio nascosto: failing_rules e what_to_change vengono esposti a ogni iterazione, così chi legge può capire perché una bozza è stata rifiutata e valutare se il critic avesse ragione. Un loop di Reflection che stampa solo il testo finale ha scartato proprio la parte che vale la pena ispezionare.
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
Ogni iterazione richiede due o tre chiamate al modello, quindi un loop di tre iterazioni può proseguire a lungo dietro un'unica chiamata silenziosa. Correzione applicata qui: emettere il numero e la fase dell'iterazione prima di ogni chiamata, così è possibile distinguere un loop ancora in esecuzione da uno che si è bloccato.
reflection.py:runP10 · Ottimizzare per la guida, non solo per l'inizio
La rubric viene fissata all'inizio e il loop prosegue fino all'accettazione o all'esaurimento. Un operatore che osserva un'iterazione fallire per una ragione con cui non concorda non può modificare la rubric né accettare la bozza così com'è senza riavviare il processo. Correzione: verificare tra un'iterazione e l'altra la presenza di una rubric modificata o di un override dell'operatore, e trattare l'esaurimento come un punto decisionale anziché come una restituzione.
reflection.py:run
Lo decide la tua implementazione, non il pattern. Nessun verdetto.
P5 · Sostituire la magia implicita con modelli mentali chiari
Stabilire se chi legge debba capire che la risposta è stata revisionata, e quante volte, è una decisione del wrapper. Questa implementazione mostra l'intero percorso; una versione che restituisce solo il testo accettato presenta una risposta revisionata come se fosse una prima bozza. Il pattern non decide quale delle due versioni si ottiene.
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 →