Agent Memory & Planning
Short-term, long-term and episodic memory; task decomposition and planning for complex agents.
Memory & planning
Memory lets an agent persist context across turns and sessions. Short-term = the current conversation. Long-term = facts learned across sessions (user preferences, past decisions). Episodic = records of past runs.
Planning is task decomposition: the agent breaks a complex task into sub-tasks, executes them, and synthesises. Without planning, agents thrash; with it, they converge faster and more reliably.
Why memory and planning matter
Without memory, every conversation starts from scratch — no personalisation, no learning. Without planning, agents attack complex tasks in one shot and fail. Memory + planning turn a single-shot completion into an adaptive, improving system.
Memory architecture
Short-term: conversation history (sliding window, summarised when too long). Long-term: vector store of facts/preferences per user, retrieved at query time. Episodic: log of past runs, retrieved when similar tasks arise. Planning: a planner LLM call decomposes the task into steps before the executor agent runs.
Memory + planning
from dataclasses import dataclass, field
@dataclass
class Memory:
short_term: list[dict] = field(default_factory=list)
user_id: str = ""
def add_message(self, role: str, content: str):
self.short_term.append({"role": role, "content": content})
# Summarise if history too long
if len(self.short_term) > 20:
self._summarise()
def _summarise(self):
"""Compress old messages into a summary to free context budget."""
old = self.short_term[:15]
recent = self.short_term[15:]
summary = llm_summarise(old)
self.short_term = [{"role": "system", "content": f"Conversation summary: {summary}"}] + recent
def recall_long_term(self, query: str) -> list[dict]:
"""Retrieve relevant long-term memories for this user."""
qemb = embed(query)
return vector_store.search(
embedding=qemb,
filter={"user_id": self.user_id},
k=3,
)
def plan(task: str) -> list[str]:
"""Decompose a complex task into steps."""
response = client.chat.completions.create(
model="gpt-4o",
messages=[
{"role": "system", "content": "Decompose the task into 3-7 concrete steps. Return as JSON array of strings."},
{"role": "user", "content": task},
],
response_format={"type": "json_object"},
)
import json
return json.loads(response.choices[0].message.content)["steps"]
def execute_with_plan(task: str, memory: Memory, tools: dict) -> str:
"""Plan, then execute each step, accumulating results."""
steps = plan(task)
memory.add_message("system", f"Plan: {steps}")
results = []
for i, step in enumerate(steps):
memory.add_message("user", f"Step {i+1}: {step}")
result = run_agent(step, tools, memory)
results.append(result)
memory.add_message("assistant", result)
# Synthesise
return run_agent(f"Synthesise final answer from: {results}", tools, memory)Experiment: planning vs no planning
Compare single-shot vs plan-then-execute on a complex task.
What to observe
Planning trades latency and cost for success rate. The more complex the task, the bigger the win. For simple tasks, single-shot is fine. The break-even is around 3 steps — below that, skip planning.
Production memory & planning
Production memory: summarise long histories (don't truncate — you lose context), persist long-term memory per user in a vector store, and version the memory schema. Production planning: decompose, execute in parallel where possible, synthesise, and reflect. Always trace the plan + execution for debugging.
Challenge
Your agent forgets the user's name mid-conversation. Design a memory system that persists key facts (name, preferences) across sessions without exceeding context budget.
Production checklist
Production checklist
0 of 8 checked
Knowledge check
Your conversation history exceeds the context window. What's the BEST approach?
Complete
You can now build agents with memory and planning. This completes the Building AI Agents series. Next: agentic workflows — graphs, multi-agent and MCP.
Mark this chapter as complete
Track your progress and unlock the next chapter.