OrkaJS
Orka.JS

Chaînes

Chaînes pré-construites pour les patterns LLM courants : question-réponse sur documents, récupération conversationnelle avec mémoire, et résumé multi-stratégie. Chaque chaîne orchestre la récupération, la construction de contexte et la génération en un seul appel.

Pourquoi des Chaînes ?

Construire un pipeline RAG from scratch nécessite plusieurs étapes : récupérer les documents, construire le contexte, formater les prompts, appeler le LLM et gérer les erreurs. Les chaînes encapsulent ces patterns dans des composants réutilisables et testés avec une observabilité intégrée via les étapes intermédiaires.

🔍

RetrievalQA

Q&R simple sur documents

💬

Conversational

Chat avec mémoire + récupération

📝

Summarization

Stuff, map-reduce, refine

📚

QA Chain

QA avancé avec stratégies

# RetrievalQAChain

La chaîne la plus simple pour le question-réponse sur documents. Elle récupère les documents pertinents, construit une fenêtre de contexte et génère une réponse en un seul appel. Parfaite pour les requêtes simples sur une base de connaissances.

import { RetrievalQAChain } from 'orkajs/chains/retrieval-qa';
import { VectorRetriever } from 'orkajs/retrievers/vector';
import { OpenAIAdapter } from 'orkajs/adapters/openai';
 
const llm = new OpenAIAdapter({ apiKey: process.env.OPENAI_API_KEY! });
 
const retriever = new VectorRetriever({
llm,
vectorDB: myVectorDB,
topK: 5
});
 
const chain = new RetrievalQAChain({
llm,
retriever,
collection: 'documentation',
returnSources: true, // Include source documents in result
maxSourceTokens: 3000, // Max tokens for context window
systemPrompt: 'You are a helpful assistant. Answer based ONLY on the provided context.'
});
 
const result = await chain.call('How do I configure authentication?');
 
console.log(result.answer);
// "To configure authentication, you need to..."
 
console.log(result.sources);
// [{ id: 'doc-1', score: 0.92, content: '...' }, ...]
 
console.log(result.intermediateSteps);
// [
// { name: 'retrieve', input: '...', output: 'Found 5 relevant documents', latencyMs: 120 },
// { name: 'generate', input: '...', output: '...', latencyMs: 850 }
// ]
 
console.log(result.usage);
// { promptTokens: 1200, completionTokens: 350, totalTokens: 1550 }

📊 Étapes Intermédiaires

Chaque chaîne retourne intermediateSteps avec le nom, l'entrée, la sortie et la latence de chaque étape. Cela vous donne une observabilité complète de l'exécution de la chaîne — utile pour le débogage, le monitoring et l'optimisation.

# ConversationalRetrievalChain

Étend RetrievalQA avec la mémoire conversationnelle. Elle condense automatiquement les questions de suivi en utilisant l'historique du chat, pour que le retriever reçoive une requête autonome même quand l'utilisateur demande "Et le deuxième ?" ou "Peux-tu expliquer plus ?". La chaîne maintient son propre historique de chat en interne.

import { ConversationalRetrievalChain } from 'orkajs/chains/conversational-retrieval';
 
const chain = new ConversationalRetrievalChain({
llm,
retriever,
collection: 'documentation',
returnSources: true,
maxHistoryLength: 10, // Keep last 10 messages in context
// Optional: customize how follow-up questions are condensed
condenseQuestionPrompt: `Given the following conversation and a follow-up question,
rephrase the follow-up as a standalone question.
 
Chat History:
{{history}}
 
Follow-up: {{question}}
 
Standalone Question:`
});
 
// First question — normal retrieval
const result1 = await chain.call('What authentication methods are supported?');
console.log(result1.answer);
// "Orka AI supports JWT, OAuth2, and API key authentication..."
 
// Follow-up — automatically condensed with history
const result2 = await chain.call('How do I configure the second one?');
// Internally condensed to: "How do I configure OAuth2 authentication?"
console.log(result2.answer);
// "To configure OAuth2, you need to set up..."
 
// Another follow-up
const result3 = await chain.call('What are the required scopes?');
// Condensed to: "What are the required OAuth2 scopes?"
console.log(result3.answer);
 
