Skip to content
advanced Phase 11 · Memory and Conversation State

Evaluating Memory Quality

Test whether a system recalls the right facts and forgets what it should not keep.

45m
0 problems
Topic Progress 0%

Seeding Facts the Model Must Not Forget

The complaint that starts every memory investigation is unanswerable on its own: "it forgot what I told it." There is no ground truth in production logs — you cannot score an answer when nobody recorded what the answer should have been. So the suite manufactures ground truth: write a fact into a session, bury it under real turns, then ask.

CASES = [
    {"seed": ["My standup moved to 9:15am UTC starting Monday."],
     "bury": 40,
     "probe": "What time is my standup?",
     "expect": "9:15"},
    {"seed": ["Decision: ship without the export button."],
     "bury": 60,
     "probe": "Are we shipping the export button?",
     "expect": "without the export button"},
]

def evaluate(cases, session, ask, bury=None):
    hits = 0
    for c in cases:
        depth = bury if bury is not None else c["bury"]
        session.reset()
        session.load(seeded_history(c["seed"]))       # plant the fact
        session.bury(filler_turns(depth))             # then hide it under real dialogue
        answer = ask(c["probe"], session.history())
        hits += c["expect"].lower() in answer.lower()
    return hits, len(cases)

Input: 12 cases, each planting one fact and burying it under 40–120 filler turns before probing.

Output: (7, 12) — 58% recall, and every one of the 5 misses came from a case with 60 or more turns between the seed and the probe.

Three properties make this a measurement rather than a demo. The expected value is known before the model answers, so scoring is mechanical. The filler is real turns — trimmed, summarized, re-ordered by whatever the session under test does — because synthetic filler never exercises the retention policy that usually causes the loss. And distance is a parameter: varying bury turns a single suite into a curve, which is where the interesting failure lives.

The seeds must survive whatever compression the session applies, so a suite that plants a fact and then loses it inside summarization is measuring Summarizing Long Conversations as much as the store itself. Designing the datasets properly — paraphrases, distractors, unseeded negatives — is Building Eval Datasets.

Forgetting Wrong and Remembering Never

Recall alone scores only half the failure space. A system that always repeats the first thing it heard is perfect on recall and unusable in practice, while one that invents confident details never fails a "did it forget?" test either. Four metrics cover the space:

Metric What it counts The bug it exposes
Recall accuracy probes where the planted value appears facts evicted by trimming or swallowed by compression
Staleness rate answers repeating a superseded value "10:00" after the user moved the meeting to 14:00
False-memory rate asserted details absent from any turn preferences and names the user never gave
Correct abstention "you never said" answers on unseeded probes confident invention when the log has nothing to support it

Staleness is scored against a corrected conversation — the fact exists twice, and only the newer value is true:

STALE_CASES = [
    {"seed":    ["Standup is at 10:00."],
     "correct": ["Actually, 14:00 works better for me."],
     "probe":   "What time is my standup?",
     "accept":  "14:00",
     "reject":  "10:00"},
]

def staleness(cases, session, ask):
    repeats = 0
    for c in cases:
        session.reset()
        session.load(seeded_history(c["seed"] + c["correct"]))
        answer = ask(c["probe"], session.history()).lower()
        if c["reject"] in answer and c["accept"] not in answer:
            repeats += 1                             # asserted the dead value
    return repeats, len(cases)

Input: 10 correction cases, each with the old value and the new value both present in the log.

Output: (2, 10) — 20% staleness: two answers insisted on the superseded time.

The false-memory half works the same way, pointed at negatives. Ask about something the log never contains — "what editor do I prefer?" when no editor was ever mentioned — and count any concrete answer as a failure while counting an abstention as a pass. Together the two directions separate the three real bugs: the memory dropped a fact (recall), the memory kept the wrong version (staleness), or the memory never had it and the model supplied one anyway (false recall). Each maps to a different fix in Managing Conversation History, Semantic Memory with Vectors, and the prompting layer respectively.

Where Recall Actually Breaks

A single accuracy number hides the only fact worth knowing: memory systems do not degrade evenly. They hold flat and then fall off a cliff at the point where the retention policy starts discarding — so the suite runs at several session lengths and reports the curve.

LENGTHS = [10, 40, 100, 250]

def sweep(cases, session, ask):
    report = {}
    for n in LENGTHS:
        hits, total = evaluate(cases, session, ask, bury=n)   # same cases, re-buried deeper
        report[n] = round(hits / total, 3)
    return report

Input: the 12-case suite against a session using a 1,500-token window with a rolling summary, each case re-buried at every length.

Output: {10: 1.0, 40: 0.92, 100: 0.58, 250: 0.25} — flat, then a knee between 40 and 100 turns.

The knee is the deliverable. It tells you which mechanism is failing: recall collapsing at exactly the length where trimming begins indicts the retention policy; staleness rising while recall holds indicts the summary; both flat with false-memory climbing indicts the extraction gate in the store. Fixing any of them without the curve means you can only claim a new single number, not a repaired behavior.

