All posts
Fundamentals

Reflection, teaching an agent to check its own work

A plan sets direction. Reflection checks whether the agent is still on it.

Reflection is the agent pausing to ask "how is this going, and is the plan still right?" Like planning, it is a tool, and its real worth shows up in the one situation a reactive agent handles worst: failure.

When an agent should pause

People pause at natural moments in work, and an agent should pause at the same ones.

After a meaningful step. A stage is done; take a beat to confirm it actually is before moving on.

When a tool fails. A search returns nothing, an API errors. This is the moment that matters most, and the one a reactive agent skips.

When synthesising several sources. A lot of material has come in, some of it conflicting. Before combining it, stop and reconcile.

Reflection is a tool, too

The reflection tool is deliberately simple: a free-form assessment, plus one flag.

@tool
def reflect(assessment: str, need_replan: bool = False) -> str:
    """Pause to assess progress. Set need_replan if the current plan no longer fits."""
    context.state["last_reflection"] = assessment
    return "Replan advised." if need_replan else "Continue."

The assessment is free text because reflection takes a different form each time, whatever the situation calls for. "Both sources disagree on the distance; the question wants closest approach, so I will use the perigee" is a reflection. So is "the search failed twice; the tool may be down".

The need_replan flag is the important half. It is a signal to the planning tool: the current plan no longer fits, so it should be rewritten. That one boolean is how reflection and planning talk to each other, and the next post builds on it.

The real value: recovering from failure

Reflection earns its keep when something breaks. To see it, give an agent a tool that always fails.

@tool
def wikipedia(query: str) -> str:
    """Look something up on Wikipedia."""
    raise ConnectionError("Wikipedia API unavailable")   # always fails, on purpose

Run a reactive agent against a task that needs it, and watch the failure mode from the first post in this chapter: it calls wikipedia, gets an error, calls wikipedia again, gets the same error, and keeps going until it hits the step limit. It never asks why.

Now add reflection. The pattern changes.

Action:  wikipedia("Earth-Moon distance")
Observe: ERROR: Wikipedia API unavailable

Action:  reflect("Wikipedia has failed twice with the same error. The tool is likely down.
                  I should stop retrying it and use web search for this instead.",
                  need_replan=True)
Observe: Replan advised.

Action:  write_plan([..., "Find the Earth-Moon distance via web search instead", ...])
Action:  search_web("Earth to Moon closest approach distance km")
Observe: about 356,500 km

The agent stopped repeating the failing call, diagnosed the cause, and changed its approach. That is the whole difference. Failure recovery comes from a moment built into the loop, where the agent is made to ask why something went wrong before trying again, rather than from a bigger model or a cleverer tool.

Without reflection, a failed tool is retried to the step limit. With it, the agent diagnoses and reroutes.Without reflection, a failed tool is retried to the step limit. With it, the agent diagnoses and reroutes.

Reflection catches the quiet failures too

The loud failures, an error, an empty result, are easy to see. Reflection also catches the quiet ones, which are the more dangerous.

The early-victory failure is quiet: the agent has two of the three facts it needs and decides that is enough. A reactive agent answers and stops. A reflecting agent, prompted to check against its plan before answering, notices task three is still pending and goes back for it.

Action:  reflect("I have the marathon record and the pace. The plan still lists the Moon
                  distance as pending. I do not have the answer yet.")
Observe: Continue.

That reflection is the guard against confidently answering from partial information, the failure the calibration post warned about. The plan gives it something concrete to check against: not "do I feel done?" but "is every task marked done?"

What to take from this

  • Reflection is a tool: a free-form assessment plus a need_replan flag that tells the planning tool the current plan no longer fits.
  • Its real value is failure recovery. A reflecting agent diagnoses why a tool failed and changes approach, instead of repeating the same call to the step limit.
  • It also catches the quiet failures, like answering from partial information, by giving the agent its plan to check against before it declares victory.

Planning sets direction and reflection checks it, and the need_replan flag already hints at how they connect. The next post puts them together into one loop.