<?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: Yasasasswini Idimukkala</title>
    <description>The latest articles on DEV Community by Yasasasswini Idimukkala (@yasasasswini_idimukkala_0).</description>
    <link>https://dev.to/yasasasswini_idimukkala_0</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3840555%2F62852c37-fa97-4322-a369-07063f35a41c.png</url>
      <title>DEV Community: Yasasasswini Idimukkala</title>
      <link>https://dev.to/yasasasswini_idimukkala_0</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/yasasasswini_idimukkala_0"/>
    <language>en</language>
    <item>
      <title>Building a Memory Layer with Hindsight: From Stateless Feedback to Pattern-Aware Systems</title>
      <dc:creator>Yasasasswini Idimukkala</dc:creator>
      <pubDate>Mon, 23 Mar 2026 17:47:04 +0000</pubDate>
      <link>https://dev.to/yasasasswini_idimukkala_0/building-a-memory-layer-with-hindsight-from-stateless-feedback-to-pattern-aware-systems-54n4</link>
      <guid>https://dev.to/yasasasswini_idimukkala_0/building-a-memory-layer-with-hindsight-from-stateless-feedback-to-pattern-aware-systems-54n4</guid>
      <description>&lt;p&gt;I used to think feedback systems were stateless—until I saw Hindsight pull a previous wrong approach and adjust the next hint without being told.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Actually Built
&lt;/h2&gt;

&lt;p&gt;This project started as a fairly standard coding practice platform. A user writes code, we execute it in a sandbox, compare outputs, and return pass/fail with some explanation. If you’ve built anything like a mini-LeetCode, you already know the shape:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A frontend where users solve problems&lt;/li&gt;
&lt;li&gt;A Python backend handling submissions&lt;/li&gt;
&lt;li&gt;An execution layer (sandboxed, Firecracker-style isolation)&lt;/li&gt;
&lt;li&gt;An “AI layer” that explains failures&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At first, everything was request → response. Clean. Predictable. Also… kind of useless after the third failed attempt.&lt;/p&gt;

&lt;p&gt;The interesting part of this repo is the memory layer—what I called “Hindsight”—that sits between code execution and feedback generation. Instead of treating each submission independently, it stores and reuses past attempts to influence future hints.&lt;/p&gt;

&lt;p&gt;Concretely, the system looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Frontend → API → Execution Engine → Analyzer → Hindsight → Feedback वापस to user
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The only non-trivial piece there is Hindsight. Everything else is plumbing.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem: Stateless Feedback Breaks Down Fast
&lt;/h2&gt;

&lt;p&gt;Initially, my feedback loop looked like this:&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;evaluate_submission&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;test_cases&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="nf"&gt;run_in_sandbox&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;code&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;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;passed&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;status&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;pass&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;explanation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;analyze_failure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&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;status&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;fail&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;hint&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;explanation&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This works fine for a single attempt. But once a user fails multiple times, it becomes obvious how broken this model is.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The system repeats the same hint&lt;/li&gt;
&lt;li&gt;It doesn’t recognize patterns&lt;/li&gt;
&lt;li&gt;It doesn’t escalate guidance&lt;/li&gt;
&lt;li&gt;It doesn’t adapt&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If someone makes the same off-by-one error three times, returning the same generic explanation is borderline insulting.&lt;/p&gt;

&lt;p&gt;I didn’t need a better model. I needed memory.&lt;/p&gt;

&lt;h2&gt;
  
  
  The First Attempt: Just Store Attempts
&lt;/h2&gt;

