Workflows, steps, and agents
The three primitives in AGNT5 — what they are, how they fit together, and which one you reach for when.
A workflow orchestrates work; a step is a checkpointed unit of that work; an agent is an LLM-driven loop that runs inside a step.
import httpx
from agnt5 import Agent, FunctionContext, WorkflowContext, function, workflow
researcher = Agent(
name="researcher",
model="openai/gpt-4o-mini",
instructions="Summarize the article in three sentences.",
)
@function
async def fetch_article(ctx: FunctionContext, url: str) -> str:
# Side effect lives in a step. The workflow body never makes the HTTP call.
async with httpx.AsyncClient() as client:
response = await client.get(url)
return response.text
@function
async def summarize(ctx: FunctionContext, body: str) -> str:
# The agent's non-determinism is contained inside this step.
result = await researcher.run(body)
return result.output
@workflow
async def research(ctx: WorkflowContext, url: str) -> str:
article = await ctx.step(fetch_article, url)
summary = await ctx.step(summarize, article)
return summaryThe research workflow is the orchestrator. fetch_article and summarize are steps. The researcher Agent is the agent. All three primitives appear in nine lines of orchestration code.
The mental model
A workflow is a function decorated with @workflow that drives a sequence of steps to produce a result. Its body looks like ordinary async Python — variables, branches, loops, exception handlers — but AGNT5 treats it as a recipe to be executed reliably across crashes. The workflow body must be deterministic: replay must arrive at the same call sites in the same order, every time.
A step is the unit of work the workflow delegates. Steps are where side effects happen — HTTP calls, database writes, file I/O, LLM calls. Each call to ctx.step(...) checkpoints its input and output to the run’s journal. On recovery, replay reads the checkpoint instead of re-running the side effect. You can pass a @function-decorated handler (the recommended form, shown above) or a name plus a callable when the step wraps arbitrary async work.
An agent is an LLM-driven loop: given instructions, a model, and optional tools, it picks actions and refines its output until it satisfies the goal or hits an iteration limit. Because an agent’s output depends on the model’s stochastic sampling, it is non-deterministic by definition. The way AGNT5 reconciles that with deterministic workflows is to host the agent’s call inside a step. The agent runs once, the step journals its result, and the workflow body sees a deterministic value on replay.
Why it works this way
Three primitives, one separation of concerns: orchestrate, execute, decide. The split exists so each piece can do exactly one job. The workflow stays deterministic and replay-safe; the step is the single chokepoint where non-determinism is allowed and recorded; the agent is free to be as stochastic as the model permits, because its output is captured the first time and replayed thereafter.
You could imagine an alternative where workflows directly call LLMs without a step boundary. AGNT5 rejects that shape because there would be no way to recover a crashed run without re-billing every prompt — and re-running a tool-using agent against the same input does not in general produce the same tool calls. The step boundary is what makes the durability guarantee tractable.
Edge cases and gotchas
ctx.stepversusctx.task. Older code in this repository usesctx.task(...). New code usesctx.step(...). Both still work; lead withctx.stepeverywhere.- An agent is not a peer of a workflow. Agents always run inside a step boundary, even when invoked directly from a
@function. There is noctx.agent(...); you callAgent.run(...)(or its async variant) from inside a@function, and the workflow reaches the agent viactx.step. - The word “step” is overloaded. A step in a workflow (this page) is a checkpointed call. A reasoning step inside an agent loop is one iteration of the agent’s plan-act-observe cycle. They are not the same thing — when ambiguity matters, say “workflow step” or “agent iteration”.
- Agents calling agents are still inside steps. When one agent uses another agent as a tool, or when one agent hands off to another, the whole chain runs inside the step that invoked the first agent. The journal records one step result, not a sub-tree.
agentis lowercase in prose. The Python class isAgent; in body text the noun isagent, never “AI agent” or “Agent”.
Related concepts
- Durable execution — what the step boundary buys you.
- Determinism — why workflows have rules — what the workflow body is and is not allowed to do.
- Event sourcing and replay — how the journal turns a crashed run into a resumable one.