Module 2 of 10, lesson 1 of 5
Anatomy: components and memory
Name the modules of an agent and choose the types of memory it actually needs.
You can describe an agent as a cycle of modules. Each depends on the one before: a mistake at the input spreads to every later decision.
- Perception: receive and clean inputs (requests, APIs, logs, sensors).
- Reasoning: weigh the options.
- Planning: split the goal into steps and dependencies.
- Action: execute, often through a tool call.
- Communication: report to a human or another agent.
- Learning: use the result to do better next time.
One caveat. In most current LLM agents, these modules aren’t separate components: reasoning and planning happen in the same model call. The breakdown helps you think about the system, not necessarily code it.
Four memories
“Memory” covers very different needs.
- Working memory: the context visible right now. It has a ceiling, and performance degrades when you overfill it.
- Semantic memory: what the agent knows in general. In practice, this is often plain Markdown files loaded at startup.
- Procedural memory: how to do things. A short description stays visible, and the detailed instructions load only when needed.
- Episodic memory: what happened. A raw transcript helps little; a summary of decisions and their lessons helps a lot.
Forgetting is a feature
Memory that only grows ends up polluting decisions. You have to decide what expires, what gets summarized and what gets corrected. With personal data, this is also a legal duty.
Add a memory to solve a problem you’ve observed, not because the diagram shows four.
Worked example: Fitting memories into a budget
The context window is a budget. Before each call, the agent keeps the most useful memories that fit, not all of them.
function pickMemories(memories, budget) {
const sorted = [...memories].sort((a, b) => b.score - a.score);
const kept = [];
let used = 0;
for (const memory of sorted) {
if (used + memory.tokens > budget) continue;
kept.push(memory.id);
used += memory.tokens;
}
return kept;
}
const memories = [
{ id: 'refund-policy', score: 0.9, tokens: 300 },
{ id: 'full-transcript', score: 0.8, tokens: 900 },
{ id: 'last-order', score: 0.6, tokens: 100 },
];
pickMemories(memories, 500);
['refund-policy', 'last-order']
The transcript scores well but does not fit, so a smaller memory takes its place. This is why a distilled note beats a saved conversation.
Try it in the editor
// Rough estimate: about 4 characters per token.const countTokens = (message) => Math.ceil(JSON.stringify(message.content).length / 4);function trimContext(messages, maxTokens) { // TODO return messages;}module.exports = { trimContext, countTokens };