Failure modes in the harness itself:

  • Substring scoring — expecting 9:15 accepts 19:15, and expecting 10:00 inside "not 10:00" counts as a hit. Score on normalized answers with boundaries, and treat the rejected value as a failure even when the accepted one also appears.
  • Identical filler every run — the suite then measures one lucky ordering. Shuffle filler and vary the seed phrasing.
  • One seed wording per fact — models that pattern-match the planted sentence score well and still miss the fact when the user phrases it differently.
  • Flaky gates — sampling at nonzero temperature makes the same commit pass or fail. Fix temperature or run each case three times and take the majority.
  • Two changes per run — altering the trim policy and the model at once makes the curve unattributable.
  • Aggregate-only reporting — one blended score lets a recall regression hide behind an abstention improvement. Publish all four metrics per length.

Wiring the sweep into CI so a merge that moves the knee blocks the build is Regression Suites; the harness structure it plugs into is Offline Eval Harness.

Quiz

1. Why plant facts instead of scoring memory against production conversations?

Question 1 options

2. What does a high recall score with a high staleness rate describe?

Question 2 options

3. A sweep returns {10: 1.0, 40: 0.92, 100: 0.58, 250: 0.25}. Where does the system start failing, and what does that locate?

Question 3 options

4. Your false-memory check asks about an unmentioned preference and counts any concrete answer as a failure. What is this really testing?

Question 4 options

Flashcards

Question

How is a labeled memory recall case constructed?

Answer

Seed a known fact into the session, bury it under 40–120 real filler turns, then probe with a question whose expected value you wrote down before the model answered. Varying the bury distance turns the suite into a curve.

Question

What four metrics does a memory eval report, and what does each catch?

Answer

Recall accuracy (dropped facts), staleness rate (repeating superseded values), false-memory rate (asserting details never provided), correct abstention (confident answers where the log has nothing).

Question

What is staleness in a memory system?

Answer

Repeating a value the user has since corrected. Score it with cases where the old and new values both exist in the log: accepting the new value passes, asserting the dead one fails.

Question

Why sweep a memory eval across session lengths instead of reporting one score?

Answer

Memory fails at a boundary, not gradually. The knee in the curve sits where the retention policy starts discarding, which identifies the failing mechanism — trim, summary, or extraction gate — instead of just the magnitude.

Question

What makes a memory eval gate reliable enough for CI?

Answer

Fixed temperature or majority-of-three scoring, shuffled filler and varied seed phrasing per run, boundary-aware matching instead of naive substring tests, and all four metrics reported per length rather than one blended number.

Revision Notes

Key Takeaways

  • 1. Ground truth is manufactured: plant the fact, bury it under real turns, record the expected answer first
  • 2. Recall alone is insufficient — staleness and false-memory scores cover the two ways memory is wrong without forgetting
  • 3. Sweep session lengths; the knee in the curve identifies the failing mechanism, the aggregate score does not
  • 4. Score with boundaries and normalize answers, or substring tests inflate every metric
  • 5. Stabilize the harness — fixed temperature, shuffled filler, per-length metrics — before letting it gate a merge

Interview Tips

  • • Describe a memory bug as a measurement problem first: production logs lack labels, so you seed your own
  • • Name all four metrics and the distinct fix each one points to; interviewers check that you do not collapse them into accuracy
  • • Show the sweep numbers and locate the knee out loud — the reasoning matters more than the arithmetic
  • • Be ready for "how do you keep the eval from being flaky?" — temperature, majority voting, and boundary-aware scoring

Cheat Sheet

Memory Evaluation Cheat Sheet

Case shape

seed  -> known fact written into the session
bury  -> 40/100/250 real filler turns (exercises trim + summary)
probe -> question with an expected value recorded in advance
score -> normalized match, boundaries enforced

Metrics

Metric Failure it exposes
Recall accuracy fact evicted by trimming or compression
Staleness rate repeated superseded value after a correction
False-memory rate asserted detail absent from every turn
Correct abstention confident answer where the log has nothing

Staleness case: seed "Standup is at 10:00." + correction "Actually 14:00." -> accept 14:00, reject 10:00. Repeating the dead value counts even when the new one also appears.

Sweep: lengths [10, 40, 100, 250] -> report the curve. Flat-then-cliff means a bounded mechanism; the knee locates the eviction boundary.

Read the knee: recall falls -> retention policy. Staleness rises while recall holds -> summary compression. False memory climbs -> extraction gate in the store.

Harness failure modes: naive substring matching, identical filler each run, one seed phrasing, nonzero temperature (flaky gates), two variables changed per run, single blended score hiding a component regression.

Pipeline: datasets → Building Eval Datasets → harness → Offline Eval Harness → gate → Regression Suites.

Continue Learning

Before You Start

  • No prerequisites listed.

Next Topics

Related Reading