Skip to content

SynapCores / Recipe

Stop RAG Answering With Outdated Facts

RAG retrieves what is relevant, not what is still true. Give your AI agent the current value of every fact, the note it came from, and what was true on any date, with CREATE MEMORY, CURRENT, TRACE and RECALL AS OF in plain SQL.

All recipes· agents· 15 minutesintermediateen

Part of the guide AI agent memory: types, architecture and how to build it, with every agent-memory recipe in one place.

Instance: localhost:8080

Opens your running SynapCores (Stop RAG Answering With Outdated Facts will be staged for a preview — nothing runs until you click Run). No instance yet? Install free in ~30s.

Share

Objective

Ask a RAG pipeline "which plan is this customer on?" and it searches for notes that sound like the question. It finds the signup note, the upgrade note and an agent's guess from last week, and the model picks one. Similarity tells you what's relevant. It doesn't tell you what's still true.

In this recipe you load nine months of account notes for one customer, watch plain RAG and RAG-with-recency answer from them, then store the same notes in a SynapCores memory object and ask again. The memory object knows the current value of each fact, which note it came from, which value it replaced, and what was true on any past date. Everything is SQL, in one database, on the free Community Edition.

Every query and number below ran on SynapCores 2.4.3 with the default models (all-minilm embeddings, qwen2.5:7b for GENERATE). Generation uses temperature 0 and a fixed seed, so you get the same answers.

Step 1: Load the account notes

Fourteen notes about Harborline Logistics, the way a support and success team writes them: a kickoff call, tickets, two billing events, a contact who left, a cloud migration, an agent's guess, and one call note typed in four months late. noted_at is when it happened; filed_at is when it was typed in.

CREATE TABLE IF NOT EXISTS account_notes (
  note_id    INTEGER PRIMARY KEY,
  account_id TEXT,
  noted_at   TIMESTAMP,
  filed_at   TIMESTAMP,
  source     TEXT,
  body       TEXT,
  embedding  VECTOR(384)
);

INSERT INTO account_notes (note_id, account_id, noted_at, filed_at, source, body) VALUES
 (1,  'harborline', '2026-02-11 15:00:00', '2026-02-11 16:00:00', 'kickoff call',  'Kickoff call. Harborline runs its whole platform on Azure: AKS clusters and Azure SQL in West Europe.'),
 (2,  'harborline', '2026-02-11 15:05:00', '2026-02-11 16:05:00', 'kickoff call',  'Main contact at Harborline is Dana Okafor, VP of Platform. Dana prefers email.'),
 (3,  'harborline', '2026-03-04 09:00:00', '2026-03-04 09:00:00', 'billing system','Harborline signed the Pro plan, billed annually.'),
 (4,  'harborline', '2026-04-18 11:20:00', '2026-04-18 12:00:00', 'support ticket','Ticket 3981: Azure SQL connection pool exhausted on the Harborline Azure deployment. Fixed by raising the pool limit.'),
 (5,  'harborline', '2026-05-22 14:00:00', '2026-05-22 17:00:00', 'QBR',           'Quarterly review. Harborline''s Azure spend keeps growing; they asked about Azure reserved instances.'),
 (6,  'harborline', '2026-06-30 08:40:00', '2026-06-30 09:00:00', 'support ticket','Ticket 4412: checkout API latency spike for Harborline, p99 above 2 seconds for 40 minutes.'),
 (7,  'harborline', '2026-07-08 16:00:00', '2026-07-08 16:30:00', 'CSM call',      'Dana Okafor says Harborline is evaluating AWS because they want Bedrock.'),
 (8,  'harborline', '2026-08-01 00:00:00', '2026-08-01 00:00:00', 'billing system','Harborline upgraded from Pro to the Enterprise plan.'),
 (9,  'harborline', '2026-08-19 10:00:00', '2026-08-19 10:30:00', 'CSM call',      'Dana Okafor has left Harborline. The new main contact is Priya Raman, Head of Infrastructure.'),
 (10, 'harborline', '2026-09-02 13:00:00', '2026-09-02 13:10:00', 'CSM call',      'Priya Raman prefers Slack Connect over email for anything urgent.'),
 (11, 'harborline', '2026-09-15 18:00:00', '2026-09-15 18:30:00', 'CSM call',      'Harborline completed its move from Azure to AWS eu-west-1. The old Azure subscriptions are being shut down.'),
 (12, 'harborline', '2026-10-06 09:30:00', '2026-10-06 10:00:00', 'support ticket','Ticket 5127: IAM role misconfiguration on the new Harborline account blocked a deploy. Resolved.'),
 (13, 'harborline', '2026-10-20 07:00:00', '2026-10-20 07:00:00', 'agent note',    'Usage looks lower this month. Harborline may have downgraded to the Team plan.'),
 (14, 'harborline', '2026-06-12 15:00:00', '2026-10-28 17:45:00', 'CSM call',      'Call notes from June 12, filed late: Harborline is all-in on Azure and happy with AKS.');

