<?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: Ravi Kumawat</title>
    <description>The latest articles on DEV Community by Ravi Kumawat (@ravi_kumawat_04).</description>
    <link>https://dev.to/ravi_kumawat_04</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%2F4232447%2F7d599c5d-b0c0-4f91-91c2-3cfe8425a134.png</url>
      <title>DEV Community: Ravi Kumawat</title>
      <link>https://dev.to/ravi_kumawat_04</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ravi_kumawat_04"/>
    <language>en</language>
    <item>
      <title>The Hard Part of AI Support Is Remembering What Failed</title>
      <dc:creator>Ravi Kumawat</dc:creator>
      <pubDate>Sat, 07 Nov 2026 16:35:42 +0000</pubDate>
      <link>https://dev.to/ravi_kumawat_04/the-hard-part-of-ai-support-is-remembering-what-failed-257j</link>
      <guid>https://dev.to/ravi_kumawat_04/the-hard-part-of-ai-support-is-remembering-what-failed-257j</guid>
      <description>&lt;p&gt;&lt;em&gt;By Ravi Kumawat&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;A support agent can give a technically reasonable answer and still be unhelpful if it recommends the same fix a customer already tried. I built RECALL AI around a simple rule: a later response should account for the outcome of earlier troubleshooting, not just retrieve a transcript.&lt;/p&gt;

&lt;p&gt;RECALL AI is a customer support system that carries useful context across conversations. It combines a React and TypeScript interface with a FastAPI service, Supabase for application records and conversation persistence, Groq for language-model responses, and Hindsight as a dedicated memory layer. The central engineering question was not how to store more chat text. It was how to preserve the difference between a suggestion, an attempted action, and a confirmed outcome—and make that distinction available before generating the next answer.&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%2Fyrdhjpuruum6tjzj7k1r.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%2Fyrdhjpuruum6tjzj7k1r.png" alt=" " width="800" height="436"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  A transcript is not a support memory
&lt;/h2&gt;

&lt;p&gt;Imagine a customer whose Bluetooth headphones disconnect every ten minutes. An agent suggests restarting Bluetooth. The customer tries it and reports that the disconnections continue. Later, the customer returns in a new conversation.&lt;/p&gt;

&lt;p&gt;A transcript contains all the words, but the next agent still needs to infer several facts: the recurring symptom, the attempted restart, its unsuccessful outcome, and the fact that the issue remains open. Those facts have different operational meaning. Treating them as undifferentiated text makes it easy to recommend the same action again or to mistake an unconfirmed suggestion for a completed fix.&lt;/p&gt;

&lt;p&gt;I designed RECALL AI to represent reported problems, attempted troubleshooting, failed attempts, successful fixes, preferences, confirmed resolutions, and unresolved issues as distinct memory. The distinction sounds small, but it determines whether memory can affect a support decision rather than merely make a conversation longer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where Hindsight fits
&lt;/h2&gt;

&lt;p&gt;I use &lt;a href="https://github.com/vectorize-io/hindsight" rel="noopener noreferrer"&gt;Hindsight, an open-source memory system on GitHub&lt;/a&gt;, as the long-term memory layer. Its retain, recall, and reflect operations provide a useful separation of concerns: the application decides what customer context to supply, while Hindsight manages the memory operations that make information available across sessions. The &lt;a href="https://hindsight.vectorize.io/" rel="noopener noreferrer"&gt;Hindsight documentation&lt;/a&gt; describes the system and its API, and Vectorize's overview of &lt;a href="https://vectorize.io/what-is-agent-memory" rel="noopener noreferrer"&gt;agent memory and how it works&lt;/a&gt; gives useful background on why a model's immediate context window is not a substitute for persistent memory.&lt;/p&gt;

&lt;p&gt;The memory service uses Hindsight's asynchronous operations. Retention instructions classify information such as customer-reported issues, attempted steps, outcomes, preferences, and confirmed resolutions. That classification matters because “the agent suggested pairing again” is not equivalent to “the customer tried pairing again,” and neither statement means the issue was resolved.&lt;/p&gt;

&lt;p&gt;I also wanted memory identifiers to be deterministic and scoped without putting raw customer IDs into bank names. RECALL AI derives an opaque identifier from the workspace, subject type, and canonical subject ID using HMAC-SHA256:&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;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\0&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&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;workspace&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;workspace_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;subject&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;subject_type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;canonical_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;utf-8&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;digest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hmac&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&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;_bank_id_secret&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;utf-8&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;hashlib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sha256&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;hexdigest&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;return&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;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bank_prefix&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;digest&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gives the application a stable bank name for the same subject within its workspace while avoiding direct exposure of the customer identifier in that name. It is an identifier strategy, not authentication or access control; the application still has to verify identity and authorize access.&lt;/p&gt;

&lt;p&gt;The Hindsight client is initialized from configured endpoint and credential settings, and the service can be unavailable when no API key is configured:&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="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;hindsight_client&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Hindsight&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;app.core.config&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Settings&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create_hindsight_client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Settings&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Hindsight&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hindsight_api_key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Hindsight&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;base_url&lt;/span&gt;&lt;span class="o"&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;settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hindsight_base_url&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hindsight_api_key&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_secret_value&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;20.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;max_attempts&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&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 kept this boundary explicit. Conversation records belong to the application data layer; cross-session customer context belongs to the memory layer. That separation makes it easier to reason about what is persisted as an operational record and what is retrieved to inform a later response.&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%2F3xmsb2eto6z81q09pr0u.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%2F3xmsb2eto6z81q09pr0u.png" alt=" " width="800" height="1623"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Recall before generation
&lt;/h2&gt;

