Memory Systems
Short-term memory is what sits in the context window right now. Long-term memory is facts you saved on purpose and pull back only when the current task needs them.
Why Does This Exist?
Say you use a coding assistant for weeks. In week one you tell it you test with pytest and you want type hints everywhere. In week eleven you expect type hinted code and pytest fixtures without repeating yourself. That only happens if something from week one survived after its window closed.
A model call is stateless. It reads exactly the tokens in its context window and keeps none of them afterward. Context engineering decides what fits inside that window right now. Memory systems decide what stays true next time, without pasting every old chat into every new prompt.
This page covers that system: what counts as short term, what earns a durable write, and how retrieval pulls facts back. The split inside durable storage lives in episodic and semantic memory. The loop that spends memory on real tasks lives in AI agent architecture.
Think of It Like This
A whiteboard versus a filing cabinet
You work in a room with a whiteboard and a filing cabinet down the hall.
The whiteboard is the context window. Anything written on it sits right in front of you and costs nothing to read. When the meeting ends and someone wipes it for the next team, everything on it is gone.
The cabinet is durable storage. Filing takes a real decision. You do not file every napkin sketch, only the note worth keeping, like the client's test runner. A filed note does nothing for today's meeting until someone walks down the hall, pulls that one folder, and copies it back onto the board.
Where it stops: paper never argues with itself, but stored facts do. You change your mind, two folders disagree, and the cabinet will not settle it for you. And a cabinet never charges rent per page, while every retrieved line costs window space, so pulling the whole drawer each morning would bury the board you meant to help.
How It Actually Works
Model calls forget by design, so an agent needs two kinds of memory. Short-term memory is whatever sits in the window right now. Long-term memory is facts written somewhere durable on purpose, plus the two decisions that make them useful: what earns a write, and what gets retrieved.
Short-term ends when the window closes
Call the window: the most tokens one request may hold, input plus output. Call the live contents : system instructions, recent turns, and whatever retrieval just added. is free because it is already there, and temporary because the next session starts with an empty . Nothing is saved by staying in .
If the frame itself is fuzzy, start with context windows and the token math in context budgeting.
Long-term needs a write and a retrieve
Call the durable pile : one row per saved fact, each with its source. A write moves a fact from into . A retrieve moves a few rows from back into for one request. Saved and used are two steps, and skipping the second is how teams end up with a store nobody reads.
The write is a durability judgment. A one off instruction, like renaming one variable today, belongs in and dies there. A repeated preference, like pytest over unittest stated twice and confirmed by your edits, earns a row in . When in doubt, do not write. Every extra row taxes every future search.
Retrieval is usually similarity search over embeddings. Call the current task : the query vector for what you need right now. Call each stored fact : fact number written as a vector. Call its score against : how closely fact matches this task. Call how many you pull back: usually one or two. You rank by and copy only the top rows into . The rest stay filed.
Worked example: three facts ranked, one pulled back
Take three stored facts, each as a two dimensional vector so you can check the math. Real stores use hundreds of dimensions. The arithmetic is the same.
Here stands in for writing a pytest fixture, is prefers pytest, is deploys on Fridays, and is likes demo GIFs.
Score with cosine similarity, which is alignment divided by lengths. The length of a vector is , the straight line distance from the origin.
Lengths first: , and the same for . . .
Dots next: . . .
Divide: . . . Ranked, beats beats . With you pull back only. The diagram shows these three scores and the one returning arrow.
Now the token reason this matters. Name for the fixture window. System needs tokens and today's task needs . Pasting six old sessions at tokens each costs , so the request wants . Overflow is . Something must give.
The store holds twelve facts at tokens each: tokens total, which is of the window. Pulling the winner costs . The request becomes , leaving spare. Same history, two shapes: one never fits, one barely registers.
That is the trade. Persistence costs judgment at write time and a search at read time, and it buys recall across sessions for a fraction of the window. Skip it for single session tools like a summarizer, where yesterday says nothing about today and the machinery never pays off. It assumes past choices predict future ones. When you switch runners or tastes, old rows lie until a new episode overwrites them, which is why the overwrite rule in episodic and semantic memory is load bearing.
Code
The snippet reruns the ranking and the token fit, so you can check both numbers yourself.
import math
def norm(v: list[float]) -> float: """Straight-line length from the origin.""" return math.sqrt(sum(x * x for x in v))
def cosine(u: list[float], v: list[float]) -> float: """Alignment divided by lengths, between -1 and 1.""" return sum(x * y for x, y in zip(u, v)) / (norm(u) * norm(v))
query: list[float] = [3.0, 1.0]memories: list[list[float]] = [[3.0, 1.0], [2.0, 2.0], [0.0, 3.0]]
scores: list[float] = [round(cosine(query, m), 4) for m in memories]print(scores) # -> [1.0, 0.8944, 0.3162]print(scores.index(max(scores))) # -> 0
store_total: int = 12 * 15paste_cost: int = 6 * 500request_paste: int = 400 + 800 + paste_costrequest_memory: int = 400 + 800 + 15print(store_total) # -> 180print(request_paste - 4000) # -> 200print(request_memory) # -> 1215Watch Out For
Filing every turn as a permanent fact
Symptom: the store grows by hundreds of rows a week and retrieval starts returning one off details with the same confidence as real preferences. A query about tests pulls back a renamed variable from March alongside the pytest preference, and the model treats both as instructions.
Fix: write distilled facts, not transcript lines. Require a second confirmation before a write, keep the source episode on every row, and overwrite rivals instead of appending beside them.
Pulling the whole store into every prompt
Symptom: session twelve opens with all forty stored facts pasted in just to be safe. The prompt costs more, answers slower, and the one fact today needed now competes with thirty nine it did not.
Fix: retrieve top only, usually one or two, ranked against today's task. Leave the rest filed and unread until some query actually calls for them.
The Quick Version
- Short-term memory is the live context window. It costs nothing to read and it vanishes when the session ends.
- Long-term memory is facts saved on purpose in durable storage. Saved facts do nothing until retrieval copies them back into the window.
- Writes should record repeated preferences, not one off task details. A fact confirmed twice earns storage.
- Retrieval ranks stored facts against the current task and pulls back the top one or two, leaving the rest filed.
- Three facts scoring 1.0, 0.8944, and 0.3162 retrieve the 1.0 match, turning a 3000-token paste into a 15-token pull.
- Twelve 15-token facts cost 180 tokens total, about 4.5 percent of a 4000-token window.