Skip to main content

Memory and planning

โฑ 27 min

Hookโ€‹

An agent computes a company's net revenue perfectly โ€” then, asked the very next question, computes it again from scratch, because it kept nothing from the first answer. A second agent is handed "produce the quarterly summary" and immediately calls a "write report" tool, having never gathered a single number. One agent has no memory; the other has no plan. Both fail the moment a task takes more than one step, which is to say: on any task worth using an agent for.

Conceptโ€‹

The loop from Lesson 1 runs many times, and two things make those repetitions add up to real work: the agent has to remember what it has learned, and it has to sequence what it does next. Those are memory and planning.

Memory is the state the agent carries from one loop iteration to the next. In its simplest and most important form it is a scratchpad: a running record of the observations the agent has gathered and the intermediate results it has produced. When the loop "observes" a tool result, it writes that result into memory; when it next "plans," it reads memory to decide what is still needed. In the toy loop you will build in the lab, memory is a plain Python dictionary. That is not a simplification for teaching โ€” a dictionary threaded through the loop is genuinely the core of working memory. Larger systems layer more on top (conversation history, longer-term stores, retrieval), but the mechanism is the same: state that survives between steps.

Planning is turning a goal into an ordered sequence of actions. The central skill is task decomposition: breaking a goal too big for one tool call into subgoals that each map to a call. "Compute net revenue" decomposes into "compute gross" then "subtract refunds"; the second subgoal cannot start until the first has written its result into memory. That ordering โ€” and the dependency it encodes โ€” is the plan.

There are two honest styles of planning, and you will meet both:

StyleHow it decidesWhen it fits
Plan-then-actDecompose the whole goal into an ordered plan up front, then execute itTasks whose steps are knowable in advance
Step-by-stepPlan one action, act, observe, then plan the next from the resultTasks where each step depends on what you just saw

Real tasks break, which is why the loop matters more than any fixed plan. A tool returns an error, a value is missing, an assumption was wrong. Replanning on failure is the agent's response: it observes the failure and chooses a different next action โ€” insert a missing lookup, retry with different arguments, or give up cleanly. Because control flow lives in the loop, replanning is just the next "plan" beat reacting to the last "observe." This is also where the iteration cap from Lesson 1 earns its place: replanning that never converges must still terminate.

Keep the discipline from the first two lessons. The plan and the memory you reason about are observable artifacts โ€” the ordered list of subgoals the agent emitted, and the recorded contents of the scratchpad. They are not a window into a model's private reasoning; they are the concrete state and actions in the record. When a multi-step run goes wrong, you debug it by reading the plan and the scratchpad at each step, not by guessing what the model "meant."

๐Ÿง  Knowledge check
1. What is an agent's working memory, in its simplest form?
2. What does task decomposition produce?

Worked exampleโ€‹

Let me show decomposition and memory working together, deterministically and with no LLM. The goal is "net revenue." The planner decomposes it into an ordered plan, and the executor threads results through a memory dictionary so each step can use what the last one produced.

FACTS = {"units": 1200, "price": 9.50, "refund_rate": 0.05}


def decompose(goal):
"""Turn a goal into an ordered plan of subtasks โ€” the observable plan."""
return {
"net_revenue": ["compute_gross", "apply_refunds"],
}[goal]


def execute(subtask, memory):
"""Run one subtask, reading earlier results from memory (the scratchpad)."""
if subtask == "compute_gross":
return FACTS["units"] * FACTS["price"]
if subtask == "apply_refunds":
return memory["compute_gross"] * (1 - FACTS["refund_rate"])
raise KeyError(subtask)


def run_plan(goal, max_steps=8):
plan = decompose(goal)
memory = {}
for step, subtask in enumerate(plan, 1):
if step > max_steps: # iteration cap โ€” bounded even if a plan misbehaves
break
memory[subtask] = execute(subtask, memory)
print(f"step {step}: {subtask} -> {memory[subtask]}")
return memory


memory = run_plan("net_revenue")
print("final:", memory["apply_refunds"])
step 1: compute_gross -> 11400.0
step 2: apply_refunds -> 10830.0
final: 10830.0

Watch the two ideas cooperate. decompose returns the plan โ€” an ordered list, ["compute_gross", "apply_refunds"], that you can read and log. The executor runs each subtask and writes its result into memory, so when apply_refunds runs, it reads memory["compute_gross"] rather than recomputing it. The dependency is real: swap the order and the second step fails, because the value it needs is not in memory yet. That is decomposition and working memory in their smallest honest form โ€” and the max_steps guard is the same bounded-loop instinct, carried over so no plan can run away.

Hands-onโ€‹

Now give an agent a two-step goal and watch it decompose and remember. The exercise below opens in the Agent Builder with a goal that no single tool can satisfy.

โ–ถ Agent Builderaa101-decompose-and-remember
Build it in Agent Builder โ†—

Do this: run the agent and open the trace. Identify the plan โ€” the ordered subgoals it chose โ€” and then find, for a later step, the memory entry it read that an earlier step wrote. Finally, break a tool so one step fails, re-run, and watch the agent replan. Success criteria: you can point to the decomposition, name one cross-step memory dependency, and describe how the replan differed from the original plan โ€” all from the trace, not from any guess about the model's reasoning.

Recapโ€‹

  • You can define memory as the state threaded between loop iterations, and describe a scratchpad as observations and intermediate results the loop writes and reads.
  • You can explain planning as decomposing a goal into an ordered sequence of tool calls, and identify the dependency that fixes their order.
  • You can contrast plan-then-act with step-by-step planning and say which suits a task whose steps depend on what was just observed.
  • You can describe replanning on failure as the next plan beat reacting to a failed observation, and say why the iteration cap still bounds it.

That completes the core loop: observe and plan and act, tools that are bounded and validated, and memory and planning that turn single steps into finished work. Next you put all of it together in the lab โ€” a bounded, deterministic agent you build and run yourself, with two tools, a scratchpad, an iteration cap, and an explicit stop.

๐Ÿง  Knowledge check
1. A tool call in the middle of a plan returns an error. In a loop-based agent, what is "replanning"?
2. Why is an iteration cap still necessary once an agent can replan on failure?
๐Ÿ“Œ Key takeaways
  • Memory is the state an agent threads between loop steps โ€” a scratchpad the "observe" beat writes and the "plan" beat reads โ€” so later steps build on earlier results. - Planning is decomposing a goal into an ordered sequence of tool calls; the ordering encodes real dependencies between subgoals. - Replanning on failure is the next plan beat reacting to a failed observation, and the iteration cap still bounds it so a non-converging run always terminates.