// Check intermediate steps to see the condensed question
console.log(result2.intermediateSteps);
// [
// { name: 'condense_question', input: 'How do I configure the second one?',
// output: 'How do I configure OAuth2 authentication?' },
// { name: 'retrieve', ... },
// { name: 'generate', ... }
// ]
 
// Manage history
console.log(chain.getChatHistory());
// [{ role: 'user', content: '...' }, { role: 'assistant', content: '...' }, ...]
 
chain.clearHistory(); // Reset conversation

🔄 Flux de Condensation de Question

  1. L'utilisateur demande: "Comment configurer le deuxième ?"
  2. Le LLM condense: Utilise l'historique pour produire "Comment configurer OAuth2 ?"
  3. Le retriever cherche: Avec la question autonome
  4. Le LLM répond: En utilisant le contexte récupéré + l'historique complet

# SummarizationChain

Résume un ou plusieurs documents en utilisant trois stratégies différentes, chacune avec des compromis différents pour la qualité, la vitesse et le coût.

📦 Stuff

Concatène tous les documents en un seul prompt. Le plus rapide, le moins cher, mais limité par la fenêtre de contexte.

Idéal pour : docs courts

🗺️ Map-Reduce

Résume chaque chunk indépendamment (map), puis combine tous les résumés (reduce). Gère une taille de document illimitée.

Idéal pour : gros docs

🔄 Refine

Commence avec le premier chunk, puis affine itérativement le résumé avec chaque chunk suivant. Meilleure qualité mais séquentiel.

Idéal pour : qualité

- Stratégie Stuff

import { SummarizationChain } from 'orkajs/chains/summarization';
 
// Stuff: all documents in one prompt
const stuffChain = new SummarizationChain({
llm,
strategy: 'stuff',
systemPrompt: 'Provide a clear and concise summary.'
});
 
const result = await stuffChain.call([
'Document 1 content here...',
'Document 2 content here...',
'Document 3 content here...'
]);
 
console.log(result.answer); // Combined summary
console.log(result.usage); // Token usage (1 LLM call)

- Stratégie Map-Reduce

// Map-Reduce: summarize chunks independently, then combine
const mapReduceChain = new SummarizationChain({
llm,
strategy: 'map-reduce',
maxChunkSize: 3000, // Split large docs into 3000-char chunks
combinePrompt: 'Combine the following summaries into a single coherent summary:\n\n{{summaries}}\n\nCombined Summary:'
});
 
const result = await mapReduceChain.call([
veryLongDocument1, // 50,000 chars
veryLongDocument2 // 30,000 chars
]);
 
console.log(result.answer);
console.log(result.intermediateSteps);
// [
// { name: 'map_chunk_0', ... }, // Summary of chunk 1
// { name: 'map_chunk_1', ... }, // Summary of chunk 2
// ...
// { name: 'reduce_combine', ... } // Final combined summary
// ]

- Stratégie Refine

// Refine: iteratively improve summary with each chunk
const refineChain = new SummarizationChain({
llm,
strategy: 'refine',
maxChunkSize: 3000,
refinePrompt: `Here is an existing summary:
{{existingSummary}}
 
Refine this summary with the following additional context:
{{context}}
 
Refined Summary:`
});
 
const result = await refineChain.call([longDocument]);
 
console.log(result.answer);
console.log(result.intermediateSteps);
// [
// { name: 'initial_summary', ... }, // First chunk summary
// { name: 'refine_1', ... }, // Refined with chunk 2
// { name: 'refine_2', ... }, // Refined with chunk 3
// ...
// ]

# QAChain — Question-Réponse Avancé

QAChain combine la récupération avec le question-réponse multi-stratégie. Contrairement à RetrievalQAChain qui utilise toujours l'approche "stuff", QAChain supporte les stratégies stuff, map-reduce et refine pour répondre aux questions sur de grands ensembles de documents.

import { QAChain } from 'orkajs/chains/qa';
 
// Stuff strategy (default) — fast, for small document sets
const stuffQA = new QAChain({
llm,
retriever,
collection: 'docs',
strategy: 'stuff',
returnSources: true
});
 
const result1 = await stuffQA.call('What is the rate limit?');
 
// Map-Reduce strategy — for many retrieved documents
const mapReduceQA = new QAChain({
llm,
retriever,
collection: 'docs',
strategy: 'map-reduce',
returnSources: true,
systemPrompt: 'Answer precisely based on the documents. Cite sources when possible.'
});
 
