The evidence chain
Most memory systems store what you said. The questions that break them are when it stopped being true and where it came from. Those need a data model, not a bigger vector index.
A retrieved memory is not a floating string
It points at the turn that produced it:
evidence_id = ev_fact:fact_6ada707b…
support = "Can you recommend a good beach on Oahu that's not too crowded?"
predicate = user wants a not-too-crowded beach on Oahu
entities = location=Oahu | occasion=birthday
source = session_40 / turn_10 ← the exact turn, not "some chat"
date = 2023-05-25The chain
FactEvent ──▶ SourceSpan ──▶ RawTurn
the fact the span of the message
we derived text it came as it was
from actually sentThis is a foreign-key chain, not a similarity score. The difference shows up in two places:
- When a user asks "why do you think that about me?" there is an answer, and it is the sentence they actually typed.
- When compliance asks where a stored fact came from, there is a row — not a ranked list of things that were nearby in embedding space.
A key-value memory cannot produce this field no matter how good its retrieval is, because it never recorded the link in the first place.
Where you see it
| surface | field |
|---|---|
| Python | block.citations from build_context |
| HTTP | retrieved_evidence[].source_trace_ids |
| MCP | every read tool returns the evidence ids |
| published benchmark artifacts | sodamem_lme_retrieved_context.json — 8,427 rows, 16.9 per question, none empty |
Retrieval you can audit
Same query, same store, same result, every time. There is no sampling step in the default path, so a retrieval that surprised you can be reproduced exactly instead of argued about.
/v1/events records every add, supersede and delete with its reason, which makes "why did the agent forget X" answerable after the fact rather than a shrug. See HTTP API · events.
Next
- Four time axes — how a fact stops being true
- Retrieval tiers — what each tier costs

