|
| 1 | +**UnifiedKB Demos**. Demonstrations of the `UnifiedKB` API—a unified interface for managing multiple knowledge base backends with automatic result aggregation, supporting: |
| 2 | + |
| 3 | +- **In-memory stores** (RAMDataDB, RAMMessageDB) |
| 4 | +- **PostgreSQL + pgvector** (DataDB, MessageDB) |
| 5 | +- **Qdrant** vector database |
| 6 | +- **Onya** knowledge graphs |
| 7 | +- Any backend implementing the `KBBackend` protocol |
| 8 | + |
| 9 | +# Key Features |
| 10 | + |
| 11 | +- **Unified interface**: One API for all backends |
| 12 | +- **Automatic aggregation**: Results from multiple backends automatically merged and sorted |
| 13 | +- **Parallel execution**: Searches run concurrently across backends using asyncio |
| 14 | +- **Backend management**: Enable/disable backends without removing them |
| 15 | +- **Selective operations**: Target all backends or specific ones |
| 16 | +- **Weight-based scoring**: Prioritize results from certain backends |
| 17 | +- **Error isolation**: One backend failure doesn't break the entire operation |
| 18 | + |
| 19 | +# Demos |
| 20 | + |
| 21 | +## `simple_unified_demo.py` |
| 22 | + |
| 23 | +Complete introduction to UnifiedKB showing: |
| 24 | + |
| 25 | +1. Setting up multiple backend stores |
| 26 | +2. Registering backends with UnifiedKB |
| 27 | +3. Inserting documents across all backends |
| 28 | +4. Searching and aggregating results |
| 29 | +5. Searching specific backends only |
| 30 | +6. Managing backends (enable/disable) |
| 31 | +7. Selective insertion to specific backends |
| 32 | + |
| 33 | +**Requirements:** |
| 34 | +```bash |
| 35 | +uv pip install ogbujipt sentence-transformers |
| 36 | +``` |
| 37 | + |
| 38 | +**Run:** |
| 39 | +```bash |
| 40 | +python demo/unified-kb/simple_unified_demo.py |
| 41 | +``` |
| 42 | + |
| 43 | +**Expected output:** |
| 44 | +``` |
| 45 | +====================================================================== |
| 46 | +UnifiedKB Demo: Unified Knowledge Base API |
| 47 | +====================================================================== |
| 48 | +
|
| 49 | +[1] Loading embedding model... |
| 50 | + ✓ Model loaded: all-MiniLM-L6-v2 |
| 51 | +
|
| 52 | +[2] Setting up backend stores... |
| 53 | + ✓ Backend 1: General programming KB (in-memory) |
| 54 | + ✓ Backend 2: Language-specific KB (in-memory) |
| 55 | + ✓ Backend 3: Cache KB (in-memory) |
| 56 | +
|
| 57 | +[3] Creating UnifiedKB and registering backends... |
| 58 | + ✓ Registered 3 backends |
| 59 | +
|
| 60 | +[4] Backend information: |
| 61 | + • general: |
| 62 | + - Weight: 1.5 |
| 63 | + - Enabled: True |
| 64 | + - Scope: general |
| 65 | + ... |
| 66 | +``` |
| 67 | + |
| 68 | +# Usage Patterns |
| 69 | + |
| 70 | +## Basic Setup |
| 71 | + |
| 72 | +```python |
| 73 | +from ogbujipt.memory.unified import UnifiedKB |
| 74 | +from ogbujipt.store.ram import RAMDataDB |
| 75 | +from sentence_transformers import SentenceTransformer |
| 76 | + |
| 77 | +# Create backends |
| 78 | +model = SentenceTransformer('all-MiniLM-L6-v2') |
| 79 | +backend1 = RAMDataDB(embedding_model=model, collection_name='kb1') |
| 80 | +await backend1.setup() |
| 81 | + |
| 82 | +# Create unified KB |
| 83 | +kb = UnifiedKB() |
| 84 | +kb.add_backend('main', backend1, weight=1.5, |
| 85 | + metadata={'type': 'vector', 'scope': 'general'}) |
| 86 | +``` |
| 87 | + |
| 88 | +## Searching All Backends |
| 89 | + |
| 90 | +```python |
| 91 | +# Searches all enabled backends in parallel |
| 92 | +async for result in kb.search('machine learning', limit=10): |
| 93 | + print(f'{result.score:.3f} [{result.source}]: {result.content}') |
| 94 | +``` |
| 95 | + |
| 96 | +## Searching Specific Backends |
| 97 | + |
| 98 | +```python |
| 99 | +# Search only certain backends |
| 100 | +async for result in kb.search('query', backends=['main', 'cache']): |
| 101 | + print(result.content) |
| 102 | +``` |
| 103 | + |
| 104 | +## Inserting Content |
| 105 | + |
| 106 | +```python |
| 107 | +# Insert to all enabled backends (default) |
| 108 | +result_ids = await kb.insert( |
| 109 | + 'Python is great for ML', |
| 110 | + metadata={'topic': 'programming'} |
| 111 | +) |
| 112 | + |
| 113 | +# Insert to specific backends only |
| 114 | +result_ids = await kb.insert( |
| 115 | + 'Temporary data', |
| 116 | + backends=['cache'], |
| 117 | + metadata={'ttl': 3600} |
| 118 | +) |
| 119 | +``` |
| 120 | + |
| 121 | +## Managing Backends |
| 122 | + |
| 123 | +```python |
| 124 | +# List all backends |
| 125 | +for name, info in kb.list_backends().items(): |
| 126 | + print(f'{name}: weight={info["weight"]}, enabled={info["enabled"]}') |
| 127 | + |
| 128 | +# Temporarily disable a backend |
| 129 | +kb.disable_backend('slow_backend') |
| 130 | + |
| 131 | +# Re-enable it later |
| 132 | +kb.enable_backend('slow_backend') |
| 133 | + |
| 134 | +# Remove backend completely |
| 135 | +kb.remove_backend('old_backend') |
| 136 | +``` |
| 137 | + |
| 138 | +## Backend Weights |
| 139 | + |
| 140 | +Weights influence result scoring during aggregation: |
| 141 | + |
| 142 | +```python |
| 143 | +# Higher weight = results from this backend ranked higher |
| 144 | +kb.add_backend('primary', pg_store, weight=2.0) # Most important |
| 145 | +kb.add_backend('secondary', ram_store, weight=1.0) # Standard |
| 146 | +kb.add_backend('cache', cache_store, weight=0.5) # Less important |
| 147 | +``` |
| 148 | + |
| 149 | +# Design Philosophy |
| 150 | + |
| 151 | +## Composability over Monolith |
| 152 | + |
| 153 | +UnifiedKB is a thin orchestration layer that doesn't duplicate backend capabilities—it coordinates them. Each backend maintains its own: |
| 154 | +- Connection pools |
| 155 | +- Embedding models |
| 156 | +- Indexing strategies |
| 157 | +- Configuration |
| 158 | + |
| 159 | +## Explicit over Implicit |
| 160 | + |
| 161 | +No hidden magic: |
| 162 | +- Parallel execution is explicit (using asyncio.gather()) |
| 163 | +- Error handling is clear (one backend failure doesn't break others) |
| 164 | +- Backend selection is explicit (via `backends` parameter) |
| 165 | +- Scoring/aggregation is transparent (simple sort by score) |
| 166 | + |
| 167 | +## Protocol-based Design |
| 168 | + |
| 169 | +Any object implementing the `KBBackend` protocol works: |
| 170 | + |
| 171 | +```python |
| 172 | +class MyCustomBackend: |
| 173 | + async def search(self, query, limit=5, **kwargs): |
| 174 | + # Your implementation |
| 175 | + ... |
| 176 | + |
| 177 | + async def insert(self, content, metadata=None, **kwargs): |
| 178 | + # Your implementation |
| 179 | + ... |
| 180 | + |
| 181 | + async def delete(self, item_id, **kwargs): |
| 182 | + # Your implementation |
| 183 | + ... |
| 184 | +``` |
| 185 | + |
| 186 | +# Advanced Topics |
| 187 | + |
| 188 | +## Result Aggregation |
| 189 | + |
| 190 | +Currently, UnifiedKB uses simple score-based aggregation: |
| 191 | +1. Execute searches in parallel across all selected backends |
| 192 | +2. Collect all results |
| 193 | +3. Sort by score (descending) |
| 194 | +4. Return top N results |
| 195 | + |
| 196 | +**Coming soon:** |
| 197 | +- Score normalization across different backend types |
| 198 | +- Result deduplication (fuzzy matching) |
| 199 | +- Cross-encoder re-ranking |
| 200 | +- Query classification and intelligent routing |
| 201 | + |
| 202 | +## Error Handling |
| 203 | + |
| 204 | +Backend operations are isolated: |
| 205 | +- Search: Failed backends are logged but don't block others |
| 206 | +- Insert: Partial success is possible (some backends succeed, others fail) |
| 207 | +- Delete: Each backend's result tracked independently |
| 208 | + |
| 209 | +## Performance Considerations |
| 210 | + |
| 211 | +- **Parallel execution**: All backend operations run concurrently |
| 212 | +- **Streaming results**: Results yielded as they're aggregated (not all buffered) |
| 213 | +- **Timeout support**: Can be added per-backend if needed |
| 214 | +- **Connection pooling**: Handled by each backend independently |
| 215 | + |
| 216 | +# Next Steps |
| 217 | + |
| 218 | +After exploring this demo, check out: |
| 219 | +- `demo/ram-store/` - In-memory backend examples |
| 220 | +- `demo/pg-hybrid/` - PostgreSQL hybrid search examples |
| 221 | +- `demo/kgraph/` - Onya knowledge graph examples |
| 222 | +- `test/memory/test_unified.py` - Comprehensive unit tests |
| 223 | + |
| 224 | +# Roadmap |
| 225 | + |
| 226 | +Upcoming UnifiedKB features: |
| 227 | +- Query classification for intelligent routing |
| 228 | +- Score normalization strategies |
| 229 | +- Result deduplication (fuzzy matching) |
| 230 | +- Integration with search strategies (hybrid, sparse, dense) |
| 231 | +- MCP (Model Context Protocol) support |
| 232 | +- Advanced aggregation (RRF, cross-encoder reranking) |
0 commit comments