Objective
An agent that only ranks memories by meaning will happily resurface a stale fact over a fresh one. Real recall blends relevance (semantic match to the current message) with recency (how recently the memory was formed), so "what's the user working on now?" beats "what they worked on a year ago." Here you'll build a single recall query that scores both — no separate cache, no time-decay service, just SQL. The same store works from any framework or a voice agent — see Use it from your agent at the end.
Step 1: Create the agent memory store with an age
One row per memory, with an embedding for semantic recall and an age_days we can decay against.
CREATE TABLE IF NOT EXISTS recipe_agent_recall (
memory_id INTEGER PRIMARY KEY,
agent_id TEXT,
content TEXT,
age_days INTEGER, -- how many days ago this memory was formed
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
embedding VECTOR(384)
);
Step 2: Write memories formed at different times
Two are about the same topic (the user's job) but one is stale and one is current; recency should break the tie.
INSERT INTO recipe_agent_recall (memory_id, agent_id, content, age_days) VALUES
(1,'assistant','The user was a junior analyst learning SQL.', 900),
(2,'assistant','The user is now a senior data engineer leading a Rust platform team.', 3),
(3,'assistant','The user prefers concise, technical answers.', 7),
(4,'assistant','The user once asked about beginner Python tutorials.', 820),
(5,'assistant','The user is currently migrating a service to async Rust.', 1);
Step 3: Embed the memories
The embedding model runs inside the database — no external API call.
UPDATE recipe_agent_recall SET embedding = EMBED(content);
Step 4: Recall by meaning only (the naive baseline)
Pure semantic recall — note it can't tell the stale job memory from the current one.
SELECT memory_id, content, age_days,
COSINE_SIMILARITY(embedding, EMBED('What is the user''s current role?')) AS relevance
FROM recipe_agent_recall
WHERE agent_id = 'assistant'
ORDER BY relevance DESC
LIMIT 3;
Step 5: Add a recency score with explicit time decay
Turn "days since the memory was formed" into a 0..1 freshness score that fades smoothly over time.
SELECT memory_id, content, age_days,
1.0 / (1.0 + age_days / 30.0) AS recency
FROM recipe_agent_recall
WHERE agent_id = 'assistant'
ORDER BY recency DESC
LIMIT 5;
Step 6: Blend relevance + recency into one recall score
Weight semantic match 70% and freshness 30% so the current job memory wins over the stale one.
SELECT memory_id, content,
COSINE_SIMILARITY(embedding, EMBED('What is the user''s current role?')) * 0.7
+ (1.0 / (1.0 + age_days / 30.0)) * 0.3 AS score
FROM recipe_agent_recall
WHERE agent_id = 'assistant'
ORDER BY score DESC
LIMIT 3;
Step 7: Ground a reply in the freshest relevant memory
Materialize the single top blended memory (projecting the score so we can order by it), then feed it to GENERATE() so the agent answers from current context.
CREATE TABLE IF NOT EXISTS recipe_agent_recall_top (
memory_id INTEGER PRIMARY KEY,
content TEXT,
score DOUBLE
);
INSERT INTO recipe_agent_recall_top (memory_id, content, score)
SELECT memory_id, content,
COSINE_SIMILARITY(embedding, EMBED('What is the user''s current role?')) * 0.7
+ (1.0 / (1.0 + age_days / 30.0)) * 0.3 AS score
FROM recipe_agent_recall
WHERE agent_id = 'assistant'
ORDER BY score DESC
LIMIT 1;
SELECT GENERATE(
'Given this current fact about the user: ' || content ||
'. Greet the user in one sentence that references what they do now.') AS greeting
FROM recipe_agent_recall_top;
Cleanup (Optional)
DROP TABLE IF EXISTS recipe_agent_recall;
DROP TABLE IF EXISTS recipe_agent_recall_top;
Expected Outcomes
- Step 4 ranks both job memories near each other — pure meaning can't tell the current role from the stale one.
- Step 5 orders memories purely by freshness, newest first.
- Step 6 promotes "now a senior data engineer" above "was a junior analyst," because recency breaks the semantic tie.
- Step 7 greets the user from their current role — recall that is both on-topic and up to date.
You now have recency-aware semantic recall: the agent remembers what's relevant and what's recent, in one query.
Use it from your agent (framework-agnostic — this is the whole point)
Recall is just data + one scoring query, so any agent uses it with no framework lock-in:
- REST / SDK —
POST /v1/query/execute(any language), or@synapcores/sdkclient.executeQuery(...). Your agent runs the Step-2INSERTto remember and the Step-6SELECTto recall the freshest relevant context before each turn. (In production, store the real timestamp and computeage_daysfrom it client-side, or with your engine's date functions, before scoring.) - MCP (native, on by default) — point any MCP client (Claude Code, Cursor, a custom loop, a voice runtime) at
ws://<your-instance>/mcp?token=<jwt>(the JWT comes from onePOST /v1/auth/login→access_token). Thequerytool runs the blended recall; theexecutetool writes new memories — "remember this" and "what's relevant now?" become MCP tool calls, no SDK required. - Any framework — OpenClaw's memory plugin, LangChain / LlamaIndex / Semantic Kernel, a custom loop, or a voice agent all read and write the same store. The database is the brain; the framework is swappable.
Key Concepts Learned
- Pure semantic recall ignores time — agents need both relevance and recency.
- A
1 / (1 + age/halflife)term turns an age into a smooth 0..1 freshness score, all in SQL. - Weighting
COSINE_SIMILARITYand recency into one score gives human-like recall in a single query. - Because it's plain data ops (SQL / REST / MCP), recency-aware recall works for any agent — the agent-agnostic backend pattern this cluster builds on.