The agent that forgot the budget
Agent memory is whatever an agent carries from one step, or one run, to the next. A model remembers nothing by itself, so memory is text your program puts back into the prompt.
A shopping agent suggests a 60 pound coat to a customer who said 30. The budget was said in step one and was gone by step nine.
By the end of this lesson
Note
I can decide what an agent remembers between steps and between runs.
Key words
| Word | What it means | Example |
|---|---|---|
| Scratchpad | notes an agent keeps during one run | |
| Summary | a shorter text that stands for a longer one | |
| Store | memory kept between runs | |
| Memory budget | the most text memory may hold | 120 characters |
| Context window | the most text a model reads at once |
The cells students fill in are left blank in this preview.
Three kinds of memory
The scratchpad is the list of messages and notes inside one run. A summary replaces a long history with a short text. A store lives between runs, in a file or a database.
All three end up in the prompt. So every remembered line is sent again at every step, and you pay for it each time.
Worked example: the three kinds compared
| Kind | Lives for | Grows by | Main risk |
|---|---|---|---|
| Scratchpad | One run | Every step | Fills the context window |
| Summary | One run or more | Replaced each time | Loses a detail that mattered |
| Store | Between runs | Every run | Old facts that are no longer true |
The cells students fill in are left blank in this preview.
Write the model helper, part 1
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const base = process.env.AI_URL || \"http://ai:4000\";' 'async function ask(messages, sample, max) {' ' try {' ' const res = await fetch(base + \"/v1/chat/completions\", {' ' method: \"POST\",' ' headers: { \"content-type\": \"application/json\", authorization: \"Bearer lab\" },' ' body: JSON.stringify({ model: \"lab\", messages: messages, max_tokens: max || 200 }),' ' signal: AbortSignal.timeout(30000),' ' });' ' if (res.ok === false) throw new Error(\"status \" + res.status);' ' return (await res.json()).choices[0].message.content;' > model.js"Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
Write the model helper, part 2
docker exec -w /home/node/project agent sh -c "printf '%s\n' ' } catch (error) {' ' console.log(\"Recorded sample: no model answered (\" + error.message + \").\");' ' const list = [].concat(sample);' ' const turn = messages.filter(function (m) { return m.role === \"assistant\"; }).length;' ' return list[Math.min(turn, list.length - 1)];' ' }' '}' 'module.exports = { ask };' >> model.js"Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
Write the memory demo, part 1
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const { ask } = require(\"./model\");' 'const notes = [' ' { text: \"Customer is Mia, a gift for her brother\", important: true },' ' { text: \"It is raining today\", important: false },' ' { text: \"Budget is 30 pounds\", important: true },' ' { text: \"Socks cost 4 pounds\", important: false },' ' { text: \"A scarf costs 18 pounds\", important: false },' ' { text: \"Mia dislikes anything red\", important: true },' '];' 'const BUDGET = 120;' 'const memory = [];' 'function size() { return memory.reduce(function (n, m) { return n + m.text.length; }, 0); }' > memory1.js"Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
Write the memory demo, part 2
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'function remember(note) {' ' memory.push(note);' ' while (size() > BUDGET) {' ' const at = memory.findIndex(function (m) { return m.important === false; });' ' if (at === -1) break;' ' console.log(\" forgot: \" + memory[at].text);' ' memory.splice(at, 1);' ' }' ' console.log(\"Added, \" + memory.length + \" notes, \" + size() + \" characters\");' '}' >> memory1.js"Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
Write the memory demo, part 3
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'async function main() {' ' notes.forEach(remember);' ' const joined = notes.map(function (n) { return n.text; }).join(\". \");' ' const ask1 = [{ role: \"user\", content: \"Summarise these notes in one sentence for an agent that continues the work: \" + joined }];' ' const sample = \"Mia wants a gift for her brother, up to 30 pounds, nothing red.\";' ' console.log(\"Summary: \" + await ask(ask1, sample, 80));' '}' 'main();' >> memory1.js"Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
Run it and watch what is forgotten
docker exec -w /home/node/project agent node memory1.jsShown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
One possible output
| Line | What it may show |
|---|---|
| Added, 5 notes, 119 characters | The count and the characters after each note |
| forgot: It is raining today | The sixth note takes the store over 120, so the oldest unimportant note goes |
| forgot: Socks cost 4 pounds | Still over, so the next unimportant note goes |
| Added, 4 notes, 106 characters | Three important notes and the scarf are kept |
| Summary | One sentence naming the budget and the colour to avoid |
The cells students fill in are left blank in this preview.
Decide what is worth remembering
Keep what the next step needs: what the user asked for, limits they set, decisions made and facts a tool returned. Drop small talk and raw tool output.
Keep important notes word for word. A summary is written by a model and can change a number. Summarise the chatter and pin the facts.
A common mistake
Warning
Many people think more memory makes a better agent. Memory is paid for at every step and old notes can mislead. Keep what the next step needs, and give every store a size limit and a way to forget.