Commencer avec LangChain.js : Construire des applications LLM pratiques
Par Jake Morrison LangChain.js est un de ces outils. Il offre une manière structurée de créer des applications alimentées par de grands modèles de langage (LLMs) en utilisant JavaScript ou TypeScript. Si vous travaillez avec des LLMs dans l’écosystème JavaScript, comprendre LangChain.js est essentiel. Ce guide vous conduira à travers des étapes pratiques et concrètes pour commencer et construire de réelles applications.
Qu’est-ce que LangChain.js et pourquoi l’utiliser ?
LangChain.js est une bibliothèque conçue pour aider les développeurs à créer des applications alimentées par des LLMs. Elle ne remplace pas les LLMs eux-mêmes, mais fournit plutôt une couche d’abstraction et un ensemble d’outils pour enchaîner différents appels LLM, intégrer d’autres sources de données et gérer des interactions complexes.
Pensez-y de cette manière : appeler directement une API LLM est comme utiliser une requête de base de données brute. LangChain.js fournit un ORM (Object-Relational Mapper) pour les LLMs. Il gère des modèles courants tels que :
* **Chaînes :** Combinaison de plusieurs appels LLM ou autres étapes en une séquence.
* **Prompts :** Gestion et formatage des entrées pour les LLMs.
* **Agents :** Permettre aux LLMs de prendre des décisions et d’utiliser des outils.
* **Récupération :** Intégration de données externes (vos documents, bases de données) avec les LLMs.
* **Mémoire :** Donner aux LLMs un moyen de se souvenir des interactions passées.
Le principal avantage d’utiliser LangChain.js est l’augmentation de la productivité et de la maintenabilité. Vous écrivez moins de code répétitif, la logique de votre application devient plus claire, et il est plus facile de changer de fournisseur LLM ou d’ajouter de nouvelles fonctionnalités. Pour quiconque construit avec des LLMs en JavaScript, LangChain.js offre des avantages significatifs.
Configuration de votre projet LangChain.js
Avant d’écrire du code, préparons notre environnement. Vous aurez besoin de Node.js installé.
1. **Créez un nouveau répertoire de projet :**
`
mkdir langchain-js-project
cd langchain-js-project
`
2. **Initialisez un projet Node.js :**
`
npm init -y
`
3. **Installez LangChain.js et un fournisseur LLM :**
Nous commencerons avec OpenAI pour nos exemples, mais LangChain.js prend en charge beaucoup d’autres (Hugging Face, Google, etc.).
`
npm install langchain @openai/openai
`
Si vous préférez TypeScript :
`
npm install langchain @openai/openai typescript @types/node ts-node
npx tsc –init
`
Ensuite, mettez à jour `tsconfig.json` pour inclure `“moduleResolution”: “node”` et `“esModuleInterop”: true`.
4. **Configurez votre clé API :**
Vous aurez besoin d’une clé API OpenAI. Ne codez jamais les clés API en dur dans votre code. Utilisez des variables d’environnement.
Créez un fichier `.env` à la racine de votre projet :
`
OPENAI_API_KEY=YOUR_OPENAI_API_KEY
`
Pour charger les variables d’environnement, installez `dotenv` :
`
npm install dotenv
`
Ensuite, tout en haut de votre fichier de script principal (par exemple, `index.js` ou `src/index.ts`) :
`
require(‘dotenv’).config();
`
ou pour TypeScript/ESM :
`
import ‘dotenv/config’;
`
Maintenant, vous êtes prêt à commencer à construire avec LangChain.js.
Votre premier appel LLM avec LangChain.js
Faisons un appel simple à un LLM.
`
// index.js
require(‘dotenv’).config();
const { OpenAI } = require(‘langchain/llms/openai’);
async function simpleLLMCall() {
const model = new OpenAI({ temperature: 0.7 }); // la température contrôle le caractère aléatoire
const prompt = “Quelle est la capitale de la France ?”;
const result = await model.call(prompt);
console.log(result);
}
simpleLLMCall();
`
Pour exécuter ceci : `node index.js`.
Vous devriez voir “Paris.” ou une sortie similaire. Il s’agit de l’interaction la plus basique. LangChain.js enveloppe l’appel API OpenAI, le rendant cohérent avec d’autres fournisseurs LLM.
Travail avec les prompts : Modèles et variables
Incorporer des prompts directement dans votre code peut devenir désordonné. LangChain.js fournit `PromptTemplate` pour gérer les prompts de manière efficace.
`
// promptExample.js
require(‘dotenv’).config();
const { OpenAI } = require(‘langchain/llms/openai’);
const { PromptTemplate } = require(‘langchain/prompts’);
async function templatedPrompt() {
const model = new OpenAI({ temperature: 0.7 });
const template = “Quel est un bon nom pour une entreprise qui fabrique {product} ?”;
const prompt = new PromptTemplate({
template: template,
inputVariables: [“product”],
});
const formattedPrompt = await prompt.format({ product: “chaussettes colorées” });
console.log(“Formatted Prompt :”, formattedPrompt);
const result = await model.call(formattedPrompt);
console.log(“Résultat LLM :”, result);
}
templatedPrompt();
`
Exécutez ceci avec `node promptExample.js`.
Cela démontre comment `PromptTemplate` vous permet de définir des prompts avec des espaces réservés (`{product}`) que vous pouvez remplir dynamiquement. C’est essentiel pour construire des applications flexibles.
Chaînes : Connecter des appels LLM et de la logique
Les chaînes sont là où LangChain.js brille vraiment. Elles vous permettent de combiner plusieurs étapes, y compris les appels LLM, en un seul flux de travail cohérent.
Chaîne LLM simple
Le `LLMChain` est la chaîne la plus basique. Elle prend un `PromptTemplate` et un LLM, puis formatte le prompt et le passe au LLM.
`
// llmChainExample.js
require(‘dotenv’).config();
const { OpenAI } = require(‘langchain/llms/openai’);
const { PromptTemplate } = require(‘langchain/prompts’);
const { LLMChain } = require(‘langchain/chains’);
async function simpleLLMChain() {
const model = new OpenAI({ temperature: 0.7 });
const template = “Quelle est la meilleure {activity} à faire dans {city} ?”;
const prompt = new PromptTemplate({
template: template,
inputVariables: [“activity”, “city”],
});
const chain = new LLMChain({ llm: model, prompt: prompt });
const result = await chain.call({ activity: “nourriture”, city: “Rome” });
console.log(“Résultat de la Chaîne :”, result);
// L’objet résultat contiendra une propriété ‘text’ avec la sortie du LLM.
}
simpleLLMChain();
`
Exécutez avec `node llmChainExample.js`.
Remarquez comment `chain.call()` prend un objet avec les variables d’entrée, et il gère le formatage du prompt et l’appel au LLM. C’est une manière plus propre d’interagir avec votre LLM.
Chaînes séquentielles : Flux de travail en plusieurs étapes
Parfois, vous devez effectuer plusieurs appels LLM en séquence, où la sortie d’un appel devient l’entrée pour le suivant. C’est là que `SimpleSequentialChain` est utile.
`
// sequentialChainExample.js
require(‘dotenv’).config();
const { OpenAI } = require(‘langchain/llms/openai’);
const { PromptTemplate } = require(‘langchain/prompts’);
const { LLMChain, SimpleSequentialChain } = require(‘langchain/chains’);
async function multiStepChain() {
const model = new OpenAI({ temperature: 0.7 });
// Chaîne 1 : Générer un nom d’entreprise
const nameTemplate = “Quel est un bon nom pour une entreprise qui fabrique {product} ?”;
const namePrompt = new PromptTemplate({
template: nameTemplate,
inputVariables: [“product”],
});
const nameChain = new LLMChain({ llm: model, prompt: namePrompt });
// Chaîne 2 : Décrire l’entreprise
const descriptionTemplate = “Écrivez un court slogan marketing engageant pour une entreprise nommée {companyName}.”;
const descriptionPrompt = new PromptTemplate({
template: descriptionTemplate,
inputVariables: [“companyName”],
});
const descriptionChain = new LLMChain({ llm: model, prompt: descriptionPrompt });
// Combiner les deux dans une chaîne séquentielle
const overallChain = new SimpleSequentialChain({
chains: [nameChain, descriptionChain],
verbose: true, // Défini sur true pour voir les étapes intermédiaires
});
const result = await overallChain.run(“chaussures écologiques”);
console.log(“Résultat Global :”, result);
}
multiStepChain();
`
Exécutez avec `node sequentialChainExample.js`.
Le `SimpleSequentialChain` prend la sortie de `nameChain` et l’utilise automatiquement comme entrée (`companyName`) pour `descriptionChain`. Ce modèle est extrêmement puissant pour décomposer des tâches complexes. LangChain.js facilite cela.
Récupération Augmentée par Génération (RAG) : Utiliser vos propres données
Les LLMs sont puissants, mais leur connaissance est limitée à leurs données d’entraînement. Pour des applications nécessitant des informations actuelles ou des connaissances spécifiques à un domaine (les documents de votre entreprise, par exemple), vous devez augmenter la connaissance du LLM avec des données externes. Cela s’appelle la Récupération Augmentée par Génération (RAG).
L’idée principale de RAG est :
1. **Charger les données :** Obtenez vos documents (PDF, fichiers texte, pages web).
2. **Diviser les données :** Divisez de grands documents en morceaux plus petits et gérables.
3. **Intégrer les données :** Convertissez ces morceaux en représentations numériques (intégrations) en utilisant un modèle d’intégration.
4. **Stocker les données :** Stockez ces intégrations dans une base de données vectorielle.
5. **Récupérer :** Lorsqu’un utilisateur pose une question, intégrez la question, recherchez dans la base de données vectorielle des morceaux de documents similaires.
6. **Augmenter le prompt :** Ajoutez ces morceaux récupérés au prompt du LLM.
7. **Générer :** Le LLM utilise ce prompt augmentÉ pour générer une réponse plus informée.
LangChain.js fournit des composants pour chacune de ces étapes.
Étape 1 : Chargement des documents
LangChain.js dispose de plusieurs chargeurs de documents. Utilisons un `TextLoader`.
“`javascript
// ragExample.js – Partie 1 : Chargement des documents
require(‘dotenv’).config();
const { TextLoader } = require(‘langchain/document_loaders/fs/text’);
async function loadDocuments() {
// Créez un fichier texte fictif pour la démonstration
const fs = require(‘fs’);
fs.writeFileSync(‘my_document.txt’, `
Le rapide renard brun saute par-dessus le chien paresseux.
Ce document parle de divers animaux et de leurs comportements.
Les chiens sont connus pour leur loyauté et leur ludisme.
Les renards sont des créatures rusées et solitaires.
Les chats aiment dormir et chasser les souris.
`);
const loader = new TextLoader(“my_document.txt”);
const docs = await loader.load();
console.log(“Documents chargés :”, docs);
// Chaque doc aura pageContent et metadata
}
loadDocuments();
“`
Étape 2 : Division des documents
Les grands documents doivent être divisés en morceaux plus petits afin qu’ils s’insèrent dans la fenêtre de contexte du LLM et pour améliorer la pertinence des récupérations.
“`javascript
// ragExample.js – Partie 2 : Division
require(‘dotenv’).config();
const { TextLoader } = require(‘langchain/document_loaders/fs/text’);
const { RecursiveCharacterTextSplitter } = require(‘langchain/text_splitter’);
async function splitDocuments() {
// (Supposons que my_document.txt soit déjà créé à partir de la Partie 1)
const loader = new TextLoader(“my_document.txt”);
const docs = await loader.load();
const splitter = new RecursiveCharacterTextSplitter({
chunkSize: 100, // Max caractères par morceau
chunkOverlap: 20, // Chevauchement entre les morceaux pour maintenir le contexte
});
const splitDocs = await splitter.splitDocuments(docs);
console.log(“Documents divisés :”, splitDocs);
}
splitDocuments();
“`
Étape 3 & 4 : Intégration et stockage des documents (stockage vectoriel)
C’est ici que les intégrations et les bases de données vectorielles entrent en jeu. Nous allons utiliser des intégrations OpenAI et `HNSWLib` (un stockage vectoriel local en mémoire) pour la simplicité. En production, vous devriez utiliser une base de données vectorielle dédiée comme Pinecone, Chroma, Weaviate, etc.
“`javascript
// ragExample.js – Partie 3 : Intégrations et stockage vectoriel
require(‘dotenv’).config();
const { OpenAIEmbeddings } = require(‘langchain/embeddings/openai’);
const { HNSWLib } = require(‘langchain/vectorstores/hnswlib’);
const { RecursiveCharacterTextSplitter } = require(‘langchain/text_splitter’);
const { TextLoader } = require(‘langchain/document_loaders/fs/text’);
const { OpenAI } = require(‘langchain/llms/openai’);
const { RetrievalQAChain } = require(‘langchain/chains’);
async function ragApplication() {
// 1. Charger et diviser les documents
const fs = require(‘fs’);
fs.writeFileSync(‘my_document.txt’, `
Le rapide renard brun saute par-dessus le chien paresseux.
Ce document parle de divers animaux et de leurs comportements.
Les chiens sont connus pour leur loyauté et leur ludisme.
Les renards sont des créatures rusées et solitaires.
Les chats aiment dormir et chasser les souris.
Un groupe de chats s’appelle une clowder.
`);
const loader = new TextLoader(“my_document.txt”);
const rawDocs = await loader.load();
const splitter = new RecursiveCharacterTextSplitter({
chunkSize: 100,
chunkOverlap: 20,
});
const docs = await splitter.splitDocuments(rawDocs);
// 2. Créer des intégrations et stocker dans le stockage vectoriel
const embeddings = new OpenAIEmbeddings();
const vectorStore = await HNSWLib.fromDocuments(docs, embeddings);
// 3. Créer un récupérateur
const retriever = vectorStore.asRetriever();
// 4. Créer la chaîne QA de récupération
const model = new OpenAI({ temperature: 0 });
const chain = RetrievalQAChain.fromLLM(model, retriever);
// 5. Poser une question
const question = “Comment s’appelle un groupe de chats ?”;
const result = await chain.call({ query: question });
console.log(“Réponse RAG :”, result.text);
const anotherQuestion = “Parle-moi des renards.”;
const anotherResult = await chain.call({ query: anotherQuestion });
console.log(“Autre réponse RAG :”, anotherResult.text);
}
ragApplication();
“`
Exécutez avec `node ragExample.js`.
Cet exemple regroupe toutes les pièces du RAG. La `RetrievalQAChain` abstrait le processus de récupération des documents pertinents et de leur utilisation pour répondre à une question. C’est une manière pratique de créer des chatbots ou des systèmes de question-réponse sur vos données spécifiques en utilisant LangChain.js.
Agents : LLM utilisant des outils
Les agents permettent aux LLM de prendre des décisions sur les outils à utiliser et dans quel ordre, en fonction des entrées de l’utilisateur. Cela permet des interactions plus dynamiques et complexes. Les outils peuvent être n’importe quoi : un moteur de recherche, une calculatrice, un appel API à vos systèmes internes, ou même une autre chaîne LLM.
Créons un agent simple qui peut effectuer des calculs en utilisant un outil `Calculator`.
“`javascript
// agentExample.js
require(‘dotenv’).config();
const { OpenAI } = require(‘langchain/llms/openai’);
const { initializeAgentExecutorWithOptions } = require(‘langchain/agents’);
const { Calculator } = require(‘langchain/tools/calculator’);
async function runAgent() {
const model = new OpenAI({ temperature: 0 });
const tools = [new Calculator()];
// Initialiser l’exécutant d’agent
const executor = await initializeAgentExecutorWithOptions(tools, model, {
agentType: “openai-functions”, // Utiliser la fonction d’appel de fonctionnalités d’OpenAI
verbose: true, // Voir le processus de pensée de l’agent
});
console.log(“Appel de l’agent avec une question mathématique simple…”);
const result1 = await executor.call({ input: “Quelle est 123 plus 456 ?” });
console.log(“Résultat de l’agent 1 :”, result1.output);
console.log(“\nAppel de l’agent avec une question plus complexe…”);
const result2 = await executor.call({ input: “Quelle est la racine carrée de 625 multipliée par 3 ?” });
console.log(“Résultat de l’agent 2 :”, result2.output);
}
runAgent();
“`
Exécutez avec `node agentExample.js`.
Avec `verbose: true`, vous verrez les “pensées” de l’agent : il identifie qu’un calcul est nécessaire, utilise l’outil `Calculator`, puis fournit la réponse. C’est un modèle puissant pour construire des applications qui vont au-delà de la simple génération de texte. LangChain.js simplifie la configuration de ces agents.
Mémoire : Maintenir le contexte de conversation
Par défaut, les LLM sont sans état. Chaque appel API est indépendant. Pour les applications de conversation, le LLM doit se souvenir des interactions passées. LangChain.js propose différents types de mémoire pour y parvenir.
`ConversationBufferMemory` est un choix courant, stockant l’historique brut des conversations.
“`javascript
// memoryExample.js
require(‘dotenv’).config();
const { OpenAI } = require(‘langchain/llms/openai’);
const { ConversationChain } = require(‘langchain/chains’);
const { ConversationBufferMemory } = require(‘langchain/memory’);
async function conversationalApp() {
const model = new OpenAI({ temperature: 0.7 });
const memory = new ConversationBufferMemory();
const chain = new ConversationChain({ llm: model, memory: memory });
console.log(“Démarrage de la conversation…”);
let result1 = await chain.call({ input: “Salut, je m’appelle Jake.” });
console.log(“Utilisateur : Salut, je m’appelle Jake.”);
console.log(“IA :”, result1.response);
let result2 = await chain.call({ input: “Quel est mon nom ?” });
console.log(“Utilisateur : Quel est mon nom ?”);
console.log(“IA :”, result2.response);
let result3 = await chain.call({ input: “Raconte-moi un fait amusant sur JavaScript.” });
console.log(“Utilisateur : Raconte-moi un fait amusant sur JavaScript.”);
console.log(“IA :”, result3.response);
}
conversationalApp();
“`
Exécutez avec `node memoryExample.js`.
`ConversationBufferMemory` maintient l’historique des discussions, permettant au LLM de répondre correctement à la question « Quel est mon nom ? » en se basant sur le tour précédent. LangChain.js propose d’autres types de mémoire pour des cas d’utilisation plus avancés, comme le résumé de conversations ou le stockage de parties spécifiques uniquement.
Conseils Pratiques pour le Développement LangChain.js
1. **Commencez Simple :** Ne tentez pas de construire un agent complexe avec plusieurs outils et mémoires immédiatement. Commencez par des appels LLM simples et `LLMChain`.
2. **Utilisez `verbose: true` :** Lors du débogage de chaînes ou d’agents, définir `verbose: true` est précieux. Cela montre les étapes intermédiaires, les prompts envoyés et les réponses reçues, vous aidant à comprendre pourquoi votre chaîne pourrait ne pas se comporter comme prévu.
3. **Gérez les Clés API de Manière Sécurisée :** Utilisez toujours des variables d’environnement pour vos clés API. Ne les engagez jamais dans le contrôle de version.
4. **Comprenez les Fenêtres de Contexte :** Faites attention aux limites de tokens de votre LLM choisi. Des prompts longs, un historique de conversation étendu ou de nombreux documents récupérés peuvent rapidement dépasser ces limites. LangChain.js dispose d’outils pour gérer cela, mais il est important d’être conscient.
5. **Expérimentez avec `temperature` :** Le paramètre `temperature` contrôle le caractère aléatoire de la sortie du LLM. Des valeurs plus élevées (par exemple, 0.7-1.0) entraînent des réponses plus créatives et moins déterministes. Des valeurs plus basses (par exemple, 0.0-0.3) sont plus factuelles et cohérentes. Ajustez cela en fonction des besoins de votre application.
6. **Explorez la Documentation :** La documentation de LangChain.js est complète. Si vous recherchez un chargeur, une chaîne ou un outil spécifique, il y a de fortes chances qu’il y soit couvert.
7. **Considérez les Stockages de Vecteurs en Production :** Pour les applications RAG en conditions réelles, remplacez `HNSWLib` par une base de données vectorielle persistante comme Pinecone, Chroma ou Weaviate. Cela vous permettra de mettre à l’échelle et de stocker de grandes quantités de données.
8. **Gestion des Erreurs :** Implémentez une gestion des erreurs solide pour les appels API, surtout dans des environnements de production. Des problèmes de réseau, des limites de fréquence ou des entrées invalides peuvent entraîner des échecs.
Questions Fréquemment Posées sur LangChain.js
Q1 : LangChain.js est-il uniquement pour les modèles OpenAI ?
Non, LangChain.js prend en charge une large gamme de fournisseurs de LLM, y compris OpenAI, les modèles Hugging Face (via `HuggingFaceHub` ou `HuggingFaceInference`), Google (par exemple, `GoogleGenerativeAI`), Anthropic et bien d’autres. Les abstractions de base de LangChain.js vous permettent de remplacer les fournisseurs de LLM avec un minimum de modifications de code.
Q2 : Quelle est la différence entre `model.call()` et `chain.call()` ou `chain.run()` ?
`model.call()` est la manière la plus directe d’interagir avec une instance unique de LLM, en passant un prompt de chaîne brute. `chain.call()` est utilisé pour les chaînes et prend un objet contenant des variables d’entrée (qui sont ensuite formatées par un `PromptTemplate` si utilisé dans la chaîne). `chain.run()` est une méthode de commodité pour les chaînes qui n’ont qu’une seule variable d’entrée et renvoie directement le texte de sortie. Pour des chaînes plus complexes avec plusieurs entrées/sorties, `chain.call()` est préféré.
Q3 : Comment puis-je intégrer LangChain.js avec ma base de données ou API existante ?
Vous pouvez intégrer LangChain.js avec vos systèmes existants en créant des outils personnalisés pour les agents. Un outil est essentiellement une fonction qui prend une chaîne d’entrée et renvoie une chaîne de sortie. Vous pouvez définir un outil qui interroge votre base de données, appelle une API interne, ou effectue toute autre logique personnalisée, puis rendre cet outil disponible à votre agent LangChain.js. Pour RAG, vous pouvez créer des implémentations personnalisées de `DocumentLoader` pour charger des données de vos sources spécifiques.
Q4 : Quand devrais-je utiliser LangChain.js plutôt que d’appeler directement les API LLM ?
Utilisez LangChain.js lorsque vous devez construire des applications qui impliquent plus qu’un simple appel isolé de LLM. Si votre application nécessite :
* Raisonnement en plusieurs étapes (chaînes).
* Intégration de données externes (RAG).
* Donner au LLM accès à des outils externes (agents).
* Gestion de l’historique des conversations (mémoire).
* Changement facile de fournisseurs de LLM.
* Code plus structuré et maintenable.
Si vous faites simplement un prompt unique pour des tests, les appels API directs peuvent être plus simples, mais pour toute application pratique, LangChain.js devient rapidement indispensable.
Conclusion
LangChain.js est un cadre puissant pour construire des applications LLM sophistiquées en JavaScript et TypeScript. En comprenant ses composants clés—LLMs, Prompts, Chains, Retrieval, Agents et Memory—vous pouvez construire des systèmes intelligents qui vont au-delà de la simple génération de texte. Ce guide a fourni un point de départ pratique et actionnable. Des appels LLM simples aux pipelines RAG complexes et aux agents, LangChain.js offre les outils pour donner vie à vos idées d’automatisation IA. Commencez à expérimenter, construisez de petits projets, et vous découvrirez rapidement le potentiel de LangChain.js dans votre flux de développement.
🕒 Published: