DEV Community

Kathi Harshith Reddy
Kathi Harshith Reddy

Posted on Edited on

My Agent Saw Acme Repeating Nova's Mistakes Using Hindsight








A rep promises a SOC 2 report by Friday and never sends it. Two weeks later, the customer's security lead asks about it in front of six stakeholders, and the rep's champion is visibly embarrassed. Nothing about that deal was hard. The rep just forgot.
That failure is what I built DealMemory to prevent. It's a sales copilot that remembers every call, objection, stakeholder and promise, then briefs you before the next conversation. This post is about the design decision that made it useful: letting Hindsight reason across deals instead of just searching within one.
What the system does
DealMemory is a small Streamlit app with three moving parts:
• Streamlit UI (app.py): pick an account, log interactions, ask questions.
• Groq (llm.py): the LLM, called through its OpenAI-compatible endpoint with a simple retry loop.
• Hindsight (memory.py): the memory layer, wrapped in three functions: retain, recall and reflect.
A request follows one of two paths. The sidebar toggle decides whether a question touches Hindsight at all. With memory on, a briefing or playbook question goes to reflect, and a chat question first calls recall and hands the results to the model as context. With memory off, the same text goes straight to Groq with no context. Logging a new interaction calls retain with today's date, so the very next question can already see it.
I seeded it with three sample accounts so I could test behavior I could verify: Acme Logistics (open), Brightpath Health (closed won) and Nova Retail (closed lost). Each has five interactions: a discovery call, a pricing call, a security email, a demo and, for the closed deals, an outcome note.
The interesting part is what those three stories have in common. In every deal, the buyer asked for the SOC 2 report, the CFO pushed back on annual upfront pricing, and Zoho came up as the cheaper option. What differed was how each was handled:

Brightpath (won)
Nova (lost)
SOC 2 report
Sent within 24 hours
Sent 13 days late
CFO
In the demo
Never engaged
Pricing
Quarterly billing plus a pilot, offered early
Annual only
Acme looks like Nova in the making. The report is overdue and the CFO skipped the demo. A system that can see that pattern is worth more than a CRM full of notes.
The notes are written the way a rep would write them: plain sentences about who was on the call, what they objected to, and what I promised by when. There is no schema and no fields to fill in. Hindsight takes that text as it comes, and I never wrote a parser for "I promised both by Friday."
One memory bank, not one per account
The core decision was to put every account into a single Hindsight bank. The obvious design is one bank per deal, which keeps things tidy and makes cross-deal learning impossible. I wanted the agent to notice that a current deal resembles a lost one.
To keep accounts distinguishable inside a shared bank, I put the account name in the text itself:
def retain_interaction(account: str, kind: str, note: str, date: str) -> None:
_client().retain(
bank_id=BANK_ID,
content=f"[{account}] {kind} on {date}: {note}",
context=f"sales interaction with {account}",
timestamp=f"{date}T10:00:00Z",
)
Every memory carries its account, its type of interaction and a real timestamp. That timestamp matters. When a briefing says "promised on October 21, still not sent on October 28," it's working from dates I supplied, not dates the model guessed.
Three small choices here do real work. The account name sits inside the content, so a question about Acme mostly surfaces Acme's memories even though every account shares one bank. It is a soft filter, not a guarantee: recall can still pull in another account's memories, as the limitations below explain. The context string tells Hindsight what kind of memory this is. And the timestamp is the date of the interaction, not the moment I clicked load, so each memory is dated when the call happened.
Recall for lookups, reflect for judgment
Hindsight gives you two ways to read memory, and choosing between them was the most useful thing I learned.
recall returns the memories most relevant to a query. I use it in the chat: before the model answers, I fetch relevant memories and put them in the prompt. It's good at "what did I promise Acme?"
reflect asks Hindsight to reason over the bank and produce an answer. I use it for the two questions that need synthesis:
def reflect(query: str) -> str:
"""Ask Hindsight to reason over the bank and return an answer."""
answer = _client().reflect(bank_id=BANK_ID, query=query)
return answer.text
The "What has worked on past deals?" button asks across all closed deals, won and lost, what made the difference, then which lessons apply to the account I've selected. That's not a lookup. It's comparing Nova's failures with Brightpath's wins and applying the result to Acme. Doing that with recall plus a prompt would mean me writing the comparison logic. With reflect, the comparison happens inside the memory layer.
The chat takes the other path. It calls recall with the account and the question, formats the returned memories as a bulleted block labeled "Relevant memory," and sends that plus the question to the model. The system prompt tells the model to ground every claim in that context and cite dates, and, when it has none, to say what it would need to know. That is why the memory-off answers lean toward asking for details.
The off switch
The most valuable feature for testing was a sidebar toggle that turns memory off. With it on, the question goes to reflect. With it off, the same question goes straight to the LLM:
if use_memory:
st.session_state["playbook"] = memory.reflect(q)
else:
st.session_state["playbook"] = llm.ask(SYSTEM, q)
Same prompt, same model, same screen. The only variable is whether Hindsight is involved. That gave me an honest before/after instead of a vibe.
What actually changes
With memory off, "Brief me for my next call" on Acme gets a polite request for context. It lays out a table of what it wants, from stakeholders to open promises, and asks what I'd need to provide. It can't know anything about Acme, so it asks.
[Screenshot: briefing with memory OFF]
With memory on, the briefing is built from the retained notes. It names the stakeholders (Rahul Mehta as the champion, CFO Priya Nair as budget owner, Anil Verma in security), the Rs 38 lakh annual price Priya hesitated over, Zoho coming up repeatedly, the SOC 2 report and data-residency details that are still outstanding, and the November 12 decision meeting. It then recommends a six-month pilot with quarterly billing, as at Brightpath, and getting the CFO involved early so Acme does not end up like Nova.
[Screenshot: briefing with memory ON]
The playbook button is where it gets more interesting. With memory on, it names both deals and dates the lessons: the CFO in the demo (Brightpath, 2026-09-05), quarterly billing and a pilot (Brightpath, 2026-09-30), and security documents within 24 hours versus the 13-day delay at Nova (2026-08-27). The briefing then turns those into actions for Acme.
[Screenshot: playbook with memory ON, then OFF]
In the chat, I can also open an expander showing which memories were recalled for an answer. It makes the memory visible, which is what separates "the agent remembers" from "the agent sounds confident."
Lessons

  1. Put the entity in the text. Putting the account name in every retained memory helps one bank serve many accounts, though it is a hint, not a filter.
  2. Match the operation to the question. Lookups want recall. Questions that compare and conclude want reflect.
  3. A shared bank is what makes cross-deal learning possible. Per-account banks would have been cleaner and far less useful.
  4. Outcome notes are the highest-value memories. The "closed lost, here's what went wrong" entries do the heavy lifting in the playbook. If you only log activity, you get recall. If you log outcomes, you get lessons.
  5. Build the off switch first. Without a memory-off baseline, I'd have had no way to tell whether Hindsight was adding anything. What doesn't work yet • Duplicates. Loading an account's history twice stores it twice, and I haven't added dedupe on the write side. • The chat has no conversation history. Each question is answered independently, so follow-ups like "what about the CFO?" lose context. • Small data. Three accounts and fifteen interactions are enough to show the idea, not to prove it at scale. I haven't measured accuracy. • Reflect is slower than a plain LLM call, because it reasons over the bank. For a pre-call briefing that's fine; for something interactive it would matter. • Recall isn't scoped by account. Because everything lives in one bank, a question about one account can return memories from another. In my tests, a Brightpath question pulled in an Acme note about the security lead. • The model can over-read a note. When I asked what I had promised Nova, it listed things the notes say were never offered, such as a pilot, as if they were promises. Grounding the answer in memory does not stop the model from misreading it. If you want to try the memory layer yourself, the Hindsight documentation is the place to start, and Vectorize's overview of agent memory explains the ideas behind it.

Top comments (0)