<?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: Samad Sadiq</title>
    <description>The latest articles on DEV Community by Samad Sadiq (@samad117).</description>
    <link>https://dev.to/samad117</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%2F4233416%2F45f17c61-0283-4951-810e-bd8de4e7a180.png</url>
      <title>DEV Community: Samad Sadiq</title>
      <link>https://dev.to/samad117</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/samad117"/>
    <language>en</language>
    <item>
      <title>“Hindsight Stopped My On-Call Agent From Repeating a Failed Fix”</title>
      <dc:creator>Samad Sadiq</dc:creator>
      <pubDate>Mon, 09 Nov 2026 14:39:10 +0000</pubDate>
      <link>https://dev.to/samad117/hindsight-stopped-my-on-call-agent-from-repeating-a-failed-fix-14ja</link>
      <guid>https://dev.to/samad117/hindsight-stopped-my-on-call-agent-from-repeating-a-failed-fix-14ja</guid>
      <description>&lt;p&gt;"A practical incident-triage design that remembers failed fixes, not just successful ones, so an on-call agent learns from real operator feedback."&lt;/p&gt;

&lt;h1&gt;
  
  
  Hindsight Stopped My On-Call Agent From Repeating a Failed Fix
&lt;/h1&gt;

&lt;p&gt;At 03:47 UTC, &lt;code&gt;billing-api&lt;/code&gt; threw &lt;code&gt;HikariPool-1 - Connection is not available, request timed out after 30000ms&lt;/code&gt;. Every LLM I know gives the same answer: the pool is too small, so increase it. In our team, that exact fix had already been tried, pushed Postgres past &lt;code&gt;max_connections&lt;/code&gt;, and made the outage worse. A stateless model cannot know that. So I built an on-call agent that does.&lt;/p&gt;

&lt;p&gt;This is how I wired &lt;a href="https://github.com/vectorize-io/hindsight" rel="noopener noreferrer"&gt;Hindsight&lt;/a&gt;, an open-source agent memory system, into an incident triage loop, and what I learned about making an agent remember corrections instead of just documents.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem: the answer is in someone's head
&lt;/h2&gt;

&lt;p&gt;When production breaks, the first question is: "Have we seen this before, and what did we do?" The answer usually lives in a Slack thread, a post-mortem no one can find, or the head of whoever was on call six months ago.&lt;/p&gt;

&lt;p&gt;The root cause is also usually specific to your architecture. In my test data, the pool-exhaustion alert is not a sizing problem at all. A nightly invoice-reconciliation job (&lt;code&gt;recon-batch&lt;/code&gt;) runs without a statement timeout and holds connections open past 02:00 UTC. The fix that worked was terminating those sessions with &lt;code&gt;pg_terminate_backend&lt;/code&gt; and setting &lt;code&gt;statement_timeout=60s&lt;/code&gt; on the role. The fix that failed was doubling the pool from 50 to 100.&lt;/p&gt;

&lt;p&gt;No amount of prompt engineering gets that context into a model that has never seen your system. It has to come from memory.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the system does
&lt;/h2&gt;

&lt;p&gt;An engineer pastes an alert. The agent:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Recalls&lt;/strong&gt; similar past incidents and corrections from Hindsight.&lt;/li&gt;
&lt;li&gt;Asks the LLM (Groq, &lt;code&gt;gpt-oss-120b&lt;/code&gt; with a &lt;code&gt;qwen3-32b&lt;/code&gt; fallback) for a structured JSON verdict grounded in those memories.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Retains&lt;/strong&gt; the alert and the still-unconfirmed verdict.&lt;/li&gt;
&lt;li&gt;Waits for the engineer to mark the verdict right or wrong, with the actual cause, the actual fix, and any fix that failed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Retains&lt;/strong&gt; that feedback so the next similar alert benefits from it.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It is a FastAPI backend (&lt;code&gt;app/main.py&lt;/code&gt;), a single-page UI, and one file, &lt;code&gt;app/memory.py&lt;/code&gt;, that holds every Hindsight call. Each team gets its own memory bank via &lt;code&gt;bank_id&lt;/code&gt;, so one team's incidents never bleed into another's.&lt;/p&gt;

&lt;h2&gt;
  
  
  The through-line: store the failures, not just the fixes
&lt;/h2&gt;

&lt;p&gt;Most "agent memory" demos store what worked. The design decision I care about most is storing what did &lt;strong&gt;not&lt;/strong&gt; work in a form the model cannot ignore.&lt;/p&gt;

