Imagine chatting with an AI assistant who forgets everything you said just one message ago. Frustrating, right? That’s exactly what happens when you build a chatbot without memory. In this post, I’ll show you how to fix that using LangChain’s memory features in .NET—giving your AI apps the power to remember.
We’ll dive into practical examples with ConversationBufferMemory
, attaching memory to chains and agents, and building a working memory-enabled chatbot—all in clean, modern C#. If you’re working with large language models in .NET, understanding memory is essential to creating intelligent and context-aware applications.
Memory Interfaces in LangChain.NET
LangChain’s memory system allows your AI to retain conversational context. That context might be the last message, the last ten turns, or even structured data saved to a database.
The easiest way to get started is using ConversationBufferMemory
, which holds past interactions in RAM:
var memory = new ConversationBufferMemory();
await memory.SaveContextAsync(new ChatHistory
{
Inputs = { ["input"] = "Hello" },
Outputs = { ["output"] = "Hi there! How can I help you?" }
});
You can later retrieve that memory during the next step in a chain. This is especially useful in simple chatbot flows or proof-of-concept demos.
To read what’s stored:
var history = await memory.LoadAsync();
Console.WriteLine("Chat History:");
foreach (var turn in history.Turns)
{
Console.WriteLine($"User: {turn.Input} -> AI: {turn.Output}");
}
For more advanced use cases—where persistence or customization is required—you can implement your own memory provider using IChatMemory
. For example, connecting to Redis:
public class RedisChatMemory : IChatMemory
{
private readonly IConnectionMultiplexer _redis;
public RedisChatMemory(IConnectionMultiplexer redis) => _redis = redis;
public async Task<ChatHistory> LoadAsync()
{
// Load from Redis
}
public async Task SaveContextAsync(ChatHistory history)
{
// Save to Redis
}
}
This structure is ideal for multi-user environments, scalable memory, and when integrating long-term storage with session management.
Attaching Memory to Chains and Agents
Once you have a memory implementation, you attach it to your Chain
or Agent
. This is where memory becomes truly functional—enabling the AI to “remember” prior messages during inference.
Chain Example:
var chain = new LLMChain(promptTemplate, openAiModel)
{
Memory = memory
};
Every input to this chain will now automatically include previous interactions.
Agent Example:
var agent = new AgentExecutor(tools, promptTemplate, openAiModel)
{
Memory = memory
};
This works well for tool-augmented agents, where memory includes prior tool results and questions.
Real-life Use Case:
Imagine a support chatbot:
- User: “My email isn’t syncing.”
- Bot: “Are you using Outlook or Gmail?”
- User: “Outlook.”
Memory allows the bot to use “Outlook” in follow-up logic without re-asking.
Without memory, that flow breaks entirely.
Example App: Memory-Enabled Chatbot
Let’s put it all together in a memory-enabled chatbot:
var memory = new ConversationBufferMemory();
var prompt = new PromptTemplate("You are a helpful assistant. {history}\nUser: {input}\nAI:");
var model = new OpenAIChatModel("gpt-4");
var chain = new LLMChain(prompt, model)
{
Memory = memory
};
while (true)
{
Console.Write("User: ");
var input = Console.ReadLine();
var output = await chain.RunAsync(new Dictionary<string, string> { ["input"] = input });
Console.WriteLine($"AI: {output}");
}
Explanation: Each user message is saved in memory. When you send the next input, LangChain builds the prompt by combining the previous conversation ({history}
) with the new user message. This enables contextual replies without needing to manually manage history.
Tip: To format
history
cleanly, customize the string rendering:
var prompt = new PromptTemplate("{history}\nUser: {input}\nAI:")
{
HistorySeparator = "\n"
};
This way, memory stays readable and fits nicely into your prompts.
Performance Considerations
Memory is powerful—but not free. It can quickly balloon your prompt size and affect performance.
1. Token Usage:
Every saved interaction increases the prompt length. If your model has a 4K or 8K token limit, long histories will truncate the newest or oldest turns.
Solution: Use a summarizing memory that distills older history:
// Pseudo-code: Combine with LLM to summarize older turns into a synopsis.
2. Memory Growth:
RAM-based memories like ConversationBufferMemory
keep everything in memory.
Solution: Offload to disk or Redis after X turns. E.g.,
if (memory.Count > 10)
await archiveMemory.SaveAsync(memory);
3. Session-specific Memory:
In real applications, memory must be isolated per session or user.
var memoryMap = new Dictionary<string, IChatMemory>();
var userId = GetSessionId();
var memory = memoryMap[userId];
4. Auto-clear Memory:
For support bots or finite interactions:
if (endOfSession)
memory.Clear();
5. Debugging Tips:
Always inspect token usage when debugging latency:
Console.WriteLine(await memory.LoadAsync());
This will help you balance context richness with model limits and API costs.
FAQ: Common Questions About LangChain Memory
Yes, but each chain must share the same IChatMemory
instance.
You can print the ChatHistory
from memory using LoadAsync()
.
Absolutely. Just map memory instances by session ID in your app.
Conclusion: Make Your AI Smarter with Memory
Adding memory to your LangChain-powered .NET app is like giving your AI a brain. It transforms interactions from robotic Q&A to rich, flowing conversations. Whether you’re prototyping or building production-level tools, memory is essential for context-aware AI.
So, what will your AI remember next?
Leave a comment or explore more on the blog for the next post in this LangChain.NET series.