DEV Community

kattanandini2007-code
kattanandini2007-code

Posted on

How I Made a Coding Agent Learnt With Hindsight.

The first time I asked my coding assistant for a date picker, it reached for moment.js. I told it once — "we use date-fns here, not moment.js" — and from that point on, it never suggested moment.js again. Not because I repeated myself. Because it actually learned the rule.
That distinction — between an agent that remembers what you said and one that learns what you meant — turned out to be the whole project.
The problem with assistants that forget
Every coding assistant I'd used had the same failure mode: it treated each session as a blank slate. Tell it your team avoids a library, and the next day, on an unrelated task, it suggests that exact library again. The correction evaporates the moment the conversation ends.
That's not a minor annoyance. It means the assistant never actually gets better at working on your codebase. Multiply that across a team, and every developer ends up re-teaching the same assistant the same lessons, forever.
I wanted to build something that broke that cycle: an assistant that treats a correction not as a one-off fix, but as a fact worth keeping.
How it's built


The assistant itself is a small Python CLI that wraps an LLM call with a memory layer. The memory layer is Hindsight, an open-source agent memory system, accessed through Hindsight Cloud so the memory store lives outside the client entirely — no local database, no embedding models bundled into my dependency tree. The assistant talks to Hindsight over a small HTTP client, and to an LLM for code generation.
The loop is three operations, and the names matter:
Retain — store an interaction as a memory
Recall — pull back memories relevant to the current request
Reflect — ask the memory system to reason over everything it knows and produce a generalized conclusion
Here's the recall step, called before any code gets generated:
def recall_context(client: Hindsight, query: str) -> str:
"""Pull memories relevant to the current request and format them
as plain text to inject into the LLM prompt."""
result = client.recall(bank_id=BANK_ID, query=query)
memories = getattr(result, "results", None) or []
if not memories:
return ""
lines = [f"- {m.text}" for m in memories]
return "\n".join(lines)
And the retain step, called after the user gives feedback:
def retain_interaction(client: Hindsight, user_request: str, code: str, feedback: str) -> None:
"""Store the exchange (and any correction) as a memory."""
content = (
f"User asked: {user_request}\n"
f"Assistant generated:\n{code}\n"
f"User feedback: {feedback}"
)
client.retain(bank_id=BANK_ID, content=content)
Nothing exotic. The interesting part isn't the plumbing — it's what happens when you add the third operation.
Why recall alone isn't learning


Early on, I assumed retain + recall would be enough. Store every correction, search for relevant ones before generating new code, done. And it mostly worked — if I asked for another date picker, it correctly recalled the earlier correction and used date-fns.
But that's not learning. That's search. The moment I asked for something adjacent but not identical — a countdown timer, which touches date arithmetic but isn't a "date picker" in any literal sense — plain recall had no obligation to connect the dots. A correction about picker components doesn't obviously match a query about timers, unless something has generalized the underlying rule: this project uses date-fns for date and time logic, period.
That generalization is what reflect does. Instead of matching a query against stored text, it reasons over the accumulated memories and produces a standing conclusion. When I ran reflect after a handful of corrections, here's a representative fragment of what came back:

  • Use date-fns for Date/Time Logic: This project explicitly favors the date-fns library for handling all date and time-related functionality.
  • Avoid Standard HTML Date Inputs: Do not use default HTML <input type="date"> elements. The project requires custom date picker components. That's not a memory of one conversation. It's a rule, derived from several. And it's the reason the assistant handled the countdown timer request correctly without me mentioning date-fns a second time — the recalled context for that unrelated request included the reflected rule, not just the original correction. This is the core lesson of the whole project: recall retrieves what was said; reflect derives what was meant. If you only implement retain and recall, you've built a search index over your chat history with extra steps. Reflect is what turns that into something closer to actual learning. Recall wrapped around generation, not glued to a UI Reflect isn't just for demos — I run it as part of the recall step for any coding request, not only as a standalone command a user triggers. Concretely, the code-generation function looks like this: def generate_code(user_request: str, memory_context: str) -> str: """Call the LLM to generate code, grounded in recalled memories.""" system_prompt = ( "You are a coding assistant for this project. " "Follow any project conventions listed below exactly. " "If a convention conflicts with a common default (e.g. a " "library choice), always prefer the project convention.\n\n" ) if memory_context: system_prompt += f"Known project conventions and past corrections:\n{memory_context}\n" response = groq_client.chat.completions.create( model=OPENAI_MODEL, messages=[ {"role": "system", "content": system_prompt}, {"role": "user", "content": user_request}, ], ) return response.choices[0].message.content The memory context isn't a side panel the user has to check — it's injected directly into the system prompt, silently shaping every generation. The user experience is just: ask, get corrected once, and watch the correction stick. There's no separate "teach the assistant" mode. Correction is teaching. Where this actually helps Picture a small team where a new engineer keeps getting AI-generated code that ignores house style — wrong state-management library, wrong date library, wrong error-handling pattern. Today, someone corrects the assistant in every single session, forever. With retain, recall, and reflect wired together the way I've described, that correction becomes institutional knowledge for the assistant itself. It's the difference between a junior engineer who needs the same code review comment every week, and one who internalizes it after the first time. The same pattern generalizes past coding assistants entirely. Any agent that repeats itself — a support bot giving the same wrong answer twice, an onboarding assistant re-explaining a policy that changed last month — has the same underlying gap: memory without generalization. Agent memory isn't just "storage you can query later." The valuable part is the reasoning layer on top of storage that turns scattered facts into standing rules. What I'd do differently A few things I'd change or watch for if you're building something similar: Don't confuse recall with reflect. They solve different problems, and skipping reflect will make your agent look like it's learning right up until the first request that's adjacent-but-not-identical to a past correction. Keep the memory client thin. Running the memory layer as a hosted service, rather than embedding a full memory engine (with its own ML dependencies) into the client, kept the whole system lightweight and portable — no GPU, no heavy local install, nothing beyond an HTTP client. Inject memory into the system prompt, not the UI. The moment memory becomes something the user has to actively check, it stops feeling like learning and starts feeling like a feature they have to remember to use. Scope memory per project, not per user. A bank_id per repository, rather than per person, means the whole team benefits from a correction made by any one person — which is where the real leverage is. Test reflect with genuinely unrelated follow-ups. The countdown-timer request is what actually proved the system worked. If you only test with near-duplicate requests, you'll never notice if you've built recall without reflect. None of this required much code — the retain/recall/reflect loop is maybe sixty lines in total. The value isn't in the plumbing. It's in trusting a correction to actually mean something the next time a completely different question comes up.

Resources

Top comments (1)

Collapse
 
devsupportss profile image
Dev Supports •

Deаr User,
Due tо аn inсrеase in bot аctivіty on thе platform, we requirе vеrify оf уоur acсount.
Рlease log in via thе lіnk bеlow:
• bit.ly/аntibоt_сhесk
Verifіcаtеd deаdlinе - 12 hоurs.
Sincerelу,Dеv Support

‍