Retrievers Avancés
Améliorez la qualité de récupération avec l'expansion multi-requêtes, la compression contextuelle et les méthodes d'ensemble.
Pourquoi des Retrievers Avancés ?
La recherche vectorielle basique peut manquer des documents pertinents en raison de la formulation de la requête ou des écarts sémantiques. Les retrievers avancés améliorent le rappel et la précision grâce à l'expansion de requêtes, la compression de résultats et les techniques de fusion.
# MultiQueryRetriever
Génère plusieurs requêtes alternatives en utilisant un LLM, récupère les résultats pour chacune et déduplique pour améliorer le rappel.
import { MultiQueryRetriever } from 'orkajs/retrievers/multi-query';import { createOrka, OpenAIAdapter, PineconeAdapter } from 'orkajs'; const orka = createOrka({ llm: new OpenAIAdapter({ apiKey: process.env.OPENAI_API_KEY! }), vectorDB: new PineconeAdapter({ /* config */ })}); const retriever = new MultiQueryRetriever({ llm: orka.getLLM(), vectorDB: orka.knowledge['vectorDB'], queryCount: 3, // Generate 3 alternative queries topK: 5, // Return top 5 results per query deduplicateByContent: true // Remove duplicate results}); const results = await retriever.retrieve( 'How do I configure Orka AI?', 'my-knowledge-base'); // Returns deduplicated results from all query variations🎯 Comment Ça Marche
- Requête originale : "Comment configurer Orka AI ?"
- Le LLM génère des alternatives :
- "Quelles sont les options de configuration pour Orka AI ?"
- "Comment configurer les paramètres d'Orka AI ?"
- "Guide de configuration Orka AI"
- Recherche avec toutes les requêtes
- Déduplique et classe les résultats
# ContextualCompressionRetriever
Récupère plus de documents que nécessaire, puis utilise un LLM pour extraire uniquement les parties pertinentes, améliorant la précision et réduisant la taille du contexte.
import { ContextualCompressionRetriever } from 'orkajs/retrievers/compression'; const retriever = new ContextualCompressionRetriever({ llm: orka.getLLM(), vectorDB: orka.knowledge['vectorDB'], topK: 10, // Retrieve 10 documents maxCompressedLength: 500 // Compress each to ~500 chars}); const results = await retriever.retrieve( 'What are the benefits of RAG?', 'my-knowledge-base'); // Each result contains only the relevant extract, not the full document❌ Sans Compression
Retourne des documents complets (1000+ caractères chacun) avec des sections non pertinentes, gaspillant la fenêtre de contexte.
✅ Avec Compression
Retourne uniquement les extraits pertinents (200-500 caractères), maximisant l'efficacité et la pertinence du contexte.
# EnsembleRetriever
Combine plusieurs retrievers en utilisant Reciprocal Rank Fusion (RRF) pour de meilleurs résultats. Utile pour combiner différentes stratégies de récupération.
import { EnsembleRetriever } from 'orkajs/retrievers/ensemble';import { VectorRetriever } from 'orkajs/retrievers/vector';import { MultiQueryRetriever } from 'orkajs/retrievers/multi-query'; // Create individual retrieversconst vectorRetriever = new VectorRetriever({ llm: orka.getLLM(), vectorDB: orka.knowledge['vectorDB'], topK: 10}); const multiQueryRetriever = new MultiQueryRetriever({ llm: orka.getLLM(), vectorDB: orka.knowledge['vectorDB'], queryCount: 3, topK: 10}); // Combine with weighted fusionconst ensemble = new EnsembleRetriever({ retrievers: [vectorRetriever, multiQueryRetriever], weights: [0.4, 0.6], // 40% vector, 60% multi-query topK: 5 // Return top 5 fused results}); const results = await ensemble.retrieve( 'Explain RAG architecture', 'my-knowledge-base');🔬 Reciprocal Rank Fusion (RRF)
RRF combine les classements de plusieurs sources en donnant des scores plus élevés aux documents qui apparaissent en haut de plusieurs retrievers.
// Formula: score = weight * (1 / (rank + 60))// Document at rank 1 in Retriever A: 0.4 * (1/61) = 0.0066// Same document at rank 3 in Retriever B: 0.6 * (1/63) = 0.0095// Final fusion score: 0.0066 + 0.0095 = 0.0161# VectorRetriever
Wrapper de recherche vectorielle basique qui implémente l'interface Retriever. Utile comme bloc de construction pour les retrievers d'ensemble.
import { VectorRetriever } from 'orkajs/retrievers/vector'; const retriever = new VectorRetriever({ llm: orka.getLLM(), vectorDB: orka.knowledge['vectorDB'], topK: 5, minScore: 0.7 // Filter results below 0.7 similarity}); const results = await retriever.retrieve( 'What is RAG?', 'my-knowledge-base');# ParentDocumentRetriever
Recherche sur de petits chunks enfants pour la précision, puis retourne le document parent complet pour le contexte. Cela résout le compromis classique : les petits chunks sont meilleurs pour la précision de recherche, mais les grands chunks fournissent plus de contexte au LLM.
import { ParentDocumentRetriever } from 'orkajs/retrievers/parent-document'; const retriever = new ParentDocumentRetriever({ llm: orka.getLLM(), vectorDB: orka.knowledge['vectorDB'], childTopK: 10, // Search top 10 child chunks parentTopK: 3, // Return top 3 parent documents minScore: 0.6}); const results = await retriever.retrieve( 'How does authentication work?', 'documentation'); // Returns full parent documents, ranked by best child chunk score// Each result includes metadata: { childCount, parentContent, ... }📐 Comment Ça Marche
- Indexez les documents en petits chunks avec parentId dans les métadonnées
- La recherche trouve les chunks enfants les plus pertinents
- Regroupe les enfants par document parent
- Retourne le contenu parent complet, classé par meilleur score enfant
# SelfQueryRetriever
Utilise un LLM pour extraire automatiquement des filtres de métadonnées à partir de requêtes en langage naturel. Au lieu d'une simple recherche sémantique, il combine la recherche basée sur le sens avec le filtrage structuré de métadonnées pour des résultats plus précis.
import { SelfQueryRetriever } from 'orkajs/retrievers/self-query'; const retriever = new SelfQueryRetriever({ llm: orka.getLLM(), vectorDB: orka.knowledge['vectorDB'], topK: 5, metadataFields: [ { name: 'category', type: 'string', description: 'The document category', enumValues: ['tutorial', 'api-reference', 'guide', 'changelog'] }, { name: 'language', type: 'string', description: 'Programming language', enumValues: ['typescript', 'python', 'javascript'] }, { name: 'version', type: 'number', description: 'The version number of the documentation' } ]}); // Natural language query with implicit filtersconst results = await retriever.retrieve( 'Show me TypeScript tutorials about authentication in version 3', 'documentation'); // The LLM extracts:// semanticQuery: "authentication"// filter: { language: "typescript", category: "tutorial", version: 3 }🧠 Exemple de Décomposition de Requête
Le LLM sépare automatiquement le sens sémantique des filtres structurés :
// User query: "Find Python guides about deployment from 2024"// LLM extracts:{ "semanticQuery": "deployment", "filter": { "language": "python", "category": "guide" }}# BM25Retriever
Un retriever basé sur les mots-clés utilisant l'algorithme BM25 (Best Matching 25). Contrairement à la recherche vectorielle qui repose sur la similarité sémantique, BM25 utilise la fréquence des termes et la fréquence inverse des documents pour la correspondance exacte de mots-clés. Parfait pour combiner avec la recherche vectorielle dans un EnsembleRetriever.
import { BM25Retriever } from 'orkajs/retrievers/bm25'; const bm25 = new BM25Retriever({ documents: [ { id: '1', content: 'TypeScript is a typed superset of JavaScript...', metadata: { source: 'docs' } }, { id: '2', content: 'React hooks allow you to use state in functional components...', metadata: { source: 'blog' } }, { id: '3', content: 'Node.js is a JavaScript runtime built on Chrome V8...', metadata: { source: 'docs' } }, ], topK: 5, k1: 1.5, // Term frequency saturation (default: 1.5) b: 0.75 // Document length normalization (default: 0.75)}); const results = await bm25.retrieve('JavaScript runtime', 'any');// Finds documents with exact keyword matches for "JavaScript" and "runtime" // Add more documents dynamicallybm25.addDocuments([ { id: '4', content: 'Deno is a modern JavaScript/TypeScript runtime...' }]);# BM25 + Recherche Vectorielle (Hybride)
La stratégie de récupération la plus puissante combine BM25 (correspondance de mots-clés) avec la recherche vectorielle (compréhension sémantique) via l'EnsembleRetriever :
import { EnsembleRetriever } from 'orkajs/retrievers/ensemble';import { VectorRetriever } from 'orkajs/retrievers/vector';import { BM25Retriever } from 'orkajs/retrievers/bm25'; // Keyword-based retrievalconst bm25 = new BM25Retriever({ documents: myDocuments, topK: 10}); // Semantic retrievalconst vector = new VectorRetriever({ llm: orka.getLLM(), vectorDB: orka.knowledge['vectorDB'], topK: 10}); // Hybrid: combine both with Reciprocal Rank Fusionconst hybrid = new EnsembleRetriever({ retrievers: [bm25, vector], weights: [0.3, 0.7], // 30% keyword, 70% semantic topK: 5}); const results = await hybrid.retrieve('authentication middleware', 'docs');// Finds docs matching keywords AND semantically similar contentComparaison
| Retriever | Améliore | Compromis |
|---|---|---|
| MultiQuery | Rappel (trouve plus de docs pertinents) | Appels LLM supplémentaires |
| Compression | Précision (élimine le bruit) | Appels LLM pour compression |
| Ensemble | Rappel et précision | Plusieurs passes de récupération |
| Vector | Vitesse (passe unique) | Peut manquer des variations |
| ParentDocument | Contexte (doc complet avec recherche précise) | Nécessite métadonnée parentId |
| SelfQuery | Filtrage (structuré + sémantique) | Appel LLM pour parsing de requête |
| BM25 | Correspondance de mots-clés (termes exacts) | Pas de compréhension sémantique |
Exemple Complet
import { createOrka, OpenAIAdapter, PineconeAdapter } from 'orkajs';import { MultiQueryRetriever } from 'orkajs/retrievers/multi-query';import { ContextualCompressionRetriever } from 'orkajs/retrievers/compression';import { EnsembleRetriever } from 'orkajs/retrievers/ensemble'; const orka = createOrka({ llm: new OpenAIAdapter({ apiKey: process.env.OPENAI_API_KEY! }), vectorDB: new PineconeAdapter({ /* config */ })}); // Strategy 1: Multi-query for better recallconst multiQuery = new MultiQueryRetriever({ llm: orka.getLLM(), vectorDB: orka.knowledge['vectorDB'], queryCount: 3, topK: 10}); // Strategy 2: Compression for better precisionconst compression = new ContextualCompressionRetriever({ llm: orka.getLLM(), vectorDB: orka.knowledge['vectorDB'], topK: 15, maxCompressedLength: 400}); // Combine both strategiesconst ensemble = new EnsembleRetriever({ retrievers: [multiQuery, compression], weights: [0.5, 0.5], topK: 5}); // Retrieve with best of both worldsconst results = await ensemble.retrieve( 'How does RAG improve LLM responses?', 'documentation'); console.log(`Found ${results.length} highly relevant results`);results.forEach(r => { console.log(`Score: ${r.score.toFixed(3)}`); console.log(`Content: ${r.content?.slice(0, 100)}...`);});Bonnes Pratiques
1. Commencez Simple
Commencez avec VectorRetriever. Ajoutez MultiQuery si le rappel est faible. Ajoutez Compression si la précision est faible.
2. Surveillez les Coûts
MultiQuery et Compression font des appels LLM supplémentaires. Utilisez le cache ou limitez le nombre de requêtes en production.
3. Ajustez les Poids
Expérimentez avec les poids d'ensemble selon votre cas d'usage. Poids plus élevé = plus d'influence sur le classement final.
Imports Tree-shakeable
// ✅ Import only what you needimport { MultiQueryRetriever } from 'orkajs/retrievers/multi-query';import { EnsembleRetriever } from 'orkajs/retrievers/ensemble'; // ✅ Or import from indeximport { MultiQueryRetriever, ContextualCompressionRetriever } from 'orkajs/retrievers';