By default, an agent has amnesia. Each call is independent — ask “track order ORD-4821,” then “start a return for it,” and the second message has no idea what “it” is. To build anything conversational, you need memory. But “memory” is really three different things with three different solutions: short-term conversation state, persistence across sessions, and long-term recall. This article covers each in .NET.
It builds on the tool-using agent tutorial.
Short-term memory: threads
Within a single conversation, memory means keeping the message history so the model can resolve references and follow context. The Microsoft Agent Framework models this as a thread — create one and pass it on every call:
var thread = agent.GetNewThread();
await agent.RunAsync("Track order ORD-4821", thread);
// The thread carries history, so "it" resolves correctly:
await agent.RunAsync("Start a return for it — wrong size", thread);
The thread accumulates the exchange, so each turn sees what came before. This is the memory you need 90% of the time, and it’s nearly free to use.
The catch: history grows, and you pay for all of it
Every message in the thread is re-sent to the model on every turn — so a long conversation means a large, expensive, and eventually context-window-busting prompt. Two standard fixes:
- Windowing — keep only the last N turns verbatim.
- Summarization — periodically compress older turns into a running summary and keep recent turns in full.
A common hybrid: recent turns verbatim + a rolling summary of everything older. You preserve continuity without paying to resend the entire history each time. (This is one of the biggest levers in cost control.)
Persistence: memory that survives a restart
A thread in memory vanishes when your process restarts or the user comes back tomorrow. For real applications, you persist the conversation state — serialize the thread and store it keyed by conversation or user:
// Save after each turn
var state = thread.Serialize(); // conceptual: export thread state
await store.SaveAsync(conversationId, state);
// Restore when the user returns
var saved = await store.LoadAsync(conversationId);
var thread = agent.DeserializeThread(saved); // conceptual: rebuild the thread
Store it wherever you already keep state — a database, Redis, or blob storage. Now a user’s conversation resumes exactly where they left off, across devices and restarts. Mind data-retention and privacy: stored conversations may contain personal data, so apply the same care as any user data (and see securing agents).
Long-term memory: recall beyond the conversation
Sometimes you want the agent to remember facts across many separate conversations — a user’s preferences, past issues, account details. Cramming all history into the thread doesn’t scale. Instead, treat long-term memory as retrieval: store facts as embeddings and pull the relevant ones into context when needed — the same machinery as RAG, pointed at “things we know about this user” instead of documents.
The pattern: after a conversation, extract durable facts and write them to a vector store; at the start of a new conversation, retrieve the ones relevant to the current topic and add them to the context. The agent appears to “remember” the user without you replaying every past chat.
Choosing the right kind
Match the mechanism to the need — using a heavier one than necessary just adds cost and complexity:
- Single conversation → thread (with windowing/summarization once it grows).
- Resume later / across devices → persist the serialized thread.
- Facts across many conversations → long-term memory via retrieval.
Most agents need the first, many need the second, and only some need the third. Start with threads; add persistence when users return; add long-term memory only when the agent genuinely needs to recall across sessions.
Note: thread serialization APIs (
Serialize,DeserializeThread) and memory abstractions are still evolving in the Agent Framework. Verify method names against the current docs; the three-layer model — threads, persistence, retrieval-based long-term memory — is the durable design.
Takeaway
“Give my agent memory” is three problems. Threads handle in-conversation context and cover most needs — just window or summarize as they grow so you don’t pay to resend everything. Persist serialized threads so conversations survive restarts and follow users across devices. And for facts that must outlive a single chat, use retrieval-based long-term memory. Pick the lightest layer that solves your problem, and add the others only when the use case demands them.
Have a correction or a topic you want covered? Email mani.bc72@gmail.com.