<?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: MOHITH GOWDA</title>
    <description>The latest articles on DEV Community by MOHITH GOWDA (@mohith__gowda).</description>
    <link>https://dev.to/mohith__gowda</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%2F4236212%2F45d3caba-4e5e-4cd9-a310-0cb296985f0e.png</url>
      <title>DEV Community: MOHITH GOWDA</title>
      <link>https://dev.to/mohith__gowda</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mohith__gowda"/>
    <language>en</language>
    <item>
      <title># The 0 / 5 / 20 incident learning curve: watching an agent get better without retraining</title>
      <dc:creator>MOHITH GOWDA</dc:creator>
      <pubDate>Tue, 10 Nov 2026 03:31:17 +0000</pubDate>
      <link>https://dev.to/mohith__gowda/-the-0-5-20-incident-learning-curve-watching-an-agent-get-better-without-retraining-1e7h</link>
      <guid>https://dev.to/mohith__gowda/-the-0-5-20-incident-learning-curve-watching-an-agent-get-better-without-retraining-1e7h</guid>
      <description>&lt;p&gt;"It learns over time" is the most common claim made about AI agents with memory, and the hardest one to show. You can't just say it. You have to run the same question at different points in the agent's life and put the answers side by side.&lt;/p&gt;

&lt;p&gt;That's what I did with DejaVu, an incident-response agent that remembers past incidents and which fixes worked. Same alert, same model, same code; the only thing that changes is how much history is in memory. This article walks through the three answers and how the experiment is built.&lt;/p&gt;

&lt;p&gt;Repo: &lt;a href="https://github.com/cybersleuth25/Deja-Vu" rel="noopener noreferrer"&gt;https://github.com/cybersleuth25/Deja-Vu&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The question
&lt;/h2&gt;

&lt;p&gt;The alert is the same every time:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Checkout 500s: DB connection pool exhausted (payments-api)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It's a good test case because the "obvious" fix, restarting the connection pool, is the one that keeps failing for this team. An agent that learns should stop recommending it.&lt;/p&gt;

&lt;h2&gt;
  
  
  0 incidents: the generic answer
&lt;/h2&gt;

&lt;p&gt;With an empty memory, the agent can only fall back to standard advice:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Do first:&lt;/strong&gt; Restart connection pool (generic); Restart service (generic); Increase pool size (generic)&lt;br&gt;
&lt;strong&gt;Avoid:&lt;/strong&gt; none&lt;br&gt;
No similar incidents in memory.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is the "before" picture. It's what you'd get from any chatbot, and its first suggestion is precisely the one that has never worked here.&lt;/p&gt;

&lt;h2&gt;
  
  
  5 incidents: the first match
&lt;/h2&gt;

&lt;p&gt;After the five oldest incidents are in memory, one of them is a DB pool outage:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Matched:&lt;/strong&gt; INC-0004&lt;br&gt;
&lt;strong&gt;Do first:&lt;/strong&gt; Roll back pool config (1/1)&lt;br&gt;
&lt;strong&gt;Avoid:&lt;/strong&gt; Restart connection pool (failed 1/1)&lt;br&gt;
The change "Tidy db config" touched db/pool.py 3h ago; a change to the same files preceded INC-0004.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;One past incident is already enough to flip the advice. The agent now leads with the rollback, warns against the restart, and notices that a recent change to the same file came before the last outage too. With a single data point, "1/1" is a hint rather than proof, and the answer shows the count so the engineer can judge that.&lt;/p&gt;

&lt;h2&gt;
  
  
  20 incidents: a track record
&lt;/h2&gt;

&lt;p&gt;With the first 20 incidents in memory:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Matched:&lt;/strong&gt; INC-0004, INC-0009, INC-0014, INC-0020&lt;br&gt;
&lt;strong&gt;Do first:&lt;/strong&gt; Roll back pool config (3/4); Patch connection leak (1/1)&lt;br&gt;
&lt;strong&gt;Avoid:&lt;/strong&gt; Restart connection pool (failed 4/4)&lt;br&gt;
This outage on payments-api matches 4 past incidents... Roll back pool config worked 3 of 4 times; Restart connection pool failed 4 of 4.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;code&gt;[Screenshot 1: terminal output showing the three answers at 0, 5 and 20 incidents]&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Now it's a track record. The restart has failed four times out of four. The rollback worked three times out of four, and the one time it didn't, the cause was a connection leak, which is why "Patch connection leak" now appears as a second option. That's the kind of nuance an engineer only gets from someone who was there for all four outages.&lt;/p&gt;

&lt;p&gt;Nothing was retrained between these runs. The model is the same at 0, 5 and 20. Only the memory grew.&lt;/p&gt;

