|
1 |
| -This intelligent AI assistant is designed to support real-world, multi-session use with Redis as its memory core. |
2 |
| - |
3 |
| -What you get: |
4 |
| - - **Smart Memory**: Ephemeral context that expires automatically, long-term facts retained forever |
5 |
| - - **Semantic Search**: Recall relevant info by meaning using vector search |
6 |
| - - **Zero Maintenance**: Auto-expiring short-term memory without a need to track timestamps manually |
7 |
| - - **Multi-User**: Isolated memory per user |
8 |
| - - **Learning**: Assistant learns user preferences and context over time. |
| 1 | +This tutorial demonstrates how to build an AI assistant's memory system with Redis as its memory core. |
9 | 2 |
|
10 | 3 | **Note**: Requires [Redis 8](https://hub.docker.com/_/redis/tags) for `HSETEX`, which adds per-field TTL for hashes, which is ideal for managing short-term memory with precision.
|
11 | 4 |
|
12 | 5 | ### Architecture Overview
|
13 |
| -| Layer | Description | |
14 |
| -| ---------- | ---------- | |
15 |
| -| `Working Memory`| `Short-term chat context (ephemeral)` | |
16 |
| -| `Knowledge Base` | `Long-term memory: persistent facts and preferences` | |
17 |
| -| `Vector Search` | `Unified semantic recall across both layers` | |
18 |
| - |
19 |
| -### Working Memory (Ephemeral) |
20 |
| -Stores recent user and assistant messages with per-message TTL. Uses: |
21 |
| - - `HSETEX` to add field-level expiration to hashes to store all temporary messages in a single key while managing TTLs per message, simplifying short-term memory management without needing multiple keys. |
22 |
| - - `ZADD` for message ordering (based on timestamp) |
23 |
| - |
24 |
| -```redis:[run_confirmation=true] Recent conversations with TTL based on importance |
25 |
| -// Step 1: Add message with TTL (5 mins) |
26 |
| -HSETEX user:alice:session:001 EX 300 FIELDS 1 msg:001 "What's the weather?" |
27 |
| -// Step 2: Track order with timestamp (epoch seconds) |
28 |
| -ZADD user:alice:session:001:zorder 1717935001 msg:001 |
29 |
| -// Another message (30 min TTL) |
30 |
| -HSETEX user:alice:session:001 EX 1800 FIELDS 1 msg:002 "I need a dentist appointment" |
31 |
| -ZADD user:alice:session:001:zorder 1717935030 msg:002 |
32 |
| -// Assistant reply (2 hour TTL) |
33 |
| -HSETEX user:alice:session:001 EX 7200 FIELDS 1 msg:003 "Booked for Tuesday 2 PM" |
34 |
| -ZADD user:alice:session:001:zorder 1717935090 msg:003 |
| 6 | +| Layer | Description | Data type | |
| 7 | +| ---------- | ---------- | ---------- | |
| 8 | +| `Session History`| `Recent conversation context` | List | |
| 9 | +| `Rate Limiting` | `Per-user request throttling` | Hash | |
| 10 | +| `Knowledge Base` | `Long-term facts and preferences` | Hash | |
| 11 | + |
| 12 | +### Session History |
| 13 | +AI assistants need context from previous messages to provide coherent responses. Without conversation history, each interaction would be isolated and the AI couldn't reference what was discussed earlier. We can store conversation history using Redis lists - simple, ordered, and efficient for chat scenarios. |
| 14 | + |
| 15 | +```redis:[run_confirmation=true] Store conversation history |
| 16 | +// Add user message to session |
| 17 | +LPUSH user:alice:history:session_001 '{"type": "human", "content": "What's the weather like?", "timestamp": 1717935001}' |
| 18 | +
|
| 19 | +// Add AI response |
| 20 | +LPUSH user:alice:history:session_001 '{"type": "ai", "content": "It's sunny with 75°F temperature.", "timestamp": 1717935002}' |
| 21 | +
|
| 22 | +// Add another user message |
| 23 | +LPUSH user:alice:history:session_001 '{"type": "human", "content": "Should I bring an umbrella?", "timestamp": 1717935003}' |
| 24 | +
|
| 25 | +// Add AI response |
| 26 | +LPUSH user:alice:history:session_001 '{"type": "ai", "content": "No umbrella needed today!", "timestamp": 1717935004}' |
| 27 | +``` |
| 28 | +### Reading Conversation History |
| 29 | +Now we can retrieve conversation history to provide context to the AI. |
| 30 | + |
| 31 | +```redis:[run_confirmation=true] Read conversation history |
| 32 | +// Get last 5 messages (most recent first) |
| 33 | +LRANGE user:alice:history:session_001 0 4 |
| 34 | +
|
| 35 | +// Get all messages in session |
| 36 | +LRANGE user:alice:history:session_001 0 -1 |
| 37 | +
|
| 38 | +// Get specific message by index |
| 39 | +LINDEX user:alice:history:session_001 0 |
| 40 | +
|
| 41 | +// Check how many messages in session |
| 42 | +LLEN user:alice:history:session_001 |
35 | 43 | ```
|
| 44 | +### Session Expiration |
| 45 | +Without expiration, conversation history would accumulate indefinitely, consuming memory and potentially exposing sensitive information. TTL ensures privacy and efficient memory usage. |
36 | 46 |
|
37 |
| -```redis:[run_confirmation=true] Reading the session in order |
38 |
| -ZRANGEBYSCORE user:alice:session:001:zorder -inf +inf |
39 |
| -// Then for each msg ID: |
40 |
| -HGET user:alice:session:001 msg:001 |
41 |
| -HGET user:alice:session:001 msg:002 |
42 |
| -HGET user:alice:session:001 msg:003 |
| 47 | +```redis:[run_confirmation=true] Session expiration |
| 48 | +// Set session to expire in 24 hours |
| 49 | +EXPIRE user:alice:history:session_001 86400 |
| 50 | +
|
| 51 | +// Set session to expire in 1 hour |
| 52 | +EXPIRE user:alice:history:session_001 3600 |
| 53 | +
|
| 54 | +// Check remaining TTL |
| 55 | +TTL user:alice:history:session_001 |
| 56 | +
|
| 57 | +// Remove expiration (make persistent) |
| 58 | +PERSIST user:alice:history:session_001 |
43 | 59 | ```
|
44 | 60 |
|
45 |
| -```redis:[run_confirmation=true] ZSET cleanup (recommended in background): |
46 |
| -// For each msg:XXX in the ZSET, check if it still exists |
47 |
| -HEXISTS user:alice:session:001 msg:003 |
48 |
| -// If not, clean up: |
49 |
| -ZREM user:alice:session:001:zorder msg:003 |
| 61 | +### Rate Limiting |
| 62 | +Rate limiting prevents abuse and ensures fair resource usage. Without it, users could overwhelm the system with requests, degrading performance for everyone. |
| 63 | + |
| 64 | +```redis:[run_confirmation=true] Initialize Rate Limiting |
| 65 | +// First request - set counter with 1-minute TTL |
| 66 | +HSETEX user:alice:rate_limit EX 60 FIELDS 1 requests_per_minute 1 |
| 67 | +
|
| 68 | +// Check current request count |
| 69 | +HGET user:alice:rate_limit requests_per_minute |
50 | 70 | ```
|
51 | 71 |
|
52 |
| -### Knowledge Base (Persistent) |
53 |
| -Long-term memory: stores important facts, user preferences, and context across sessions. These never expire. |
54 |
| -`embedding` is a binary-encoded `FLOAT32[]` used for vector similarity that can be generated using sentence-transformers or similar libraries. Demo uses 8-dim vectors; production models typically use 128–1536 dimensions. |
55 |
| - |
56 |
| -```redis:[run_confirmation=true] Important user information that never expires |
57 |
| -// User preferences - need vector fields for search |
58 |
| -HSET user:alice:knowledge:pref:001 user_id "alice" memory_type "knowledge" content "prefers mornings before 10 AM" importance 9 timestamp 1717935000 embedding "\x40\x20\x00\x00\x3f\x80\x00\x00\x40\x40\x00\x00\x3f\x40\x00\x00\x40\x60\x00\x00\x40\x00\x00\x00\x3f\x00\x00\x00\x40\x80\x00\x00" |
59 |
| -HSET user:alice:knowledge:pref:002 user_id "alice" memory_type "knowledge" content "likes detailed explanations" importance 8 timestamp 1717935000 embedding "\x3f\x40\x00\x00\x40\x60\x00\x00\x40\x00\x00\x00\x40\x20\x00\x00\x3f\x80\x00\x00\x40\x40\x00\x00\x40\x80\x00\x00\x3f\x00\x00\x00" |
60 |
| -// Personal facts |
61 |
| -HSET user:alice:knowledge:personal:001 user_id "alice" memory_type "knowledge" content "allergic to shellfish" importance 10 timestamp 1717935000 embedding "\x40\x00\x00\x00\x40\x20\x00\x00\x3f\x80\x00\x00\x40\x40\x00\x00\x40\x60\x00\x00\x3f\x40\x00\x00\x40\x80\x00\x00\x3f\x00\x00\x00" |
62 |
| -HSET user:alice:knowledge:personal:002 user_id "alice" memory_type "knowledge" content "golden retriever named Max" importance 7 timestamp 1717935000 embedding "\x3f\x80\x00\x00\x40\x40\x00\x00\x40\x00\x00\x00\x3f\x40\x00\x00\x40\x80\x00\x00\x40\x20\x00\x00\x3f\x00\x00\x00\x40\x60\x00\x00" |
63 |
| -// Work context |
64 |
| -HSET user:alice:knowledge:work:001 user_id "alice" memory_type "knowledge" content "Senior PM at TechCorp" importance 8 timestamp 1717935000 embedding "\x40\x40\x00\x00\x3f\x00\x00\x00\x40\x80\x00\x00\x40\x20\x00\x00\x3f\x80\x00\x00\x40\x60\x00\x00\x40\x00\x00\x00\x3f\x40\x00\x00" |
65 |
| -HSET user:alice:knowledge:work:002 user_id "alice" memory_type "knowledge" content "leading Project Apollo" importance 9 timestamp 1717935000 embedding "\x40\x60\x00\x00\x40\x80\x00\x00\x3f\x40\x00\x00\x40\x00\x00\x00\x40\x20\x00\x00\x3f\x80\x00\x00\x40\x40\x00\x00\x3f\x00\x00\x00" |
| 72 | +The `HINCR` command allows you to atomically increment the counter, preventing race conditions in high-concurrency scenarios. |
| 73 | + |
| 74 | +```redis:[run_confirmation=true] Increment Requests |
| 75 | +// Increment request counter |
| 76 | +HINCR user:alice:rate_limit requests_per_minute |
| 77 | +
|
| 78 | +// Check if field exists and get count |
| 79 | +HEXISTS user:alice:rate_limit requests_per_minute |
| 80 | +HGET user:alice:rate_limit requests_per_minute |
| 81 | +
|
| 82 | +// Check TTL on the hash |
| 83 | +TTL user:alice:rate_limit |
| 84 | +``` |
| 85 | + |
| 86 | +Different time windows serve different purposes - per-minute prevents burst attacks, per-hour prevents sustained abuse, per-day enforces usage quotas. |
| 87 | +```redis:[run_confirmation=true] Rate Limiting with Different Time Windows |
| 88 | +// Set multiple rate limits with different TTLs |
| 89 | +HSETEX user:alice:rate_limit EX 60 FIELDS 2 requests_per_minute 1 requests_per_hour 1 |
| 90 | +
|
| 91 | +// Daily rate limit (24 hours) |
| 92 | +HSETEX user:alice:rate_limit EX 86400 FIELDS 1 requests_per_day 1 |
| 93 | +
|
| 94 | +// Check all rate limits |
| 95 | +HGETALL user:alice:rate_limit |
| 96 | +``` |
| 97 | + |
| 98 | +### Knowledge Base (Persistent Memory) |
| 99 | +AI assistants become more helpful when they remember user preferences, important facts, and context across sessions. This creates a personalized experience that improves over time. |
| 100 | +Remembering user preferences (meeting times, communication style) enables the AI to provide more relevant and personalized responses without asking the same questions repeatedly. |
| 101 | + |
| 102 | +```redis:[run_confirmation=true] Store User Preferences |
| 103 | +// Always secure sensitive data using encryption at rest, access control (Redis ACLs), and comply with data protection laws (e.g., GDPR). |
| 104 | +// Store morning preference |
| 105 | +HSET user:alice:knowledge:pref:001 user_id "alice" content "prefers morning appointments before 10 AM" importance 9 timestamp 1717935000 embedding "\x40\x20\x00\x00\x3f\x80\x00\x00\x40\x40\x00\x00\x3f\x40\x00\x00\x40\x60\x00\x00\x40\x00\x00\x00\x3f\x00\x00\x00\x40\x80\x00\x00" |
| 106 | +
|
| 107 | +// Storing communication preference |
| 108 | +HSET user:alice:knowledge:pref:002 user_id "alice" content "likes detailed explanations with examples" importance 8 timestamp 1717935000 embedding "\x3f\x40\x00\x00\x40\x60\x00\x00\x40\x00\x00\x00\x40\x20\x00\x00\x3f\x80\x00\x00\x40\x40\x00\x00\x40\x80\x00\x00\x3f\x00\x00\x00" |
| 109 | +
|
| 110 | +// Store work schedule preference |
| 111 | +HSET user:alice:knowledge:pref:003 user_id "alice" content "works remotely on Fridays" importance 7 timestamp 1717935000 embedding "\x40\x80\x00\x00\x3f\x00\x00\x00\x40\x40\x00\x00\x40\x20\x00\x00\x3f\x80\x00\x00\x40\x60\x00\x00\x40\x00\x00\x00\x3f\x40\x00\x00" |
| 112 | +
|
| 113 | +// Store health information |
| 114 | +HSET user:alice:knowledge:personal:001 user_id "alice" content "allergic to shellfish and nuts" importance 10 timestamp 1717935000 embedding "\x40\x00\x00\x00\x40\x20\x00\x00\x3f\x80\x00\x00\x40\x40\x00\x00\x40\x60\x00\x00\x3f\x40\x00\x00\x40\x80\x00\x00\x3f\x00\x00\x00" |
| 115 | +
|
| 116 | +// Store pet information |
| 117 | +HSET user:alice:knowledge:personal:002 user_id "alice" content "has a golden retriever named Max, 3 years old" importance 7 timestamp 1717935000 embedding "\x3f\x80\x00\x00\x40\x40\x00\x00\x40\x00\x00\x00\x3f\x40\x00\x00\x40\x80\x00\x00\x40\x20\x00\x00\x3f\x00\x00\x00\x40\x60\x00\x00" |
| 118 | +
|
| 119 | +// Store family information |
| 120 | +HSET user:alice:knowledge:personal:003 user_id "alice" content "married to Bob, two kids Sarah (8) and Tom (5)" importance 9 timestamp 1717935000 embedding "\x40\x60\x00\x00\x40\x00\x00\x00\x3f\x40\x00\x00\x40\x80\x00\x00\x40\x20\x00\x00\x3f\x80\x00\x00\x40\x40\x00\x00\x3f\x00\x00\x00" |
66 | 121 | ```
|
67 | 122 |
|
68 | 123 | ### Vector Search: Semantic Memory Recall
|
| 124 | +Traditional keyword search misses semantic meaning. When a user asks about "scheduling meetings," vector search can find relevant information about "prefers morning appointments" even though the keywords don't match exactly. |
| 125 | + |
69 | 126 | Indexing persistent memory (knowledge base) for semantically meaningful search.
|
70 | 127 |
|
71 |
| -```redis:[run_confirmation=true] Create a vector index |
72 |
| -FT.CREATE idx:kbmemory |
| 128 | +```redis:[run_confirmation=true] Create a Vector Index |
| 129 | +// Create index for semantic search |
| 130 | +FT.CREATE idx:knowledge |
73 | 131 | ON HASH
|
74 | 132 | PREFIX 1 user:
|
75 | 133 | SCHEMA
|
76 | 134 | user_id TAG
|
77 |
| - memory_type TAG |
78 | 135 | content TEXT
|
79 | 136 | importance NUMERIC
|
80 | 137 | timestamp NUMERIC
|
81 | 138 | embedding VECTOR HNSW 6
|
82 | 139 | TYPE FLOAT32
|
83 |
| - DIM 8 // DIM = embedding size; 8 used here for simplicity — in production, use 128 to 1536 |
84 |
| - DISTANCE_METRIC COSINE // COSINE = measures semantic closeness |
| 140 | + DIM 8 // DIM = embedding size, DIM 8 is just for demo purposes. In real use, embeddings are usually 128–1536 dimensions. |
| 141 | + DISTANCE_METRIC COSINE |
85 | 142 | ```
|
86 | 143 |
|
87 | 144 | ### Search for most relevant memory entries
|
88 |
| -Find the top 5 most semantically relevant knowledge memory entries for user "alice" by performing a vector similarity search on the embedding field. |
89 | 145 |
|
90 |
| -```redis:[run_confirmation=false] Find top 5 related messages by meaning |
91 |
| -FT.SEARCH idx:kbmemory |
| 146 | +```redis:[run_confirmation=false] Find Top 5 Most Relevant Knowledge Items |
| 147 | +FT.SEARCH idx:knowledge |
92 | 148 | "(@user_id:{alice}) => [KNN 5 @embedding $vec AS score]"
|
93 | 149 | PARAMS 2 vec "\x40\x00\x00\x00\x40\x20\x00\x00\x3f\x80\x00\x00\x40\x40\x00\x00\x40\x60\x00\x00\x3f\x40\x00\x00\x40\x80\x00\x00\x3f\x00\x00\x00"
|
94 |
| - RETURN 4 content memory_type importance score |
| 150 | + RETURN 4 content importance score timestamp |
95 | 151 | SORTBY score ASC
|
96 | 152 | DIALECT 2
|
97 | 153 | ```
|
98 | 154 |
|
99 |
| -### Search for High-Importance Knowledge Items Only |
100 |
| -This query finds the top 3 most relevant knowledge memories for user "alice" that have an importance score above 7. |
101 |
| - |
102 |
| -```redis:[run_confirmation=false] Knowledge-only search |
103 |
| -FT.SEARCH idx:kbmemory |
104 |
| - "(@user_id:{alice} @memory_type:{knowledge} @importance:[7 +inf]) => [KNN 3 @embedding $vec AS score]" |
105 |
| - PARAMS 2 vec "\x40\x40\x00\x00\x3f\x00\x00\x00\x40\x80\x00\x00\x40\x20\x00\x00\x3f\x80\x00\x00\x40\x60\x00\x00\x40\x00\x00\x00\x3f\x40\x00\x00" |
106 |
| - RETURN 4 content importance score timestamp |
107 |
| - SORTBY score ASC |
108 |
| - DIALECT 2 |
| 155 | +```redis:[run_confirmation=false] Search High-Importance Items Only |
| 156 | +FT.SEARCH idx:knowledge |
| 157 | + "(@user_id:{alice} @importance:[8 +inf]) => [KNN 3 @embedding $vec AS score]" |
| 158 | + PARAMS 2 vec "\x40\x40\x00\x00\x3f\x00\x00\x00\x40\x80\x00\x00\x40\x20\x00\x00\x3f\x80\x00\x00\x40\x60\x00\x00\x40\x00\x00\x00\x3f\x40\x00\x00" |
| 159 | + RETURN 4 content importance score timestamp |
| 160 | + SORTBY score ASC |
| 161 | + DIALECT 2 |
109 | 162 | ```
|
110 |
| - |
111 |
| -### Search Working Memory Excluding Messages Older Than a Certain Timestamp |
112 |
| -Retrieve the most similar knowledge memories for user "alice" that were created after a given timestamp (e.g., 1717935000), ensuring you only get recent context: |
113 |
| -```redis:[run_confirmation=false] Session-only search |
114 |
| -FT.SEARCH idx:kbmemory |
115 |
| - "(@user_id:{alice} @memory_type:{knowledge} @timestamp:[1717935000 +inf]) => [KNN 5 @embedding $vec AS score]" |
116 |
| - PARAMS 2 vec "\x3f\x80\x00\x00\x40\x40\x00\x00\x40\x00\x00\x00\x3f\x40\x00\x00\x40\x80\x00\x00\x40\x20\x00\x00\x3f\x00\x00\x00\x40\x60\x00\x00" |
117 |
| - RETURN 4 content timestamp memory_type score |
118 |
| - SORTBY score ASC |
119 |
| - DIALECT 2 |
| 163 | +```redis:[run_confirmation=false] Search Recent Knowledge Only |
| 164 | +FT.SEARCH idx:knowledge |
| 165 | + "(@user_id:{alice} @timestamp:[1717935000 +inf]) => [KNN 5 @embedding $vec AS score]" |
| 166 | + PARAMS 2 vec "\x3f\x80\x00\x00\x40\x40\x00\x00\x40\x00\x00\x00\x3f\x40\x00\x00\x40\x80\x00\x00\x40\x20\x00\x00\x3f\x00\x00\x00\x40\x60\x00\x00" |
| 167 | + RETURN 4 content timestamp score |
| 168 | + SORTBY score ASC |
| 169 | + DIALECT 2 |
120 | 170 | ```
|
121 | 171 |
|
122 |
| -### Monitoring Memory State |
123 |
| -Use these queries to inspect what’s stored in memory. |
| 172 | +### Memory State Monitoring |
| 173 | +Understanding what's stored in memory helps debug issues, optimize performance, and ensure data quality. It's also essential for user privacy compliance. |
| 174 | +```redis:[run_confirmation=false] Monitor user sessions |
| 175 | +// Scan 10,000 keys to find user sessions |
| 176 | +SCAN 0 MATCH user:*:history:* COUNT 10000 |
124 | 177 |
|
125 |
| -```redis:[run_confirmation=false] Check memory state |
126 |
| -// Check active session memory |
127 |
| -HGETALL user:alice:knowledge:pref:001 // Example for one preference item |
128 |
| -
|
129 |
| -// View user knowledge |
130 |
| -HGETALL user:alice:knowledge:pref:001 |
131 |
| -
|
132 |
| -// Search user's memories |
133 |
| -FT.SEARCH idx:kbmemory |
134 |
| - "@user_id:{alice}" |
135 |
| - RETURN 3 content memory_type importance |
| 178 | +// Get session statistics |
| 179 | +LLEN user:alice:history:session_001 |
| 180 | +TTL user:alice:history:session_001 |
136 | 181 | ```
|
137 |
| - |
138 | 182 | ### Data Cleanup
|
139 |
| - |
140 |
| -For privacy compliance, delete all user-related keys. |
141 |
| - |
142 |
| -```redis:[run_confirmation=true] Complete user removal |
| 183 | +```redis:[run_confirmation=true] Delete user data |
| 184 | +// Remove all user data (GDPR compliance) |
| 185 | +DEL user:alice:history:session_001 |
| 186 | +DEL user:alice:history:session_002 |
| 187 | +DEL user:alice:rate_limit |
143 | 188 | DEL user:alice:knowledge:pref:001
|
144 | 189 | DEL user:alice:knowledge:pref:002
|
| 190 | +DEL user:alice:knowledge:pref:003 |
145 | 191 | DEL user:alice:knowledge:personal:001
|
146 | 192 | DEL user:alice:knowledge:personal:002
|
| 193 | +DEL user:alice:knowledge:personal:003 |
147 | 194 | DEL user:alice:knowledge:work:001
|
148 | 195 | DEL user:alice:knowledge:work:002
|
149 |
| -DEL vmemory:alice:001 |
150 |
| -DEL vmemory:alice:kb:001 |
| 196 | +DEL user:alice:knowledge:work:003 |
151 | 197 | ```
|
152 | 198 |
|
153 | 199 | ### Next Steps
|
|
0 commit comments