<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Abhas</title>
    <description>The latest articles on DEV Community by Abhas (@abhas_18s).</description>
    <link>https://dev.to/abhas_18s</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4192680%2Fba3b59ee-ff51-470b-bddd-57a4ced79f3c.png</url>
      <title>DEV Community: Abhas</title>
      <link>https://dev.to/abhas_18s</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/abhas_18s"/>
    <language>en</language>
    <item>
      <title># Patient Zero: Building an Immune System for AI Agent Memory</title>
      <dc:creator>Abhas</dc:creator>
      <pubDate>Sun, 18 Oct 2026 18:24:03 +0000</pubDate>
      <link>https://dev.to/abhas_18s/-patient-zero-building-an-immune-system-for-ai-agent-memory-kcm</link>
      <guid>https://dev.to/abhas_18s/-patient-zero-building-an-immune-system-for-ai-agent-memory-kcm</guid>
      <description>&lt;p&gt;&lt;strong&gt;Built for the WeMakeDevs × FalkorDB "Graph Hacks: Context for AI Agents" hackathon.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The bug nobody can trace
&lt;/h2&gt;

&lt;p&gt;Imagine three AI agents sharing one memory. A Researcher agent reads an email that says a vendor changed bank details. It stores the claim. A vendor-portal scrape repeats the same claim, so the memory now says &lt;strong&gt;"IBAN confirmed by 2 sources."&lt;/strong&gt; The Invoicing agent trusts it and queues a payment. The Scheduler agent merges the same claim into a vendor profile.&lt;/p&gt;

&lt;p&gt;Then security tells you the email was spoofed.&lt;/p&gt;

&lt;p&gt;Now what? Which memories came from that email? Which decisions relied on them? Which beliefs are still valid because they have independent support? With a vector store, you can't answer any of this. It stores &lt;em&gt;what is similar&lt;/em&gt;, not &lt;em&gt;what came from where&lt;/em&gt;. The usual fix is to wipe the memory and start over.&lt;/p&gt;

&lt;p&gt;That is the problem we built &lt;strong&gt;Patient Zero&lt;/strong&gt; to solve.&lt;/p&gt;

&lt;h2&gt;
  
  
  The idea: memory with lineage
&lt;/h2&gt;

&lt;p&gt;Patient Zero treats agent memory like a body that can get infected. Every fact an agent stores is a node in FalkorDB, and every relationship records where it came from:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Source → Observation → Belief → Decision → Action
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once lineage is a first-class part of the graph, four things become possible that a normal database or vector store handles poorly:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Catch false corroboration.&lt;/strong&gt; "Confirmed by 2 sources" means nothing if both sources trace back to the same origin.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Measure the blast radius.&lt;/strong&gt; Flag one source and see every belief, decision and action it contaminated, across every agent.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Repair instead of wipe.&lt;/strong&gt; Keep the beliefs that still have independent support.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Learn from the incident.&lt;/strong&gt; Turn the failure into a rule that blocks the next attack.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The graph model
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cypher"&gt;&lt;code&gt;&lt;span class="py"&gt;Nodes:&lt;/span&gt;  &lt;span class="n"&gt;Agent&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Source&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Observation&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Belief&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Decision&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Action&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Rule&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Incident&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Observation&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="ss"&gt;[&lt;/span&gt;&lt;span class="nc"&gt;:FROM&lt;/span&gt;&lt;span class="ss"&gt;]&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Source&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;
&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Belief&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="ss"&gt;[&lt;/span&gt;&lt;span class="nc"&gt;:DERIVED_FROM&lt;/span&gt; &lt;span class="ss"&gt;{&lt;/span&gt;&lt;span class="n"&gt;jid&lt;/span&gt;&lt;span class="ss"&gt;}]&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Observation&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;Belief&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;
&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Decision&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="ss"&gt;[&lt;/span&gt;&lt;span class="nc"&gt;:RELIED_ON&lt;/span&gt;&lt;span class="ss"&gt;]&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Belief&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;
&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Action&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="ss"&gt;[&lt;/span&gt;&lt;span class="nc"&gt;:EXECUTES&lt;/span&gt;&lt;span class="ss"&gt;]&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Decision&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;
&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Source&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="ss"&gt;[&lt;/span&gt;&lt;span class="nc"&gt;:MIRRORS&lt;/span&gt;&lt;span class="ss"&gt;]&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Source&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;
&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Rule&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="ss"&gt;[&lt;/span&gt;&lt;span class="nc"&gt;:LEARNED_FROM&lt;/span&gt;&lt;span class="ss"&gt;]&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Incident&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two details do most of the work:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;MIRRORS&lt;/code&gt;&lt;/strong&gt; links a source that copies another (a scrape of the same email, a syndicated post). Without it, copies look like independent confirmation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;jid&lt;/code&gt; on &lt;code&gt;DERIVED_FROM&lt;/code&gt;&lt;/strong&gt; groups premises into justifications. Edges with the same &lt;code&gt;jid&lt;/code&gt; are AND-premises; different &lt;code&gt;jid&lt;/code&gt;s are alternative justifications. This is how the graph can say "this belief has two independent reasons to exist."&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What the Guardian agent does
&lt;/h2&gt;

