Vai al contenuto principaleVai al footer
PatternscriptBaseEseguibileguided-flow

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

Vista rapida del flusso

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

Reflection: stesura, critica, revisione -> Load environment keys -> Initialize OpenAI client -> Send chat request -> Constrain output schema -> Send message payload

Trigger

Reflection: stesura, critica, revisione

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à

    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

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

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

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

Dipende dall'implementazione

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.

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

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.

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