Agentic Design Patterns¶
Working, tested LangGraph implementations of the six agentic design
patterns from Anthropic's "Building Effective
Agents" —
five fixed workflows and one open-ended agent loop — plus a
seventh, human-in-the-loop. Each pattern is a small, self-contained
LangGraph StateGraph you can read start to finish in a couple of
minutes, run from the CLI, and poke at in tests.
No API key required
Every pattern runs and is unit-tested offline, against a small deterministic fake chat model. Point it at a real model with one environment variable when you want to — see Running against a real model below.
Patterns¶
-
Prompt Chaining
Linear chain + a gate — decomposing a task into sequential LLM calls with a programmatic checkpoint between them.
-
Routing
Classify → dispatch — sending different inputs to specialized prompts instead of one generic one.
-
Parallelization
Fan-out → fan-in — running independent LLM calls concurrently and joining the results.
-
Orchestrator-Workers
Dynamic fan-out via
Send— when the number of parallel subtasks is decided by the model at runtime. -
Evaluator-Optimizer
Generate ⇄ evaluate loop — iterative refinement against explicit pass/fail criteria.
-
ReAct Agent
Tool-call loop — the open-ended "agent": the model decides whether to act or answer, turn by turn.
-
Human-in-the-Loop
Agent loop + approval gate — pausing before a side-effecting tool call until a human approves or denies it.
Start with prompt_chaining (simplest) and end with human_in_the_loop
(built on react_agent, least predictable plus a human in the mix). Each
pattern's own page has a Mermaid diagram of its graph plus notes on where
it fits, where it doesn't, architectural tradeoffs, production infra
choices, production readiness, and relevant open-source components.
Beyond the patterns¶
-
Architecture Overview
The cross-pattern view: a decision tree for picking between the seven patterns and a latency/cost/risk comparison table.
-
Harnesses and Loops
How these patterns map onto real agent harnesses — Claude Code included — plus memory, guardrails, MCP tools, and evals.
-
Real-World Examples
The decision tree applied to concrete scenarios, including a couple of cases where the tempting first choice is wrong.
-
Under deepagents
The same patterns re-expressed in LangChain's
deepagents— what a framework supplies for free and what it costs you. Now lives in its own repo,ai-harnesses.
Quick start¶
Requires Python 3.13+ and uv.
uv sync
uv run main.py list
uv run main.py run react-agent "What is (12 + 8) * 3?"
uv run main.py run prompt-chaining "Why LangGraph is a good fit for agents"
run prints a narrated trace — how many LLM calls, in what shape,
which branch ran, how many loop turns — from each pattern's
run.py::render:
orchestrator-workers · orchestrator picks the subtask count at runtime → dynamic fan-out via Send
────────────────────────────────────────────────────────────
1. orchestrator (LLM) → planned 3 subtasks: pricing; onboarding; support quality
2. fan-out via Send → 3 worker runs in parallel (each sees only its own subtask)
3. synthesizer (LLM) → final_report (worker results merged via the operator.add reducer)
Add --raw for the untouched state dict, or --otel for the same run as
an OpenTelemetry GenAI span tree (uv sync --group otel first) — see
docs/observability.md.
Or activate the venv and use python directly:
source .venv/bin/activate
python main.py run evaluator-optimizer "Write a tagline for Nimbus"
python -m patterns.orchestrator_workers.run "Write a report covering pricing, onboarding, and support quality."
python main.py run human-in-the-loop "Send a message to Alice: the report is ready."
Running against a real model¶
By default every pattern uses shared/llm/fake.py's FakeChatModel — a
scripted stand-in that lets the whole repo run offline. To use a real
model instead, set an API key and, optionally, LLM_PROVIDER:
export ANTHROPIC_API_KEY=sk-ant-... # or OPENAI_API_KEY=sk-...
export LLM_PROVIDER=anthropic # or "openai"; auto-detected from the key if omitted
uv run main.py run react-agent "What's a good LangGraph pattern for a support bot?"
See shared/llm/factory.py for provider selection and ANTHROPIC_MODEL /
OPENAI_MODEL overrides.
Testing¶
Project layout¶
agentic-design-patterns/
├── main.py # Typer CLI: list/run patterns
├── docs/
│ ├── architecture-overview.md # cross-pattern decision tree + comparison table
│ ├── harnesses-and-loops.md # pointer to the essay, now in the ai-harnesses repo
│ ├── observability.md # the narrated trace vs. the --otel span tree
│ └── real-world-examples.md # the decision tree applied to concrete scenarios
├── patterns/
│ ├── prompt_chaining/
│ ├── routing/
│ ├── parallelization/
│ ├── orchestrator_workers/
│ ├── evaluator_optimizer/
│ ├── react_agent/
│ └── human_in_the_loop/
│ ├── graph.py # StateGraph wiring: nodes + edges
│ ├── nodes.py # node functions + the fake-LLM script for this pattern
│ ├── run.py # `main()` + CLI entry point
│ ├── tests/test_*.py # deterministic end-to-end tests
│ └── README.md # pattern-specific diagram + notes
└── shared/
├── llm/
│ ├── factory.py # get_chat_model(provider=...) — fake / openai / anthropic
│ └── fake.py # FakeChatModel: scripted BaseChatModel, no network needed
├── render.py # formatting helpers for each run.py's narrated trace
├── obs.py # opt-in OpenTelemetry wiring for `run ... --otel`
└── tools/
├── basic.py # calculator, search_docs, word_count, send_message
└── mcp_server.py # those same tools served over MCP, for any harness
Every pattern follows the same shape: graph.py defines the StateGraph
and its edges, nodes.py holds the node functions (each backed by
shared.llm.factory.get_chat_model), run.py exposes a main(...) the
CLI calls into plus a render(result) that narrates the run, and
tests/ exercises the graph end to end against the fake LLM.
Adding a new pattern¶
mkdir -p patterns/<name>/testswith__init__.pyfiles.- Write
nodes.py: node functions plus a_fake_responderthat gives deterministic behavior for tests (see any existing pattern for the shape), wired up viaget_chat_model(responder=_fake_responder). - Write
graph.py: aTypedDictstate and abuild_graph()that wires nodes withadd_edge/add_conditional_edges. - Write
run.pywith amain(...)function, arender(result) -> strthat narrates the pattern's mechanic (useshared.renderhelpers), and a CLI-runnableif __name__ == "__main__"block that printsrender(main(...)). - Add tests in
tests/test_<name>.pyand register the pattern inmain.py'sPATTERNSdict.
Resources¶
- Building Effective Agents (Anthropic) — the taxonomy this repo follows
- Effective context engineering for AI agents (Anthropic) — memory/context management this repo doesn't implement yet; see
ai-harnesses's Harnesses and Loops - Effective harnesses for long-running agents (Anthropic)
- LangGraph documentation
- ReAct: Synergizing Reasoning and Acting in Language Models
License¶
This project is licensed under the MIT License.