Vai al contenuto principaleVai al footer
PatternscriptbeginnerEseguibileguided-flow

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

Vista rapida del flusso

Come questo esempio si muove tra input, esecuzione e risultato rivedibile

Reflection: draft, critique, revise -> Load environment keys -> Initialize OpenAI client -> Send chat request -> Constrain output schema -> Send message payload

Trigger

Reflection: draft, critique, revise

Runtime

Load environment keys

Esito

Initialize OpenAI client

Perché esiste questa pagina

Questo esempio è mostrato sia come codice sorgente reale che come pattern di interazione orientato al prodotto, così i discenti possono collegare implementazione, UX e doctrine senza lasciare la libreria.

Flusso visivoCodice realeSandbox o walkthroughAccesso MCP

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.

Strutturale

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

Gap predefinito

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:run

  • P10 · 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

Dipende dall'implementazione

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.

Come dovrebbe essere usato questo esempio nella piattaforma?

Usa prima la sandbox per comprendere il pattern di esperienza, poi ispeziona il sorgente per vedere come il confine del prodotto, il confine del modello e il confine della doctrine sono effettivamente implementati.

UX pattern: Flusso ispezionabile con confini di sistema visibili
Assicurarsi che il lavoro in background rimanga percepibile
Sostituire la magia implicita con modelli mentali chiari
Stabilire fiducia attraverso l'ispezionabilità

Riferimenti sorgente

Voce di libreria
pattern-reflection
Percorso sorgente
content/patterns/reasoning-reflection/reflection/reflection.py
Librerie
openai, pydantic, python-dotenv
Requisiti di runtime
OPENAI_API_KEY
Principi correlati
Assicurarsi che il lavoro in background rimanga percepibile, Sostituire la magia implicita con modelli mentali chiari, Stabilire fiducia attraverso l'ispezionabilità, Ottimizzare per la guida, non solo per l'inizio

reflection.py

python
"""
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)

Cosa dovrebbe ispezionare il discente nel codice?

Cerca il punto esatto in cui lo scope del sistema è delimitato: definizioni di schema, impostazione del prompt, configurazione di runtime e il punto di chiamata che trasforma l'intenzione dell'utente in un'azione concreta del modello o del workflow.

Cerca i contratti di output e la validazione
Cerca la chiamata di esecuzione esatta
Cerca cosa il prodotto potrebbe esporre all'utente

Come si relaziona la sandbox al sorgente?

La sandbox dovrebbe rendere leggibile l'UX: cosa vede l'utente, cosa sta decidendo il sistema e come il risultato diventa revisionabile. Il sorgente mostra poi come quel comportamento è effettivamente implementato.

Leggi il riepilogo dell'implementazione.
Esplora gli stati utente e di sistema.
Ispeziona il codice sorgente tenendo a mente le decisioni di doctrine evidenziate.
SandboxFlusso ispezionabile con confini di sistema visibili

Guida all'interazione

Usa la sandbox per esplorare l'esperienza visibile all'utente, il lavoro del sistema e la scelta di doctrine che l'esempio sta facendo.

Spiegazione UX

La sandbox spiega cosa dovrebbe vedere l'utente, cosa sta facendo il sistema e dove il controllo o l'ispezionabilità devono rimanere espliciti.

Spiegazione AI Design

La pagina trasforma il codice sorgente in un pattern orientato al prodotto: cosa può decidere il modello, cosa dovrebbe esporre il prodotto e dove il codice deterministico o la revisione devono subentrare.

Guida all'interazione

  1. 1Leggi il riepilogo dell'implementazione.
  2. 2Esplora gli stati utente e di sistema.
  3. 3Ispeziona il codice sorgente tenendo a mente le decisioni di doctrine evidenziate.

Visibile all'utente

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.

Lavoro del sistema

Il prodotto prepara un task delimitato per il modello o il workflow.

Perché è importante

L'interfaccia dovrebbe rendere il task delegato leggibile prima che avvenga l'automazione.

Usato in corsi e percorsi

Questo esempio attualmente è indipendente nella libreria, ma si connette comunque al sistema dei principi e alla famiglia di esempi più ampia.

Principi correlati

Runtime architecture

Usa questo esempio nei tuoi agenti

Questo esempio è disponibile anche tramite il layer agent-ready del blueprint. Usa la pagina Per agenti per recuperare MCP pubblico, export deterministici e setup per Claude o Cursor.

Definisci trigger, contesto e confini prima di aumentare l'autonomia
Rendi espliciti controllo, osservabilita e recovery nel runtime
Scegli i pattern operativi giusti prima di delegare ai workflow