Skip to content

Commit f9d9e33

Browse files
committed
docs: add comprehensive memoization system documentation
- Document LLM memoization architecture with component diagrams - Detail MemoKey structure for content-addressed caching - Explain MemoStore implementation with LRU cache and TTL expiration - Provide usage examples for engine builder integration - Include performance characteristics and cost savings analysis - Cover cache invalidation strategies and monitoring capabilities - Document future improvements and implementation notes
1 parent f3d0323 commit f9d9e33

1 file changed

Lines changed: 314 additions & 0 deletions

File tree

docs/design/memo.md

Lines changed: 314 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,314 @@
1+
# LLM Memoization System
2+
3+
## Overview
4+
5+
The memoization system provides intelligent caching for expensive LLM operations, reducing API costs and latency while maintaining semantic correctness.
6+
7+
## Architecture
8+
9+
```
10+
┌─────────────────────────────────────────────────────────────────────┐
11+
│ Memoization Layer │
12+
├─────────────────────────────────────────────────────────────────────┤
13+
│ │
14+
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
15+
│ │ Engine │───▶│ Retriever │───▶│ LlmPilot │ │
16+
│ │ Builder │ │ Pipeline │ │ │ │
17+
│ └──────────────┘ └──────────────┘ └──────────────┘ │
18+
│ │ │ │ │
19+
│ └───────────────────┴───────────────────┘ │
20+
│ │ │
21+
│ ┌────────▼────────┐ │
22+
│ │ MemoStore │ │
23+
│ │ │ │
24+
│ │ ┌───────────┐ │ │
25+
│ │ │ LRU Cache │ │ │
26+
│ │ └───────────┘ │ │
27+
│ │ ┌───────────┐ │ │
28+
│ │ │ Stats │ │ │
29+
│ │ └───────────┘ │ │
30+
│ │ ┌───────────┐ │ │
31+
│ │ │ TTL │ │ │
32+
│ │ └───────────┘ │ │
33+
│ └─────────────────┘ │
34+
│ │
35+
└─────────────────────────────────────────────────────────────────────┘
36+
```
37+
38+
## Key Components
39+
40+
### MemoKey
41+
42+
Content-addressed cache key that ensures cache hits only occur when inputs are semantically identical.
43+
44+
```rust
45+
pub struct MemoKey {
46+
/// Type of operation (Summary, PilotDecision, QueryAnalysis, etc.)
47+
pub op_type: MemoOpType,
48+
49+
/// Fingerprint of the input content (BLAKE2b-128)
50+
pub input_fp: Fingerprint,
51+
52+
/// Model identifier for cache invalidation when model changes
53+
pub model_id: Option<String>,
54+
55+
/// Version for cache invalidation when algorithm changes
56+
pub version: u32,
57+
58+
/// Additional context fingerprint (e.g., navigation context for pilot)
59+
pub context_fp: Fingerprint,
60+
}
61+
```
62+
63+
### MemoStore
64+
65+
Thread-safe LRU cache with TTL expiration and optional disk persistence.
66+
67+
```rust
68+
pub struct MemoStore {
69+
cache: Arc<RwLock<LruCache<String, MemoEntry>>>,
70+
stats: Arc<AsyncRwLock<MemoStats>>,
71+
ttl: Duration,
72+
model_id: Option<String>,
73+
version: u32,
74+
}
75+
```
76+
77+
**Features:**
78+
- LRU eviction policy (default: 10,000 entries)
79+
- TTL-based expiration (default: 7 days)
80+
- Optional disk persistence (JSON format)
81+
- Thread-safe access via `parking_lot::RwLock`
82+
83+
### Integration Points
84+
85+
| Component | Operation Type | Description |
86+
|-----------|---------------|-------------|
87+
| `LlmSummaryGenerator` | `Summary` | Node summary generation |
88+
| `LlmPilot` | `PilotDecision` | Navigation decision caching |
89+
| Query Analyzer | `QueryAnalysis` | Query complexity/intent analysis |
90+
| Content Extractor | `Extraction` | Structured data extraction |
91+
92+
## Design Principles
93+
94+
### 1. Layered Architecture
95+
96+
Each layer can be independently configured and tested:
97+
98+
```
99+
Engine → PipelineRetriever → LlmPilot → MemoStore
100+
```
101+
102+
Benefits:
103+
- `MemoStore` can be reused by multiple components
104+
- Each layer has single responsibility
105+
- Easy to mock for testing
106+
107+
### 2. Non-Intrusive Integration
108+
109+
Memoization is optional and doesn't break existing APIs:
110+
111+
```rust
112+
// Without memoization (works as before)
113+
let pilot = LlmPilot::new(client, config);
114+
115+
// With memoization (opt-in)
116+
let pilot = LlmPilot::new(client, config)
117+
.with_memo_store(store);
118+
```
119+
120+
### 3. Smart Cache Key Design
121+
122+
Cache keys include semantic context for precise invalidation:
123+
124+
```rust
125+
// Key automatically invalidates when:
126+
// - Model changes (model_id field)
127+
// - Algorithm version changes (version field)
128+
// - Input content changes (input_fp field)
129+
// - Navigation context changes (context_fp field)
130+
```
131+
132+
### 4. Cost Tracking
133+
134+
The system tracks savings to quantify the value of caching:
135+
136+
```rust
137+
pub struct MemoStats {
138+
pub entries: usize,
139+
pub hits: u64,
140+
pub misses: u64,
141+
pub tokens_saved: u64,
142+
pub cost_saved: f64,
143+
}
144+
145+
impl MemoStats {
146+
pub fn hit_rate(&self) -> f64 {
147+
let total = self.hits + self.misses;
148+
if total == 0 { 0.0 } else { self.hits as f64 / total as f64 }
149+
}
150+
}
151+
```
152+
153+
### 5. Flexible Invalidation Strategies
154+
155+
```rust
156+
// Time-based (automatic)
157+
store.with_ttl(Duration::days(7))
158+
159+
// By operation type
160+
store.invalidate_by_op_type(MemoOpType::PilotDecision)
161+
162+
// By model prefix
163+
store.invalidate_by_model_prefix("gpt-4")
164+
165+
// Manual
166+
store.remove(&key)
167+
store.clear()
168+
```
169+
170+
## Usage Examples
171+
172+
### Basic Setup
173+
174+
```rust
175+
use vectorless::memo::MemoStore;
176+
use chrono::Duration;
177+
178+
// Create with custom settings
179+
let store = MemoStore::new()
180+
.with_ttl(Duration::days(7))
181+
.with_model("gpt-4o")
182+
.with_version(1);
183+
```
184+
185+
### With Engine Builder
186+
187+
```rust
188+
use vectorless::client::EngineBuilder;
189+
190+
// Option 1: Custom memo store
191+
let memo_store = MemoStore::new()
192+
.with_ttl(Duration::days(7))
193+
.with_model("gpt-4o");
194+
195+
let engine = EngineBuilder::new()
196+
.with_workspace("./data")
197+
.with_memo_store(memo_store)
198+
.with_openai(api_key)
199+
.build()
200+
.await?;
201+
202+
// Option 2: Default (auto-created with config model)
203+
let engine = EngineBuilder::new()
204+
.with_workspace("./data")
205+
.with_openai(api_key)
206+
.build()
207+
.await?;
208+
```
209+
210+
### Monitoring Cache Performance
211+
212+
```rust
213+
// Async stats (includes all metrics)
214+
let stats = store.stats().await;
215+
println!("Hit rate: {:.2}%", stats.hit_rate() * 100.0);
216+
println!("Tokens saved: {}", stats.tokens_saved);
217+
218+
// Sync snapshot (for monitoring without async)
219+
let stats = store.stats_snapshot();
220+
println!("Cache entries: {}", stats.entries);
221+
```
222+
223+
### Cache Invalidation
224+
225+
```rust
226+
// When switching models
227+
store.invalidate_by_model_prefix("gpt-3.5");
228+
229+
// When algorithm changes
230+
store.invalidate_by_op_type(MemoOpType::PilotDecision);
231+
232+
// Manual pruning of expired entries
233+
let removed = store.prune_expired();
234+
```
235+
236+
### Persistence
237+
238+
```rust
239+
// Save to disk
240+
store.save(Path::new("./cache/memo.json")).await?;
241+
242+
// Load from disk (on startup)
243+
store.load(Path::new("./cache/memo.json")).await?;
244+
```
245+
246+
## Performance Characteristics
247+
248+
### Concurrency
249+
250+
| Component | Lock Type | Rationale |
251+
|-----------|-----------|-----------|
252+
| LRU Cache | `parking_lot::RwLock` | High-performance, allows concurrent reads |
253+
| Statistics | `tokio::sync::RwLock` | Async-compatible for integration |
254+
| Atomic Stats | `AtomicU64` | Lock-free for hot paths |
255+
256+
### Memory
257+
258+
- Default capacity: 10,000 entries
259+
- Per-entry overhead: ~200-500 bytes (depending on cached value size)
260+
- Estimated memory: 2-5 MB at full capacity
261+
262+
### Latency
263+
264+
| Operation | Typical Latency |
265+
|-----------|-----------------|
266+
| Cache hit | < 1 µs |
267+
| Cache miss (no compute) | < 5 µs |
268+
| Cache miss (with LLM) | 100-2000 ms |
269+
270+
## Cost Savings Estimation
271+
272+
### Typical Document Retrieval Scenario
273+
274+
| Scenario | Without Cache | With Cache | Savings |
275+
|----------|---------------|------------|---------|
276+
| First query | 5-10 LLM calls | 5-10 LLM calls | 0% |
277+
| Repeated query | 5-10 LLM calls | 0-1 LLM calls | **80-100%** |
278+
| Similar query | 5-10 LLM calls | 2-3 LLM calls | **50-70%** |
279+
280+
### Token Savings Example
281+
282+
```rust
283+
// Assuming GPT-4 pricing: $0.03 / 1K input tokens, $0.06 / 1K output tokens
284+
// Average Pilot decision: 500 input tokens, 100 output tokens
285+
286+
// Without cache (100 queries):
287+
// Cost = 100 * (500 * 0.03/1000 + 100 * 0.06/1000) = $2.10
288+
289+
// With 80% hit rate:
290+
// Cost = 20 * $0.021 = $0.42
291+
// Savings = $1.68 (80%)
292+
```
293+
294+
## Future Improvements
295+
296+
### Potential Enhancements
297+
298+
1. **Semantic Cache Keys**: Use embedding similarity for fuzzy matching
299+
2. **Distributed Cache**: Share cache across multiple instances via Redis
300+
3. **Compression**: Compress cached values for large responses
301+
4. **Warm-up**: Pre-populate cache with common patterns
302+
5. **Analytics Dashboard**: Real-time visualization of cache performance
303+
304+
### Implementation Notes
305+
306+
- Consider using `AtomicU64` for all stats to eliminate async lock overhead
307+
- Cache `MemoKey::fingerprint()` result for frequently used keys
308+
- Add automatic periodic persistence with configurable interval
309+
310+
## Related Documentation
311+
312+
- [Fingerprint System](./fingerprint.md) - Content-addressed hashing
313+
- [Incremental Indexing](./incremental.md) - Change detection for reindexing
314+
- [Pilot Architecture](./pilot.md) - LLM-based navigation intelligence

0 commit comments

Comments
 (0)