I originally thought customer memory was a storage problem. It turned out to be a retrieval and trust problem.
When I built FUEGO, a customer relationship memory agent, I wanted one simple behaviour: before a customer meeting, the system should remember what happened before without forcing someone to reconstruct the relationship from scattered meetings, tickets, and notes.
The key design decision was separating structured customer records from long-term semantic memory. SQLite remains the source of truth for customers, meetings, and support tickets. Hindsight handles the historical context that needs to be retrieved when a question is asked. Groq then turns that context into a useful response.
That separation made the rest of the system much easier to reason about.
The architecture:
FUEGO uses a Next.js frontend and a Python/FastAPI backend. The frontend lets a user select a customer, add meetings and support tickets, prepare for a meeting, inspect commitments and solutions, and ask questions.
The backend connects SQLite, Hindsight, and Groq.
SQLite stores concrete records. Hindsight provides long-term customer context. Groq uses the retrieved context to generate the final response.
I wanted those responsibilities to stay separate. A database row is not the same thing as a memory, and a retrieved memory is not automatically a fact that should overwrite a structured record.
Why ordinary customer history was not enough
A customer relationship contains different kinds of information.
A meeting might say that a customer reported slow dashboard filtering. A support ticket might describe the same problem technically. A later meeting might say that an optimization improved performance. Another ticket might say that monitoring was added but has not yet been validated.
A database can retrieve those individual records. The harder question is:
What should I remember about this customer before the next meeting?
That is where Hindsight became useful.
I use Hindsight as the memory layer rather than treating old conversations as one giant prompt. Its model of retaining information, recalling relevant memories, and reflecting across memories gave me a cleaner boundary for customer context. The Hindsight documentation describes these memory operations in more detail.
Retain what happened, recall what matters
When a meeting or support ticket is recorded, FUEGO persists the structured record in SQLite and makes the important context available to Hindsight.
Conceptually, the operation looks like:
hindsight.retain(
bank_id=MEMORY_BANK,
content=customer_context,
)
Later, when the user asks a question, I retrieve relevant history instead of sending the entire customer record to the model:
memories = hindsight.recall(
bank_id=MEMORY_BANK,
query=query,
)
That changed how I thought about memory. I stopped treating memory as "chat history that we append to the prompt" and started treating it as a separate retrieval boundary.
This also lines up with the broader idea of agent memory: keeping information is only part of the problem. The useful part is bringing previous experience back when it becomes relevant.
SQLite stays the source of truth
I deliberately did not make Hindsight the database.
FUEGO has structured records for customers, meetings, and support tickets, with fields such as dates, titles, priorities, statuses, solutions, and outcomes.
That lets the system distinguish between:
SQLite:
"Ticket GGL-302 is Open."
and:
Memory:
"Monitoring gaps were discussed and additional alerting was attempted."
Those statements are related, but they have different roles.
The database tells me what the application has recorded. Memory helps retrieve the history surrounding that record.
The same rule applies to commitments. If a meeting says the FUEGO team will provide a performance improvement plan, the system records that commitment. It does not later assume the commitment was completed just because another conversation happened.
I would rather surface "needs confirmation" than manufacture completion.
Remembering solutions, not just problems
One of the most useful parts of the design is remembering what happened after a problem was reported.
For example, a customer history can contain:
Problem:
Analytics dashboard was slow when filtering large datasets.
Solution:
Created aggregated tables and simplified the semantic model.
Outcome:
Dashboard response time improved.
Another issue might contain:
Problem:
Insufficient monitoring for dashboard refresh failures.
Solution:
Added additional monitoring and alerting.
Outcome:
Not confirmed.
FUEGO can classify these as worked, partially worked, or not confirmed.
That distinction matters. "We tried this" is not the same as "this solved the problem." An unsuccessful or unverified approach is still useful memory because it changes what I would want to discuss next.
Reflection helps with higher-level questions
Recall is useful when I need relevant historical context. Reflection becomes useful when the question requires synthesis across multiple memories.
Conceptually:
insight = hindsight.reflect(
bank_id=MEMORY_BANK,
query="What should I know before the next customer meeting?",
)
I think about the difference this way:
SQLite answers: Which tickets are open?
Recall answers: What did we previously try?
Reflection answers: What should I keep in mind?
That separation is cleaner than putting every old record into one large prompt and asking the LLM to figure everything out.
A concrete customer example
Consider the Google demo customer in FUEGO.
Its history includes a ticket about slow analytics dashboard filtering. The recorded solution was to create aggregated tables and simplify the semantic model, with improved response time as the outcome.
A second ticket concerns insufficient monitoring. Additional monitoring and alerting were added, but the outcome has not yet been confirmed.
Before a meeting, FUEGO can surface the current focus, recent meetings, open issues, commitments, previous solutions, and follow-ups that still need confirmation.
I can then ask questions such as:
What solutions worked for a specific company?
What did we promise?
The interesting part is not that an LLM can produce those sentences. The useful part is that the answer is based on customer history accumulated before the current request.
What I learned
1. Memory needs a boundary
SQLite owns structured records. Hindsight owns retrievable long-term context. The LLM turns those inputs into language.
Once those responsibilities were separated, debugging became much easier.
2. Retrieval matters more than storing everything
A memory system is useful only when it can surface the right history at the right time. I prefer an explicit recall step over appending every previous interaction to the prompt.
3. "Worked" is different from "we tried it"
A previous attempt should not automatically become a recommendation. Recording outcomes explicitly gives the agent useful uncertainty instead of false confidence.
4. Memory should support evidence, not replace it
I do not want an old memory to override the current customer record. Memory provides context; structured records remain the factual source of truth.
5. The best memory experience is not a memory screen
The useful moment is when someone asks a question and the system already knows why the answer matters because it remembers what happened before.
The memory is infrastructure. The customer meeting is the product experience.
Top comments (1)