set the title How Hindsight Lets a Support Chat Continue Across Sessions
How Hindsight Lets a Support Chat Continue Across Sessions A support assistant that remembers a customer’s laptop model but forgets which conversation contained it is only halfway useful. I built RecallCare around a different boundary: conversation history belongs to a thread, while customer knowledge should follow the customer. That sounds like a storage choice. In practice, it changes retrieval, prompting, failure handling, and even what I show in the interface. The central design decision was to make Hindsight’s long-term agent memory a separate system from the chat transcript, and to treat the two as complementary sources of context rather than competing databases. Two kinds of context, with different jobs RecallCare is a customer-support chat application. The frontend is a React app with a dashboard and conversation view. The backend is a FastAPI service: SQLAlchemy persists customers, conversations, and messages; authentication identifies the current customer; an LLM generates support replies; and Hindsight retains and recalls customer information across threads. Figure 1: RecallCare AI dashboard displaying retrieved customer memory insights. The distinction between transcript and memory matters. A transcript answers, “What did we say in this conversation?” Customer memory answers, “What relevant facts should I know before responding now?” A conversation can be long, but its context window is necessarily bounded. A customer’s support history can span many conversations, and copying every old message into every prompt would be wasteful and noisy. The request path makes that boundary explicit: Authenticate the customer and verify access to the requested conversation. Ask Hindsight for memories relevant to the incoming support question. Load a bounded set of recent messages from that conversation. Pass both kinds of context to the response generator. Persist the user and assistant messages to the conversation. Retain the customer’s actual message in Hindsight. The backend’s recall query is grounded in the current request while still describing the kinds of facts support tends to need: customer_memories = await recall_memory( customer_id, ( f"{message}. Retrieve relevant information about this " "customer from all previous conversations, including " "devices, preferences, contact methods, reported issues, " "previous support interactions and resolutions." ) ) I like this approach because the retrieval request has two inputs: what the customer is asking now and the support-specific context worth recovering. The question makes recall contextual; the additional terms make it less likely that a useful device detail or prior resolution is overlooked just because it is not repeated verbatim in the new message. A memory bank is not a conversation ID Each customer gets a stable Hindsight bank identifier derived from the customer ID. The identifier is a SHA-256 digest truncated to 24 hexadecimal characters, prefixed with the application name. It avoids putting the raw database ID in the bank name, and it gives every conversation for a customer the same place to retain and recall information. def get_customer_bank_id(customer_id): customer_hash = hashlib.sha256( str(customer_id).encode("utf-8") ).hexdigest()[:24] return f"recallcare-{customer_hash}" That stable mapping is the connective tissue across sessions. A new conversation still has its own ID and transcript, but Hindsight lookup uses the customer identity. A customer who starts a fresh thread about a laptop problem can therefore get relevant history from an earlier thread without loading that entire thread into the prompt. Figure 4: In a separate customer session, the assistant asks the customer to provide their laptop model and preferred support channel instead of recalling the previous customer's details. The implementation wraps Hindsight operations behind a small module rather than sprinkling client calls through route handlers. That module initializes the client from environment configuration, creates or ensures the customer bank, and exposes focused operations for retain, recall, and listing valid memories. The application lifecycle initializes and closes the client with FastAPI’s lifespan. These boundaries make it easier to reason about which code owns the external service connection and which code owns the support workflow. The Hindsight documentation explains the memory model in more detail. The application delegates retaining and retrieving durable memory to a purpose-built service, while keeping messages and conversation metadata in its relational store. You can also explore the Hindsight GitHub repository to understand its implementation. Why I keep recent messages anyway Once I had long-term memory, it was tempting to treat it as the whole context layer. That would have been a mistake. Hindsight is not a replacement for the current thread. The recent transcript contains details of the ongoing exchange—what the assistant just suggested, which troubleshooting step failed, and what the customer is responding to. Long-term memory should add durable history, not reconstruct every turn. RecallCare loads the latest 20 messages for the selected conversation, orders them chronologically, and labels them separately from customer memories before sending them to the model: memories_for_ai = [ "LONG-TERM CUSTOMER MEMORY: " + item for item in customer_memories ] memories_for_ai.extend([ "CURRENT CONVERSATION CONTEXT: " + item for item in conversation_context ]) The labels are intentionally plain. They communicate provenance and scope: one set represents recalled facts about the customer; the other is recent dialogue from this conversation. The response instructions then tell the model to use supplied memories, pay attention to dates, prefer newer explicitly dated information when facts conflict, and ask for confirmation when the order cannot be established. This separation also gives the system a graceful behavior when recall is unavailable. The chat endpoint catches recall failure, logs it, and continues with an empty set of long-term memories. The assistant can still answer using the current message and recent thread, rather than making an external memory outage a hard failure for every support interaction. That is a deliberate availability trade-off: a reply may be less personalized, but the support flow remains usable. In a production deployment I would make this degradation visible in operational telemetry, not silently treat it as equivalent to a successful recall. Retaining messages is not the same as retaining truth RecallCare retains the customer’s message with a timestamp after the assistant response has been saved. The timestamp gives later reasoning a way to distinguish a newer statement from an older one. The prompt makes that temporal rule explicit: an explicitly dated correction should take priority over older undated information about the same preference. This matters for ordinary support facts. Suppose a customer once asked for email updates, then later says, “Please call me instead.” A memory system that only stores both statements has preserved data but has not resolved the useful question: which preference should the next support interaction follow? The dashboard adds a lightweight presentation layer for that problem. It cleans formatting artifacts from memory text, removes exact and near-duplicate records, groups facts into device information, support preferences, previous support activity, and other information, and tries to identify explicit preference updates. When it sees a newer dated update, it can show the current preference and hide older conflicting preference records from the default view. If it cannot confidently resolve email-versus-phone records, it displays a conflict instead of guessing. That last behavior is important. A UI that confidently renders stale information is worse than one that asks a human to confirm. The dashboard shows both the current preference when it can infer it and the records that informed the view; the user can also expand the list to inspect all memory records. The cleanup logic is not a substitute for Hindsight’s retrieval. It is a read-side aid for making retained information understandable to a person. There is a limitation worth being clear about: the system retains the customer’s messages, not a fully verified profile fact with an authoritative source and resolution workflow. A timestamp helps order statements, but it does not prove that the newest statement is true or that two differently worded memories refer to the same preference. That distinction should shape both the product behavior and any production rollout. I would rather surface uncertainty than imply that memory is infallible. The prompt is part of the interface contract Memory quality is not only about retrieval. The model needs rules for how to use what it receives. RecallCare’s prompt says not to invent customer details, not to claim information is missing when it is present in memory, and not to claim a memory was saved unless the application confirms it. It also distinguishes a dated correction from older undated information. Those rules are small, but they make the memory boundary legible. Retrieval supplies candidate facts; the prompt sets expectations for how they influence an answer. For example, if the customer asks, “Can you send the warranty steps to my preferred contact method?” and Hindsight returns an older email preference plus a later dated phone correction, the assistant should use phone as the current preference. If the records conflict without reliable dates, it should ask rather than select one arbitrarily. The generator also keeps response behavior separate from memory mechanics. It receives a list of prepared context strings and builds the system prompt around them: if memories: memory_context = "\n".join( f"- {memory}" for memory in memories ) else: memory_context = ( "No relevant previous customer information " "was retrieved." ) That lets the chat route decide what context to supply while the generator focuses on producing a concise support answer. It is a modest seam, not a grand abstraction, but it prevents the model integration from having to know how Hindsight banks are named or how SQL conversations are queried. What this changes in an interaction Consider a customer who has previously discussed a particular laptop and reported intermittent charging trouble. In a later, separate conversation, they write: “It stopped charging again after the update.” The new thread begins with no transcript of the earlier support exchange. Recall retrieves relevant history from the customer’s bank; the backend also loads the recent messages from the new thread, which may be empty. The assistant can respond with context about the known device and prior issue, then ask a focused follow-up rather than starting with “What model is your laptop?” Figure 2: A customer shares their Dell Inspiron 15 laptop model and email support preference. If the customer corrects a support preference in that same exchange, the message is retained with a timestamp. Later interactions can retrieve the correction alongside older information. The prompt tells the assistant how to treat dated changes, and the dashboard gives a person a way to inspect what has been retained and see when records disagree. Figure 3: Hindsight retrieves the customer's previously shared laptop model and preferred support channel in a new conversation. That is the practical payoff: not that the assistant remembers everything, but that it can use relevant history without confusing the history of one thread with the identity and ongoing needs of a customer. Lessons I would carry into another system Give transcripts and memory different responsibilities. A transcript is ordered dialogue. Memory is selectively retrieved customer context. Mixing the two creates oversized prompts and unclear data ownership. Make identity stable across conversations. A customer-level memory key is what turns separate threads into continuity. Keep the mapping centralized and avoid deriving it ad hoc in every route. Treat time and disagreement as first-class problems. Storing two preferences is easy; knowing which one to use is not. Preserve dates, define how corrections affect older facts, and surface unresolved conflicts. Design for partial availability. Memory recall should not automatically become a total chat outage. Decide what degraded behavior looks like, and make it observable so an outage is not mistaken for a personalized answer. Show people what the system remembers. A memory dashboard is not just a debugging convenience. It gives operators and customers a way to inspect, question, and correct the context that shapes future replies. The most useful decision in RecallCare was not adding another context source. It was deciding that each source has a different lifetime and purpose. Hindsight gives the application a customer-level memory boundary; the conversation store preserves what happened in a particular thread. Once those roles are explicit, cross-session support becomes a tractable engineering problem—with real trade-offs around relevance, stale facts, service failures, and trust, rather than a vague promise that the assistant will “remember.”
This is a summary aggregated from Dev.to. Read the complete article on the original site:
Read full article at Dev.to