Reflexion
Reflexion retries a task and learns from its failures. After each attempt an evaluator scores the output; if the score is below the threshold, a reflector writes a one-sentence “lesson” (written advice) that the agent reads on its next attempt, and can keep for future runs. Use it for tasks with a checkable notion of success where repeated mistakes are the problem. From Shinn et al. 2023, Reflexion: Language Agents with Verbal Reinforcement Learning.
The reflector sees the attempt’s actions and observations (its tool
calls and their results) as well as its output. An attempt that runs
out of turns (the base’s max_turns) counts as a failed trial: it
scores 0, and Reflexion reflects and retries while attempts remain.
(Before 0.17 it ended the run.) A budget or guardrail stop still ends
it.
Lesson storage modes
Two storage modes for the persisted lessons:
- Monotonic block (the default). Every lesson is appended to
memory.<lessons_block_name>and shown to the agent on every subsequent attempt. Simple, but every recent lesson is shown whether or not it applies to the task at hand. - Selective recall (recommended). Pass
lesson_store=aVectorStore. Lessons are stored as embedded chunks; before each attempt, only the top-k most relevant lessons for the current task are retrieved and surfaced. Avoids context bloat and keeps tutorial advice scoped to where it applies.
Lessons quote the failing prompt, so they stay with their user: each
lesson is tagged with the run’s user_id, and a run recalls only its
own user’s lessons plus any tagged user_id="*"
(SHARED_LESSON in loomflow.architecture.reflexion), which is how
to seed curated lessons for everyone. Untagged lessons written by
releases before 0.17 are no longer recalled. In the monotonic-block
mode, lessons go to the run’s own memory block, one per line, and the
block keeps the last max_lessons (default 10).
Usage
from loomflow import Agent, HashEmbedder
from loomflow.architecture import Reflexion
from loomflow.vectorstore import InMemoryVectorStore
agent = Agent(
"Solve the puzzle.",
model="claude-opus-4-7",
architecture=Reflexion(
max_attempts=3,
threshold=0.85,
lesson_store=InMemoryVectorStore(embedder=HashEmbedder()),
),
)
result = await agent.run("...")For cross-session learning, swap InMemoryVectorStore for
PostgresVectorStore and the lessons persist across processes.
Wrapping any base architecture
Reflexion(base=...) defaults to ReAct but accepts any architecture.
The killer combination is Reflexion of Supervisor. The team
learns across attempts which worker handles which intent best:
from loomflow import Agent
from loomflow.architecture import Reflexion, Supervisor
agent = Agent(
"...",
model="claude-opus-4-7",
architecture=Reflexion(
base=Supervisor(workers={"researcher": ..., "writer": ...}),
max_attempts=3,
threshold=0.85,
lesson_store=InMemoryVectorStore(embedder=HashEmbedder()),
),
)When Reflexion pays off
- Tasks with a clear evaluator signal (test pass/fail, JSON schema match, factual correctness against ground truth).
- Repeated runs of the same problem class. Lessons compound.
- Worth roughly 1.5–3× ReAct cost; pays back when the alternative is shipping wrong answers to users.
Without an evaluator, Reflexion does nothing useful. The lesson
generator only fires when evaluator(output) < threshold. Pass an
evaluator that returns a meaningful score for your domain (test
runner, regex match, structured-output validity, etc.).