Two Entry Points
WorkflowGraph and support .run(input).
The Primitives
Sequential
Chain steps — output flows forward:Branch (predicate routing)
Route based on output:Loop (while-do)
Exit condition checked before body:Loop (do-while / repeatUntilOutput)
Body runs first, exit condition reads actual output:Parallel (static fan-out)
Run steps concurrently, collect results:Parallel (dynamic fan-out)
Fan-out determined at runtime:Decision (LLM-routed)
LLM picks which step to run:Gate (quality checkpoint)
Approve, reject, or retry with feedback:Verdict (score, reasoning, per-judge judgments) is written to AgentContext under JUDGE_VERDICT for the retry step to consume.
Supervisor (autonomous delegation)
LLM picks sub-agents each iteration:Error Recovery
Route exceptions to recovery steps:BackTo (cyclic back-edge)
Jump back to an earlier step when a condition is met — a lightweight escape hatch for retry patterns:backTo creates an EdgeCondition.BackEdge in the graph IR — a real edge, visible in traces. The next .step() chains from the same node (not the back-edge target), so merge runs when the predicate returns false.
Use RunOptions.maxIterations() as a circuit breaker to prevent infinite loops:
backTo vs repeatUntil: repeatUntil creates structured loop nodes (entry → body → check → exit). backTo is a single edge — no extra nodes, no loop wrapper. Use it when you need a quick “retry this section” without the overhead of a full loop construct.
Terminate
Exit early from anywhere:Step Types
Steps are the atomic unit. Each wraps a different kind of execution:| Step Type | What It Wraps | Execution Time |
|---|---|---|
Step.named("n", lambda) | Any lambda | Depends |
ChatClientStep.of(chat, template) | Single Spring AI call | Seconds |
ClaudeStep.of(template) | Full Claude CLI agent session | Minutes |
AgentClientStep.of(client, template) | AgentClient abstraction | Minutes |
A2AStep.of(url) | Remote A2A agent | Minutes |
Steps.of(fn) | Pure deterministic function | Milliseconds |
Steps.retrying(n, step) | Retry wrapper | Per attempt |
Steps.outputOf("step-name") | Read prior step’s output | Instant |
The key differentiator:
ClaudeStep runs a full agentic loop internally — many turns, many tool calls, minutes of execution. The workflow sees it as one step. This is the opposite of frameworks where each LLM API call is a separate workflow activity.Composability
Workflows implementStep<I, O>. Nest them freely:
RunOptions
Runtime constraints — not DSL verbs, hints to the executor:The Graph IR
Every primitive compiles to real nodes and typed edges — not opaque lambdas:- A branch is 4 nodes (gateway → then-step, else-step → join) + 4 edges
- A loop has a back-edge from body to entry
- A parallel has fork and join nodes with
BranchIndexedges
TraceRecorder sees every transition; Markov analysis works on the edge data.
Related
Getting Started
Install, first workflow, four-layer architecture
Complete Examples
8 runnable integration tests validated against GPT-4.1