&lt;h2&gt;
  
  
  How the experiment works
&lt;/h2&gt;

&lt;p&gt;The script sorts the incident history by date and replays it in windows: the first 0, then the first 5, then the first 20. For each window it asks the agent the same question:&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="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;steps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;live&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;scenario&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"checkout_500s"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;history&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;sorted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;load_seed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"opened_at"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;alert&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;detector&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;primary_alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sim_events&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scenario&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;live_mem&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;HindsightMemory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;live&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;
    &lt;span class="n"&gt;rows&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;steps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;window&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;history&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;live_mem&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;mem&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;WindowedMemory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;live_mem&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;window&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;mem&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;MockMemory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;mem&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;retain_many&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;window&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;analyzer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;investigate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;window&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mem&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;use_memory&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;llm&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"n"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;rows&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two details keep the experiment honest:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Time order.&lt;/strong&gt; The windows follow the real order of the incidents, so at "5 incidents" the agent only knows what the team would have known at that point.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The live run uses the real memory.&lt;/strong&gt; With &lt;code&gt;--live&lt;/code&gt;, recall goes to the actual &lt;a href="https://github.com/vectorize-io/hindsight" rel="noopener noreferrer"&gt;Hindsight&lt;/a&gt; bank. A thin wrapper drops any recalled incident outside the current window, so the agent can't peek at the future even though the bank holds everything.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The LLM is switched off for this run (&lt;code&gt;llm=None&lt;/code&gt;), so the text comes from a fixed template. That's on purpose: if the answer changes, it's because the memory changed, not because the model phrased something differently.&lt;/p&gt;

&lt;h2&gt;
  
  
  What "getting better" means here
&lt;/h2&gt;

&lt;p&gt;Looking at the three answers, the improvement is concrete:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Memory&lt;/th&gt;
&lt;th&gt;Recommends the failing fix first?&lt;/th&gt;
&lt;th&gt;Warns against it?&lt;/th&gt;
&lt;th&gt;Cites past incidents?&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;0 incidents&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5 incidents&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes (1/1)&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;20 incidents&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes (4/4)&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;And it keeps going after that. In the live app, every time an engineer clicks &lt;strong&gt;Worked&lt;/strong&gt; or &lt;strong&gt;Failed&lt;/strong&gt; on an action, the verdict is retained into Hindsight memory, and the next similar alert includes it. The learning curve doesn't stop at 20; it's what happens every time someone records an outcome.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;[Screenshot 2: the app's incident view with the Worked and Failed buttons]&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;If you want the background on why this is different from fine-tuning or plain retrieval, Vectorize's &lt;a href="https://vectorize.io/what-is-agent-memory" rel="noopener noreferrer"&gt;What is agent memory&lt;/a&gt; explains it well, and the &lt;a href="https://hindsight.vectorize.io/" rel="noopener noreferrer"&gt;Hindsight docs&lt;/a&gt; cover how retain and recall work.&lt;/p&gt;

&lt;h2&gt;
  
  
  Limitations
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The history is synthetic and clean.&lt;/strong&gt; The 32 incidents were written for this project, with patterns planted on purpose. Real histories are messier, so a real curve would rise more slowly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It depends on people recording outcomes.&lt;/strong&gt; If nobody clicks &lt;strong&gt;Worked&lt;/strong&gt; or &lt;strong&gt;Failed&lt;/strong&gt;, there's no track record to learn from.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;One question, one curve.&lt;/strong&gt; The experiment uses a single alert type. Other incident types improve too (credential stuffing goes from generic advice to "Block ASN at WAF worked 3 of 4"), but I only built the side-by-side for this one.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Takeaway
&lt;/h2&gt;

&lt;p&gt;If you claim your agent learns, show the curve: same question, growing memory, answers side by side. For DejaVu, the jump from "restart the pool" to "restarting the pool failed 4 of 4, roll back the config instead" is the whole product in one table, and it happened without retraining anything.&lt;/p&gt;

&lt;p&gt;Hindsight docs: &lt;a href="https://hindsight.vectorize.io/" rel="noopener noreferrer"&gt;https://hindsight.vectorize.io/&lt;/a&gt;&lt;br&gt;
Hindsight on GitHub: &lt;a href="https://github.com/vectorize-io/hindsight" rel="noopener noreferrer"&gt;https://github.com/vectorize-io/hindsight&lt;/a&gt;&lt;br&gt;
What is agent memory: &lt;a href="https://vectorize.io/what-is-agent-memory" rel="noopener noreferrer"&gt;https://vectorize.io/what-is-agent-memory&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Tags: @Code.in&lt;/p&gt;

</description>
      <category>agents</category>
      <category>ai</category>
      <category>github</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
