DEV Community

Manyala Vivek Vardhan
Manyala Vivek Vardhan

Posted on

How I Used Hindsight to Catch Sales Objections Buried in CRM Logs

If you've ever worked in B2B sales—or written software for people who do—you know that the biggest threat to closing a deal isn't a competitor. It's forgetting what your prospect told you three months ago.

When I set out to build DealMind AI, the goal was simple: build an agent that analyzes a prospect's history and generates a highly specific preparation brief for the sales representative's next call. I initially thought I could just take the latest notes from the CRM, dump them into a large language model (LLM), and ask it for a strategic recommendation.

It didn't work. By only looking at the latest CRM snapshot, the agent completely missed the chronological narrative. If a prospect suddenly claimed they didn't have budget, the agent would just recommend offering a discount. What the agent didn't know was that three weeks prior, the prospect had explicitly stated budget was not a blocker.

To catch these structural shifts and contradictions, the agent needed long-term memory. Here is the story of how I built an architecture that retrieves relevant historical context at the exact moment it's needed using Hindsight, and the hard lessons I learned about LLM nondeterminism along the way.

The Architecture: Splitting the Timeline

The core problem with standard CRM workflows is that they prioritize the present. To give our agent a true sense of time, I split the context gathering into two distinct flows: the immediate CRM reality, and the deep historical memory.


(Above: Our architecture splits standard CRM retrieval from historical Hindsight retrieval)

Our backend is built on FastAPI. When a request comes in for call preparation, we do not blindly append the entire deal history into the prompt. That approach quickly bloats the context window, increases latency, and degrades the LLM's reasoning capabilities. Instead, we use agent memory to actively recall only the interactions that matter right now.

Here is the high-level flow of how we retrieve that memory:

# Contextualize the user's query with the specific deal ID
scoped_query = f"[Deal: {request.deal_id}] {request.query}"

# Recall relevant historical memories from our Hindsight bank
hindsight_resp = hindsight.recall(
    bank_id=settings.hindsight_bank_id,
    query=scoped_query
)

# Safely extract the historical text
memories = [ans.text for ans in hindsight_resp.answers]
Enter fullscreen mode Exit fullscreen mode

We then take these chronological memories and stitch them together with the latest CRM snapshot. This enriched, multi-turn payload is sent to our Groq-powered analysis service.


(Above: The agent detects the exact contradiction regarding budget by comparing the retrieved timeline)

Because we prompt the LLM to specifically look for changes in position rather than just summarizing the text, the agent immediately flags contradictions. It alerts the representative: "Earlier, they said budget was open. Now, they are asking for a phased rollout due to budget constraints." This transforms raw historical text into an actionable strategic warning.

The Pitfall of LLM Nondeterminism

Getting the agent to recall the active deal's history was only half the battle. I also wanted the agent to draw upon institutional knowledge by looking at past closed deals that faced similar objections.

I implemented a secondary cross-deal retrieval query. I asked Hindsight to look across all our historical data for closed deals involving budget objections and pricing pressure. Hindsight successfully returned the historical context, including the identifiers of past deals (e.g., DEAL-002 which we won, and DEAL-003 which we lost).

Initially, I passed these cross-deal memories directly into the LLM and asked it to format a similar_deals JSON array containing the outcome and tactics used.

This was a massive mistake.

LLMs are inherently nondeterministic. Because DEAL-003 was a lost deal, the LLM frequently decided it was "irrelevant" to successful call preparation and silently dropped it from the output array. Sometimes the UI would show both deals; sometimes it would only show the win.

I was relying on the LLM to map retrieved data into a strict data structure, and it was silently filtering my evidence.

The Fix: Deterministic Grounding

I realized I needed to stop trusting the LLM to handle the existence of seeded historical records. The LLM is great at generating natural-language evidence summaries, but it should not be in charge of data integrity.

I completely refactored the backend route. Instead of asking the LLM to format the deals, I used Python to intercept the raw text returned by Hindsight, regex-extract the deal IDs, and map them deterministically against our actual database (in this case, a seeded closed_deals.json file).


(Above: Decoupling the ID extraction from the LLM ensures deterministic grounding)

By decoupling the retrieval mapping from the LLM generation, the system became 100% deterministic. The LLM still writes the strategic analysis, but the historical evidence—the actual WON and LOST deals rendered in the UI—is explicitly grounded in verified data.

Lessons Learned

Building DealMind AI taught me several harsh but valuable lessons about designing memory-augmented agents:

1. Separate your context layers

Do not blindly merge your agent's immediate reality (the latest CRM note) with its long-term memory. By keeping them conceptually separate, you can run A/B baseline comparisons to prove that your retrieval pipeline is actually adding value, rather than just adding noise.

2. Guard against current-deal contamination

When retrieving "similar past examples," your agent will almost always retrieve the active deal's own history because it is an exact semantic match. You must actively filter out the active context ID (if match != request.deal_id) before presenting the results to the user.

3. Let the LLM reason, let the code map

The LLM should be used to detect contradictions and generate strategy. It should not be used to decide whether a historical record exists. Use tools like Hindsight to retrieve the raw unstructured memory, but use strict deterministic code to map those retrieved identifiers back to your source of truth.

4. Visibility builds trust


(Above: Exposing the WON/LOST past deal evidence directly in the UI builds immediate trust)

If an agent gives a sales rep a contrarian piece of advice based on a three-month-old memory, the rep will ignore it unless the provenance is obvious. We exposed the exact number of memories used and rendered the WON/LOST past deal evidence directly in the UI. If the user can't see the memory, they won't trust the insight.

If you are building agents that need to remember complex, multi-turn interactions without bloating context windows, you should check out the Hindsight docs. It completely changed how I think about providing chronological context to language models.

Shout-out to: Code.in

Top comments (0)