On this page
Quick Answer
An agentic workflow (2026) is an LLM system where the model's calls are wired together by code you control, not by the model deciding its own next step. In practice that means five reusable patterns: prompt chaining, routing, parallelization, orchestrator-workers, and evaluator-optimizer. This tutorial builds all five from scratch in Python with runnable code, using Claude as the model. Most teams reaching for an "AI agent" actually need one of these workflows first: they are cheaper, more predictable, and far easier to debug.
Workflow or agent? Start here
The words get used interchangeably, but the distinction is the whole game. Anthropic's engineering team drew the cleanest line in their December 2024 guide, Building Effective Agents: a workflow is a system where LLM calls and tools are orchestrated through predefined code paths, while an agent is a system where the LLM directs its own process and tool use dynamically.
That single difference decides your debugging story. In a workflow, when something breaks you can point at the exact line of Python that fired the wrong call. In an agent, the control flow lives inside the model's reasoning, so a failure means re-reading a transcript and guessing. The practical advice that falls out of this: reach for a workflow whenever the steps are knowable in advance, and only escalate to an open-ended agent when the task genuinely cannot be scripted.
Everything below is a workflow. Each pattern is a few lines of orchestration code around plain model calls. If you have already built a single agent loop, this is the layer underneath it.
Setup: one helper, five patterns
Install the SDK and set your key. The examples use the official Anthropic Python SDK; the same shapes translate to any provider.
pip install anthropic
export ANTHROPIC_API_KEY="sk-ant-..."
We build every pattern on one tiny helper so the orchestration logic stays visible. The model is claude-opus-4-8, priced at $5 per million input tokens and $25 per million output tokens as of 2026; drop to a cheaper tier for the classifier and gate steps if cost matters.
All five snippets share this
call helper. It is deliberately boring: one request in, the text out.
import anthropic
client = anthropic.Anthropic()
MODEL = "claude-opus-4-8"
def call(prompt: str, system: str = "") -> str:
msg = client.messages.create(
model=MODEL,
max_tokens=1024,
system=system,
messages=[{"role": "user", "content": prompt}],
)
return msg.content[0].text
Pattern 1: Prompt chaining
Chaining splits a task into fixed steps, feeding each output into the next. The value is the gate between steps: a cheap code check that stops the chain when an intermediate result is wrong, before you spend tokens on the rest.
def outline_then_write(topic: str) -> str:
outline = call(f"Write a 3-bullet outline for a short post about: {topic}")
# Gate: a plain-Python check, no model call needed.
if "-" not in outline and "1" not in outline:
raise ValueError("Outline gate failed; stopping the chain early.")
return call(f"Expand this outline into a 150-word draft:\n\n{outline}")
print(outline_then_write("why small AI workflows beat big agents"))
Use chaining when a task decomposes cleanly into sequential subtasks and each one is easy to verify. It trades a little latency for a lot of accuracy.
Pattern 2: Routing
Routing classifies the input first, then sends it to a specialized handler. This keeps each prompt focused instead of stuffing every instruction into one giant system prompt.
def route(question: str) -> str:
category = call(
"Classify this question as one word: BILLING, TECHNICAL, or OTHER.\n\n"
f"{question}"
).strip().upper()
systems = {
"BILLING": "You are a concise billing support agent.",
"TECHNICAL": "You are a senior engineer. Answer with a short code snippet.",
}
system = systems.get(category, "You are a helpful generalist.")
return call(question, system=system)
print(route("My deploy fails with exit code 137. What is happening?"))
Routing shines when your inputs fall into distinct buckets that each deserve different handling. A small, fast model for the classifier step and a stronger one for the answer is a common and effective split.
Pattern 3: Parallelization
When subtasks are independent, run them at once and merge the results. Here three reviewers inspect the same diff in parallel, each with a narrow job.
import concurrent.futures
def review(diff: str) -> dict:
checks = {
"security": "List only security risks in this diff. Be terse.",
"style": "List only style issues in this diff. Be terse.",
"tests": "List missing tests for this diff. Be terse.",
}
with concurrent.futures.ThreadPoolExecutor() as pool:
futures = {
name: pool.submit(call, f"{prompt}\n\n{diff}")
for name, prompt in checks.items()
}
return {name: f.result() for name, f in futures.items()}
for section, notes in review("def pay(amt): db.execute('UPDATE bal SET n=' + amt)").items():
print(section, "->", notes)
Parallelization cuts wall-clock time and, by giving each call a single narrow focus, often raises quality too. Use it for independent checks, multi-perspective reviews, or fanning one question across several sources.
Pattern 4: Orchestrator-workers
Sometimes you cannot list the subtasks up front. An orchestrator call plans them dynamically, worker calls execute each one, and a final call synthesizes the pieces.
import json
def orchestrate(task: str) -> str:
plan = call(
"Break this task into a JSON list of 2 to 4 subtasks. "
'Return only JSON, like ["subtask one", "subtask two"].\n\n'
f"Task: {task}"
)
subtasks = json.loads(plan)
results = [
call(f"Complete this subtask and return only the result:\n{s}")
for s in subtasks
]
return call("Combine these partial results into one answer:\n\n" + "\n\n".join(results))
print(orchestrate("Compare Postgres and SQLite for a solo-founder SaaS."))
This is the closest pattern to a true agent, because the number and shape of the steps is decided at runtime. Keep the planning prompt strict about output format so json.loads does not choke; validating the plan before running it is the natural next gate.
Pattern 5: Evaluator-optimizer
Pair a generator with a critic and loop until the critic is satisfied or you hit a cap. The loop lives in Python, so it can never run away.
def evaluator_optimizer(brief: str, max_rounds: int = 3) -> str:
draft = call(f"Write a one-sentence tagline for: {brief}")
for _ in range(max_rounds):
verdict = call(
"Score this tagline from 1 to 10 and give one concrete fix. "
'Reply as JSON {"score": int, "fix": str}.\n\n'
f"{draft}"
)
data = json.loads(verdict)
if data["score"] >= 8:
return draft
draft = call(f"Rewrite this tagline applying the fix '{data['fix']}':\n\n{draft}")
return draft
print(evaluator_optimizer("a calendar app for freelancers"))
Use this when you have a clear quality signal and iteration reliably improves the answer, such as copy, code that must pass a test, or a translation you can grade. The max_rounds cap is the whole safety story: a workflow bounds its own cost, an unbounded agent does not.
How the patterns compose
These five are building blocks, not a menu you pick one from. A real system chains them: route an incoming request, orchestrate its subtasks, review each result in parallel, then run an evaluator-optimizer pass on the final draft. Because every join is ordinary code, you can log, cache, retry, and unit-test each seam.
If you later find you truly need dynamic tool use and open-ended control flow, frameworks exist to help: LangGraph models agent control flow as a graph, and the OpenAI Agents SDK offers a hosted loop. Both are worth reaching for once a plain workflow provably is not enough. Start with the code above; add a framework only when the pain is real.
Verify your install
Before wiring any of this into a real project, confirm the SDK and your key work end to end. This should print a single word.
import anthropic
client = anthropic.Anthropic()
resp = client.messages.create(
model="claude-opus-4-8",
max_tokens=16,
messages=[{"role": "user", "content": "Reply with the single word: ready"}],
)
print(resp.content[0].text) # expect: ready
If you see ready, every pattern above will run. If you get an authentication error, re-check ANTHROPIC_API_KEY; if the import fails, re-run pip install anthropic.
Next, wire the workflow you just learned into a hands-on build: our build your first AI agent from scratch walkthrough turns the orchestrator pattern into a working loop, and adding human-in-the-loop control to a LangGraph agent shows where a person belongs in the graph once you graduate from workflows to agents.
Written by
Ren OkabeRen Okabe builds and breaks agent systems, then writes down the runnable version. Principal-engineer voice, code first.
Frequently asked questions
What is an agentic workflow?
An agentic workflow is an LLM system whose steps are orchestrated by code you write, using patterns like prompt chaining, routing, and orchestrator-workers, rather than by the model choosing its own path. As of 2026 it is the cheaper, more predictable alternative to a fully autonomous agent.
What is the difference between an agentic workflow and an AI agent?
In a workflow, predefined code decides which LLM call runs next; in an agent, the model itself decides dynamically. Workflows are easier to debug and cost-bound; agents are more flexible but harder to control. Start with a workflow and escalate only when the task genuinely cannot be scripted.
What are the main agentic workflow patterns?
Five recur: prompt chaining (sequential steps with gates), routing (classify then dispatch), parallelization (independent calls run at once), orchestrator-workers (a planner spawns subtasks), and evaluator-optimizer (generate then critique in a bounded loop).
Do I need a framework like LangGraph to build one?
No. Every pattern here is a few lines of plain Python around normal model calls. Reach for LangGraph or the OpenAI Agents SDK only when you need dynamic tool use and graph-based control flow that a straight workflow cannot express.
How do I keep an agentic workflow from running away on cost?
Because the control flow is code, you bound it directly: cap loop iterations (as in the evaluator-optimizer example), add gates that stop a chain early, and route cheap steps to a smaller model. An unbounded agent loop has none of these guarantees by default.
Related tutorials
Build your first AI agent from scratch in 30 minutes
An AI agent is just a loop: you call a model, the model asks to run a tool, you run it, you feed the result back, and you repeat until the model is done. In this tutorial you build that loop yourself in plain TypeScript against the Anthropic Messages API, no framework. You will wire up two tools (read a file, run a calculation), let the model orchestrate them, add a turn cap and basic guardrails, then verify the whole thing end to end. The result is a small research agent you fully understand and can extend with your own tools.
LangGraph Human-in-the-Loop (2026): Pause an Agent for Approval with interrupt()
A runnable LangGraph human-in-the-loop tutorial for 2026: pause an agent mid-run with interrupt(), review the proposed action, then approve, edit, or reject it with Command(resume=...). Includes the checkpointer requirement and the re-run-from-the-top gotcha that breaks most tutorials.
How to Make an AI Agent in 2026 (Step by Step)
The five parts every AI agent has, a runnable Python example in about 40 lines, and an honest guide to the guardrails and framework choices most tutorials skip.