AI & ML — ADVANCED
AI Agents in Production: Memory Types, Real Code, and Failure Modes
Everything so far has shown an agent succeeding. Production systems have to plan for the version where it doesn’t — a tool called with the wrong argument, a loop that never ends, a task that costs far more than expected.
The Quick Answer
“Memory,” loosely used in the earlier levels, actually splits into four distinct kinds, each serving a different purpose. And the same tool-calling loop covered in Intermediate can be written out as real, runnable code — including the guardrails a production system actually needs, since an ungoverned loop can hallucinate arguments, run forever, or quietly rack up a large bill before anyone notices.
The Four Kinds of Agent Memory
The CoALA framework (Sumers et al.) describes a language agent’s memory as “modular memory components” rather than one single thing. Applied to an agent built around tool calling, four distinct kinds show up in practice:
Working memory
The current context window — recent messages, tool results, and the immediate task state. Resets when the session ends unless deliberately saved.
Episodic memory
Records of specific past events — “last Tuesday, this tool call failed for this reason.” Useful for learning from what actually happened before.
Semantic memory
General factual knowledge, often stored and retrieved the same way covered in Vector Database — not a specific event, just “what is true.”
Procedural memory
Knowledge of how to do something — a skill. This can live in the model’s own weights, or as explicit code/instructions the agent has access to.
Source: Sumers et al., “Cognitive Architectures for Language Agents,” arXiv:2309.02427
The “memory” from the earlier levels — the growing context window during one task — is working memory. Anything that needs to survive past that one session, like a knowledge base an agent can search, has to be built deliberately as episodic or semantic memory instead.
How the Four Memory Types Relate to Each Other
These four types aren’t independent boxes — they connect through exactly two named actions, and every connection passes through working memory. Retrieval reads information from a long-term store into working memory, so the model can use it right now. Learning writes information from working memory back out into a long-term store, so it survives past the current session. A third action, reasoning, only updates working memory itself — it’s the model actually thinking, using whatever is currently in view.
The detail worth noticing: no long-term store ever hands information directly to another long-term store. Something learned in episodic memory doesn’t become semantic knowledge on its own — it has to be retrieved back into working memory first, reasoned over, and then written out again as a separate learning action if it belongs in semantic memory too.
What Each Memory Type Is For, and When to Check It
When an agent gives a wrong answer, the fastest way to narrow down the cause is knowing which memory type it likely came from:
| Memory type | What it’s for | Check this when… |
|---|---|---|
| Working memory | Holds the current context — the task at hand, recent messages, tool results seen so far. | The agent contradicts something from earlier in the same conversation, or seems to “forget” a tool result it just received. |
| Episodic memory | Stores records of specific past events — what happened, when, and with what outcome. | The agent repeats a mistake it should already know about, or can’t recall how a similar past task actually went. |
| Semantic memory | Stores general factual knowledge, independent of any one event — “what is true.” | The agent states a fact that’s outdated, wrong, or missing entirely — not a one-off event, but knowledge that should be reliably known. |
| Procedural memory | Stores how to do something — a skill, an instruction, a tool definition. | The agent gets the facts right but performs the wrong steps, calls the wrong tool, or follows an outdated process. |
A Debug Guideline for Agent Memory Issues
Once the table above points at a likely memory type, work through these steps in order rather than guessing at a fix:
1. Reproduce with the full trace visible
Capture the exact working memory at the moment of the wrong answer — every message, every tool call and result. Most memory bugs are invisible without seeing this full trace, since the failure is rarely in the final text itself.
2. Ask what kind of thing was wrong
A wrong recent fact points at working memory. A repeated past mistake points at episodic memory. A wrong general fact points at semantic memory. A right fact but wrong action points at procedural memory. This single question narrows the search dramatically before touching any code.
3. Check retrieval before blaming the model
For episodic or semantic issues, confirm the relevant information was actually retrieved into working memory at all — a retrieval miss looks identical to a “hallucination” from the outside, but the fix is completely different (fix the search, not the model).
4. Check what actually got written, not just what should have
For a learning (write) issue, confirm the record that should exist in long-term memory is actually there, in the form later retrieval expects — a common failure is writing successfully but in a format or location retrieval never looks in.
Most agent “hallucinations” that get blamed on the model are actually a retrieval miss or a working-memory truncation — the model answering correctly given what it could actually see, which simply wasn’t the right information. Checking memory first, before assuming the model itself is wrong, saves a lot of wasted debugging time.
A Real Tool-Calling Agent Loop, in Code
Here’s the same Tokyo temperature task from earlier levels, written as an actual runnable agent loop — a fixed decision function standing in for what a real model would decide at each step, real tool functions, and a hard cap on iterations:
MAX_ITERATIONS = 5
def get_weather(location):
return 21 # Celsius
def convert_temperature(value, from_unit, to_unit):
if from_unit == 'C' and to_unit == 'F':
return round(value * 9/5 + 32, 1)
raise ValueError('unsupported conversion')
def decide_next_action(state):
if 'weather' not in state:
return {'tool': 'get_weather', 'args': {'location': 'Tokyo, Japan'}}
if 'fahrenheit' not in state:
return {'tool': 'convert_temperature',
'args': {'value': state['weather'], 'from_unit': 'C', 'to_unit': 'F'}}
return {'tool': None, 'final_answer':
f"It's {state['weather']}°C ({state['fahrenheit']}°F) in Tokyo."}
TOOLS = {'get_weather': get_weather, 'convert_temperature': convert_temperature}
def run_agent_loop(max_iterations=MAX_ITERATIONS):
state = {}
for i in range(max_iterations):
action = decide_next_action(state)
if action['tool'] is None:
return action['final_answer']
result = TOOLS[action['tool']](**action['args'])
if action['tool'] == 'get_weather':
state['weather'] = result
elif action['tool'] == 'convert_temperature':
state['fahrenheit'] = result
raise RuntimeError(f'Exceeded max_iterations ({max_iterations}) without completing task')Running this produces the same two-step trace shown in the Intermediate diagram — a weather lookup, then a conversion, then a final answer — and it actually executes, verified line for line. The max_iterations check is doing real work here, not just decoration: swapping in a decision function that never reaches a final answer causes the loop to raise an error at exactly 5 iterations, instead of calling tools forever.
What Goes Wrong in Production
The loop above is the happy path. Real deployments have to plan for all three of these failing on their own:
Hallucinated tool arguments
The model can generate an argument that looks plausible but refers to something that doesn’t exist — a made-up city, a nonexistent file path, an invalid ID. Strict argument validation inside the tool itself is the real defense, not just trusting the model’s output.
Runaway loops
An agent that never satisfies its own stopping condition will keep calling tools indefinitely. A hard cap on iterations — enforced by the application, not the model — is a non-negotiable production guardrail.
Cost explosion
Every reasoning step and every tool result adds tokens to the context, and every iteration is a fresh model call. A task that should take 3 steps but loops 40 times before hitting a cap can cost an order of magnitude more than expected.
A concrete version of the first failure mode: a get_weather tool that only recognizes a fixed set of real cities will correctly reject a call like location="Neo Tokyo, Japan" — a plausible-sounding but nonexistent place a model might generate — with a clear error, rather than silently returning nonsense or crashing further downstream.
Fun Fact
Some tasks genuinely need more than one agent working together — a lead agent that plans and delegates, and separate subagents that each work a narrow slice of the problem in their own isolated context. That’s covered in depth, with a real measured performance result, in Context Engineering Advanced’s multi-agent case study.
Test Yourself
According to the CoALA framework, what is the difference between episodic and semantic memory?
Can episodic memory hand information directly to semantic memory, without passing through working memory?
An agent correctly recalls a fact but performs the wrong sequence of steps to use it. Which memory type does the debug guideline point to first?
In the worked agent-loop code on this page, why does the loop include a max_iterations cap?
Why is validating a tool’s arguments inside the tool itself important, rather than trusting the model’s output?