Prompt Chaining: Split One Big Task Into Smaller Steps
The travel assistant starts with one prompt:
Plan a relaxed Hangzhou day trip. I like tea, local food, and easy walking.
The model may write a route in one shot. But if you need it to extract preferences, draft an itinerary, and format the final answer, those jobs blur together inside one prompt. The model may miss a constraint or change content while formatting.
Prompt Chaining does not make the model more autonomous. It does the opposite: when the steps are known, Python owns the path.
One Sentence
Prompt Chaining turns one large prompt into fixed smaller steps, so each step has one job and intermediate outputs can be inspected.
What Breaks Without It
| Problem | What it looks like | Risk |
|---|---|---|
| One prompt contains every instruction | Output looks complete | You cannot tell which part failed |
| Extraction and writing are mixed | The answer reads well | Budget or walking constraints may disappear |
| Final answer is generated directly | Fewer calls | UI, tests, and reviewers cannot inspect intermediate work |
What This Pattern Changes
| Who | Owns |
|---|---|
| Python | Step order, passing outputs forward, trace |
| Model | One small task per step |
| Parser / checker | Optional validation at step boundaries |
The next step is chosen by code, not by the model.
Walk Through One Trace
| Step | Input | Output | Why split it |
|---|---|---|---|
| 1 Extract preferences | User request | tea, local food, easy walking |
Capture constraints first |
| 2 Draft itinerary | Preferences | West Lake, Tea Museum, Hefang Street | Focus on route |
| 3 Format answer | Draft | Concise final itinerary | Focus on presentation |
Flow
flowchart LR
I["User request"] --> A["Step 1: Extract preferences"]
A --> B["Step 2: Draft itinerary"]
B --> C["Step 3: Format answer"]
C --> O["Final itinerary"]
Code Walk
Define fixed steps:
steps = [
PromptStep(name="extract_preferences", user_prompt="Extract travel preferences from: {input}"),
PromptStep(name="draft_itinerary", user_prompt="Draft an itinerary from these preferences:\n{input}"),
PromptStep(name="format_final", user_prompt="Format this itinerary as a concise final answer:\n{input}"),
]
Run them in order:
out = run_prompt_chain(model, initial_input=task, steps=steps, tracer=tracer)
Full example:
from __future__ import annotations
from pathlib import Path
from agent_patterns_lab.patterns.workflow_chaining import PromptStep, run_prompt_chain
from agent_patterns_lab.runtime import MockLLM, Tracer
def main() -> None:
tracer = Tracer()
model = MockLLM(
[
"Preferences: tea, local food, easy walking. Constraint: relaxed one-day trip.",
"Draft: Morning West Lake, afternoon Tea Museum, evening Hefang Street.",
"Final itinerary: West Lake -> China National Tea Museum -> Hefang Street.",
]
)
steps = [
PromptStep(name="extract_preferences", user_prompt="Extract travel preferences from: {input}"),
PromptStep(name="draft_itinerary", user_prompt="Draft an itinerary from these preferences:\n{input}"),
PromptStep(name="format_final", user_prompt="Format this itinerary as a concise final answer:\n{input}"),
]
out = run_prompt_chain(
model,
initial_input="Plan a relaxed one-day Hangzhou trip. I like tea, local food, and easy walking.",
steps=steps,
tracer=tracer,
)
print(out)
trace_path = tracer.export_jsonl(Path(".traces") / "11_prompt_chaining.jsonl")
print(f"[trace] {trace_path}")
if __name__ == "__main__":
main()
Run:
UV_CACHE_DIR=.uv_cache PYTHONPATH=src uv run --no-sync python examples/11_prompt_chaining.py
Nearby Patterns
| Pattern | Who decides next | Use when |
|---|---|---|
| Chatbot | No next step | One answer is enough |
| Prompt Chaining | Python fixes the order | Steps are known |
| Routing | Code/model picks a branch | Inputs need different flows |
| ReAct | Model chooses from observations | Tool results change the next step |
When To Use It
- You can write the steps before the run starts.
- You want intermediate artifacts.
- Each step has a clear input and output.
- A fixed workflow is cheaper and easier to test than an open loop.
When Not To Use It
- The next step depends on a fresh tool result.
- One model call is enough.
- Step boundaries are vague.
- Latency is tight and extra calls are not worth it.
Costs And Common Failures
| Failure | Symptom | Fix |
|---|---|---|
| Over-splitting | Tiny task becomes many calls | Merge steps without independent value |
| Error propagation | Bad extraction poisons later steps | Validate at step boundaries |
| Interface drift | Step output changes shape | Use structured output |
| Thin trace | Only final answer is logged | Record every step input/output |
What To Read Next
If the steps are fixed, use Prompt Chaining.
If inputs need different paths, read Routing. If tool observations change the path, read ReAct.