&lt;p&gt;When an engineer submits feedback, &lt;code&gt;record_feedback&lt;/code&gt; in &lt;code&gt;app/triage.py&lt;/code&gt; builds a plain-text record with explicit markers:&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;parts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Engineer feedback on incident &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;incident_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; (service &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;service&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;unknown&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;).&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;The alert was: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;alert&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;The agent&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s suggested root cause was &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;CORRECT&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;correct&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;WRONG&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;.&lt;/span&gt;&lt;span class="sh"&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;if&lt;/span&gt; &lt;span class="n"&gt;actual_cause&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;parts&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="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CONFIRMED ROOT CAUSE: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;actual_cause&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&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;actual_fix&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;parts&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="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;FIX THAT WORKED: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;actual_fix&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&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;f&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;failed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;parts&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="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;FIX THAT DID NOT WORK (do not suggest again for this kind of alert): &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That text is retained into Hindsight with a per-incident &lt;code&gt;document_id&lt;/code&gt;, so a later correction to the same incident replaces the record instead of duplicating it:&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;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;retain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;bank_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;bank_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;incident outcome: engineer correction&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;document_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;incident_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;-feedback&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;tags&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;incident&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;correction&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;service:&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;retain_async&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I use &lt;code&gt;retain_async=False&lt;/code&gt; on purpose. If an engineer corrects the agent and a similar alert fires a minute later, the correction has to be recallable right away.&lt;/p&gt;

&lt;p&gt;On the way back out, recall runs before every verdict:&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;resp&lt;/span&gt; &lt;span class="o"&gt;=&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;_client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;recall&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bank_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;bank_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_tokens&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2048&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;budget&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;mid&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;texts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;getattr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;text&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&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;r&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="p"&gt;[])][:&lt;/span&gt;&lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The markers then do their job in the prompt. Memories containing &lt;code&gt;CONFIRMED ROOT CAUSE:&lt;/code&gt; are sorted first and labelled &lt;code&gt;VERIFIED PAST INCIDENT&lt;/code&gt;, and the system prompt says that if a memory records a failed fix, the model must not suggest it again and must say it is avoiding it because of past incident memory. The response also returns &lt;code&gt;cited_memories&lt;/code&gt;, and the UI shows a "Memories used" panel, so an engineer can see exactly why the agent said what it said.&lt;/p&gt;

&lt;h2&gt;
  
  
  Recall quality was the real work
&lt;/h2&gt;

&lt;p&gt;Retaining is the easy half. Getting the &lt;em&gt;right&lt;/em&gt; memory back for an alert that does not look like the original was where I spent most of my time.&lt;/p&gt;

&lt;p&gt;Two alerts about the same failure rarely share words. One is a &lt;code&gt;HikariPool&lt;/code&gt; timeout on &lt;code&gt;billing-api&lt;/code&gt;; the next is a &lt;code&gt;pgbouncer: no more connections allowed&lt;/code&gt; on &lt;code&gt;invoice-service&lt;/code&gt;. Same cause, different vocabulary, different service.&lt;/p&gt;

&lt;p&gt;So &lt;code&gt;build_recall_query&lt;/code&gt; enriches the raw alert with domain concepts before it goes to Hindsight:&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;if&lt;/span&gt; &lt;span class="nf"&gt;any&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;alert_lower&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;hikaripool&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pgbouncer&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;max_client_conn&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;connection is not available&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)):&lt;/span&gt;
    &lt;span class="n"&gt;q_parts&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;database connection pool postgres recon-batch statement_timeout&lt;/span&gt;&lt;span class="sh"&gt;"&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;any&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;alert_lower&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;getaddrinfo&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;eai_again&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;read udp&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;:53&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;lookup&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;dns&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;coredns&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)):&lt;/span&gt;
    &lt;span class="n"&gt;q_parts&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;dns name resolution coredns udp lookup timeout&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It is a hand-written list for eight failure patterns, and I am under no illusion that it generalizes. It is a pragmatic bridge between how alerts are written and how post-mortems are written.&lt;/p&gt;

&lt;p&gt;The repo supports two Hindsight deployments behind one &lt;code&gt;Memory&lt;/code&gt; protocol: the &lt;code&gt;hindsight-client&lt;/code&gt; SDK against Hindsight Cloud or a self-hosted Docker container, and the Hindsight Inference API, where candidate memories are scored with &lt;code&gt;hs-decide-1-flash&lt;/code&gt; through a Cohere-compatible &lt;code&gt;rerank&lt;/code&gt; call. Because both sit behind the same &lt;code&gt;retain&lt;/code&gt;/&lt;code&gt;recall&lt;/code&gt; interface, &lt;code&gt;triage.py&lt;/code&gt; does not know or care which one is running.&lt;/p&gt;

&lt;h2&gt;
  
  
  What happened: 0 of 24 without memory
&lt;/h2&gt;

