Self-Consistent Agent Memory: Refuse to Remember a Contradiction

Gate MEMORY_STORE with NLI_VERIFY over MEMORY_RECALL — an agent memory that rejects facts contradicting what it already knows, and logs the conflict instead of silently absorbing it

All recipes· ml· 20 minutesadvanceden
Instance: localhost:8080

Opens your running SynapCores (Self-Consistent Agent Memory: Refuse to Remember a Contradiction will be staged for a preview — nothing runs until you click Run). No instance yet? Install free in ~30s.

Share

Self-Consistent Agent Memory: Refuse to Remember a Contradiction

Objective

Every agent framework has the same failure: memory is append-only trust. A user misspeaks, a scraped page lies, another agent hallucinates — and the falsehood is stored next to the facts, recalled with equal confidence forever. This is memory poisoning, and the usual answer ("review your memories sometimes") doesn't scale.

SynapCores co-locates agent memory (MEMORY_STORE / MEMORY_RECALL) and an NLI verifier in the same process, so the fix is structural: before a candidate memory is stored, verify it against what the namespace already believes. A contradiction is rejected and logged; everything else is stored. The whole check is one statement over the recall set.

Step 1: Establish what the agent knows

MEMORY_STORE auto-creates the namespace on first write — no schema step.

SELECT MEMORY_STORE('recipe_consistent_mem',
  'The customer prefers to be contacted by email, never by phone.') AS m1;
SELECT MEMORY_STORE('recipe_consistent_mem',
  'The customer is on the Aurora plan, which includes a 14-day return window.') AS m2;
SELECT MEMORY_STORE('recipe_consistent_mem',
  'The customer manages procurement for a mid-size logistics company.') AS m3;

Step 2: Ground truth — what does recall surface for a contact question?

SELECT content, similarity
FROM MEMORY_RECALL('recipe_consistent_mem', 'how should we contact the customer', 3);

Expected: the email-preference memory ranks first. This recall set is exactly the evidence the gate will verify against.

Step 3: The gate — verify a candidate memory against recalled beliefs

A candidate arrives (from a user message, another agent, a scraper): "The customer prefers phone contact." Verify it against the namespace before storing:

SELECT NLI_VERIFY('nli-default',
  'The customer prefers phone contact.',
  NLI_ANCHORS(content)) AS gate
FROM MEMORY_RECALL('recipe_consistent_mem', 'The customer prefers phone contact.', 5);

Expected: verdict LIE (observed confidence ≈ 0.999), with violated_anchor = the stored email-preference memory. The recall query uses the candidate itself as the probe, so the memories most likely to conflict are exactly the ones retrieved.

Step 4: Route on the verdict — store, or log the conflict

CREATE TABLE IF NOT EXISTS recipe_consistent_mem_conflicts (
    id INT PRIMARY KEY,
    candidate TEXT,
    verdict TEXT,
    violated_anchor TEXT,
    confidence DOUBLE,
    seen_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

The contradicting candidate goes to the conflict log, not to memory:

INSERT INTO recipe_consistent_mem_conflicts (id, candidate, verdict, violated_anchor, confidence)
SELECT 1,
       'The customer prefers phone contact.',
       JSON_UNQUOTE(JSON_EXTRACT(gate, '$.verdict')),
       JSON_UNQUOTE(JSON_EXTRACT(gate, '$.violated_anchor')),
       CAST(JSON_EXTRACT(gate, '$.confidence') AS DOUBLE)
FROM (
  SELECT NLI_VERIFY('nli-default',
    'The customer prefers phone contact.',
    NLI_ANCHORS(content)) AS gate
  FROM MEMORY_RECALL('recipe_consistent_mem', 'The customer prefers phone contact.', 5)
) checked
WHERE JSON_UNQUOTE(JSON_EXTRACT(gate, '$.verdict')) = 'LIE';
SELECT candidate, verdict, violated_anchor FROM recipe_consistent_mem_conflicts;

Expected: 1 row — the phone-contact candidate, verdict LIE, violated anchor naming the email-preference memory. A human (or a supervising agent) now sees that there is a conflict and which belief it collides with — instead of two contradictory memories being served with equal confidence.

Step 5: A compatible candidate passes the same gate

"The customer works in logistics procurement." restates an existing belief:

SELECT JSON_UNQUOTE(JSON_EXTRACT(NLI_VERIFY('nli-default',
  'The customer works in logistics procurement.',
  NLI_ANCHORS(content)), '$.verdict')) AS verdict
FROM MEMORY_RECALL('recipe_consistent_mem', 'The customer works in logistics procurement.', 5);

Expected: TRUTH — the gate lets it through, and your application follows with the MEMORY_STORE (or MEMORY_UPSERT with a dedup policy, so restatements merge instead of piling up).

Step 6: Decide your policy for UNVERIFIED_PLAUSIBLE

A genuinely new fact — "The customer attended the October product webinar." — contradicts nothing and is entailed by nothing:

SELECT JSON_UNQUOTE(JSON_EXTRACT(NLI_VERIFY('nli-default',
  'The customer attended the October product webinar.',
  NLI_ANCHORS(content)), '$.verdict')) AS verdict
FROM MEMORY_RECALL('recipe_consistent_mem', 'The customer attended the October product webinar.', 5);

Expected: UNVERIFIED_PLAUSIBLE. This is the policy fork, and it is yours, not the model's: store it (new knowledge is usually unverified — the sane default for memory), store it flagged with lower confidence metadata, or quarantine it. What the gate guarantees is only this: nothing that contradicts established memory gets in silently.

Cleanup (Optional)

DROP TABLE IF EXISTS recipe_consistent_mem_conflicts;
SELECT MEMORY_FORGET('recipe_consistent_mem', id) AS forgotten
FROM MEMORY_RECALL('recipe_consistent_mem', 'customer', 10);

Use it from your app

  • Wrap the gate in your agent's memory-write path: recall-with-the-candidate → verify → route on verdict. One extra round trip, ~tens of milliseconds on CPU since v2.1.2.
  • Pair with MEMORY_UPSERT policies: the NLI gate handles contradiction, upsert dedup handles repetition — different failure modes, both covered.
  • Why in-DB: memory, recall, and the verifier share one engine; there is no window where another writer slips a contradiction in between your check and your store (run both in one transaction).

Key Concepts Learned

  • Memory poisoning is a write-path problem; filtering at recall time is too late because retrieval ranks by similarity, not truth.
  • Probing recall with the candidate itself retrieves exactly the beliefs most likely to conflict with it.
  • Contradiction precedence makes LIE a reliable rejection signal, while UNVERIFIED_PLAUSIBLE is a policy decision — most memories are new facts, and rejecting everything unverified would make the agent unable to learn.
  • The conflict log is as valuable as the gate: it turns silent corruption into a reviewable queue with the exact violated belief attached.

Tags

nliagent-memorymemory-storememory-recallnli-verifynli-anchorsmemory-poisoningsql

Run this on your own machine

Install SynapCores Community Edition free, paste the SQL or Cypher above into the bundled web UI, and watch it run.

Download Free CE