Modern AI agents are expected to do more than answer isolated questions. They need to remember user preferences, track previous interactions, recall completed tasks, and maintain context across conversations. Without persistent memory, every interaction starts from scratch, leading to repetitive questions and a poor user experience.
While Large Language Models (LLMs) have limited context windows, external memory systems enable AI agents to retain information over long periods. Redis is a popular choice for implementing this capability because it offers high-performance storage, flexible data structures, and support for vector search.
In this article, you'll learn how to design a long-term memory architecture for AI agents using Redis, implement memory management in a .NET application, and follow production-ready best practices.
Understanding AI Agent Memory
Human memory consists of different types, and AI agents can benefit from a similar approach.
| Memory Type | Purpose | Example |
|---|---|---|
| Working Memory | Current conversation | Latest user request |
| Short-Term Memory | Recent interactions | Last few messages |
| Long-Term Memory | Persistent knowledge | User preferences, completed tasks |
| Semantic Memory | Facts and knowledge | Company policies, documentation |
| Episodic Memory | Past events | Previous support tickets |
Long-term memory allows an AI agent to provide personalized and consistent responses over time.
Why Redis for AI Memory?
Redis is an in-memory data store known for its speed and versatility. It supports multiple data structures and can serve as both a cache and a persistent memory layer.
Benefits include:
Extremely low-latency reads and writes
Key-value storage for user profiles
Hashes for structured data
Sorted sets for ranking memories
Time-to-live (TTL) support
Vector search capabilities through Redis Stack
Horizontal scalability
These features make Redis well-suited for AI workloads that require frequent memory access.
Memory Architecture
A production AI memory architecture typically separates different responsibilities.
User
|
AI Application
|
Memory Manager
/ | \
Working Long-Term Vector
Memory Memory Search
| | |
Cache Redis Redis Stack
|
Business Data
The Memory Manager decides what information should be stored, retrieved, updated, or removed.
What Should Be Stored?
Not every conversation deserves permanent storage.
Good candidates include:
User preferences
Language selection
Notification settings
Frequently used commands
Completed workflows
Long-term project information
Avoid storing:
Temporary prompts
Sensitive credentials
Session tokens
Duplicate information
Large conversation histories without summarization
Keeping memory relevant improves retrieval quality and reduces storage costs.
Setting Up Redis
Using Docker is one of the easiest ways to start Redis locally.
docker run -d \
--name redis \
-p 6379:6379 \
redis
Install the .NET Redis client.
dotnet add package StackExchange.Redis
Connecting to Redis
Create a reusable Redis service.
using StackExchange.Redis;
public class RedisService
{
private readonly IDatabase database;
public RedisService()
{
var connection =
ConnectionMultiplexer.Connect("localhost:6379");
database = connection.GetDatabase();
}
public IDatabase Database => database;
}
Using dependency injection ensures the connection is shared efficiently across the application.
Storing User Memory
Store structured information as JSON.
public class UserMemory
{
public string UserId { get; set; } = "";
public string PreferredLanguage { get; set; } = "";
public string FavoriteProduct { get; set; } = "";
}
Save the object in Redis.
using System.Text.Json;
public async Task SaveMemory(
UserMemory memory)
{
string json =
JsonSerializer.Serialize(memory);
await database.StringSetAsync(
$"memory:{memory.UserId}",
json);
}
Using JSON allows the schema to evolve without creating multiple Redis keys.
Retrieving Memory
Loading user memory is straightforward.
public async Task<UserMemory?> GetMemory(
string userId)
{
var json = await database.StringGetAsync(
$"memory:{userId}");
if (json.IsNullOrEmpty)
return null;
return JsonSerializer.Deserialize<UserMemory>(
json!);
}
If no memory exists, the application can initialize a default profile.
Using Hashes for Structured Data
Redis hashes are useful when updating individual fields.
await database.HashSetAsync(
"user:1001",
new HashEntry[]
{
new("Language","English"),
new("Department","Finance"),
new("Timezone","UTC")
});
Retrieve a single field.
var language =
await database.HashGetAsync(
"user:1001",
"Language");
Hashes reduce unnecessary serialization when only a few fields change.
Implementing Memory Expiration
Some memories should expire automatically.
Example:
await database.StringSetAsync(
"session:123",
"conversation",
TimeSpan.FromHours(24));
TTL is useful for:
Temporary sessions
Cached summaries
Authentication state
Short-term memory
Long-term preferences generally should not expire.
Semantic Memory with Redis Vector Search
AI agents often need to retrieve memories based on meaning rather than exact keywords.
Example workflow:
User Question
|
Generate Embedding
|
Redis Vector Search
|
Relevant Memories
|
LLM Response
Instead of searching for exact text, semantic search retrieves conceptually related memories, improving personalization and response quality.
Join the conversation! Your thoughts help the community grow.