Appearance
Loops
Some work is iterative by nature. Three rounds of research, each refining the last. A list of items, each handled the same way. A back-and-forth refinement that stops when the output is good enough. A loop step handles all three patterns.
A loop body is just a small workflow that Z.E.N. runs over and over. You decide how it ends: after a fixed number of iterations, when a condition you describe is satisfied, or once you've covered every item in a list. Each pass through the body can read what the previous pass produced, so the rounds can build on each other instead of starting from scratch.
When to use a loop vs. just more steps
If you know up front you want exactly three rounds and each round is different, write three steps. If the iteration count depends on something dynamic, or all iterations do the same thing, use a loop. The line is "if I'd be copy-pasting steps, I should use a loop."
Advanced: the three loop shapes
Until a condition is met
The most common loop. Run the body, check a condition, run again if the condition isn't met yet:
yaml
nodes:
- id: refine-draft
type: loop
max_iterations: 5
until:
type: prompt
prompt: |
Is this draft ready to ship? Respond yes or no.
$iteration.output
body:
- id: write-draft
type: prompt
prompt: |
Write a draft based on the brief: $BRIEF
{{#previous}}
The previous draft had this feedback: $previous.feedback
Address it directly.
{{/previous}}Z.E.N. runs the body, checks the until: condition, runs the body again if the condition returned "no." The loop stops when until: returns yes or when max_iterations: is reached, whichever comes first.
A fixed number of times
For research workflows, content batches, anything where you know up front how many rounds you want:
yaml
- id: research-rounds
type: loop
iterations: 3
body:
- id: search-round
type: prompt
prompt: |
Round $iteration of 3. Find sources we haven't seen yet on "$TOPIC".
Already seen: $previous.sourcesThree rounds. Each round can see what the previous round did via $previous.<field>.
Per item in a list
To run the body once for each thing in a list:
yaml
- id: process-each
type: loop
over: $items.output
body:
- id: handle-item
type: prompt
prompt: |
Handle this item: $item
Return the handled version.If $items.output is a list of ten things, the body runs ten times. The current item is $item. Z.E.N. runs them serially by default; add concurrency: 4 on the loop node to fan out.
What $iteration and $previous mean
Inside a loop body, you have two extra variables:
$iterationis the current iteration index (1-based) plus the output of the body so far this round.$previousis the body output from the prior iteration, if there was one.
You use these to write a body that builds on what the prior round did, instead of starting from scratch each time.
When the loop fails
If a body iteration fails, by default the whole loop fails. Override with on_failure: continue to keep going past failures:
yaml
- id: process-each
type: loop
over: $items.output
on_failure: continue
body:
- id: handle-item
type: prompt
...When the loop finishes, the loop node's output is the list of body outputs (with null entries for any iteration that failed).