&lt;p&gt;My first version of Hindsight was embarrassingly simple. I just stored previous submissions and their outcomes.&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;class&lt;/span&gt; &lt;span class="nc"&gt;AttemptStore&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;__init__&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;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;attempts&lt;/span&gt; &lt;span class="o"&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;save&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;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;problem_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;attempt&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="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;problem_id&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;attempts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setdefault&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&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="n"&gt;attempt&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;get_history&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;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;problem_id&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;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;attempts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;problem_id&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;Each &lt;code&gt;attempt&lt;/code&gt; looked roughly like:&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="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;code&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;...&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;error&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;IndexError&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;analysis&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;off-by-one in loop&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;timestamp&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then I wired it into the feedback path:&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;history&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;attempt_store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_history&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;problem_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;hint&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;generate_hint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;current_attempt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;history&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This felt like progress. It wasn’t.&lt;/p&gt;

&lt;p&gt;All I had done was move from stateless → slightly stateful. The system &lt;em&gt;had&lt;/em&gt; history, but it didn’t &lt;em&gt;use&lt;/em&gt; it meaningfully.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Shift: Memory Needs Structure, Not Just Storage
&lt;/h2&gt;

&lt;p&gt;The real change came when I stopped thinking of Hindsight as a log and started treating it as a pattern extractor.&lt;/p&gt;

&lt;p&gt;Instead of passing raw history into the hint generator, I introduced a small layer that compresses attempts into “mistake patterns”.&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;extract_patterns&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;history&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;patterns&lt;/span&gt; &lt;span class="o"&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;attempt&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;history&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="n"&gt;attempt&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;analysis&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;patterns&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;patterns&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&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="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;patterns&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now instead of this:&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="nf"&gt;generate_hint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;current&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;history&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I had this:&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;patterns&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;extract_patterns&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;history&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;generate_hint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;current&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;patterns&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This seems trivial, but it changes the behavior significantly.&lt;/p&gt;

&lt;p&gt;Instead of:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“There might be an off-by-one error”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You get:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“You’ve had off-by-one issues in previous attempts—check your loop bounds again.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That one line made the system feel like it was actually paying attention.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where Hindsight Actually Fits
&lt;/h2&gt;

&lt;p&gt;At a high level, Hindsight sits between analysis and feedback:&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;process_submission&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;problem_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;code&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="nf"&gt;run_in_sandbox&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;analysis&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;analyze_failure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;attempt&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;code&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;analysis&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;analysis&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;passed&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;passed&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;attempt_store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;problem_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;attempt&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="n"&gt;attempt_store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_history&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;problem_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;patterns&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;extract_patterns&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;history&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;hint&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;generate_hint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;analysis&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;patterns&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;passed&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;passed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;hint&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;hint&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It’s deliberately dumb infrastructure:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No embeddings&lt;/li&gt;
&lt;li&gt;No vector search&lt;/li&gt;
&lt;li&gt;No heavy retrieval&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Just structured memory + aggregation.&lt;/p&gt;

&lt;p&gt;That decision was intentional. I didn’t want infra complexity before I had behavior worth scaling.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where Hindsight Surprised Me
&lt;/h2&gt;

&lt;p&gt;The biggest surprise wasn’t that it worked—it’s &lt;em&gt;how quickly it changed user behavior&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Before Hindsight:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Users brute-forced solutions&lt;/li&gt;
&lt;li&gt;Repeated the same mistake&lt;/li&gt;
&lt;li&gt;Ignored hints&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After Hindsight:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Users slowed down&lt;/li&gt;
&lt;li&gt;They started reading hints&lt;/li&gt;
&lt;li&gt;They corrected patterns instead of guessing&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The system didn’t become “smarter”. It became &lt;em&gt;contextual&lt;/em&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  A Concrete Example
&lt;/h3&gt;

&lt;p&gt;Problem: reverse a string&lt;/p&gt;

&lt;p&gt;User attempts:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Uses incorrect indexing → fails&lt;/li&gt;
&lt;li&gt;Fixes something else, same indexing bug → fails&lt;/li&gt;
&lt;li&gt;Tries slicing incorrectly → fails&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Without memory:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Check your indexing logic.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;With Hindsight:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“You’ve repeated indexing issues across attempts. Focus on how you're accessing elements rather than changing approach.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That’s a completely different interaction.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I Didn’t Go Full Vector DB
&lt;/h2&gt;

