<?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: Boini chandana</title>
    <description>The latest articles on DEV Community by Boini chandana (@boini_chandana_f3b4d0bbde).</description>
    <link>https://dev.to/boini_chandana_f3b4d0bbde</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%2F4148517%2F208ed7eb-5d71-4901-bde4-145f0430a868.png</url>
      <title>DEV Community: Boini chandana</title>
      <link>https://dev.to/boini_chandana_f3b4d0bbde</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/boini_chandana_f3b4d0bbde"/>
    <language>en</language>
    <item>
      <title>I Built an Agent That Learns From Confirmed Incidents</title>
      <dc:creator>Boini chandana</dc:creator>
      <pubDate>Tue, 29 Sep 2026 04:43:10 +0000</pubDate>
      <link>https://dev.to/boini_chandana_f3b4d0bbde/ai-incident-response-agentpowered-by-hindsight-11h3</link>
      <guid>https://dev.to/boini_chandana_f3b4d0bbde/ai-incident-response-agentpowered-by-hindsight-11h3</guid>
      <description>&lt;p&gt;The most dangerous sentence in incident response is "we've seen this before." It's usually true enough to be useful and wrong often enough to hurt, and I wanted to build a tool that helps with the first part without amplifying the second.&lt;/p&gt;

&lt;p&gt;RecallOps is an incident-response assistant. An on-call engineer describes what's broken, the system pulls up similar incidents from our history, and it proposes what to check first. The interesting part isn't the retrieval. It's what we decided to let the system remember, and what we decided to make it forget.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it does and how it hangs together
&lt;/h2&gt;

&lt;p&gt;The whole thing is a small FastAPI service and a static HTML page. No frontend build step, no queue, no database of our own. Everything stateful lives in a memory bank managed by &lt;a href="https://github.com/vectorize-io/hindsight" rel="noopener noreferrer"&gt;Hindsight&lt;/a&gt;, an open-source agent memory system. The service exposes four routes that matter:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;POST /api/incidents/analyze&lt;/code&gt; takes a title, service, severity, and error description, and returns similar past incidents plus a structured analysis.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;POST /api/incidents/resolve&lt;/code&gt; takes the engineer's confirmed fix and observed outcome and writes it back to memory.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;GET /api/memories&lt;/code&gt; lists what the bank currently holds, so anyone can audit it.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;GET /api/health&lt;/code&gt; reports the bank in use.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Hindsight's Python client gives us three operations, and each one maps to a step in the workflow: &lt;code&gt;recall&lt;/code&gt; retrieves similar incidents, &lt;code&gt;reflect&lt;/code&gt; reasons over them, and &lt;code&gt;retain&lt;/code&gt; stores new experience. If you're new to the concept, Vectorize has a good primer on &lt;a href="https://vectorize.io/what-is-agent-memory" rel="noopener noreferrer"&gt;what agent memory is&lt;/a&gt;, and the &lt;a href="https://hindsight.vectorize.io/" rel="noopener noreferrer"&gt;Hindsight docs&lt;/a&gt; cover the client API. I won't rehash either.&lt;/p&gt;

&lt;p&gt;The flow for one incident looks like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The engineer submits an incident.&lt;/li&gt;
&lt;li&gt;We build a query from service, title, and description, and call &lt;code&gt;recall&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;We hand the top five results to &lt;code&gt;reflect&lt;/code&gt; with a tightly constrained prompt.&lt;/li&gt;
&lt;li&gt;The engineer sees the evidence and the analysis side by side.&lt;/li&gt;
&lt;li&gt;Later, once a human confirms what fixed it, we call &lt;code&gt;retain&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Step 5 is the one I want to spend most of this article on.&lt;/p&gt;

&lt;h2&gt;
  
  
  The through-line: memory should hold outcomes, not opinions
&lt;/h2&gt;

&lt;p&gt;The obvious way to build this is to write everything into memory: the incident, the model's analysis, the engineer's follow-up, the chat transcript. More context, better recall, right?&lt;/p&gt;

&lt;p&gt;I think that's a trap for incident response specifically. If the model's analysis goes into memory, then next time a similar incident appears, the model retrieves its own earlier guess and treats it as evidence. A wrong hypothesis becomes a "historical incident" that supports itself. You've built a feedback loop that launders speculation into fact, and it does so quietly, because each individual retrieval looks reasonable.&lt;/p&gt;

&lt;p&gt;So the rule in RecallOps is that the only thing that ever gets retained is what a human confirmed. Here is the write path in full:&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="nd"&gt;@app.post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/api/incidents/resolve&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&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;ResolveRequest&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="nf"&gt;ensure_bank&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="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;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;: payment-gateway incident. &lt;/span&gt;&lt;span class="sh"&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 resolution: &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;resolution&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="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Observed outcome: &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;outcome&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="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Runbook: &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;runbook&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="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;Human-confirmed incident resolution. Store as future experience; do not store model reasoning.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;metadata&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_id&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;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="p"&gt;),&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;source&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;engineer-confirmed&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;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ok&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;True&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_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&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="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Three things are doing work here. The write is triggered by an explicit button, "Mark as Resolved &amp;amp; Save Memory", not by the analysis finishing. The content is limited to the confirmed resolution, the observed outcome, and the runbook that was used. And the &lt;code&gt;context&lt;/code&gt; and &lt;code&gt;metadata&lt;/code&gt; fields tag every record with its provenance, so &lt;code&gt;source: engineer-confirmed&lt;/code&gt; is something we can filter or audit on later.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;analyze&lt;/code&gt; route, by contrast, never calls &lt;code&gt;retain&lt;/code&gt;. Reading is free; writing is a decision.&lt;/p&gt;

