Repository navigation
LLM Memory 2 · Ship it without forgetting: instance amnesia and the replay tax #16
akash-coded
started this conversation in
Exercises
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Exercise 2: Ship It Without Forgetting
Language: Python
Topics: Instance Amnesia, 3-Axis debugging, Restart Test, Replay Tax, trim_messages, LangGraph persistence, thread_id, checkpointers
Level: Intermediate
Work in pairs. Run the scaffold once, then attempt each question before you open its answer. Fourteen questions across six formats: predict, read, order, match, debug, and build. Everything runs offline.
The customer for every question: Rao, PNR
JX48Q2, Gold tier, BLR to DEL leg cancelled.Scaffold (run once)
Part A: Predict
Q1. Predict the output · Predict
The Replay Tax, measured.
Show answer
Messages re-sent per turn grow 2, 4, 6. The running total (12) climbs faster than the turn count (3). That widening gap is the quadratic Replay Tax.
Q2. Predict the output · Predict
Show answer
token_counter=lencounts one per message, somax_tokens=4keeps four messages.include_system=Trueforces the system message in, thenstrategy="last"keeps the three most recent: a3, q4, a4.Skeptic asks: the kept window starts on an ai turn (a3) with no matching human. Is that safe to send? It runs, but a dangling ai turn can confuse some models. Pass
start_on="human"and the window becomes['system', 'human', 'ai'], beginning cleanly on q4. Trim by count is blunt; trim on turn boundaries is safer.Part B: Read
Q3. Read and classify · Read
Bug report: "the copilot answers fine, but after we scaled to three servers it forgets customers at random." Which of the 3 axes is the fault?
session_id)RunnableWithMessageHistory)Show answer
(c) WHERE. Each server has its own in-process store, so a request routed to a fresh replica sees no history. The id is right and the wiring is right; the storage is not shared. Fix: one shared backend (Redis or Postgres).
Q4. Multi-select · Read
Which storages pass the Restart Test (history survives a process restart)? Pick all.
Show answer
Pass: (b), (c), (d).
Fail: (a) and (e) both live in process memory and die on restart. SQLite passes on a single instance; Redis and Postgres pass across replicas too.
Q5. True or false · Read
MemorySaversurvives a process restart.SqliteSaverwrites checkpoints to a file, so it survives a restart.MemorySaverforSqliteSaverchanges the graph logic.PostgresSaveris the rung you climb to when one instance is not enough.Show answer
1 False (in-memory, lost on restart).
2 True.
3 False (only the saver changes; nodes and edges stay identical).
4 True.
Part C: Order and match
Q6. Order the ladder · Order
Put these storages in order of increasing durability and reach. Lowest first.
Shuffled cards:
Redis,in-memory dict,Postgres,SQLite file,LangGraph checkpointer on PostgresShow answer
Each rung buys back one failure: restart, then concurrency, then horizontal scale, then resumable agent state.
Q7. Match symptom to root cause · Match
Bank: restart wiped in-process RAM; requests hit different replicas with unshared storage.
Show answer
Part D: Debug and fix
Each snippet has one defect. Name the bad line, give a one line fix.
Q8. Debug · Debug
Symptom: prints
I do not have your PNR. Could you share it?even though Rao just gave it.Show answer
Bad:
botAandbotBuse separate dicts, so turn two lands on a store that never saw turn one. This is Instance Amnesia.Fix: back both bots with one shared store:
In production the shared store is Redis or Postgres, not a dict.
Q9. Debug · Debug
Symptom: the last line raises
ValueError: No checkpointer set, and turns never persist.Show answer
Bad line:
graph = b.compile()has no checkpointer, so there is nothing to save or read state from.Fix:
graph = b.compile(checkpointer=MemorySaver()), or a durable saver in production.Q10. Debug · Debug
Symptom: prints
['human', 'ai', 'human', 'ai']. The agent loses its instructions and drifts off script.Show answer
Bad:
include_systemdefaults to False, so the system message is trimmed away with the old turns.Fix: add
include_system=True:Now the output starts with
system.Part E: Diagram to code
Q11. Boilerplate, fill the TODOs · Build
Build a durable agent that survives a restart.
flowchart LR REQ[request with thread id] --> G[LangGraph agent node] G --> M[model call] G --> CK[(SqliteSaver on disk)] CK --> FILE[(checkpoint file survives restart)]Complete the TODOs:
Show answer
Reopen the same file with a fresh connection and the history is still there. That is the Restart Test passing where the in-memory dict failed.
Q12. Bare code, write from scratch · Build
Write a graph node that caps context before calling the model.
flowchart LR STATE[state messages] --> TRIM[keep system plus last 6] TRIM --> CALL[model invoke] CALL --> OUT[new ai message]Requirements:
call_model_cap(state)forMessagesStatemessageskeyShow answer
This is where the Replay Tax gets paid down: the node sees at most seven messages, no matter how long the thread grows.
Part F: Predict and reason
Q13. Case study · Predict
Prod runs three replicas behind a load balancer, each with its own in-memory store. Rao's turn one (
I am Rao, PNR JX48Q2.) lands on replica A. His turn two (What is my PNR?) is routed to replica B.Show answer
I do not have your PNR. Could you share it?Replica B's store is empty for Rao.Q14. Predict the output · Predict
Show answer
raothread: two turns, each adds a human and an ai message, so 4.mehtathread: one turn, so 2.thread_idmeans a different checkpoint. The raw graph adds no system message (no prompt template), so counts are pure turn pairs.Skeptic asks: the counts differ, but did the threads truly stay separate, or did we just not look? Ask for Rao's PNR on the
mehtathread. You get ZZ90Q1, not JX48Q2. Isolation is real, and it is keyed entirely bythread_id.If Q9 and Q10 bit you, those two lines (missing checkpointer, missing
include_system) are the two most common production regressions in this stack. Ship neither.All reactions