Flow Graphs¶
Flow graphs let you author the agent's path explicitly — conditional routing and bounded loops (refine→judge, retry-until-pass, escalation) declared as nodes and edges under the same workflow: key, with crash-safe checkpointing built in.
Quick Start: Refine → Judge → Ship¶
A writing pipeline that drafts, gets judged, loops back to refine until the judge approves (at most 5 times), then ships:
id: draft-shipper
name: Draft Shipper
description: Draft, judge, refine until approved, then publish.
trigger: channel
channels: [http]
llm:
provider: anthropic
model: claude-sonnet-4-6
system_prompt: Run the drafting flow.
agents: [editor] # peer used by the ship node
workflow:
entry: refine # default: first node
max_node_executions: 50 # global safety budget (default 100)
nodes:
- id: refine
tool: improve_draft # kind inferred: tool
input: '{"topic": {{.trigger | printf "%q"}}, "feedback": {{.verdict.feedback | printf "%q"}}}'
output: draft
- id: judge
tool: evaluate_draft
input: '{"draft": {{.draft | printf "%q"}}}'
output: verdict # JSON result → fields addressable in predicates
- id: ship
agent: editor # kind=agent → invoked as agent__editor
input: 'Publish this approved draft: {{.draft}}'
edges:
- {from: refine, to: judge, max_iterations: 6}
- {from: judge, to: refine, if: '{{not .verdict.ok}}', max_iterations: 5}
- {from: judge, to: ship} # fallback — order matters
- {from: ship, to: end} # "end" (or no edge) terminates
enabled: true
The judge's tool returns JSON like {"ok": false, "feedback": "tighten the
intro"}; the back edge fires while ok is false, and its max_iterations: 5
guarantees the loop terminates. When ok turns true (or the budget runs
out), the fallback edge ships the draft.
Nodes¶
| Field | Notes |
|---|---|
id |
Unique within the flow; checkpoint keys derive from it. |
kind |
tool | agent | branch. Usually inferred: tool: set → tool, agent: set → agent, neither → branch. |
tool |
Tool to invoke (any tool the agent can normally call). |
agent |
Peer agent to invoke as agent__<id> — declare it in the agent's agents: list. |
input |
Go template over flow vars producing the node's input. |
output |
Flow variable that stores this node's result. |
on_error |
abort (default) | skip | retry. |
branch nodes do no work — they exist purely to fan edges out from one
decision point.
Edges and Predicates¶
Edges leaving a node are evaluated in declaration order; the first edge
whose if renders truthy and whose traversal budget remains is taken. No
eligible edge ends the flow with the last node's result.
| Field | Notes |
|---|---|
from / to |
Node IDs. to: end (or omitted) terminates the flow. |
if |
Go template predicate over flow vars. Empty, true, or any non-zero output = take the edge; empty string, false, or 0 = don't. |
max_iterations |
How many times this edge may be traversed per run. Default 1. |
Flow variables are .trigger (the inbound message) plus every node's
output. Tool results that are JSON documents are unwrapped so predicates
can address fields ({{.verdict.ok}}); plain text stays a string.
Bounded Cycles¶
Cycles terminate by construction:
- Every edge defaults to
max_iterations: 1— a back edge must explicitly raise its budget to loop (max_iterations: 5above). max_node_executions(default 100) backstops the entire run; exceeding it aborts the flow.
Warning
Remember to raise max_iterations on the forward edge into a looped
node too — in the example, refine → judge carries max_iterations: 6
because it is traversed once per refinement pass.
Checkpointing and Resume¶
Each node visit checkpoints under <node>#<visit> in the same store as
linear workflows. Resuming a crashed run ID restores completed
visits — flow variables included — and recomputes the same deterministic
path, so only unfinished work executes. Side effects are never repeated.
Graphs are validated at load time: duplicate or missing node IDs, unknown edge endpoints, bad kinds, and unknown entry nodes are refused with precise errors before the agent ever runs.
Two Ways to Run a Flow¶
The same graph serves two entry points:
- Workflow runs — cron/scheduled or triggered runs execute the graph through the checkpointing workflow executor (the example above).
- Chat runs — set
reasoning.strategy: flowand chat messages route through the graph as a reasoning strategy. Node actions go through the engine's standard tool-policy bridge (sandbox, allowlists, confirmation gates), and each node visit surfaces as areasoning.stepevent in the Chat thinking section and Activity feed — see Reasoning Strategies.
Seeing the Graph¶
The Flow View page renders graph-form agents
read-only: nodes laid out in BFS columns, entry wired from the trigger,
predicates and ↺×N iteration budgets labeled on edges, cycle back-edges
highlighted, and terminal edges into Output. Editing the graph visually is
planned; for now the YAML is the source of truth.
When To Use a Flow¶
- Quality loops: draft → judge → refine until pass.
- Retry-until-success with an escalation path on exhaustion.
- Multi-branch routing where the next step depends on structured output.
Prefer linear steps when the pipeline never branches, and a free-form reasoning strategy when the model should invent the plan itself.