&lt;p&gt;The seed records follow the same shape. Each one has a root cause, a resolution, and an observed outcome, and nothing that reads like a guess:&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;memory_text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;return &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;Incident: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;m&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="si"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&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;m&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="si"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&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;Root cause: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;root_cause&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="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&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;Successful resolution: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;resolution&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="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&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;Runbook: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;runbook&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="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&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;Observed outcome: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;outcome&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="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;I like this template because it's boring. "Observed outcome" is the field that matters most. A resolution without an outcome is a story someone told; a resolution with one is a data point.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where Hindsight earns its place
&lt;/h2&gt;

&lt;p&gt;I could have built the retrieval side with a vector store and some glue. I didn't, for a reason that only became obvious once I used it for this: incident history isn't a pile of documents, it's accumulated experience, and the questions you ask of it are different.&lt;/p&gt;

&lt;p&gt;An on-call engineer at 3am doesn't type "Payment Gateway Timeout" and want documents containing those words. They want "have we seen something like this, and what happened?" That's why the recall query is built from the service, the title, and the raw error text together:&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;query&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&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;incident&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;incident&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;error_description&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;recalled&lt;/span&gt; &lt;span class="o"&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;4096&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;memories&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;result_to_dict&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="n"&gt;recalled&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I cap it at five memories on purpose. More than that and the reasoning step starts averaging across incidents that only share vocabulary. The &lt;code&gt;budget="mid"&lt;/code&gt; setting is a middle position on how hard recall works; I haven't needed to turn it up.&lt;/p&gt;

&lt;p&gt;The other piece is the bank itself. &lt;code&gt;ensure_bank()&lt;/code&gt; creates it with a mission statement: "Help engineers diagnose production incidents using verified historical experience." That's short, but it gives the memory system a purpose to organize around, and the word "verified" in it is a constraint I wrote for myself as much as for Hindsight.&lt;/p&gt;

&lt;p&gt;The practical benefit is that I didn't have to design a retention schema, an embedding pipeline, or a ranking layer. I wrote the discipline about what goes in, and the memory layer handled storage and retrieval. Every new confirmed resolution is available to the next &lt;code&gt;recall&lt;/code&gt; call immediately, with no re-indexing step in our code.&lt;/p&gt;

&lt;h2&gt;
  
  
  The reasoning step, and making it distrust its own evidence
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;reflect&lt;/code&gt; is where the recalled history becomes a recommendation. I spent more time on this prompt than on the rest of the service combined, and most of that time went into rules that make the model less confident:&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;Rules&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="mf"&gt;1.&lt;/span&gt; &lt;span class="n"&gt;Similar&lt;/span&gt; &lt;span class="n"&gt;symptoms&lt;/span&gt; &lt;span class="n"&gt;do&lt;/span&gt; &lt;span class="n"&gt;NOT&lt;/span&gt; &lt;span class="n"&gt;prove&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;same&lt;/span&gt; &lt;span class="n"&gt;root&lt;/span&gt; &lt;span class="n"&gt;cause&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="mf"&gt;2.&lt;/span&gt; &lt;span class="n"&gt;Explicitly&lt;/span&gt; &lt;span class="n"&gt;explain&lt;/span&gt; &lt;span class="n"&gt;why&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;best&lt;/span&gt; &lt;span class="n"&gt;historical&lt;/span&gt; &lt;span class="n"&gt;memory&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="n"&gt;relevant&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="mf"&gt;3.&lt;/span&gt; &lt;span class="n"&gt;Explicitly&lt;/span&gt; &lt;span class="n"&gt;explain&lt;/span&gt; &lt;span class="n"&gt;what&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="n"&gt;different&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;still&lt;/span&gt; &lt;span class="n"&gt;unknown&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="mf"&gt;4.&lt;/span&gt; &lt;span class="n"&gt;Recommend&lt;/span&gt; &lt;span class="n"&gt;verification&lt;/span&gt; &lt;span class="n"&gt;checks&lt;/span&gt; &lt;span class="n"&gt;before&lt;/span&gt; &lt;span class="n"&gt;risky&lt;/span&gt; &lt;span class="n"&gt;production&lt;/span&gt; &lt;span class="n"&gt;changes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="mf"&gt;5.&lt;/span&gt; &lt;span class="n"&gt;Treat&lt;/span&gt; &lt;span class="n"&gt;previous&lt;/span&gt; &lt;span class="n"&gt;solutions&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;references&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;commands&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="mf"&gt;6.&lt;/span&gt; &lt;span class="n"&gt;Never&lt;/span&gt; &lt;span class="n"&gt;invent&lt;/span&gt; &lt;span class="n"&gt;evidence&lt;/span&gt; &lt;span class="n"&gt;that&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;incident&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;memories&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="mf"&gt;7.&lt;/span&gt; &lt;span class="n"&gt;Keep&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;answer&lt;/span&gt; &lt;span class="n"&gt;concise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The output schema forces the same posture. Alongside &lt;code&gt;likely_cause&lt;/code&gt; and &lt;code&gt;recommended_action&lt;/code&gt;, the model must fill in &lt;code&gt;why_relevant&lt;/code&gt;, &lt;code&gt;what_different&lt;/code&gt;, &lt;code&gt;checks&lt;/code&gt;, and &lt;code&gt;warnings&lt;/code&gt;. You can't return a recommendation without also stating how the current incident differs from the historical one. Rule 3 is the one I'd keep if I could only keep one.&lt;/p&gt;