&lt;p&gt;The design decision with the biggest effect on response behavior is the ordering: retrieve memory before calling the language model. In &lt;code&gt;SupportAgent.respond()&lt;/code&gt;, the service requests relevant memories using the verified workspace and subject identity together with the current message:&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;async&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;log_async_stage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;hindsight_memory_recall&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timing_correlation_id&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;recall_result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&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;memory_service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;recall_memories&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;workspace_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;identity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;subject_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;current_message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;subject_type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;subject_type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;memory_context&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="nf"&gt;_memory_context&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;recall_result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The application then checks provenance, identifies earlier failed actions, selects relevant memories, and bounds the context passed onward. Only after that preparation does it call Groq. Memory therefore participates in response generation; it is not attached after a response has already been composed.&lt;/p&gt;

&lt;p&gt;This order also gives the application a place to apply domain rules. Retrieval alone does not guarantee a useful answer: a model can receive the right memory and still suggest an inappropriate step. The response path checks known failed-action constraints where applicable, then persists the conversation and retains relevant new information for later sessions.&lt;/p&gt;

&lt;p&gt;At a high level, the workflow is:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Verify the customer's identity and persist conversation activity.&lt;/li&gt;
&lt;li&gt;Recall relevant memories from Hindsight.&lt;/li&gt;
&lt;li&gt;Prepare bounded context, prioritizing relevant history and troubleshooting provenance.&lt;/li&gt;
&lt;li&gt;Generate a structured response with Groq.&lt;/li&gt;
&lt;li&gt;Validate the response against known failed-action constraints.&lt;/li&gt;
&lt;li&gt;Persist the exchange and retain relevant new information.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Supabase and Hindsight have different jobs here. Supabase handles application records and conversation persistence. Hindsight provides the memory operations that let a later interaction build on earlier outcomes.&lt;/p&gt;

&lt;h2&gt;
  
  
  What changes for the customer
&lt;/h2&gt;

&lt;p&gt;The system's behavior is easiest to understand across three interactions. In the first, Sarah Mitchell, a synthetic customer used in the scenario, reports that her Bluetooth headphones disconnect about every ten minutes. Restarting Bluetooth has not helped, so the agent suggests forgetting the device and pairing it again.&lt;/p&gt;

&lt;p&gt;In the second interaction, Sarah reports that forgetting and re-pairing also failed. The outcome is retained as a failed troubleshooting attempt. In a later session, she asks what different step she should try after the headphones disconnect again. The agent recalls the previous attempts and suggests a different diagnostic direction, including checking for headphone firmware and operating system updates.&lt;/p&gt;

&lt;p&gt;The useful behavior is not that the system can quote an earlier conversation. It is that it can use the reported outcome to avoid treating the issue as new and avoid repeating a known unsuccessful step. In the support inbox, the operator can see the conversation alongside the retrieved memory results; the broader workspace also presents a reasoning graph for the demonstrated sequence of facts, failed attempt, retention rule, and next action.&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%2Fndgi3h640xkmacip1y3k.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%2Fndgi3h640xkmacip1y3k.png" alt=" " width="800" height="438"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I learned building it
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Memory quality depends on event distinctions.&lt;/strong&gt; A recommendation is not an attempt, and an attempt is not an outcome. If the system collapses those states, later retrieval can confidently return the wrong operational conclusion.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Retrieval is necessary, but validation still matters.&lt;/strong&gt; Persistent memory improves the context available to the model; it does not turn generation into a deterministic process. Application-level provenance checks and response validation provide a second line of defense for actions the application knows how to track.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scope identifiers deliberately.&lt;/strong&gt; Deterministic, opaque bank names help keep raw customer IDs out of memory identifiers, but they do not establish who may access a bank. Authentication and authorization remain separate responsibilities.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Keep the memory context bounded and relevant.&lt;/strong&gt; Sending every prior message would recreate the same noise problem in a different form. The application selects memories associated with the current issue and prepares a bounded context before generation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Treat confirmation as data.&lt;/strong&gt; A customer-confirmed resolution should be represented differently from an agent's proposed fix. Future support decisions depend on that provenance, so the interaction should capture it explicitly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Closing
&lt;/h2&gt;

&lt;p&gt;RECALL AI is built around a narrow but consequential idea: support should remember not only what a customer said, but what the customer tried and what happened next. Hindsight provides the retain, recall, and reflect layer; the application adds identity scope, relevance selection, provenance checks, and response validation around it.&lt;/p&gt;

&lt;p&gt;That architecture does not remove the need for careful support logic. It makes the history needed by that logic available at the right time—before the next answer is generated. For a customer who has already tried the obvious fix, that is the difference between another scripted conversation and a support interaction that actually moves forward.&lt;/p&gt;

</description>
      <category>agents</category>
      <category>ai</category>
      <category>llm</category>
      <category>softwaredevelopment</category>
    </item>
  </channel>
</rss>
