Skip to content

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

Plan & Solve is lightweight planning.

If execution may invalidate the plan, read Planner-Executor-Replanner. If you need to search over plans, read LATS.

References