|
| 1 | +This tutorial demonstrates how to build an AI assistant's memory system with Redis as its memory core. |
| 2 | + |
| 3 | +**Note**: Requires [Redis 8](https://hub.docker.com/_/redis/tags) for `HSETEX`, which adds per-field TTL for hashes - ideal for rate limiting to ensure fair resource usage. |
| 4 | + |
| 5 | +### Architecture Overview |
| 6 | +| Layer | Description | Data type | |
| 7 | +| ---------- | ---------- | ---------- | |
| 8 | +| `Session History`| `Recent conversation context` | List | |
| 9 | +| `Rate Limiting` | `Per-user request throttling` | Hash | |
| 10 | +| `User Memory` | `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. Redis lists are simple, ordered, and efficient for storing chat transcripts. |
| 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 is the weather like?", "timestamp": 1717935001}' |
| 18 | +
|
| 19 | +// Add AI response |
| 20 | +LPUSH user:alice:history:session_001 '{"type": "ai", "content": "It is 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 | +You can retrieve recent messages to provide context to the AI. |
| 30 | + |
| 31 | +```redis:[run_confirmation=no] Read conversation history |
| 32 | +// Get the 5 most recent messages |
| 33 | +LRANGE user:alice:history:session_001 0 4 |
| 34 | +
|
| 35 | +// Get the full session |
| 36 | +LRANGE user:alice:history:session_001 0 -1 |
| 37 | +``` |
| 38 | +You may want to limit the size of history to retain only the N most recent items. Use LTRIM: |
| 39 | +```redis:[run_confirmation=yes] Read conversation history |
| 40 | +LTRIM user:alice:history:session_001 0 29 // keep only latest 30 items |
| 41 | +``` |
| 42 | + |
| 43 | +### Session Expiration |
| 44 | +Without expiration, session history will accumulate indefinitely. Expiring keys improves memory usage and ensures privacy. |
| 45 | + |
| 46 | +```redis:[run_confirmation=true] Session expiration |
| 47 | +// Set session to expire in 24 hours |
| 48 | +EXPIRE user:alice:history:session_001 86400 |
| 49 | +
|
| 50 | +// Set session to expire in 1 hour |
| 51 | +EXPIRE user:alice:history:session_001 3600 |
| 52 | +
|
| 53 | +// Check remaining TTL |
| 54 | +TTL user:alice:history:session_001 |
| 55 | +
|
| 56 | +// Remove expiration (make persistent) |
| 57 | +PERSIST user:alice:history:session_001 |
| 58 | +``` |
| 59 | + |
| 60 | +### Rate Limiting |
| 61 | +Rate limiting prevents abuse and ensures fair usage across users. Redis hashes with field-level TTL via `HSETEX` are ideal for this. |
| 62 | + |
| 63 | +```redis:[run_confirmation=true] Initialize Rate Limiting |
| 64 | +// On first request - set counter with 1-minute TTL |
| 65 | +HSETEX user:alice:rate_limit EX 60 FIELDS 1 requests_per_minute 1 |
| 66 | +``` |
| 67 | + |
| 68 | +The `HINCR` command allows you to atomically increment the counter, preventing race conditions in high-concurrency scenarios. |
| 69 | + |
| 70 | +```redis:[run_confirmation=true] Increment Requests |
| 71 | +// Increment request counter |
| 72 | +HINCRBY user:alice:rate_limit requests_per_minute 1 |
| 73 | +
|
| 74 | +// Check if field exists and get count |
| 75 | +HEXISTS user:alice:rate_limit requests_per_minute |
| 76 | +HGET user:alice:rate_limit requests_per_minute |
| 77 | +
|
| 78 | +// Check TTL on the hash |
| 79 | +TTL user:alice:rate_limit |
| 80 | +``` |
| 81 | +**Optionally**: if the count exceeds the allowed threshold, deny the operation. |
| 82 | + |
| 83 | +Different time windows serve different purposes - per-minute prevents burst attacks, per-hour prevents sustained abuse, per-day enforces usage quotas. |
| 84 | +```redis:[run_confirmation=true] Rate Limiting with Different Time Windows |
| 85 | +// Set multiple rate limits with different TTLs |
| 86 | +HSETEX user:alice:rate_limit EX 60 FIELDS 2 requests_per_minute 1 requests_per_hour 1 |
| 87 | +
|
| 88 | +// Daily rate limit (24 hours) |
| 89 | +HSETEX user:alice:rate_limit EX 86400 FIELDS 1 requests_per_day 1 |
| 90 | +
|
| 91 | +// Check all rate limits |
| 92 | +HGETALL user:alice:rate_limit |
| 93 | +``` |
| 94 | + |
| 95 | +### User Memory (Persistent Preferences) |
| 96 | +AI assistants become more helpful when they remember user preferences, schedules, or relevant facts. This persistent memory enables personalization over time. |
| 97 | + |
| 98 | +```redis:[run_confirmation=true] Store User Preferences |
| 99 | +// Always secure sensitive data using encryption at rest, access control (Redis ACLs), and comply with data protection laws (e.g., GDPR). |
| 100 | +// These values are stored with embeddings to support semantic recall later using vector search. |
| 101 | +HSET user:alice: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" |
| 102 | +
|
| 103 | +// Storing communication preference |
| 104 | +HSET user:alice: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" |
| 105 | +
|
| 106 | +// Store work schedule preference |
| 107 | +HSET user:alice: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" |
| 108 | +
|
| 109 | +// Store health information |
| 110 | +HSET user:alice: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" |
| 111 | +
|
| 112 | +// Store pet information |
| 113 | +HSET user:alice: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" |
| 114 | +
|
| 115 | +// Store family information |
| 116 | +HSET user:alice: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" |
| 117 | +``` |
| 118 | + |
| 119 | +### Vector Search: Semantic Memory Recall |
| 120 | +Semantic search allows AI to retrieve relevant memory even when exact keywords don't match. For example, a query about "meetings" might return facts about "morning appointments." |
| 121 | + |
| 122 | +Indexing persistent memory (User Memory) for semantically meaningful search. |
| 123 | + |
| 124 | +```redis:[run_confirmation=true] Create a Vector Index |
| 125 | +// Create index for semantic search |
| 126 | +FT.CREATE idx:preferences |
| 127 | + ON HASH |
| 128 | + PREFIX 1 user: |
| 129 | + SCHEMA |
| 130 | + user_id TAG |
| 131 | + content TEXT |
| 132 | + importance NUMERIC |
| 133 | + timestamp NUMERIC |
| 134 | + embedding VECTOR HNSW 6 |
| 135 | + TYPE FLOAT32 |
| 136 | + DIM 8 // DIM 8 is only for demonstration purposes. Real embeddings are typically 128–1536 dimensions depending on the model (e.g., sentence-transformers). |
| 137 | + DISTANCE_METRIC COSINE |
| 138 | +``` |
| 139 | + |
| 140 | +### Search for most relevant memory entries |
| 141 | + |
| 142 | +```redis:[run_confirmation=false] Find Top 5 Most Relevant User Memory Items |
| 143 | +FT.SEARCH idx:preferences |
| 144 | + "(@user_id:{alice}) => [KNN 5 @embedding $vec AS score]" |
| 145 | + 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" |
| 146 | + RETURN 4 content importance score timestamp |
| 147 | + SORTBY score ASC |
| 148 | + DIALECT 2 |
| 149 | +``` |
| 150 | + |
| 151 | +```redis:[run_confirmation=false] Search High-Importance Items Only |
| 152 | +FT.SEARCH idx:preferences |
| 153 | + "(@user_id:{alice} @importance:[8 +inf]) => [KNN 3 @embedding $vec AS score]" |
| 154 | + 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" |
| 155 | + RETURN 4 content importance score timestamp |
| 156 | + SORTBY score ASC |
| 157 | + DIALECT 2 |
| 158 | +``` |
| 159 | +```redis:[run_confirmation=false] Search Recent User Memories |
| 160 | +FT.SEARCH idx:preferences |
| 161 | + "(@user_id:{alice} @timestamp:[1717935000 +inf]) => [KNN 5 @embedding $vec AS score]" |
| 162 | + 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" |
| 163 | + RETURN 3 content timestamp score |
| 164 | + SORTBY score ASC |
| 165 | + DIALECT 2 |
| 166 | +``` |
| 167 | + |
| 168 | +### Memory State Monitoring |
| 169 | +Understanding what's stored in memory helps debug issues, optimize performance, and ensure data quality. It's also essential for user privacy compliance. |
| 170 | +```redis:[run_confirmation=false] Monitor user sessions |
| 171 | +// Get approximate memory usage of session |
| 172 | +MEMORY USAGE user:alice:history:session_001 |
| 173 | +
|
| 174 | +// Get session statistics |
| 175 | +LLEN user:alice:history:session_001 |
| 176 | +TTL user:alice:history:session_001 |
| 177 | +``` |
| 178 | +### Data Cleanup |
| 179 | +Remove all data related to a user (e.g., for GDPR compliance). |
| 180 | + |
| 181 | +```redis:[run_confirmation=true] Delete user data |
| 182 | +// Remove all user data (GDPR compliance) |
| 183 | +DEL user:alice:history:session_001 |
| 184 | +DEL user:alice:history:session_002 |
| 185 | +DEL user:alice:rate_limit |
| 186 | +DEL user:alice:pref:001 |
| 187 | +DEL user:alice:pref:002 |
| 188 | +DEL user:alice:pref:003 |
| 189 | +DEL user:alice:personal:001 |
| 190 | +DEL user:alice:personal:002 |
| 191 | +DEL user:alice:personal:003 |
| 192 | +DEL user:alice:work:001 |
| 193 | +DEL user:alice:work:002 |
| 194 | +DEL user:alice:work:003 |
| 195 | +``` |
| 196 | + |
| 197 | +### Next Steps |
| 198 | +Now that your assistant has memory and meaning, you can: |
| 199 | + - Combine with RAG Pipelines |
| 200 | + - Use sentence-transformers to generate high-dimensional vectors |
| 201 | + - Add [Redis Flex](https://redis.io/solutions/flex/?utm_source=redisinsight&utm_medium=app&utm_campaign=tutorials) for fallback persistence |
| 202 | + - Use Redis ACLs to isolate users, enforce quotas, and monitor usage |
0 commit comments