const result2 = await mapReduceQA.call('Compare all authentication methods');
// Map: extracts relevant info from each document
// Reduce: combines into a comprehensive answer
 
// Refine strategy — best quality for complex questions
const refineQA = new QAChain({
llm,
retriever,
collection: 'docs',
strategy: 'refine',
returnSources: true
});
 
const result3 = await refineQA.call('Explain the complete deployment process step by step');
// Starts with answer from first doc, refines with each additional doc

Exemple Complet — Pipeline RAG Production

import { createOrka } from 'orkajs/core';
import { OpenAIAdapter } from 'orkajs/adapters/openai';
import { EnsembleRetriever, VectorRetriever, BM25Retriever } from 'orkajs/retrievers';
import { ConversationalRetrievalChain, SummarizationChain } from 'orkajs/chains';
import { MemoryCache } from 'orkajs/cache/memory';
import { CachedLLM } from 'orkajs/cache/llm';
 
// Setup with caching
const baseLLM = new OpenAIAdapter({ apiKey: process.env.OPENAI_API_KEY! });
const llm = new CachedLLM(baseLLM, new MemoryCache({ maxSize: 500 }));
 
const orka = createOrka({ llm, vectorDB: myVectorDB });
 
// Hybrid retriever (BM25 + Vector)
const hybrid = new EnsembleRetriever({
retrievers: [
new BM25Retriever({ documents: myDocs, topK: 10 }),
new VectorRetriever({ llm, vectorDB: myVectorDB, topK: 10 })
],
weights: [0.3, 0.7],
topK: 5
});
 
// Conversational chain with hybrid retrieval
const chatChain = new ConversationalRetrievalChain({
llm,
retriever: hybrid,
collection: 'documentation',
returnSources: true,
maxHistoryLength: 10
});
 
// Handle user conversation
async function handleMessage(userMessage: string) {
const result = await chatChain.call(userMessage);
 
return {
answer: result.answer,
sources: result.sources?.map(s => ({
id: s.id,
score: s.score,
preview: s.content?.slice(0, 100)
})),
steps: result.intermediateSteps
};
}
 
// Summarize long documents
const summarizer = new SummarizationChain({
llm,
strategy: 'map-reduce',
maxChunkSize: 3000
});
 
async function summarizeDocuments(texts: string[]) {
const result = await summarizer.call(texts);
return result.answer;
}

Comparaison

ChaîneCas d'UsageMémoireStratégies
RetrievalQAQ&R simple sur docsstuff
ConversationalChat avec suivistuff
SummarizationRésumé de documentsstuff, map-reduce, refine
QAChainQ&R avancé avec stratégiesstuff, map-reduce, refine

Comparaison des Stratégies de Résumé

StratégieAppels LLMQualitéTaille Max Doc
stuff1⭐⭐⭐Limite fenêtre contexte
map-reduceN + 1⭐⭐Illimité
refineN⭐⭐⭐⭐Illimité

💡 Bonnes Pratiques

1. Commencez avec RetrievalQA

Pour la plupart des cas d'usage, RetrievalQAChain suffit. Passez à ConversationalRetrievalChain uniquement quand vous avez besoin de conversations multi-tours.

2. Utilisez la Récupération Hybride

Combinez BM25 + VectorRetriever dans un EnsembleRetriever pour les meilleurs résultats. La correspondance de mots-clés capture les termes exacts que la recherche sémantique pourrait manquer.

3. Surveillez les Étapes Intermédiaires

Utilisez intermediateSteps pour déboguer la qualité de récupération, identifier les étapes lentes et optimiser votre pipeline.

4. Choisissez la Bonne Stratégie de Résumé

Utilisez 'stuff' pour les documents courts (<4K tokens), 'map-reduce' pour le traitement parallèle de gros docs, et 'refine' quand la qualité du résumé est critique.

Imports Tree-shakeable

// ✅ Import only what you need
import { RetrievalQAChain } from 'orkajs/chains/retrieval-qa';
import { ConversationalRetrievalChain } from 'orkajs/chains/conversational-retrieval';
import { SummarizationChain } from 'orkajs/chains/summarization';
import { QAChain } from 'orkajs/chains/qa';
 
// ✅ Or import from index
import { RetrievalQAChain, ConversationalRetrievalChain, SummarizationChain, QAChain } from 'orkajs/chains';