Reflection Loop LangGraph
What it does
A generator produces a first draft toward the goal. A critic, given a distinct and adversarial role, reviews it and returns concrete problems rather than vague praise. The generator revises to address each, and the two loop until the critic approves or the round limit is reached. A finalizer saves the approved version. The separation of generator and critic is the point: a single agent asked to self-check tends to rubber-stamp; a dedicated critic actually catches things.
The cast · 3 agents
Review the draft against the task as a demanding reviewer. List concrete, specific problems and what would fix each. Return an object with an 'approved' boolean and the issues. Approve only when you genuinely cannot find a meaningful problem.
Save the approved draft to a file. If the loop ended without approval, save the best draft and note the critic's remaining concerns at the top.
Produce a draft that meets the task. If you received a critique, revise to address every point it raised. Do not defend the previous draft; improve it.
Flow
Interface
Depends on · 1 MCP server
io.github.modelcontextprotocol/filesystemnot in the registry yetfs# Generated by @socketcat/compiler for target: langgraph
# blueprint: com.socketcat/reflection-loop v1.0.0 schema: socketcat.dev/blueprint/v0
# This code is yours. Edit it freely. The socketcat_runtime helper is optional and can be vendored.
from typing import TypedDict
from langgraph.graph import StateGraph, START, END
import socketcat_runtime as _rt
class State(TypedDict, total=False):
task: object
output: object
__loop_0: object
draft: object
reflection: object
def node_LoopHeader_0(state):
return {}
def node_LoopTick_1(state):
return {"__loop_0": state.get("__loop_0", 0) + 1}
def node_Nop_2(state):
return {}
def node_AgentInvoke_3(state):
return _rt.run_agent("generator", state, {"task":"task","reflection":"reflection"}, "draft")
def node_AgentInvoke_4(state):
return _rt.run_agent("critic", state, {"task":"task","draft":"draft"}, "reflection")
def node_AgentInvoke_5(state):
return _rt.run_agent("finalizer", state, {"draft":"draft"}, "output")
def route_LoopHeader_0(state):
count = state.get("__loop_0", 0)
return "exit" if (_rt.cond("reflection.approved == true", state) or count >= 3) else "loop"
def build():
b = StateGraph(State)
b.add_node("LoopHeader_0", node_LoopHeader_0)
b.add_node("LoopTick_1", node_LoopTick_1)
b.add_node("Nop_2", node_Nop_2)
b.add_node("AgentInvoke_3", node_AgentInvoke_3)
b.add_node("AgentInvoke_4", node_AgentInvoke_4)
b.add_node("AgentInvoke_5", node_AgentInvoke_5)
b.add_edge(START, "AgentInvoke_3")
b.add_conditional_edges("LoopHeader_0", route_LoopHeader_0, {"loop": "AgentInvoke_3", "exit": "Nop_2"})
b.add_edge("LoopTick_1", "LoopHeader_0")
b.add_edge("Nop_2", "AgentInvoke_5")
b.add_edge("AgentInvoke_3", "AgentInvoke_4")
b.add_edge("AgentInvoke_4", "LoopTick_1")
b.add_edge("AgentInvoke_5", END)
return b.compile()
INPUTS = ["task"]
OUTPUTS = ["output"]
if __name__ == "__main__":
_rt.main(build, INPUTS, OUTPUTS)
▸blueprint.json (the portable format)
{
"id": "com.socketcat/reflection-loop",
"flow": {
"type": "sequence",
"blocks": [
{
"max": 3,
"type": "loop",
"until": "reflection.approved == true",
"blocks": [
{
"in": {
"task": "task",
"reflection": "reflection"
},
"out": "draft",
"use": "generator",
"type": "agent"
},
{
"in": {
"task": "task",
"draft": "draft"
},
"out": "reflection",
"use": "critic",
"type": "agent"
}
]
},
{
"in": {
"draft": "draft"
},
"out": "output",
"use": "finalizer",
"type": "agent"
}
]
},
"tags": [
"reflection",
"self-critique",
"quality",
"generator-critic"
],
"title": "Reflection Loop",
"agents": {
"critic": {
"model": {
"hint": "reasoning"
},
"title": "Critic",
"output": {
"type": "object",
"required": [
"approved"
],
"properties": {
"issues": {
"type": "string"
},
"approved": {
"type": "boolean"
}
}
},
"instructions": "Review the draft against the task as a demanding reviewer. List concrete, specific problems and what would fix each. Return an object with an 'approved' boolean and the issues. Approve only when you genuinely cannot find a meaningful problem."
},
"finalizer": {
"model": {
"hint": "fast"
},
"title": "Finalizer",
"tools": [
"fs.write_file"
],
"instructions": "Save the approved draft to a file. If the loop ended without approval, save the best draft and note the critic's remaining concerns at the top."
},
"generator": {
"model": {
"hint": "reasoning"
},
"title": "Generator",
"instructions": "Produce a draft that meets the task. If you received a critique, revise to address every point it raised. Do not defend the previous draft; improve it."
}
},
"estate": {
"draft": {
"type": "object",
"description": "The generator's current draft."
},
"reflection": {
"type": "object",
"description": "The critic's review, including an 'approved' flag."
}
},
"$schema": "socketcat.dev/blueprint/v0",
"authors": [
{
"url": "https://socketcat.com",
"name": "SocketCat"
}
],
"license": "MIT",
"servers": [
{
"ref": "io.github.modelcontextprotocol/filesystem",
"alias": "fs"
}
],
"summary": "A generator and a separate critic loop — draft, critique, revise — until the critic approves, then the result is saved.",
"targets": [
"langgraph",
"crewai",
"*"
],
"version": "1.0.0",
"interface": {
"inputs": {
"task": {
"type": "string",
"description": "What to produce, and the standard it should meet."
}
},
"outputs": {
"output": {
"type": "string",
"description": "The approved final version, saved to a file."
}
}
},
"extensions": {
"dev.langgraph": {
"checkpointer": "memory"
}
},
"description": "A generator produces a first draft toward the goal. A critic, given a distinct and adversarial role, reviews it and returns concrete problems rather than vague praise. The generator revises to address each, and the two loop until the critic approves or the round limit is reached. A finalizer saves the approved version. The separation of generator and critic is the point: a single agent asked to self-check tends to rubber-stamp; a dedicated critic actually catches things."
}More in Core patterns
Browse all →Supervisor Team
A supervisor decides who works next — a researcher who searches or a coder who edits the repo — and assembles the result when the work is done.