executing-plans
executing-plans
Use when executing an implementation plan in the current session as the implementer yourself — your human partner chose inline execution, or no subagent tool is available
reflection-loopImplements self-correction feedback loops (execution → evaluation/critique → refinement) to iteratively improve agent outputs through producer-critic collaboration and automated quality gates.
Independent GitHub snapshot · Automated classification · Original documentation governs
Implements self-correction feedback loops where an agent evaluates its own output or a separate critic agent reviews it against criteria, then refines the result iteratively until quality thresholds are met or maximum iterations are reached. This skill makes the model construct Producer-Critic collaboration architectures that catch errors, improve accuracy, and produce higher-quality outputs than single-pass generation.
Use this skill when:
Avoid this skill for:
Initialize the producer prompt and evaluation criteria — Define the original task for the Producer agent and establish explicit quality criteria the Critic will use. Criteria must cover: factual accuracy, adherence to requirements, code correctness (if applicable), style/tone, and completeness. Checkpoint: Verify that evaluation criteria are specific, measurable, and cover all non-negotiable requirements from the original task before proceeding.
Execute — Producer generates initial output — Invoke the Producer agent with the task prompt. On first iteration, this produces raw content; on subsequent iterations, the Producer receives both its prior output and the Critic's feedback to refine. Maintain a running message history that includes: the original task, every produced version, and every critique. Checkpoint: Confirm the Producer produced syntactically valid output (for code: no syntax errors from the parser; for text: non-empty content). If invalid, halt and report the error rather than feeding broken output into critique.
Evaluate — Critic reviews against criteria — Invoke the Critic agent with a separate system prompt that establishes a distinct persona (e.g., "senior software engineer," "meticulous fact-checker"). Feed the Critic both the original task and the current Producer output. Require structured feedback: a list of specific issues, severity levels (critical/major/minor), and actionable improvement suggestions. The Critic must also be able to signal completion with a sentinel phrase (e.g., CODE_IS_PERFECT, APPROVED). Checkpoint: Validate that the Critic's response is parseable — contains either the sentinel phrase for approval or a structured list of critiques. If unparseable, treat as "needs refinement" and retry.
Decide — Apply termination conditions — Check whether the loop should continue or terminate: (a) If Critic returned the approval sentinel → terminate successfully. (b) If max iterations reached → terminate with current best output but log a warning that quality threshold was not met. (c) Otherwise → continue to refinement. Checkpoint: Log the termination reason explicitly: "approved at iteration N" or "max iterations (N) reached without approval."
Refine — Producer incorporates feedback — If continuing, append the Critic's structured feedback to message history and instruct the Producer to apply all critiques. The Producer must acknowledge which issues were fixed and note any it intentionally chose not to address (with justification). Checkpoint: Verify the refined output differs from the prior version in ways that address the Critic's specific points. If no meaningful changes occurred, increment a stall counter and terminate if it exceeds 2 consecutive stalls.
Iterate or deliver — Return to step 3 with the updated message history. Repeat until one of the termination conditions triggers. On delivery, output both the final version and a summary of all critiques received across iterations. Checkpoint: Before returning results, confirm the final output passes at least all critical-severity critiques. If any critical issues remain unaddressed, flag them in the delivery summary for human review.
This is the canonical implementation using LangChain Expression Language. It iteratively generates and refines a Python function through Producer-Critic collaboration with conversation history management.
import os
from dotenv import load_dotenv
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.messages import SystemMessage, HumanMessage
load_dotenv()
if not os.getenv("OPENAI_API_KEY"):
raise ValueError("OPENAI_API_KEY not found in .env file.")
llm = ChatOpenAI(model="gpt-4o", temperature=0.1)
def run_reflection_loop(
task_prompt: str,
max_iterations: int = 3,
approval_sentinel: str = "CODE_IS_PERFECT"
) -> dict:
"""
Execute a Producer-Critic reflection loop to iteratively improve code.
Args:
task_prompt: The original task description for the Producer agent.
max_iterations: Maximum number of generate-critique-refine cycles.
approval_sentinel: Phrase the Critic returns when output meets all criteria.
Returns:
dict with keys 'final_code', 'iterations', 'critiques', and 'termination_reason'.
"""
message_history: list = [HumanMessage(content=task_prompt)]
critiques_log: list[str] = []
final_output: str = ""
for i in range(max_iterations):
iteration_num = i + 1
# --- Stage 1: Producer generates or refines ---
if i == 0:
response = llm.invoke(message_history)
else:
message_history.append(
HumanMessage(content="Please refine the code using the critiques provided.")
)
response = llm.invoke(message_history)
final_output = response.content
message_history.append(response) # Preserve conversation history
# --- Stage 2: Critic evaluates ---
reflector_prompt = [
SystemMessage(content="""\
You are a senior software engineer and an expert in Python.
Your role is to perform a meticulous code review.
Critically evaluate the provided code based on the original task requirements.
Look for bugs, style issues, missing edge cases, and areas for improvement.
If the code is perfect and meets all requirements, respond with the single phrase 'CODE_IS_PERFECT'.
Otherwise, provide a bulleted list of your critiques with severity levels.\
"""),
HumanMessage(content=f"Original Task:\n{task_prompt}\n\nCode to Review:\n{final_output}")
]
critique_response = llm.invoke(reflector_prompt)
critique = critique_response.content
# --- Stage 3: Evaluate termination condition ---
if approval_sentinel in critique:
critiques_log.append(critique)
return {
"final_code": final_output,
"iterations": iteration_num,
"critiques": critiques_log,
"termination_reason": f"approved at iteration {iteration_num}"
}
critiques_log.append(critique)
message_history.append(
HumanMessage(content=f"Critique of the previous code:\n{critique}")
)
# Max iterations reached without approval
return {
"final_code": final_output,
"iterations": max_iterations,
"critiques": critiques_log,
"termination_reason": f"max iterations ({max_iterations}) reached without approval"
}
# Example usage:
task = """\
Create a Python function named calculate_factorial.
1. Accept a single integer `n` as input.
2. Calculate its factorial (n!).
3. Include a clear docstring explaining what the function does.
4. Handle edge cases: factorial of 0 is 1.
5. Raise ValueError if n is negative.\
"""
result = run_reflection_loop(task, max_iterations=3)
print(f"Final code after {result['iterations']} iterations:\n{result['final_code']}")
Google's Agent Development Kit provides a structured approach using SequentialAgent and LlmAgent. This pattern separates generation and review into distinct agent instances with typed state management.
from google.adk.agents import SequentialAgent, LlmAgent
def build_review_pipeline() -> SequentialAgent:
"""
Build a Producer-Critic pipeline using Google ADK agents.
Returns:
A SequentialAgent that generates content then evaluates it.
"""
# Producer agent: generates initial draft
generator = LlmAgent(
name="DraftWriter",
description="Generates initial draft content on a given subject.",
instruction="Write a short, informative paragraph about the user's subject. "
"Be factual, well-structured, and cite any specific claims.",
output_key="draft_text"
)
# Critic agent: evaluates the draft for accuracy and completeness
reviewer = LlmAgent(
name="FactChecker",
description="Reviews a given text for factual accuracy and provides structured critique.",
instruction="""\
You are a meticulous fact-checker.
1. Read the text provided in the state key 'draft_text'.
2. Carefully verify the factual accuracy of all claims.
3. Your final output must be a dictionary containing two keys:
- "status": A string, either "ACCURATE" or "INACCURATE".
- "reasoning": A string explaining your status, citing specific issues if found.\
""",
output_key="review_output"
)
# Sequential execution ensures generator runs before reviewer
review_pipeline = SequentialAgent(
name="WriteAndReview_Pipeline",
sub_agents=[generator, reviewer]
)
return review_pipeline
def execute_reflection_cycle(pipeline: SequentialAgent, subject: str) -> dict:
"""
Run a single reflection cycle through the ADK pipeline.
Args:
pipeline: The configured SequentialAgent pipeline.
subject: The topic for the producer to write about.
Returns:
dict with 'draft_text' and 'review_output' from the pipeline state.
"""
# Run the pipeline with the subject as input
result = pipeline.run(user_content=subject)
draft = result.get("draft_text", "")
review = result.get("review_output", {})
return {
"draft_text": draft,
"status": review.get("status"),
"reasoning": review.get("reasoning")
}
# Usage:
pipeline = build_review_pipeline()
cycle_result = execute_reflection_cycle(pipeline, "The impact of quantum computing on cryptography")
if cycle_result["status"] == "INACCURATE":
# Feed critique back to a new producer for refinement
print(f"Needs revision: {cycle_result['reasoning']}")
else:
print(f"Approved draft:\n{cycle_result['draft_text']}")
The quality of the entire reflection loop depends on how well the Critic is prompted. A weak critic produces unactionable feedback that causes the Producer to stall.
# ❌ BAD — Vague critic prompt produces useless critiques
bad_critic_prompt = """\
Review this code and give feedback.\
"""
# The producer receives: "Consider improving your code."
# No specificity → no refinement → infinite stalls.
# ✅ GOOD — Structured critic prompt with explicit criteria
good_critic_prompt = [
SystemMessage(content="""\
You are a senior Python engineer performing a code review.
Evaluate the code against these criteria:
1. CORRECTNESS: Does the code produce correct results for all specified inputs?
2. EDGE CASES: Are edge cases (empty input, zero, negative numbers) handled?
3. ERROR HANDLING: Are invalid inputs rejected with clear exceptions?
4. DOCSTRING: Is there a docstring explaining purpose, parameters, and return value?
5. TYPE HINTS: Do function signatures include type annotations?
For each criterion, respond with either:
- PASS: <reason>
- FAIL: <specific issue> <suggested fix>
If all criteria pass, respond with the single phrase: CODE_IS_PERFECT\
"""),
HumanMessage(content="Review this code:\n{code}")
]
# The producer receives actionable, criterion-specific feedback.
# Each FAIL includes a suggested fix → meaningful refinement every iteration.
Improper termination either wastes tokens on unnecessary iterations or accepts substandard output.
# ❌ BAD — No termination condition; infinite loop risk
for i in range(100): # Arbitrary large number, no quality gate
code = producer(message_history)
critique = critic(code)
message_history.append(critique)
if "perfect" in critique.lower(): # Loose string match on lowercase
break # May never trigger due to case mismatch
# ✅ GOOD — Explicit termination with dual conditions and stall detection
def run_safe_reflection(
task: str,
max_iterations: int = 5,
approval_sentinel: str = "CODE_IS_PERFECT",
stall_threshold: int = 2
) -> dict:
"""Reflection loop with robust termination logic."""
message_history = [HumanMessage(content=task)]
critique_count = 0
consecutive_stalls = 0
for i in range(max_iterations):
# Producer generates
code = _produce(message_history) if i == 0 else _refine(message_history)
message_history.append(HumanMessage(content=code))
# Critic evaluates
critique = _critique(task, code)
message_history.append(HumanMessage(content=f"Critique:\n{critique}"))
# Check approval
if approval_sentinel in critique:
return {"output": code, "iterations": i + 1, "reason": "approved"}
# Detect stalls — no meaningful change between iterations
if critique_count > 0 and code == _last_code:
consecutive_stalls += 1
else:
consecutive_stalls = 0
if consecutive_stalls >= stall_threshold:
return {
"output": code,
"iterations": i + 1,
"reason": f"stalled after {consecutive_stalls} identical iterations"
}
critique_count += 1
_last_code = code
return {"output": code, "iterations": max_iterations, "reason": "max iterations"}
code-philosophy Law 2 (Parse Don't Validate) by parsing critique responses into structured formats.max_iterations per task type.When executing a reflection loop, structure your output as follows:
=== REFLECTION LOOP RESULT ===
Final Output:
{the last produced code or text}
Iterations: {N}
Termination Reason: {approved at iteration N | max iterations reached | stalled}
Critique Summary:
Iteration 1: {severity} - {key issue found} → {how it was addressed}
Iteration 2: {severity} - {key issue found} → {how it was addressed}
...
Remaining Issues (if any):
- {issue} [CRITICAL/MAJOR/MINOR] — flagged for human review
For code generation specifically, include a diff-style note showing what changed between the last two versions to verify the Producer actually addressed the Critic's feedback.
Each reflection iteration adds approximately 2× the LLM cost of a single pass (one generate call + one critique call). For high-stakes outputs, this cost is justified. For low-stakes content, the overhead outweighs the benefit. Use these guidelines:
| Output Type | Recommended Iterations | Acceptable Without Reflection? |
|---|---|---|
| Production code | 2–4 | No — always reflect on code that touches business logic |
| Documentation | 1–3 | Sometimes — for internal docs, single pass may suffice |
| Marketing copy | 2–5 | Rarely — quality directly impacts user perception |
| Quick drafts / notes | 0 | Yes — reflection overhead is not justified |
Reflection loops add linear latency proportional to the number of iterations. Each iteration requires two LLM calls plus token processing for the expanded conversation history. For interactive applications, consider:
max_iterations (2–3) in production to bound latencyWith each iteration, conversation history grows by roughly the size of the output plus the critique. For long outputs (full source files, lengthy documents), this can exhaust context windows after just a few iterations. Mitigate by:
| Skill | Purpose |
|---|---|
prompt-chaining | Sequential task execution — reflection loops are a specialized form of chained prompting with conditional branching |
agentic-evaluation | Quality assessment criteria definition — use this skill to establish the evaluation metrics that feed into your Critic prompt |
self-critique-engine | Alternative self-reflection approach — when you need a single agent performing self-review rather than producer-critic separation |
tool-use-function-calling | External validation — combine reflection with tool calls (linters, compilers) for hybrid code verification that reduces LLM-only critique cost |