&lt;p&gt;This was the painful part. &lt;code&gt;reflect&lt;/code&gt; returns text, and I'm asking for JSON. Models wrap JSON in markdown fences, add a sentence of preamble, or occasionally trail off. The parser is unglamorous and I'm not proud of it, but it has to exist:&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;parse_json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sub&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;^```(?:json)?\s*&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="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;flags&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;re&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="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sub&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;\s*```$&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="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;try&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;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;JSONDecodeError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;match&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&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="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;S&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;match&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;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;match&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;group&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And when parsing or the call fails entirely, the route doesn't return an error. It falls back to a conservative analysis built from the top recalled memory: the likely cause is lifted from that memory's "Root cause" line, and the recommended action is "Do not make a production change until the immediate checks confirm the historical failure mode." During an incident, a tool that fails closed with useful evidence beats one that fails with a stack trace.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it does in practice
&lt;/h2&gt;

&lt;p&gt;Say the payment gateway starts timing out and an engineer submits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Title:&lt;/strong&gt; Payment Gateway Timeout&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Service:&lt;/strong&gt; Payment Gateway&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Description:&lt;/strong&gt; Requests to the gateway API are timing out and checkout error rates are climbing&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Recall returns up to five prior incidents on that service, and the UI shows them in a console panel next to the analysis so the engineer can see exactly what the model was given. In our seed history, a prior "Payment Gateway Timeout" was resolved by restarting the payment service and verifying gateway connectivity. But the same history also holds an expired TLS certificate, connection pool exhaustion from stuck requests, and an upstream provider degradation. All four present as "the gateway is slow or failing."&lt;/p&gt;

&lt;p&gt;That's the situation the prompt is built for. The response has to say which memory it thinks is closest and why, then say what's different or unverified, then list checks such as service logs, upstream connectivity, and recent deployments before proposing the restart. The previous fix is shown under "Previous Solution", explicitly labeled as a reference, and the warnings block carries the reminder that historical similarity isn't proof of identical root cause.&lt;/p&gt;

&lt;p&gt;When the engineer finishes, they enter what actually fixed it and what they observed, and click the button. That record is what the next person's recall will find. If it turned out the root cause was different from the top match, the new record says so, and the history gets more accurate rather than more self-confident.&lt;/p&gt;

&lt;p&gt;The Incident Memory page lists everything in the bank as plain text. I consider that page a feature, not an admin tool: if you can't read what your agent remembers, you can't trust what it recalls.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Decide what memory is for before deciding what it holds.&lt;/strong&gt; "Everything" is not a policy. For us the answer was verified outcomes, and that one decision shaped the API, the UI, and the prompt.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Never let a model retrieve its own guesses.&lt;/strong&gt; If analysis text can flow back into memory, sooner or later it will be recalled as fact. Gate writes behind a human action and record provenance in &lt;code&gt;metadata&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Make the model state the differences.&lt;/strong&gt; Asking "what's the answer?" gets you confidence. Asking "why is this memory relevant, and what is different?" gets you something an engineer can check. A required &lt;code&gt;what_different&lt;/code&gt; field is cheap and effective.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fail closed and keep the evidence visible.&lt;/strong&gt; Structured-output parsing will fail sometimes. Design the fallback so the engineer still sees the retrieved incidents and a cautious next step, and show the raw recalled memories alongside the analysis so the model's summary is never the only source.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Let the memory layer do memory work.&lt;/strong&gt; Most of the code in this repo is validation, prompt text, and glue. I'm glad the retention and retrieval side is something I use through &lt;code&gt;retain&lt;/code&gt;, &lt;code&gt;recall&lt;/code&gt;, and &lt;code&gt;reflect&lt;/code&gt; rather than something I maintain. If you want to look under the hood, the &lt;a href="https://github.com/vectorize-io/hindsight" rel="noopener noreferrer"&gt;Hindsight repository&lt;/a&gt; is the place to start.&lt;/p&gt;

&lt;p&gt;The next thing I want to do is use the &lt;code&gt;source&lt;/code&gt; metadata to weight or filter recalls, so that records with different levels of verification can coexist in one bank without being treated the same. The write discipline is what makes that possible, and it's the piece I'd copy first into any other agent that has to learn from its own history.&lt;/p&gt;

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