UPDATE account_notes SET embedding = EMBED(body);

Step 2: Ask the way plain RAG does

Embed the question, take the three most similar notes, and hand them to the model.

SELECT note_id, noted_at, body,
       COSINE_SIMILARITY(embedding, EMBED('Which plan is Harborline on right now?')) AS relevance
FROM account_notes
WHERE account_id = 'harborline'
ORDER BY relevance DESC
LIMIT 3;

The top three are the upgrade note (#8, 0.637), the signup note (#3, 0.627) and the agent's guess (#13, 0.614). All three are about plans, and nothing in a similarity score says which one is still true. Freeze them into a context and ask the model:

CREATE TABLE IF NOT EXISTS rag_top (note_id INTEGER PRIMARY KEY, noted_at TIMESTAMP, body TEXT, score DOUBLE);
DELETE FROM rag_top;
INSERT INTO rag_top (note_id, noted_at, body, score)
SELECT note_id, noted_at, body,
       COSINE_SIMILARITY(embedding, EMBED('Which plan is Harborline on right now?')) AS score
FROM account_notes
WHERE account_id = 'harborline'
ORDER BY score DESC
LIMIT 3;

CREATE TABLE IF NOT EXISTS rag_ctx (id INTEGER PRIMARY KEY, notes TEXT);
DELETE FROM rag_ctx;
INSERT INTO rag_ctx (id, notes)
SELECT 1, GROUP_CONCAT('[note ' || note_id || ', ' || noted_at || '] ' || body, ' ') FROM rag_top;
SELECT GENERATE(
  'Answer the question in one short sentence using ONLY these account notes. Cite the note number. Notes: ' ||
  notes || ' Question: Which plan is Harborline on right now?',
  json_object('temperature', 0, 'seed', 7, 'max_tokens', 80)) AS rag_answer
FROM rag_ctx;

The answer is "Harborline is on the Team plan. [note 13]". It's wrong: the billing system says Enterprise. The model trusted the newest note, and the newest note was a guess.

Step 3: Try the usual fix, a recency boost

Blend similarity (70%) with freshness (30%), so newer notes rank higher. "Today" is fixed at 2026-11-01 so the scores are reproducible.

SELECT note_id, noted_at, body,
       COSINE_SIMILARITY(embedding, EMBED('Which plan is Harborline on right now?')) * 0.7
       + (1.0 / (1.0 + DATEDIFF('2026-11-01', noted_at) / 30.0)) * 0.3 AS score
FROM account_notes
WHERE account_id = 'harborline'
ORDER BY score DESC
LIMIT 3;

Now the guess ranks first (#13, 0.644), ahead of the upgrade (#8, 0.520). Feed these three to the same prompt and the answer is "Harborline may have downgraded to the Team plan. [note 13]". Recency measures how new a note is. It can't tell a billing record from a hunch.

Step 4: Create a memory object for accounts

One statement creates the object and five ordinary tables: episodes, facts, current state, provenance and relationships. IDENTITY scopes every read and write to one account.

CREATE MEMORY account_memory IDENTITY account_id;

Step 5: Remember the same notes, with time and authority

One REMEMBER per note, as it arrives. Three options carry what similarity can't:

  • event_time: when it happened, so a late-filed note lands in the right place on the timeline.
  • source_type: how much to trust it. The billing system is system_of_record; the agent's guess is agent_inference.
  • attribute and value: the fact the note changes. Notes that only record an event (a ticket, a review, "we're evaluating AWS") use extract = false and stay events.
REMEMBER account_memory FOR account_id = 'harborline'
  'Kickoff call. Harborline runs its whole platform on Azure: AKS clusters and Azure SQL in West Europe.'
  WITH (attribute = 'cloud_provider', value = 'Azure', event_time = '2026-02-11T15:00:00Z');

REMEMBER account_memory FOR account_id = 'harborline'
  'Main contact at Harborline is Dana Okafor, VP of Platform. Dana prefers email.'
  WITH (attribute = 'primary_contact', value = 'Dana Okafor', event_time = '2026-02-11T15:05:00Z');

REMEMBER account_memory FOR account_id = 'harborline'
  'Harborline signed the Pro plan, billed annually.'
  WITH (attribute = 'subscription_plan', value = 'Pro', source_type = 'system_of_record', event_time = '2026-03-04T09:00:00Z');

REMEMBER account_memory FOR account_id = 'harborline'
  'Ticket 3981: Azure SQL connection pool exhausted on the Harborline Azure deployment. Fixed by raising the pool limit.'
  WITH (source_type = 'tool_result', event_time = '2026-04-18T11:20:00Z', extract = false);

REMEMBER account_memory FOR account_id = 'harborline'
  'Quarterly review. Harborline''s Azure spend keeps growing; they asked about Azure reserved instances.'
  WITH (event_time = '2026-05-22T14:00:00Z', extract = false);

REMEMBER account_memory FOR account_id = 'harborline'
  'Ticket 4412: checkout API latency spike for Harborline, p99 above 2 seconds for 40 minutes.'
  WITH (source_type = 'tool_result', event_time = '2026-06-30T08:40:00Z', extract = false);

REMEMBER account_memory FOR account_id = 'harborline'
  'Dana Okafor says Harborline is evaluating AWS because they want Bedrock.'
  WITH (event_time = '2026-07-08T16:00:00Z', extract = false);

REMEMBER account_memory FOR account_id = 'harborline'
  'Harborline upgraded from Pro to the Enterprise plan.'
  WITH (attribute = 'subscription_plan', value = 'Enterprise', source_type = 'system_of_record', event_time = '2026-08-01T00:00:00Z');

REMEMBER account_memory FOR account_id = 'harborline'
  'Dana Okafor has left Harborline. The new main contact is Priya Raman, Head of Infrastructure.'
  WITH (attribute = 'primary_contact', value = 'Priya Raman', event_time = '2026-08-19T10:00:00Z');

REMEMBER account_memory FOR account_id = 'harborline'
  'Priya Raman prefers Slack Connect over email for anything urgent.'
  WITH (attribute = 'contact_channel', value = 'Slack Connect', event_time = '2026-09-02T13:00:00Z');

REMEMBER account_memory FOR account_id = 'harborline'
  'Harborline completed its move from Azure to AWS eu-west-1. The old Azure subscriptions are being shut down.'
  WITH (attribute = 'cloud_provider', value = 'AWS', event_time = '2026-09-15T18:00:00Z');

REMEMBER account_memory FOR account_id = 'harborline'
  'Ticket 5127: IAM role misconfiguration on the new Harborline account blocked a deploy. Resolved.'
  WITH (source_type = 'tool_result', event_time = '2026-10-06T09:30:00Z', extract = false);

REMEMBER account_memory FOR account_id = 'harborline'
  'Usage looks lower this month. Harborline may have downgraded to the Team plan.'
  WITH (attribute = 'subscription_plan', value = 'Team', source_type = 'agent_inference', event_time = '2026-10-20T07:00:00Z');

REMEMBER account_memory FOR account_id = 'harborline'
  'Call notes from June 12, filed late: Harborline is all-in on Azure and happy with AKS.'
  WITH (attribute = 'cloud_provider', value = 'Azure', event_time = '2026-06-12T15:00:00Z');

Every note is stored, including the guess and the late note. Two of them come back with a message explaining why they didn't change the current value:

  • The guess: "'subscription_plan' = 'Team' was not accepted as current: lower source authority than the accepted value".
  • The late note: "'cloud_provider' = 'Azure' was not accepted as current: equal authority, older validity".

Step 6: Ask for what is true now

CURRENT is a state lookup, not a similarity search, so a note that merely sounds right can't win.

CURRENT account_memory FOR account_id = 'harborline' ATTRIBUTE subscription_plan;
CURRENT account_memory FOR account_id = 'harborline' ATTRIBUTE primary_contact;
CURRENT account_memory FOR account_id = 'harborline' ATTRIBUTE contact_channel;
CURRENT account_memory FOR account_id = 'harborline' ATTRIBUTE cloud_provider;

The plan is Enterprise (authority system_of_record, valid from 2026-08-01), the contact is Priya Raman, the channel is Slack Connect and the cloud is AWS (valid from 2026-09-15). Each returns status: "ok", with confidence and the time the value became true.

The current state is also an ordinary table, so you can join it to anything else in the database:

SELECT attribute, value, authority, valid_from, previous_value
FROM _mem_account_memory_state
WHERE identity = 'harborline'
ORDER BY attribute;
attribute value authority valid_from previous_value
cloud_provider AWS user_statement 2026-09-15 18:00:00 Azure
contact_channel Slack Connect user_statement 2026-09-02 13:00:00
primary_contact Priya Raman user_statement 2026-08-19 10:00:00 Dana Okafor
subscription_plan Enterprise system_of_record 2026-08-01 00:00:00 Pro

Step 7: Show where each answer came from

TRACE returns the source note verbatim, when it happened, how the value was extracted, and what it replaced.

TRACE account_memory FOR account_id = 'harborline' ATTRIBUTE subscription_plan;
TRACE account_memory FOR account_id = 'harborline' ATTRIBUTE primary_contact;

The plan traces to "Harborline upgraded from Pro to the Enterprise plan." (system_of_record, 2026-08-01, explicit_option) and supersedes Pro. The contact traces to "Dana Okafor has left Harborline. The new main contact is Priya Raman, Head of Infrastructure." and supersedes Dana Okafor. That is the citation your UI shows next to the answer.

Step 8: Ask what was true on a past date

Ticket 4412 was opened on 2026-06-30 at 08:40. Which cloud were they on then? HISTORY gives the timeline of one fact, and RECALL ... AS OF gives the whole account as it stood at that moment.

HISTORY account_memory FOR account_id = 'harborline' ATTRIBUTE cloud_provider;

RECALL account_memory FOR account_id = 'harborline'
  ABOUT 'Which cloud was Harborline on?'
  WITH (as_of = '2026-06-30T08:40:00Z');

HISTORY shows Azure from 2026-02-11 to 2026-09-15 (superseded), then AWS from 2026-09-15 (active). RECALL classifies the question as historical and returns the state on that date: cloud Azure, contact Dana Okafor, plan Pro. That's the right context for a post-incident review, and none of it is today's values.

Step 9: Answer from the current fact, with its source, in one query

Join the current state to the note it came from and let the model phrase the answer.

SELECT GENERATE(
         'Answer the question in one short sentence using ONLY this fact. Fact: ' ||
         s.attribute || ' = ' || s.value || ' (source note: ' || e.content || ', ' || e.event_time || ')' ||
         ' Question: Which plan is Harborline on right now?',
         json_object('temperature', 0, 'seed', 7, 'max_tokens', 80)) AS answer,
       e.content    AS source_note,
       e.event_time AS noted_at,
       s.authority
FROM _mem_account_memory_state s
JOIN _mem_account_memory_episodes e ON e.id = s.source_episode_id
WHERE s.identity = 'harborline' AND s.attribute = 'subscription_plan';

The answer is "Harborline is on the Enterprise plan.", returned with its source note, its date and system_of_record.

The scoreboard

Five questions about Harborline, each answered three ways from the same fourteen notes: the top three notes by similarity, the top three by similarity plus recency, and the memory object (CURRENT + TRACE, or RECALL AS OF for the past date). Same model, same prompt, temperature 0.

Question Plain RAG RAG + recency SynapCores memory
Which cloud is Harborline on right now? AWS ✅ AWS ✅ AWS ✅
Which plan is Harborline on right now? Team ❌ Team ❌ Enterprise ✅
Who is our main contact at Harborline? Dana Okafor ❌ "not in the context" ❌ Priya Raman ✅
How do we reach the main contact for something urgent? Names Priya, no channel ❌ Names Priya, no channel ❌ Slack Connect ✅
Which cloud were they on when ticket 4412 was opened? Azure ✅ Azure ✅ Azure ✅
Correct 2 / 5 2 / 5 5 / 5

Every answer from the memory object comes with the note it came from.

Cleanup (Optional)

DROP MEMORY account_memory CASCADE;
DROP TABLE IF EXISTS account_notes;
DROP TABLE IF EXISTS rag_top;
DROP TABLE IF EXISTS rag_ctx;

Expected Outcomes

  • Step 2 retrieves notes #8, #3 and #13, and the model answers "Team plan" from the agent's guess.
  • Step 3 ranks the guess first; the answer is still Team.
  • Step 5 stores all fourteen notes; the guess and the late-filed note come back with a message saying why they didn't change the current value.
  • Step 6 returns Enterprise, Priya Raman, Slack Connect and AWS, each with its authority and valid-from time.
  • Step 7 returns the exact source note for each value and the value it replaced.
  • Step 8 returns Azure, Dana Okafor and Pro as of 2026-06-30.
  • Step 9 answers "Enterprise", with the source note alongside.

Use it from your agent (framework-agnostic)

The memory object is plain data operations, so any agent can use it:

  • REST / SDK: POST /v1/query/execute with any statement above, or the /v1/memory/* endpoints (remember, recall, current, trace, forget), which return the same envelope.
  • MCP: the tools memory_remember, memory_recall, memory_current and memory_trace give an MCP client (Claude Code, Cursor, a custom loop, a voice runtime) the same answers as a tool call.
  • A durable agent: CREATE AGENT ... WITH (memory_object = 'account_memory', memory_identity = 'harborline') recalls before the model answers and remembers after each turn. See AI agent memory, step 6.

The pattern is the same everywhere: write every note with REMEMBER, answer "what is it now?" with CURRENT, show the receipt with TRACE, and answer "what was it then?" with RECALL ... AS OF.

Key Concepts Learned

  • Similarity finds notes that are about the question. Whether a fact is still true is a separate property, and you have to store it.
  • A recency boost favours the newest note, even when the newest note is a guess.
  • event_time puts late-filed notes in the right place on the timeline; source_type lets a system of record outrank an inference.
  • CURRENT is a state lookup with three outcomes (ok, not_found, conflict), so it never returns a note just because it sounds similar.
  • TRACE is the citation, HISTORY is the timeline, and RECALL ... AS OF is the past, all in the same SQL database as your notes and embeddings.

Related: AI agent memory: types, architecture and how to build it · Recall agent memory by meaning and recency · RAG with citations

Tags

ragoutdated-informationagent-memoryai-agent-memorytemporalcurrent-stateprovenancevectorembeddingsmcp