&lt;p&gt;To measure this honestly, I built a replay that runs the same incidents with memory off and on. When memory is off, &lt;code&gt;triage.py&lt;/code&gt; skips both recall and retain, and feedback retains nothing, so the baseline cannot learn by accident. The dataset is 40 synthetic incidents across 8 recurring patterns. Each wave runs one incident from each pattern, with engineer feedback in between.&lt;/p&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%2Fvm9ecf09ghpwpzby0ttn.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%2Fvm9ecf09ghpwpzby0ttn.png" alt="Learning curve: Memory OFF vs Memory ON across three waves" width="800" height="328"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Wave&lt;/th&gt;
&lt;th&gt;Memory OFF&lt;/th&gt;
&lt;th&gt;Memory ON (Hindsight)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1 (cold start)&lt;/td&gt;
&lt;td&gt;0 / 8&lt;/td&gt;
&lt;td&gt;0 / 8&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;0 / 8&lt;/td&gt;
&lt;td&gt;6 / 8&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;0 / 8&lt;/td&gt;
&lt;td&gt;8 / 8&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Wave 1 is a tie, and it should be: nothing has been retained yet, so the agent gives a generic answer, the engineer corrects it, and Hindsight stores the correction. By wave 3, every pattern was resolved correctly. The stateless baseline stayed at zero across all 24.&lt;/p&gt;

&lt;p&gt;In wave 2, the DNS-resolution and Kafka-disk-full incidents still missed even though memory was on. Those two failed for a simple reason: the alert names were different enough that the retrieval layer did not surface the earlier correction at the right moment. The problem was not storage; it was semantic mismatch between the alert's wording and the original incident description. Once the query captured the relevant concepts, the correction became usable again.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Before:&lt;/strong&gt; a &lt;code&gt;HikariPool&lt;/code&gt; timeout alert, memory off. The agent suggested raising the pool size without context, which the matcher scored as a miss.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Agent verdict: "This is a connection pool sizing issue. Increase the maximum pool size and watch the Postgres max_connections ceiling."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;After:&lt;/strong&gt; the same pattern, memory on, with &lt;code&gt;recon-batch&lt;/code&gt; cited from a prior correction.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Agent verdict: "This is likely a leaked transaction or a long-running reconciliation job holding database connections. The earlier incident memory indicates &lt;code&gt;recon-batch&lt;/code&gt; was running without a statement timeout and the successful remediation was to terminate backend sessions with &lt;code&gt;pg_terminate_backend&lt;/code&gt; and set &lt;code&gt;statement_timeout=60s&lt;/code&gt; on the role. I am avoiding the generic "increase pool size" fix because it failed in a similar incident."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Lessons learned
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Store failures explicitly.&lt;/strong&gt; A line that says "FIX THAT DID NOT WORK" is more valuable than a line that says what worked, because it is the one thing a stateless model will keep repeating.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Retain the unconfirmed guess too, but label it.&lt;/strong&gt; The agent's own verdict is stored as "NOT yet confirmed by an engineer," so it cannot be mistaken for ground truth later.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Memory has to fail soft.&lt;/strong&gt; Hindsight timeouts and auth errors log a warning and triage continues with empty memory. An incident tool that crashes when its memory is down is worse than no tool. There are tests for exactly this.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Recall is a retrieval problem, not a storage problem.&lt;/strong&gt; My accuracy gap was never about whether Hindsight stored the correction. It was about whether the query looked enough like it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keep the baseline honest.&lt;/strong&gt; Memory-off mode bypasses retain entirely. Otherwise your "before" number quietly improves and your comparison means nothing.&lt;/li&gt;
&lt;/ol&gt;

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

&lt;p&gt;The incidents are synthetic, generated from eight patterns. The matcher that scores verdicts (&lt;code&gt;app/judge.py&lt;/code&gt;) is a heuristic, not a human reviewer, and eight patterns repeated three times says little about a messy real incident history. The domain-term list in the recall query is hand-tuned. A real deployment would need real post-mortems, more patterns, and a human-reviewed evaluation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;The code is at &lt;a href="https://github.com/FirdaushAnwer/ON-CALL-AGENT-WITH-MEMORY" rel="noopener noreferrer"&gt;github.com/FirdaushAnwer/ON-CALL-AGENT-WITH-MEMORY&lt;/a&gt;. To learn how it works under the hood, start with the &lt;a href="https://hindsight.vectorize.io/" rel="noopener noreferrer"&gt;Hindsight documentation&lt;/a&gt;, and if you want the broader idea, Vectorize's explainer on &lt;a href="https://vectorize.io/what-is-agent-memory" rel="noopener noreferrer"&gt;what agent memory is&lt;/a&gt; is a good primer. Hindsight is open source, so you can run it yourself with a single Docker command and point &lt;code&gt;HINDSIGHT_BASE_URL&lt;/code&gt; at it.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>llm</category>
      <category>python</category>
      <category>devops</category>
    </item>
  </channel>
</rss>
