← Back to curriculum

Module 8 — Agentic AI

Planning vs execution — ReAct

ReAct traces, planner and executor prompts, retry and replan logic, UI reasoning traces, and workflow vs agentic replan.

~90 min read + exercises

Planning vs execution — ReAct

Before we begin

A single agent with twelve tools and a vague system prompt often mixes strategy and API calls — hallucinated cities, skipped steps, or loops on the wrong tool.

Planning vs execution splits roles:

Planner — what should we do, in what order?
Executor — run tools with correct arguments and handle API errors.

This pattern is the backbone of your travel planner project and appears in almost every production agent interview.

Figure

Planner + executor

Planneritinerary stepsExecutorweather / maps APIs
Strategy separate from reliable tool execution.

What you will learn

  • Explain the ReAct pattern (Reason + Act) with a full trace.
  • Design planner and executor prompts and outputs.
  • Handle retries, replanning, and visible reasoning in the UI.
  • Know when a fixed workflow is enough vs full agentic replan.

Before this lesson


ReAct — Reason and Act

ReAct (Reason + Act) alternates natural language reasoning with tool actions:

  1. Thought — internal reasoning (“I need weather before outdoor activities.”)
  2. Action — tool name + arguments
  3. Observation — tool result from your app
  4. Repeat until Final answer

Why thoughts help

  • Model commits to a plan before acting — fewer random tool calls.
  • You can show thoughts in the UI — users trust “checked weather API” more than a black box.
  • Debugging — read the trace when something fails.

Optional in production: hide raw thoughts from users but log them for support.


Full worked trace — Rome weekend

User: “Sat–Sun in Rome with kids — avoid rain if possible.”

text
Thought 1: I need weather for both days before suggesting activities.
Action 1: get_weather(city="Rome", start="2025-06-28", days=2)
Observation 1: {"sat": {"condition": "rain", "high_c": 19}, "sun": {"condition": "clear", "high_c": 26}}
 
Thought 2: Saturday rainy — indoor family options. Sunday clear — parks or outdoor museums.
Action 2: search_places(city="Rome", tags=["family", "indoor"], limit=5)
Observation 2: [{"name": "Explora Museum", ...}, {"name": "Vatican Museums", ...}]
 
Thought 3: I have enough to draft a balanced itinerary.
Final answer: [structured 2-day plan with times, citing weather per day]

Count: 2 tool rounds + 1 synthesis. Without ReAct-style structure, models often answer from memory without calling tools at all.


Planner agent (design)

Job: break the user goal into ordered steps without calling external APIs (optional but recommended for reliability).

Planner system prompt (sketch):

text
You are a travel planner. Output ONLY valid JSON:
{
  "steps": [
    {"id": 1, "description": "...", "tool_hint": "get_weather"},
    ...
  ],
  "assumptions": ["budget not specified — assume mid-range"]
}
Do not invent API results. Do not call tools.

Example planner output:

json
{
  "steps": [
    {"id": 1, "description": "Fetch weekend weather for Rome", "tool_hint": "get_weather"},
    {"id": 2, "description": "Find kid-friendly venues matching weather", "tool_hint": "search_places"},
    {"id": 3, "description": "Synthesize day-by-day itinerary", "tool_hint": null}
  ],
  "assumptions": ["Travel dates: this coming Sat–Sun"]
}

Why JSON plan?

  • Executor has a checklist — easier to eval (Lesson 7).
  • Planner cannot hallucinate temp_c: 22 — only the executor sees API data.
  • UI can show plan steps before execution starts.

Executor agent (design)

Job: for one plan step, pick the right tool, validate args, run it, return structured observation.

Executor system prompt (sketch):

text
You execute ONE plan step. You have tools: get_weather, geocode_city, search_places.
Return either a tool call OR a step completion summary with facts from observations only.
Never fabricate temperatures or opening hours.

Executor for step 1:

text
Action: get_weather(city="Rome", start="2025-06-28", days=2)
Observation: {"sat": "rain", "sun": "clear"}
Step 1 complete.

If the executor role is merged with ReAct in a single agent, use the same discipline: thought → action → observation per step.


Retry and failure handling

Real APIs fail. Design explicit recovery:

text
Attempt 1: get_weather("Pariss") → error: city not found
Thought: Likely typo — retry Paris
Attempt 2: get_weather("Paris") → success

Rules:

RuleValue (example)
Max retries per tool call3
Max total loop iterations10–15
After retries exhaustedReturn error to planner for replan OR ask user

Replan example:

text
Observation: flights API rate-limited for 60s
Planner revision: skip flight prices; use train estimate; note limitation in final answer

LangGraph (Lesson 6) models execute → (success|retry|replan) as graph edges — a graph is a diagram of nodes (steps) and arrows (transitions).


Showing reasoning in the UI

Users and stakeholders want transparency. Your travel project should expose a trace array:

json
[
  {"type": "thought", "text": "Need weather first"},
  {"type": "tool", "name": "get_weather", "args": {"city": "Rome"}, "ok": true},
  {"type": "observation", "summary": "Sat rain, Sun clear"},
  {"type": "final", "text": "Day 1: Explora Museum..."}
]

Render as a collapsible timeline — not raw JSON dumps.


Workflow vs agentic replan

ApproachFlow
Fixed workflowPlan (once) → Execute step 1 → 2 → 3 → Done
Agentic replanPlan → Execute step 1 → failure → new plan → continue

Use fixed workflow when steps are stable and failures are rare. Use replan when tools fail often or user goals shift mid-session (“actually make it Florence”).

Hybrid (recommended): planner runs once; executor may retry; only serious failures trigger full replan.


Orchestration vs plain LLM

Single LLM replyOrchestrated planner + executor
Tool useMay skip toolsExecutor forced per step
DebugOne blobPer-step traces
Cost1 call2+ calls
Quality on complex tasksLowerHigher

Agent orchestration = coordinating multiple LLM calls, tools, and state — not one magic prompt.


Prompt templates (copy-paste starters)

Planner user message:

text
Goal: {user_message}
Known preferences: {memory_snippet}
Output JSON plan only.

Executor user message:

text
Current step: {step_json}
Completed observations: {prior_observations}
Execute this step using tools.

Version these in git like application code.


Check yourself

  1. Write one Thought → Action → Observation triple for a weather query.
  2. Why separate planner from executor?
  3. What happens after 3 failed get_weather retries?
  4. What would you show in the UI trace for trust?

What's next

Lesson 4 — Agent memory — short-term session state and long-term user preferences.