
The first version of our support agent could answer a customer's question.
That wasn't enough.
A customer could report a payment failure, follow a troubleshooting step, get the problem resolved, and come back later with the exact same issue. The agent would see a new message and essentially start over.
The interesting part wasn't making the agent generate another good answer.
It was making the next answer different because of what happened previously.
That became the core idea behind our Support Memory Agent: instead of treating previous conversations as simple chat history, we wanted to retain support experiences — including the problem, the action taken, and the outcome — and recall those experiences when they become relevant again.
From Conversation History to Support Experience
A typical support interaction looks something like this:
Customer
↓
Problem
↓
Troubleshooting
↓
Solution
↓
Conversation ends
The problem is what happens when the customer returns.
The previous conversation might exist somewhere, but simply having access to old messages doesn't mean the agent will use the most useful information from them.
For support, the important information isn't every sentence the customer previously typed.
It's things like:
Customer: Rahul
Platform: Android
Issue:
Payment failure
Action:
Clear app cache
Outcome:
Failed
Action:
Update PayFlow app
Outcome:
Resolved
That is much closer to an experience than a transcript.
We built our system around this distinction.
Where Hindsight Fits
We use Hindsight GitHub as the persistent memory layer.
The application has two important memory operations.
The first is recall.
When Rahul sends a new support request, the backend retrieves relevant previous experiences before asking the language model to generate a response.
The second is retain.
When a support interaction reaches an outcome, we store the experience so it can be used later.
The resulting loop looks like this:
Customer message
↓
Backend
↓
Hindsight recall
↓
Relevant previous experiences
↓
LLM
↓
Personalized response
↓
Support outcome
↓
Hindsight retain
The important detail is that Hindsight isn't simply sitting beside the application as a passive database.
The recalled memories become part of the context used to determine the next response.
You can learn more about the memory system in the Hindsight documentation and Vectorize's agent memory overview.
The Interaction That Changed the Design
Our simplest test case was a payment failure.
Imagine Rahul contacts support for the first time:
“My payment failed.”
At this point, there isn't a useful previous experience to recall.
The agent can ask for the platform and begin troubleshooting.
Rahul says he's using Android.
Suppose the first troubleshooting attempt is clearing the app cache.
It doesn't work.
We then try updating the PayFlow app and retrying the payment.
This time, the payment succeeds.
Instead of throwing away that information when the conversation ends, we retain the outcome.
The memory now contains something like:
Rahul experienced a payment failure
on Android.
Clearing the cache failed.
Updating the PayFlow app resolved
the payment failure.
Now comes the interesting part.
A few days later Rahul returns:
“My payment failed again.”
Without useful memory, the agent can only reason from the current message.
With memory, the backend recalls Rahul's previous support experience.
The response can now become:
“Updating the PayFlow app resolved this issue for you before, while clearing the cache didn't. Let's check your app version first.”
That difference is the entire point of the project.
The agent isn't simply producing a more detailed answer.
The previous outcome changed what it recommends next.
The API Is Deliberately Simple
We kept the application architecture relatively small.
The frontend communicates with a Node.js and Express backend.
For a support message, the frontend sends:
{
"customerId": "rahul-001",
"customerName": "Rahul",
"platform": "Android",
"message": "My payment failed again."
}
The backend handles the request through:
POST /api/chat
The backend then recalls relevant memories, passes those memories to the language model, and returns the response together with the memories that were used.
A simplified response looks like:
{
"success": true,
"reply": "Updating the PayFlow app resolved this issue for you before...",
"memoryUsed": true,
"memories": [
{
"text": "Rahul resolved his payment failure by updating the PayFlow app.",
"type": "observation"
}
],
"memoryCount": 1
}
We intentionally return the recalled memories to the frontend.
That makes the system easier to inspect and, more importantly, makes the memory behavior visible to the person using the application.
The Support Agent
The support request flows through a small service layer rather than putting everything inside the API route.
A simplified version of the flow looks like this:
`async function handleSupportMessage({
customerId,
customerName,
platform,
message
}) {
const memories = await recallSupportMemory({
customerId,
query: message,
platform
});
const reply = await generateSupportResponse({
customerName,
platform,
message,
memories
});
return {
reply,
memoryUsed: memories.length > 0,
memories
};
}`
This separation matters.
The route is responsible for handling HTTP requests.
The support-agent service coordinates the workflow.
The Hindsight service handles memory.
The LLM service handles response generation.
That gives us a structure like:
server/
└── src/
├── app.js
├── routes/
│ ├── chat.js
│ └── outcome.js
├── services/
│ ├── hindsight.js
│ ├── llm.js
│ └── supportAgent.js
└── config/
├── clients.js
└── env.js
The goal was to keep the memory layer independent from the rest of the support logic.
Outcome Capture Is Just as Important as Recall
One design decision became obvious while building this.
If we only recalled memories but never recorded outcomes, the system wouldn't have a meaningful feedback loop.
That's why we added:
POST /api/outcome
The request records information such as:
{
"customerId": "rahul-001",
"customerName": "Rahul",
"platform": "Android",
"issue": "Payment failure",
"action": "Update PayFlow app and retry",
"outcome": "resolved",
"notes": "Payment succeeded after the app update."
}
The backend turns this into a support experience and retains it in Hindsight.
We can do the same for failed troubleshooting.
For example:
Clear app cache → failed
Update app → resolved
Both are useful.
A failed action tells the future agent something too:
Don't blindly repeat it as the first recommendation.
Making Memory Visible
One thing we didn't want was a black-box interface where the agent magically produces a personalized response and the user has no idea why.
The interface therefore includes a dedicated Hindsight Memory panel.
For a customer like Rahul, the interface can show:
Hindsight Memory
Payment failure
Previous payment issue was resolved
by updating the PayFlow app.
Previous support interaction
Customer experienced a payment problem
on an Android device.
This gives the user a direct connection between the memory system and the response.
It also makes debugging easier.
If the agent produces an unexpected answer, we can inspect what memories were actually retrieved instead of guessing what context the model received.
A Real Before-and-After Example
The easiest way to understand the difference is to compare two interactions.
First interaction
Rahul:
"My payment failed."
Agent:
"Let's troubleshoot your payment issue."
At this point, the agent has no relevant previous experience.
After troubleshooting:
Clear cache → Failed
Update PayFlow app → Successful
That outcome is retained.
Second interaction
Later:
Rahul:
"My payment failed again."
The agent recalls:
Customer: Rahul
Platform: Android
Previous issue:
Payment failure
Previous failed action:
Clear cache
Previous successful action:
Update PayFlow app
The response can therefore prioritize the successful resolution:
"Updating the PayFlow app resolved this issue for you
before, while clearing the cache didn't. Let's check
your app version first."
The difference isn't that the second response contains more words.
The difference is that previous experience changed the decision.
One Useful Lesson: Memory Quality Matters
The first version of our testing exposed an interesting problem.
When we repeatedly inserted the same support outcome during testing, Hindsight naturally returned multiple highly similar memories.
The model could still produce a useful response, but the memory panel became noisy.
That taught us something important:
Adding memory is not the same thing as designing good memory.
A production system would need additional controls around duplicate experiences, customer isolation, memory lifecycle, and which outcomes deserve stronger consideration.
Persistent memory creates a new engineering responsibility: deciding what should be remembered and how that memory should influence future decisions.
What We Learned
- Outcomes Are More Useful Than Transcripts
A transcript tells us what people said.
An outcome tells us what happened.
For support systems, that distinction can be extremely valuable.
- Failed Attempts Are Also Knowledge
A failed troubleshooting action isn't useless data.
If a particular action failed for the same issue, the future agent should know that.
- Memory Needs to Be Observable
Showing recalled memories in the UI makes the system easier to understand and debug.
It also makes it much easier to verify whether personalization is actually coming from memory.
- Memory Should Be Part of the Architecture
We didn't want memory to be a feature added after the chatbot was finished.
The flow was designed around:
Recall
↓
Reason
↓
Respond
↓
Retain
↓
Improve future context
That changes how the application is structured from the beginning.
Where This Can Go Next
The current payment-support example is deliberately simple, but the architecture is not tied to payments.
The same pattern can apply to many support environments where previous actions and outcomes matter.
A support agent could accumulate experiences such as:
Issue
↓
Environment
↓
Action
↓
Outcome
↓
Future recommendation
The key is that the memory isn't valuable simply because it exists.
It's valuable when the agent retrieves the right previous experience at the right time and uses it to change what it does next.
That's the behavior we were trying to build.
A support agent shouldn't have to rediscover the same solution every time a customer returns.
It should be able to look back at what happened, understand what worked, avoid what didn't, and continue from there.
The goal isn't an AI that remembers everything.
It's an AI that remembers what matters.
Top comments (0)