<?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: Kadavath Priyanka</title>
    <description>The latest articles on DEV Community by Kadavath Priyanka (@kadavath_priyanka_fa8b46a).</description>
    <link>https://dev.to/kadavath_priyanka_fa8b46a</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%2F4150691%2Fa811f6d7-f512-4157-b1bc-4161f0e5dc6a.jpg</url>
      <title>DEV Community: Kadavath Priyanka</title>
      <link>https://dev.to/kadavath_priyanka_fa8b46a</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/kadavath_priyanka_fa8b46a"/>
    <language>en</language>
    <item>
      <title>My CI Agent Kept Forgetting Everything—Here’s How I Fixed Its Memory!</title>
      <dc:creator>Kadavath Priyanka</dc:creator>
      <pubDate>Tue, 29 Sep 2026 18:03:15 +0000</pubDate>
      <link>https://dev.to/kadavath_priyanka_fa8b46a/my-ci-agent-kept-forgetting-everything-heres-how-i-fixed-its-memory-664</link>
      <guid>https://dev.to/kadavath_priyanka_fa8b46a/my-ci-agent-kept-forgetting-everything-heres-how-i-fixed-its-memory-664</guid>
      <description>&lt;h1&gt;
  
  
  Persistent Memory Makes Prompt Injection Worse. Here's How We Guarded Ours
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7hz3rkjnm6svst9q3xph.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7hz3rkjnm6svst9q3xph.png" alt=" " width="800" height="704"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We built a multi-agent competitive intelligence pipeline in CrewAI. You give it a topic and competitors, and agents research the web, analyse what they find, and write a briefing where every claim carries a citation. It worked, but it forgot everything between runs. Week 8 knew nothing about weeks 1 to 7.&lt;/p&gt;

&lt;p&gt;Fixing that meant adding persistent memory. It also created a problem we hadn't had before: anything an agent stores can come back in every future run.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why memory changes the threat
&lt;/h2&gt;

&lt;p&gt;The pipeline reads web pages. If a page contains hidden instructions and an agent stores them as a "finding", that text is now in memory. Every later run retrieves it and feeds it to the Analyst. A one-off injection becomes a permanent one. That's why we treated the write path as seriously as the read path.&lt;/p&gt;

&lt;h2&gt;
  
  
  The memory layer
&lt;/h2&gt;

&lt;p&gt;The memory is a class we wrote, &lt;code&gt;HindsightStore&lt;/code&gt; (&lt;code&gt;memory/hindsight_store.py&lt;/code&gt;). It's plain Python, using an append-only &lt;code&gt;events.jsonl&lt;/code&gt; and JSON files for profiles, strategies, and predictions. It is our own code, not the Vectorize Hindsight library: &lt;a href="https://github.com/vectorize-io/hindsight" rel="noopener noreferrer"&gt;https://github.com/vectorize-io/hindsight&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Findings are stored as typed events (&lt;code&gt;CompetitorEvent&lt;/code&gt;) with a competitor, event type, date, title, description, impact score, confidence, and evidence URLs. Agents reach the store through one CrewAI tool, &lt;code&gt;HindsightStoreTool&lt;/code&gt;, with six operations: &lt;code&gt;store_event&lt;/code&gt;, &lt;code&gt;get_history&lt;/code&gt;, &lt;code&gt;get_profile&lt;/code&gt;, &lt;code&gt;search_memory&lt;/code&gt;, &lt;code&gt;get_strategy&lt;/code&gt;, and &lt;code&gt;get_predictions&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The pipeline has seven agents: Discovery, Research, &lt;strong&gt;Memory&lt;/strong&gt;, Analyst, Strategy Evolution, Prediction, and Writer. The Memory Agent stores every finding, then passes stored history and a profile to the Analyst.&lt;/p&gt;

&lt;h2&gt;
  
  
  The guards
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Injection guard.&lt;/strong&gt; Strips instruction-like patterns from fetched pages before they reach the LLM.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Memory injection guard.&lt;/strong&gt; &lt;code&gt;memory_injection_guard()&lt;/code&gt; checks queries headed for the store.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Input validation.&lt;/strong&gt; &lt;code&gt;validate_competitor_name()&lt;/code&gt; rejects names containing shell metacharacters or longer than 200 characters.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Citation guard.&lt;/strong&gt; Runs on every briefing so claims can't float free of sources.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Typed events help here too. A schema with fixed event types is a smaller surface than free text, because a stored record has to fit a shape.&lt;/p&gt;

&lt;h2&gt;
  
  
  Evidence-scaled confidence
&lt;/h2&gt;

&lt;p&gt;Memory also lets us make the output less overconfident. Each write updates a per-competitor profile:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;high_impact&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_events&lt;/span&gt;
               &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;competitor&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;competitor&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;impact_score&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mf"&gt;8.0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;high_impact&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;prof&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;risk_level&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;RiskLevel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CRITICAL&lt;/span&gt;
&lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;high_impact&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;prof&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;risk_level&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;RiskLevel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HIGH&lt;/span&gt;
&lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;prof&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;total_events&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;prof&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;risk_level&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;RiskLevel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MEDIUM&lt;/span&gt;

&lt;span class="n"&gt;prof&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;confidence_score&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.98&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.3&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prof&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;total_events&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;0.07&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Confidence starts at 0.3, adds 0.07 per event, and caps at 0.98. The Writer hedges its wording when confidence is low.&lt;/p&gt;

&lt;h2&gt;
  
  
  Before vs. after
&lt;/h2&gt;

&lt;p&gt;We tested on a fictional competitor, "NeuraCode AI", using the seeded demo data that &lt;code&gt;scripts/demo_memory.py&lt;/code&gt; runs with no LLM and no network. These are demo events, not real market data.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Before:&lt;/strong&gt; one event. The Analyst can only say NeuraCode launched a code review tool.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;After:&lt;/strong&gt; six events across product, hiring (15 ML engineers), pricing ($45/seat/month enterprise tier), M&amp;amp;A ($28M acquisition), a security scanner, and a JetBrains integration. The profile shows 6 events and 72% confidence (0.3 + 6 × 0.07).&lt;/p&gt;

&lt;p&gt;That's a controlled demo, not a benchmark, and we haven't measured quality over weeks of live use.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where the guards are weak
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Pattern matching only catches patterns we thought of. A new phrasing can slip through, and we haven't red-teamed the guards.&lt;/li&gt;
&lt;li&gt;Impact scores are LLM-generated with no rule-based floor, so the &lt;code&gt;CRITICAL&lt;/code&gt; risk level can shift with model or prompt changes.&lt;/li&gt;
&lt;li&gt;Predictions are never graded automatically. &lt;code&gt;update_prediction_status()&lt;/code&gt; exists but nothing calls it.&lt;/li&gt;
&lt;li&gt;Strategy output is parsed with regex in &lt;code&gt;crew.py&lt;/code&gt;, which is brittle.&lt;/li&gt;
&lt;li&gt;A fresh store auto-seeds demo data, which can mislead testing.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Takeaways
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Persistent memory turns injections from temporary into permanent, so validate before you store.&lt;/li&gt;
&lt;li&gt;Constrain what memory can hold with a schema.&lt;/li&gt;
&lt;li&gt;Don't call a guard "done" until someone has tried to break it.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Code: &lt;a href="https://github.com/24wh1a0510/CI---Copilot" rel="noopener noreferrer"&gt;https://github.com/24wh1a0510/CI---Copilot&lt;/a&gt;&lt;/p&gt;

</description>
      <category>agents</category>
      <category>ai</category>
      <category>python</category>
      <category>security</category>
    </item>
  </channel>
</rss>
