When to reach for it
- Result quality takes precedence over minimal latency.
- Errors can be identified by the model through targeted self-critique.
/pattern/reflexion/
The agent critically evaluates its own intermediate results and uses this feedback to improve the next steps or output in iterative loops.
When to reach for it
When it backfires
The tradeoff
Leads to higher result quality, but incurs additional token costs and can lead to false confidence.
Act, evaluate, reflect, retry. A self-critique loop bounded by a retry budget.
from typing import TypedDict
from langgraph.graph import StateGraph, END
class State(TypedDict):
prompt: str
attempt: str | None
evaluation: str | None
reflection: str | None
retries: int
max_retries: int
def attempt(state):
return {"attempt": actor_llm(state["prompt"])}
def evaluate(state):
return {"evaluation": evaluator_llm(state["attempt"])}
def reflect(state):
return {"reflection": reflector_llm(state["evaluation"]), "retries": state["retries"] + 1}
def should_continue(state):
if "PASS" in state["evaluation"]:
return "end"
if state["retries"] >= state["max_retries"]:
return "end"
return "retry"
g = StateGraph(State)
g.add_node("attempt", attempt)
g.add_node("evaluate", evaluate)
g.add_node("reflect", reflect)
g.add_edge("attempt", "evaluate")
g.add_conditional_edges("evaluate", should_continue, {
"retry": "reflect",
"end": END,
})
g.add_edge("reflect", "attempt")
g.set_entry_point("attempt")
# Initial state includes budget
reflexion = g.compile()The loop retries forever on a task that is fundamentally impossible (e.g., querying a non-existent table).
Fix · Hard cap on retries (max_retries). Treat budget exhaustion as an expected outcome, not an exception.
The reflection is too vague to change the actor's next attempt. The loop converges to the same wrong answer.
Fix · Require the reflector to cite specific errors and propose a concrete change. If it can't, route to human escalation.
Search patterns, frameworks, and pages.