&lt;p&gt;A Guardian agent (Gemini function calling over MCP tools) picks tools. &lt;strong&gt;The graph computes every answer; the LLM only chooses and narrates.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;check_independence&lt;/code&gt;&lt;/strong&gt; collapses mirrors and compares root origins per justification:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cypher"&gt;&lt;code&gt;&lt;span class="k"&gt;MATCH&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="py"&gt;b:&lt;/span&gt;&lt;span class="n"&gt;Belief&lt;/span&gt; &lt;span class="ss"&gt;{&lt;/span&gt;&lt;span class="py"&gt;id:&lt;/span&gt;&lt;span class="n"&gt;$b&lt;/span&gt;&lt;span class="ss"&gt;})&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="ss"&gt;[&lt;/span&gt;&lt;span class="py"&gt;d:&lt;/span&gt;&lt;span class="n"&gt;DERIVED_FROM&lt;/span&gt;&lt;span class="ss"&gt;]&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="py"&gt;o:&lt;/span&gt;&lt;span class="n"&gt;Observation&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="ss"&gt;[&lt;/span&gt;&lt;span class="nc"&gt;:FROM&lt;/span&gt;&lt;span class="ss"&gt;]&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="py"&gt;s:&lt;/span&gt;&lt;span class="n"&gt;Source&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;OPTIONAL&lt;/span&gt; &lt;span class="k"&gt;MATCH&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="ss"&gt;[&lt;/span&gt;&lt;span class="nc"&gt;:MIRRORS&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="ss"&gt;]&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="py"&gt;root:&lt;/span&gt;&lt;span class="n"&gt;Source&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;NOT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="ss"&gt;[&lt;/span&gt;&lt;span class="nc"&gt;:MIRRORS&lt;/span&gt;&lt;span class="ss"&gt;]&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="ss"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;RETURN&lt;/span&gt; &lt;span class="n"&gt;d.jid&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;justification&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;collect&lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="k"&gt;DISTINCT&lt;/span&gt; &lt;span class="n"&gt;root.id&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;origins&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In our demo, both "confirmations" resolve to one origin. The badge flips from &lt;em&gt;confirmed by 2 sources&lt;/em&gt; to &lt;em&gt;1 independent origin&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;blast_radius&lt;/code&gt;&lt;/strong&gt; is a forward traversal from the flagged source across agents, returning every contaminated belief, decision and action with hop counts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;find_chokepoint&lt;/code&gt;&lt;/strong&gt; finds the one memory where cutting the path stops the infection from reaching the payment.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;quarantine_and_repair&lt;/code&gt;&lt;/strong&gt; runs truth maintenance: a belief survives if &lt;em&gt;any&lt;/em&gt; justification still has all its premises intact. In the demo, the merged vendor profile keeps the facts that came from a signed invoice, loses only the poisoned bank details, and the payment is blocked.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;learn_rule&lt;/code&gt; / &lt;code&gt;pre_action_guard&lt;/code&gt;&lt;/strong&gt; writes a rule node ("block payment claims with fewer than 2 independent origins") and checks it before any action runs. When the attacker replays the same claim through a new mirror, the guard blocks it before execution.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this needs a graph
&lt;/h2&gt;

&lt;p&gt;We asked ourselves the hard question: &lt;em&gt;could this be PostgreSQL plus embeddings?&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Embeddings&lt;/strong&gt; give similarity. They cannot say which belief was derived from which observation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A recursive SQL query&lt;/strong&gt; can do a simple taint traversal. But independence checks (disjoint root sets after collapsing mirrors), chokepoint analysis, and fixpoint repair over alternative justifications are natural graph operations and awkward in SQL.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;FalkorDB multigraph&lt;/strong&gt; gives us one isolated memory graph per tenant, which matters when agents serve different customers.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Take FalkorDB out and Patient Zero stops being able to explain, repair, or defend anything. It becomes a chatbot with memory.&lt;/p&gt;

&lt;h2&gt;
  
  
  What we built
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;A FalkorDB graph with a deterministic attack scenario plus live agent writes through MCP &lt;code&gt;[confirm]&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Seven Guardian tools backed by Cypher &lt;code&gt;[confirm which ones are finished]&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;An animated dashboard that shows the infection spreading hop by hop, with a &lt;strong&gt;"Show the Cypher"&lt;/strong&gt; panel so every claim is checkable&lt;/li&gt;
&lt;li&gt;A learned guard rule that blocks a replay attack&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What was hard
&lt;/h2&gt;

&lt;p&gt;The trickiest part was expressing "survives if any justification is intact" in a graph. Putting a justification id on &lt;code&gt;DERIVED_FROM&lt;/code&gt; edges and running a small fixpoint loop over Cypher reads gave us truth maintenance without leaving the graph for the core reads.&lt;/p&gt;

&lt;p&gt;The second challenge was making independence honest. The moment we modeled &lt;code&gt;MIRRORS&lt;/code&gt; explicitly, false corroboration became visible instead of invisible.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's next
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Real integrations: email, ticketing, payments&lt;/li&gt;
&lt;li&gt;A time scrubber to replay how memory looked at any moment&lt;/li&gt;
&lt;li&gt;A vulnerability scan using centrality to flag load-bearing, single-origin beliefs &lt;em&gt;before&lt;/em&gt; an attack&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>agents</category>
      <category>ai</category>
      <category>database</category>
      <category>devchallenge</category>
    </item>
  </channel>
</rss>
