Skip to content
AI360Xpert
Gen AI

Reflection and Self-Correction

Instead of accepting the LLM's first answer, you force the LLM to pause, read its own work, actively look for mistakes, and rewrite the answer if it finds an error.

In Reflection, an Actor generates a draft, a Critic reviews the draft against strict rules, and if the Critic finds errors, the Actor must rewrite the draft until it passes.
In Reflection, an Actor generates a draft, a Critic reviews the draft against strict rules, and if the Critic finds errors, the Actor must rewrite the draft until it passes.

Why Does This Exist?

LLMs suffer from a psychological flaw similar to humans: they are overconfident in their first drafts.

If you ask an LLM to "Write a Python script to sort a list, but do not use the built-in sort() function," the LLM will eagerly write the code. Sometimes, it will accidentally use the sort() function because that is the most statistically probable way to sort a list in its training data.

If you just return that answer to the user, you have failed the prompt constraint.

However, research shows that if you take that exact same LLM, show it the code it just wrote, and ask: "Did this code use the built-in sort() function?", the LLM will instantly realize its mistake and say "Yes, my apologies, that violates the rule."

Reflection is the architectural pattern of weaponizing this capability. You explicitly insert a "Critic" loop into your application that grades the LLM's output before the user ever sees it, forcing the LLM to self-correct its own hallucinations.

Think of It Like This

The Writer and the Editor

No Reflection: You are a journalist. You write a 500-word article as fast as possible and instantly hit "Publish" on the front page of the New York Times. There are typos everywhere.

Reflection: You write the 500-word article (The Actor). Before publishing, you hand it to your Editor (The Critic). The Editor circles three typos in red ink and hands it back. You fix the typos and hand it back to the Editor. Only when the Editor says "Perfect" does the article get published.

How It Actually Works

Reflection is typically implemented using two separate system prompts, often represented as two distinct Sub-Agents.

  1. The Actor (Generator): Instructed to generate the answer.
  2. The Critic (Evaluator): Instructed to evaluate the Actor's answer against a strict rubric.

The Loop

  1. User requests a task.
  2. Actor generates Draft_1.
  3. Critic reads Draft_1. It outputs a critique: "Rule 3 was violated. You forgot to include the source links."
  4. The orchestration code catches this critique. It appends the critique to the Actor's message history and says: "Please fix these issues."
  5. Actor generates Draft_2.
  6. Critic reads Draft_2 and says: "Looks good."
  7. The orchestration code finally returns Draft_2 to the user.

Show Me the Code

This code demonstrates a simple while-loop implementing the Actor-Critic reflection pattern.

import openai
def call_actor(task, critiques=None):    prompt = f"Task: {task}\n"    if critiques:        prompt += f"Previous Draft Failed. Critic Feedback: {critiques}\nPlease rewrite."            response = openai.chat.completions.create(        model="gpt-4o",        messages=[{"role": "system", "content": "You are a writer."}, {"role": "user", "content": prompt}]    )    return response.choices[0].message.content
def call_critic(task, draft):    prompt = f"""    Task was: {task}    Draft: {draft}        Evaluate the draft. Does it perfectly satisfy the task?     If YES, output ONLY the word "PASS".    If NO, output a list of required fixes.    """        response = openai.chat.completions.create(        model="gpt-4o",        messages=[{"role": "system", "content": "You are a harsh critic."}, {"role": "user", "content": prompt}]    )    return response.choices[0].message.content
def reflection_loop(user_task, max_attempts=3):    print(f"Goal: {user_task}\n")        draft = None    critiques = None        for attempt in range(max_attempts):        print(f"--- Attempt {attempt + 1} ---")                # 1. Generate Draft        draft = call_actor(user_task, critiques)        print(f"Actor generated {len(draft.split())} words.")                # 2. Evaluate Draft        critique_result = call_critic(user_task, draft)                if critique_result.strip() == "PASS":            print("Critic: PASS. Returning final result.")            return draft        else:            print(f"Critic: FAILED. Feedback: {critique_result[:50]}...")            critiques = critique_result # Feed this back into the next loop                print("\nMax attempts reached. Returning best effort.")    return draft
# --- Execution ---task = "Write a 3 sentence poem about a cat. Do NOT use the letter 'e'."final_poem = reflection_loop(task)print(f"\nFinal Output:\n{final_poem}")
# -> Goal: Write a 3 sentence poem about a cat. Do NOT use the letter 'e'.# -> # -> --- Attempt 1 ---# -> Actor generated 20 words.# -> Critic: FAILED. Feedback: The draft contains the letter 'e' in the words "t...# -> --- Attempt 2 ---# -> Actor generated 18 words.# -> Critic: PASS. Returning final result.# -> # -> Final Output:# -> A sly cat walks on soft grass. # -> It drops down to catch a bug. # -> A good jump!

Watch Out For

Infinite Loops of Agreement

Sometimes, the Actor will rewrite the draft, but fundamentally misunderstand the Critic's instructions. The Critic will re-read the draft, output the exact same critique, and the Actor will output the exact same flawed draft. The system will loop infinitely, burning thousands of tokens and stalling your application. You must implement a max_attempts counter (usually 3 or 4) to forcibly break the loop and return the best-effort draft to the user, or escalate to a human.

The Quick Version

  • LLMs are prone to missing constraints on their first try, but are incredibly good at finding mistakes when asked to grade their own work.
  • Reflection is an architectural pattern that puts a "Critic" loop between the Generator and the End User.
  • If the Critic finds a mistake, it sends the draft back to the Generator for a rewrite.
  • This dramatically increases the quality and reliability of complex LLM tasks at the cost of higher latency and API spend.
  • Read Multi-Agent Systems to see how the Actor and Critic can be scaled into large teams of specialized reviewers.
  • Read Automatic Prompt Optimization to see how you can use Critic feedback to permanently fix the System Prompt instead of fixing individual drafts.

Related concepts