Plan & Solve: Plan First, Then Execute
Some tasks fail not because the model cannot do a step, but because it answers too soon.
A travel assistant that jumps straight to the itinerary may skip weather, route order, or packing advice. Plan & Solve adds a simple pause: write a short plan, then solve step by step.
One Sentence
Plan & Solve turns direct answering into plan first, execute second, so multi-step tasks have a visible skeleton.
What Breaks Without It
| Problem | What it looks like | Risk |
|---|---|---|
| Direct answer | Fast | Steps are skipped |
| Thinking while writing | Natural | Task decomposition is hidden |
| No plan artifact | Concise | Constraints are hard to check |
What This Pattern Changes
| Who | Owns |
|---|---|
| Planner | Creates a step list |
| Solver | Executes each step |
| Python | Stores plan, calls steps, traces |
Plans should usually be short. A long plan becomes its own management problem.
Walk Through One Trace
| Stage | Output | Next |
|---|---|---|
| Plan | Compute 2+2, Return the result |
Execute steps |
| Solve 1 | 2+2=4 |
Continue |
| Solve 2 | Result is 4 |
Summarize |
| Final | Final answer: 4 |
Stop |
For travel, the plan might be: extract preferences → check weather → draft route → packing advice.
Flow
flowchart TD
T["Task"] --> P["Create plan"]
P --> S1["Execute step 1"]
S1 --> S2["Execute step 2"]
S2 --> F["Summarize final answer"]
Code Walk
The model first returns a plan:
model = MockLLM(
[
'{"plan":["Compute 2+2","Return the result"]}',
"2+2=4",
"Result is 4",
"Final answer: 4",
]
)
Full example:
from __future__ import annotations
from pathlib import Path
from agent_patterns_lab.patterns.plan_and_solve import plan_and_solve
from agent_patterns_lab.runtime import MockLLM, Tracer
def main() -> None:
tracer = Tracer()
model = MockLLM(
[
'{"plan":["Compute 2+2","Return the result"]}',
"2+2=4",
"Result is 4",
"Final answer: 4",
]
)
result = plan_and_solve(model, task="Compute 2+2.", tracer=tracer)
print(result.answer)
trace_path = tracer.export_jsonl(Path(".traces") / "50_plan_and_solve.jsonl")
print(f"[trace] {trace_path}")
if __name__ == "__main__":
main()
Run:
UV_CACHE_DIR=.uv_cache PYTHONPATH=src uv run --no-sync python examples/50_plan_and_solve.py
Nearby Patterns
| Pattern | Who decides next | Use when |
|---|---|---|
| Prompt Chaining | Python fixes steps | Flow is known |
| Plan & Solve | Model writes a plan first | Task needs decomposition |
| PER | Replanner can change plan | Plan may become stale |
| LATS | Search compares candidates | One plan is unstable |
When To Use It
- The task has multiple steps.
- The plan can be checked before execution.
- The plan is short and executable.
- You want something cheaper than an agent loop.
When Not To Use It
- Code already knows the steps; use workflow.
- Environment changes make plans stale; use PER.
- Plan quality is unstable; use search or checker.
- The user needs a short answer.
Costs And Common Failures
| Failure | Symptom | Fix |
|---|---|---|
| Plan too long | Many unmanaged steps | Limit step count |
| Vague plan | "Analyze deeply" | Require concrete action/output |
| Execution drifts | Solver ignores plan | Trace step ids |
| Bad plan keeps running | Obvious failure continues | Upgrade to PER |
What To Read Next
Plan & Solve is lightweight planning.
If execution may invalidate the plan, read Planner-Executor-Replanner. If you need to search over plans, read LATS.