&lt;p&gt;At some point, I looked into integrating a proper memory system using embeddings and semantic retrieval. That’s where I came across &lt;a href="https://github.com/vectorize-io/hindsight" rel="noopener noreferrer"&gt;Hindsight GitHub repository&lt;/a&gt; and their docs.&lt;/p&gt;

&lt;p&gt;Also worth checking:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://hindsight.vectorize.io/" rel="noopener noreferrer"&gt;Hindsight documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://vectorize.io/features/agent-memory" rel="noopener noreferrer"&gt;agent memory concepts explained here&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;They take a much more sophisticated approach:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Store interactions as embeddings&lt;/li&gt;
&lt;li&gt;Retrieve relevant past context&lt;/li&gt;
&lt;li&gt;Feed it into agent reasoning&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That’s the “correct” long-term direction.&lt;/p&gt;

&lt;p&gt;But for this project, it would have been premature.&lt;/p&gt;

&lt;p&gt;My constraints:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Students using low-resource environments&lt;/li&gt;
&lt;li&gt;Need for predictable behavior&lt;/li&gt;
&lt;li&gt;No tolerance for latency spikes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A simple in-memory + structured pattern system beat a complex retrieval pipeline.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tradeoffs I Accepted (and Felt)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Memory is shallow
&lt;/h3&gt;

&lt;p&gt;I’m not capturing semantic similarity. If two mistakes look different but are conceptually the same, I miss it.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Patterns are naive
&lt;/h3&gt;

&lt;p&gt;Counting occurrences works, but it doesn’t capture &lt;em&gt;progression&lt;/em&gt;. A user improving still looks like “repeating errors”.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. No cross-problem learning
&lt;/h3&gt;

&lt;p&gt;Everything is scoped to &lt;code&gt;(user_id, problem_id)&lt;/code&gt;. If someone struggles with recursion everywhere, the system doesn’t connect that yet.&lt;/p&gt;

&lt;p&gt;That’s the next obvious step.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I’d Do Differently Next Time
&lt;/h2&gt;

&lt;p&gt;If I were to rebuild this with more time:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Introduce embeddings &lt;em&gt;only&lt;/em&gt; for pattern grouping
&lt;/h3&gt;

&lt;p&gt;Not full retrieval—just clustering similar mistakes.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Track transitions, not just counts
&lt;/h3&gt;

&lt;p&gt;Instead of:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;off-by-one: 3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Track:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;off-by-one → fixed → new error
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That’s actual learning progression.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Separate “mistake type” from “surface error”
&lt;/h3&gt;

&lt;p&gt;Right now, I rely too much on analyzer output strings. I’d formalize a schema:&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="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&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;loop_boundary&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;severity&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;basic&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;context&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;array iteration&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;This makes memory reusable and consistent.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lessons Learned
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Memory beats better explanations
&lt;/h3&gt;

&lt;p&gt;You don’t need a smarter model if you remember what already happened.&lt;/p&gt;

&lt;h3&gt;
  
  
  Logs are not memory
&lt;/h3&gt;

&lt;p&gt;Storing data isn’t enough. You need structure that influences decisions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Start dumb, then layer complexity
&lt;/h3&gt;

&lt;p&gt;A dictionary + counting got me surprisingly far.&lt;/p&gt;

&lt;h3&gt;
  
  
  Personalization changes behavior
&lt;/h3&gt;

&lt;p&gt;The moment users feel “seen”, they stop brute forcing.&lt;/p&gt;

&lt;h3&gt;
  
  
  Scope aggressively
&lt;/h3&gt;

&lt;p&gt;Per-user, per-problem memory sounds limited—but it’s what kept the system predictable.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>ai</category>
      <category>programming</category>
      <category>beginners</category>
    </item>
  </channel>
</rss>
