{"slug": "how-i-made-a-coding-agent-learnt-with-hindsight", "title": "How I Made a Coding Agent Learnt With Hindsight.", "summary": "A developer built a Python CLI coding assistant that pairs an LLM with Hindsight, an open-source agent memory system accessed via Hindsight Cloud, to retain corrections, recall relevant memories, and reflect over accumulated memories to generalize rules. The three-operation loop (retain, recall, reflect) lets the assistant apply a single correction — such as using date-fns instead of moment.js — to adjacent tasks like countdown timers, which plain recall alone could not do.", "body_md": "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.\n\nThat distinction — between an agent that remembers what you said and one that learns what you meant — turned out to be the whole project.\n\nThe problem with assistants that forget\n\nEvery 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.\n\nThat'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.\n\nI 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.\n\nHow it's built\n\nThe 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.\n\nThe loop is three operations, and the names matter:\n\nRetain — store an interaction as a memory\n\nRecall — pull back memories relevant to the current request\n\nReflect — ask the memory system to reason over everything it knows and produce a generalized conclusion\n\nHere's the recall step, called before any code gets generated:\n\ndef recall_context(client: Hindsight, query: str) -> str:\n\n    \"\"\"Pull memories relevant to the current request and format them\n\n    as plain text to inject into the LLM prompt.\"\"\"\n\n    result = client.recall(bank_id=BANK_ID, query=query)\n\n    memories = getattr(result, \"results\", None) or []\n\n    if not memories:\n\n        return \"\"\n\n    lines = [f\"- {m.text}\" for m in memories]\n\n    return \"\\n\".join(lines)\n\nAnd the retain step, called after the user gives feedback:\n\ndef retain_interaction(client: Hindsight, user_request: str, code: str, feedback: str) -> None:\n\n    \"\"\"Store the exchange (and any correction) as a memory.\"\"\"\n\n    content = (\n\n        f\"User asked: {user_request}\\n\"\n\n        f\"Assistant generated:\\n{code}\\n\"\n\n        f\"User feedback: {feedback}\"\n\n    )\n\n    client.retain(bank_id=BANK_ID, content=content)\n\nNothing exotic. The interesting part isn't the plumbing — it's what happens when you add the third operation.\n\nWhy recall alone isn't learning\n\nEarly 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.\n\nBut 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.\n\nThat 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:\n\n`date-fns` for Date/Time Logic: This project explicitly\nfavors the `<input type=\"date\">` elements. The project requires custom\ndate picker components.\nThat'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.\nThis 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.\nRecall wrapped around generation, not glued to a UI\nReflect 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:\ndef generate_code(user_request: str, memory_context: str) -> str:\n\"\"\"Call the LLM to generate code, grounded in recalled memories.\"\"\"\nsystem_prompt = (\n    \"You are a coding assistant for this project. \"\n    \"Follow any project conventions listed below exactly. \"\n    \"If a convention conflicts with a common default (e.g. a \"\n    \"library choice), always prefer the project convention.\\n\\n\"\n)\nif memory_context:\n    system_prompt += f\"Known project conventions and past corrections:\\n{memory_context}\\n\"\nresponse = groq_client.chat.completions.create(\n    model=OPENAI_MODEL,\n    messages=[\n        {\"role\": \"system\", \"content\": system_prompt},\n        {\"role\": \"user\", \"content\": user_request},\n    ],\n)\nreturn response.choices[0].message.content\nThe 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.\nWhere this actually helps\nPicture 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.\nThe 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.\nWhat I'd do differently\nA few things I'd change or watch for if you're building something similar:\nDon'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.\nKeep 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.\nInject 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.\nScope 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.\nTest 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.\nNone 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.\nResources", "url": "https://wpnews.pro/news/how-i-made-a-coding-agent-learnt-with-hindsight", "canonical_source": "https://dev.to/kattanandini2007code/how-i-proved-my-agent-learned-not-just-remembered-50lc", "published_at": "2026-09-27 19:00:24+00:00", "updated_at": "2026-09-27 19:31:18.972046+00:00", "lang": "en", "topics": ["ai-agents", "ai-tools", "developer-tools", "large-language-models", "ai-infrastructure"], "entities": ["Hindsight", "Hindsight Cloud", "moment.js", "date-fns", "Python"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/how-i-made-a-coding-agent-learnt-with-hindsight", "markdown": "https://wpnews.pro/news/how-i-made-a-coding-agent-learnt-with-hindsight.md", "text": "https://wpnews.pro/news/how-i-made-a-coding-agent-learnt-with-hindsight.txt", "jsonld": "https://wpnews.pro/news/how-i-made-a-coding-agent-learnt-with-hindsight.jsonld"}}