Hindsight Digital Intelligence
DEV Community

Hindsight Digital Intelligence

A sales briefing can be wrong while every sentence in it is individually true: the CFO's objection from one account gets attached to another account, and a rep walks into the call with someone else's pricing history. In this project, one field-deal_id-is the difference between useful recall and a confident account mix-up.

What it does

I built Deal Intelligence Agent as a FastAPI service with three useful API paths:

  • POST /deals/{deal_id}/log retains a call, email, or meeting note in Hindsight Cloud and appends a local copy.
  • GET /deals/{deal_id}/brief recalls one deal's history and asks Groq's openai/gpt-oss-120b to produce a structured briefing.
  • GET /patterns performs a global recall and asks Groq to find one recurring objection-resolution pattern across deals.

The web UI is vanilla HTML, CSS, and JavaScript; deals.json supplies deal names and metadata for local inspection.

The division of labor matters. Hindsight Cloud is the persistent memory engine, using retain, recall, and its TEMPR retrieval strategy. It is not the final answer generator. The application decides what scope to search, passes the retrieved text to Groq, and returns a response. That makes the application code-not an invisible prompt convention-the place where the most important boundary is expressed.

Design decision: learn only from the right outcome

A single shared memory bank is convenient for cross-deal analysis, but dangerous for a deal briefing. The same bank can contain every account's notes, so every stored item is tagged with its deal identifier. On retain, I put the identifier in three places: in the human-readable content prefix, in the Hindsight tags, and in metadata. Only the tags are used by this implementation to filter recall; the other two carry useful context and traceability.

Architecture in one pass

The request path is intentionally boring: the browser calls FastAPI, FastAPI chooses the memory scope, Hindsight Cloud retains or recalls, and Groq turns recalled text into a briefing or playbook rule. The local JSON store only supplies deal metadata and an inspection-friendly copy of logs.

flowchart TD
 UI["Web UI<br/>(dark mode, vanilla HTML/CSS/JS)"]
 API["FastAPI backend<br/>(app/main.py)"]
 H["Hindsight Cloud<br/>(retain / recall / TEMPR)"]
 B["deal-intel bank"]
 G["Groq<br/>(openai/gpt-oss-120b)"]
 J["deals.json<br/>(metadata and local log copy)"]
 UI -->|log, brief, patterns| API
 API -->|tagged retain / scoped recall| H
 H --> B
 API -->|recalled context| G
 API --> J
 G -->|briefing or playbook rule| API
 API --> UI

This separation also explains why the project can support both precision recall and cross-deal analysis. /deals/{deal_id}/brief follows the tagged path. /patterns intentionally opens the recall scope across the bank, then asks for one evidence-backed pattern instead of a general summary.

Local run

For a local run, I create a Python 3.10+ environment, install requirements.txt, set GROQ_API_KEY, HINDSIGHT_API_KEY, HINDSIGHT_BASE_URL, HINDSIGHT_BANK_ID=deal-intel, and GROQ_MODEL, then seed the synthetic records before starting Uvicorn. The seed step matters: a clean Hindsight bank should produce the cold-start response, while the seeded bank makes the objection-resolution arc inspectable.

python scripts/generate_synthetic_deals.py
uvicorn app.main:app --reload --port 8000

Code details

The retain payload puts the identifier in three places:

payload = {
 "items": [
 {
 "content": f"[Deal {deal_id}] {text.strip()}",
 "tags": [deal_id],
 "metadata": {
 "deal_id": deal_id,
 "source": "deal-intel-agent"
 }
 }
 ]
}

The recall helper has two modes. For an individual briefing, it requires a deal ID and sends it as the tag filter. For cross-deal pattern detection, it deliberately leaves the filter out. The explicit branch is a useful little piece of policy: a normal query is scoped, and global recall requires the caller to ask for it by name.

# app/hindsight.py
if scope == "deal":
 if not deal_id:
 raise ValueError("deal_id must be provided when scope is 'deal'")
 payload["tags"] = [deal_id]
 payload["tags_match"] = "any"

That code is simple, but the simplicity should not be mistaken for a security boundary. It is an application-level retrieval filter over a shared bank. A production deployment with multiple customers would also need authorization around deal IDs, controlled bank access, and tests that prove one customer cannot

Read on DEV Community ↗ ← Back to News

Comments

No comments yet. Start the discussion.