Resume Refinement Loop with Conditional Exit¶
Pipeline topology: ( resume_writer >> resume_reviewer ) * until(quality_score == “excellent”)
Tip
What you’ll learn How to compose agents into a sequential pipeline.
Source: 20_loop_until.py
from adk_fluent import Agent, Loop
# loop_until: refine a resume draft until the quality reviewer approves it
resume_writer = (
Agent("resume_writer")
.model("gemini-2.5-flash")
.instruct(
"Write or improve a professional resume based on the candidate's "
"experience. Incorporate feedback from previous reviews."
)
.writes("quality_score")
)
resume_reviewer = (
Agent("resume_reviewer")
.model("gemini-2.5-flash")
.instruct(
"Review the resume for clarity, impact, and ATS compatibility. Set quality_score to 'excellent' when satisfied."
)
)
refinement = (resume_writer >> resume_reviewer).loop_until(
lambda s: s.get("quality_score") == "excellent", max_iterations=5
)
# .until() on a Loop -- alternative syntax for complex multi-step loops
cover_letter_loop = (
Loop("cover_letter_polish")
.step(
Agent("drafter")
.model("gemini-2.5-flash")
.instruct("Draft or revise a cover letter tailored to the job description.")
)
.step(
Agent("tone_checker")
.model("gemini-2.5-flash")
.instruct("Check the cover letter tone. Set 'tone_approved' to 'yes' when professional.")
.writes("tone_approved")
)
.until(lambda s: s.get("tone_approved") == "yes")
.max_iterations(10)
)
# Native ADK has no built-in conditional loop exit. You'd need to:
# 1. Create a custom BaseAgent that evaluates a predicate
# 2. Yield Event(actions=EventActions(escalate=True)) to exit
# 3. Manually wire it into the LoopAgent's sub_agents
# This is ~30 lines of boilerplate per loop condition.
graph TD
n1(("resume_writer_then_resume_reviewer_x5 (loop x5)"))
n2["resume_writer"]
n3["resume_reviewer"]
n1 --> n2
n1 --> n3
Equivalence¶
from adk_fluent.workflow import Loop as LoopBuilder
# loop_until creates a Loop builder
assert isinstance(refinement, LoopBuilder)
# The loop has _until_predicate stored for checkpoint injection at build time
assert refinement._config.get("_until_predicate") is not None
assert refinement._config.get("max_iterations") == 5
# .until() on Loop sets the predicate
assert cover_letter_loop._config.get("_until_predicate") is not None
assert cover_letter_loop._config.get("max_iterations") == 10
# Build verifies the checkpoint agent is injected
built = refinement.build()
# Last sub_agent should be the checkpoint that checks quality_score
checkpoint = built.sub_agents[-1]
assert checkpoint.name == "_until_check"
See also
API reference: Loop