OrkaJS
Orka.JS

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

  1. Requête originale : "Comment configurer Orka AI ?"
  2. 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"
  3. Recherche avec toutes les requêtes
  4. 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 retrievers
const 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 fusion
const 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

  1. Indexez les documents en petits chunks avec parentId dans les métadonnées
  2. La recherche trouve les chunks enfants les plus pertinents
  3. Regroupe les enfants par document parent
  4. 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 filters
const 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 dynamically
bm25.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 retrieval
const bm25 = new BM25Retriever({
documents: myDocuments,
topK: 10
});
 
// Semantic retrieval
const vector = new VectorRetriever({
llm: orka.getLLM(),
vectorDB: orka.knowledge['vectorDB'],
topK: 10
});
 
// Hybrid: combine both with Reciprocal Rank Fusion
const 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 content

Comparaison

RetrieverAmélioreCompromis
MultiQueryRappel (trouve plus de docs pertinents)Appels LLM supplémentaires
CompressionPrécision (élimine le bruit)Appels LLM pour compression
EnsembleRappel et précisionPlusieurs passes de récupération
VectorVitesse (passe unique)Peut manquer des variations
ParentDocumentContexte (doc complet avec recherche précise)Nécessite métadonnée parentId
SelfQueryFiltrage (structuré + sémantique)Appel LLM pour parsing de requête
BM25Correspondance 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 recall
const multiQuery = new MultiQueryRetriever({
llm: orka.getLLM(),
vectorDB: orka.knowledge['vectorDB'],
queryCount: 3,
topK: 10
});
 
// Strategy 2: Compression for better precision
const compression = new ContextualCompressionRetriever({
llm: orka.getLLM(),
vectorDB: orka.knowledge['vectorDB'],
topK: 15,
maxCompressedLength: 400
});
 
// Combine both strategies
const ensemble = new EnsembleRetriever({
retrievers: [multiQuery, compression],
weights: [0.5, 0.5],
topK: 5
});
 
// Retrieve with best of both worlds
const 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 need
import { MultiQueryRetriever } from 'orkajs/retrievers/multi-query';
import { EnsembleRetriever } from 'orkajs/retrievers/ensemble';
 
// ✅ Or import from index
import { MultiQueryRetriever, ContextualCompressionRetriever } from 'orkajs/retrievers';