Review Loop¶
This example demonstrates an iterative review and refinement workflow where content is improved until approved.
Overview¶
graph TD
A(User Message) --> B(Drafter)
B --> C(Reviewer)
C -->|APPROVED| D(Final Response)
C -->|REJECTED| E(Refiner)
E --> C
Complete Example¶
import agentic_flow as af
from agents import SQLiteSession
# Define agents
drafter = af.Agent(
name="drafter",
instructions="""Create an initial draft response to the user's request.
Be thorough but expect feedback for improvement.""",
model="gpt-5.2",
)
reviewer = af.Agent(
name="reviewer",
instructions="""Review the draft critically.
If acceptable, respond with: APPROVED
If needs improvement, respond with: REJECTED
Followed by specific feedback on what to improve.""",
model="gpt-5.2",
model_settings=af.reasoning("medium"),
)
refiner = af.Agent(
name="refiner",
instructions="""Improve the draft based on the reviewer's feedback.
Address each point of feedback specifically.""",
model="gpt-5.2",
)
async def review_loop_flow(user_message: str, max_iterations: int = 3) -> str:
# Phase 1: Initial draft
async with af.phase("Drafting"):
draft = await drafter(user_message).stream()
# Phase 2-N: Review and refine loop
for attempt in range(max_iterations):
async with af.phase(f"Review (attempt {attempt + 1})"):
review = await reviewer(
f"Draft:\n{draft}\n\nPlease review this draft."
).stream()
if "APPROVED" in review.upper():
break
async with af.phase(f"Refinement (attempt {attempt + 1})"):
draft = await refiner(
f"Original request:\n{user_message}\n\n"
f"Current draft:\n{draft}\n\n"
f"Feedback:\n{review}"
).stream()
# Final phase
async with af.phase("Final Response", persist=True):
return draft
# Run
runner = af.Runner(
flow=review_loop_flow,
session=SQLiteSession("review_loop.db"),
)
result = runner.run_sync("Write a professional email declining a job offer")
print(result)
Key Points¶
Loop Pattern¶
Standard Python for loop with early exit:
for attempt in range(max_iterations):
review = await reviewer(draft).stream()
if "APPROVED" in review.upper():
break
draft = await refiner(draft, review).stream()
Dynamic Phase Labels¶
Phase labels can be dynamic:
Data Accumulation¶
The draft evolves through iterations:
draft = await drafter(message).stream() # Initial
draft = await refiner(draft, feedback).stream() # Refined
draft = await refiner(draft, feedback).stream() # Further refined
With Typed Review¶
Use structured output for clearer review decisions:
from pydantic import BaseModel
class Review(BaseModel):
approved: bool
feedback: str | None = None
issues: list[str] = []
reviewer = af.Agent(
name="reviewer",
instructions="Review the draft. Provide structured feedback.",
output_type=Review,
model="gpt-5.2",
)
async def typed_review_flow(user_message: str) -> str:
async with af.phase("Drafting"):
draft = await drafter(user_message).stream()
for attempt in range(3):
async with af.phase(f"Review {attempt + 1}"):
review: Review = await reviewer(f"Draft:\n{draft}").stream()
if review.approved:
break
if review.issues:
feedback = "\n".join(f"- {issue}" for issue in review.issues)
else:
feedback = review.feedback or "General improvements needed"
async with af.phase(f"Refinement {attempt + 1}"):
draft = await refiner(
f"Draft:\n{draft}\n\nIssues:\n{feedback}"
).stream()
async with af.phase("Final", persist=True):
return draft
With Quality Threshold¶
Add a quality score to control iteration:
class QualityReview(BaseModel):
score: float # 0-10
approved: bool
feedback: str
async def quality_review_flow(user_message: str, threshold: float = 8.0) -> str:
async with af.phase("Drafting"):
draft = await drafter(user_message).stream()
for attempt in range(5):
async with af.phase(f"Quality Check {attempt + 1}"):
review: QualityReview = await reviewer(f"Draft:\n{draft}").stream()
if review.approved and review.score >= threshold:
break
async with af.phase(f"Improvement {attempt + 1}"):
draft = await refiner(
f"Score: {review.score}/10\n"
f"Target: {threshold}/10\n"
f"Feedback: {review.feedback}\n"
f"Draft:\n{draft}"
).stream()
async with af.phase("Final", persist=True):
return draft
Streaming Progress¶
Show progress with a handler:
import agentic_flow as af
def progress_handler(event):
if isinstance(event, af.PhaseStarted):
if "Review" in event.label:
print(f"\nReviewing...")
elif "Refinement" in event.label:
print(f"\nRefining based on feedback...")
elif isinstance(event, af.PhaseEnded):
print(f" done ({event.elapsed_ms}ms)")
elif isinstance(event, af.AgentResult):
print(event.content)
runner = af.Runner(
flow=review_loop_flow,
session=SQLiteSession("review.db"),
handler=progress_handler,
)
Best Practices¶
- Set max iterations — Prevent infinite loops
- Clear approval criteria — Make it unambiguous when to stop
- Accumulate context — Pass original request to refiner
- Persist only final — Use
persist=Trueonly on the last phase - Use typed reviews — Structured output makes decisions clearer
Back to: Home