Iniziare con LangChain.js: Costruire applicazioni LLM pratiche
Di Jake Morrison LangChain.js è uno di quegli strumenti. Offre un modo strutturato per creare applicazioni alimentate da grandi modelli di linguaggio (LLM) utilizzando JavaScript o TypeScript. Se stai lavorando con LLM nell’ecosistema JavaScript, comprendere LangChain.js è essenziale. Questa guida ti guiderà attraverso fasi pratiche e concrete per iniziare e costruire reali applicazioni.
Cos’è LangChain.js e perché usarlo?
LangChain.js è una libreria progettata per aiutare gli sviluppatori a creare applicazioni alimentate da LLM. Non sostituisce gli LLM stessi, ma fornisce piuttosto uno strato di astrazione e un insieme di strumenti per concatenare diverse chiamate LLM, integrare altre fonti di dati e gestire interazioni complesse.
Pensalo in questo modo: chiamare direttamente un’API LLM è come utilizzare una query di database grezza. LangChain.js fornisce un ORM (Object-Relational Mapper) per gli LLM. Gestisce modelli comuni come:
* **Catene:** Combinazione di più chiamate LLM o altri passaggi in una sequenza.
* **Prompt:** Gestione e formattazione degli input per gli LLM.
* **Agenti:** Permettere agli LLM di prendere decisioni e utilizzare strumenti.
* **Recupero:** Integrazione di dati esterni (i tuoi documenti, database) con gli LLM.
* **Memoria:** Dare agli LLM un modo di ricordare interazioni passate.
Il principale vantaggio di usare LangChain.js è l’aumento della produttività e della manutenibilità. Scrivi meno codice ripetitivo, la logica della tua applicazione diventa più chiara e è più facile cambiare fornitore LLM o aggiungere nuove funzionalità. Per chiunque costruisca con LLM in JavaScript, LangChain.js offre vantaggi significativi.
Configurazione del tuo progetto LangChain.js
Prima di scrivere codice, prepariamo il nostro ambiente. Avrai bisogno di Node.js installato.
1. **Crea una nuova directory di progetto:**
`
mkdir langchain-js-project
cd langchain-js-project
`
2. **Inizializza un progetto Node.js:**
`
npm init -y
`
3. **Installa LangChain.js e un fornitore LLM:**
Inizieremo con OpenAI per i nostri esempi, ma LangChain.js supporta molti altri (Hugging Face, Google, ecc.).
`
npm install langchain @openai/openai
`
Se preferisci TypeScript:
`
npm install langchain @openai/openai typescript @types/node ts-node
npx tsc –init
`
Quindi, aggiorna `tsconfig.json` per includere `“moduleResolution”: “node”` e `“esModuleInterop”: true`.
4. **Configura la tua chiave API:**
Avrai bisogno di una chiave API OpenAI. Non codificare mai le chiavi API direttamente nel tuo codice. Usa variabili ambientali.
Crea un file `.env` alla radice del tuo progetto:
`
OPENAI_API_KEY=YOUR_OPENAI_API_KEY
`
Per caricare le variabili ambientali, installa `dotenv`:
`
npm install dotenv
`
Quindi, in cima al tuo file di script principale (ad esempio, `index.js` o `src/index.ts`):
`
require(‘dotenv’).config();
`
o per TypeScript/ESM:
`
import ‘dotenv/config’;
`
Ora sei pronto per iniziare a costruire con LangChain.js.
La tua prima chiamata LLM con LangChain.js
Facciamo una chiamata semplice a un LLM.
`
// index.js
require(‘dotenv’).config();
const { OpenAI } = require(‘langchain/llms/openai’);
async function simpleLLMCall() {
const model = new OpenAI({ temperature: 0.7 }); // la temperatura controlla il carattere casuale
const prompt = “Qual è la capitale della Francia ?”;
const result = await model.call(prompt);
console.log(result);
}
simpleLLMCall();
`
Per eseguire questo: `node index.js`.
Dovresti vedere “Parigi.” o un’uscita simile. Questa è l’interazione più basilare. LangChain.js avvolge la chiamata API OpenAI, rendendola coerente con altri fornitori LLM.
Lavorare con i prompt: Modelli e variabili
Incorporare i prompt direttamente nel tuo codice può diventare disordinato. LangChain.js fornisce `PromptTemplate` per gestire i prompt in modo 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 = “Qual è un buon nome per un’azienda che produce {product} ?”;
const prompt = new PromptTemplate({
template: template,
inputVariables: [“product”],
});
const formattedPrompt = await prompt.format({ product: “calze colorate” });
console.log(“Prompt Formattato :”, formattedPrompt);
const result = await model.call(formattedPrompt);
console.log(“Risultato LLM :”, result);
}
templatedPrompt();
`
Esegui questo con `node promptExample.js`.
Questo dimostra come `PromptTemplate` ti consente di definire prompt con segnaposto (`{product}`) che puoi riempire dinamicamente. È essenziale per costruire applicazioni flessibili.
Catene: Collegare chiamate LLM e logica
Le catene sono dove LangChain.js brilla davvero. Ti permettono di combinare più passaggi, comprese le chiamate LLM, in un unico flusso di lavoro coerente.
Catena LLM semplice
Il `LLMChain` è la catena più basilare. Prende un `PromptTemplate` e un LLM, quindi formatta il prompt e lo passa all’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 = “Qual è la migliore {activity} da fare a {city} ?”;
const prompt = new PromptTemplate({
template: template,
inputVariables: [“activity”, “city”],
});
const chain = new LLMChain({ llm: model, prompt: prompt });
const result = await chain.call({ activity: “cibo”, city: “Roma” });
console.log(“Risultato della Catena :”, result);
// L’oggetto risultato conterrà una proprietà ‘text’ con l’output dell’LLM.
}
simpleLLMChain();
`
Esegui con `node llmChainExample.js`.
Nota come `chain.call()` prende un oggetto con le variabili di input, e gestisce la formattazione del prompt e la chiamata all’LLM. È un modo più pulito di interagire con il tuo LLM.
Catene sequenziali: flussi di lavoro a più fasi
A volte, devi effettuare più chiamate LLM in sequenza, dove l’output di una chiamata diventa l’input per la successiva. È qui che `SimpleSequentialChain` è 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 });
// Catena 1: Generare un nome per l’azienda
const nameTemplate = “Qual è un buon nome per un’azienda che produce {product} ?”;
const namePrompt = new PromptTemplate({
template: nameTemplate,
inputVariables: [“product”],
});
const nameChain = new LLMChain({ llm: model, prompt: namePrompt });
// Catena 2: Descrivere l’azienda
const descriptionTemplate = “Scrivi un breve slogan di marketing coinvolgente per un’azienda chiamata {companyName}.”;
const descriptionPrompt = new PromptTemplate({
template: descriptionTemplate,
inputVariables: [“companyName”],
});
const descriptionChain = new LLMChain({ llm: model, prompt: descriptionPrompt });
// Combinare i due in una catena sequenziale
const overallChain = new SimpleSequentialChain({
chains: [nameChain, descriptionChain],
verbose: true, // Impostato su true per vedere i passaggi intermedi
});
const result = await overallChain.run(“scarpe ecologiche”);
console.log(“Risultato Globale :”, result);
}
multiStepChain();
`
Esegui con `node sequentialChainExample.js`.
Il `SimpleSequentialChain` prende l’output di `nameChain` e lo utilizza automaticamente come input (`companyName`) per `descriptionChain`. Questo modello è estremamente potente per decomporre compiti complessi. LangChain.js rende questo processo facile.
Recupero Aumentato tramite Generazione (RAG): Utilizzare i propri dati
I LLM sono potenti, ma la loro conoscenza è limitata ai dati di addestramento. Per applicazioni che richiedono informazioni aggiornate o conoscenze specifiche di un settore (i documenti della tua azienda, ad esempio), devi aumentare la conoscenza del LLM con dati esterni. Questo si chiama Recupero Aumentato tramite Generazione (RAG).
L’idea principale del RAG è:
1. **Caricare i dati:** Ottieni i tuoi documenti (PDF, file di testo, pagine web).
2. **Dividere i dati:** Suddividi documenti grandi in pezzi più piccoli e gestibili.
3. **Integrare i dati:** Trasforma questi pezzi in rappresentazioni numeriche (integrazioni) utilizzando un modello di integrazione.
4. **Memorizzare i dati:** Archivia queste integrazioni in un database vettoriale.
5. **Recuperare:** Quando un utente pone una domanda, integra la domanda, cerca nel database vettoriale pezzi di documenti simili.
6. **Aumentare il prompt:** Aggiungi questi pezzi recuperati al prompt del LLM.
7. **Generare:** Il LLM utilizza questo prompt aumentato per generare una risposta più informata.
LangChain.js fornisce componenti per ciascuna di queste fasi.
Fase 1: Caricamento dei documenti
LangChain.js ha diversi caricatori di documenti. Utilizziamo un `TextLoader`.
“`javascript
// ragExample.js – Parte 1: Caricamento dei documenti
require(‘dotenv’).config();
const { TextLoader } = require(‘langchain/document_loaders/fs/text’);
async function loadDocuments() {
// Crea un file di testo fittizio per la dimostrazione
const fs = require(‘fs’);
fs.writeFileSync(‘my_document.txt’, `
Il rapido volpe marrone salta sopra il cane pigro.
Questo documento parla di vari animali e dei loro comportamenti.
I cani sono noti per la loro lealtà e giocabilità.
Le volpi sono creature astute e solitarie.
I gatti amano dormire e cacciare i topi.
`);
const loader = new TextLoader(“my_document.txt”);
const docs = await loader.load();
console.log(“Documenti caricati:”, docs);
// Ogni doc avrà pageContent e metadata
}
loadDocuments();
“`
Fase 2: Divisione dei documenti
I documenti grandi devono essere divisi in pezzi più piccoli affinché possano rientrare nella finestra di contesto del LLM e per migliorare la pertinenza dei recuperi.
“`javascript
// ragExample.js – Parte 2: Divisione
require(‘dotenv’).config();
const { TextLoader } = require(‘langchain/document_loaders/fs/text’);
const { RecursiveCharacterTextSplitter } = require(‘langchain/text_splitter’);
async function splitDocuments() {
// (Supponiamo che my_document.txt sia già stato creato dalla Parte 1)
const loader = new TextLoader(“my_document.txt”);
const docs = await loader.load();
const splitter = new RecursiveCharacterTextSplitter({
chunkSize: 100, // Massimi caratteri per pezzo
chunkOverlap: 20, // Sovrapposizione tra i pezzi per mantenere il contesto
});
const splitDocs = await splitter.splitDocuments(docs);
console.log(“Documenti divisi:”, splitDocs);
}
splitDocuments();
“`
Fase 3 & 4: Integrazione e memorizzazione dei documenti (memorizzazione vettoriale)
È qui che entrano in gioco le integrazioni e i database vettoriali. Utilizzeremo integrazioni OpenAI e `HNSWLib` (una memorizzazione vettoriale locale in memoria) per semplicità. In produzione, dovresti utilizzare un database vettoriale dedicato come Pinecone, Chroma, Weaviate, ecc.
“`javascript
// ragExample.js – Parte 3: Integrazioni e memorizzazione vettoriale
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. Caricare e dividere i documenti
const fs = require(‘fs’);
fs.writeFileSync(‘my_document.txt’, `
Il rapido volpe marrone salta sopra il cane pigro.
Questo documento parla di vari animali e dei loro comportamenti.
I cani sono noti per la loro lealtà e giocabilità.
Le volpi sono creature astute e solitarie.
I gatti amano dormire e cacciare i topi.
Un gruppo di gatti si chiama 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. Creare integrazioni e memorizzare nella memorizzazione vettoriale
const embeddings = new OpenAIEmbeddings();
const vectorStore = await HNSWLib.fromDocuments(docs, embeddings);
// 3. Creare un recuperatore
const retriever = vectorStore.asRetriever();
// 4. Creare la catena QA di recupero
const model = new OpenAI({ temperature: 0 });
const chain = RetrievalQAChain.fromLLM(model, retriever);
// 5. Porre una domanda
const question = “Come si chiama un gruppo di gatti?”;
const result = await chain.call({ query: question });
console.log(“Risposta RAG:”, result.text);
const anotherQuestion = “Parlami delle volpi.”;
const anotherResult = await chain.call({ query: anotherQuestion });
console.log(“Un’altra risposta RAG:”, anotherResult.text);
}
ragApplication();
“`
Esegui con `node ragExample.js`.
Questo esempio riunisce tutti i pezzi del RAG. La `RetrievalQAChain` astrae il processo di recupero dei documenti pertinenti e del loro utilizzo per rispondere a una domanda. È un modo pratico per creare chatbot o sistemi di domande e risposte sui tuoi dati specifici utilizzando LangChain.js.
Agenti: LLM che utilizzano strumenti
Gli agenti consentono ai LLM di prendere decisioni sugli strumenti da utilizzare e in quale ordine, in base agli input dell’utente. Questo permette interazioni più dinamiche e complesse. Gli strumenti possono essere qualsiasi cosa: un motore di ricerca, una calcolatrice, una chiamata API ai tuoi sistemi interni, o anche un’altra catena LLM.
Creiamo un semplice agente che può eseguire calcoli utilizzando uno strumento `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()];
// Inizializzare l’esecutore dell’agente
const executor = await initializeAgentExecutorWithOptions(tools, model, {
agentType: “openai-functions”, // Utilizzare la funzione di chiamata di funzionalità di OpenAI
verbose: true, // Vedi il processo di pensiero dell’agente
});
console.log(“Chiamata all’agente con una domanda matematica semplice…”);
const result1 = await executor.call({ input: “Qual è 123 più 456?” });
console.log(“Risultato agente 1:”, result1.output);
console.log(“\nChiamata all’agente con una domanda più complessa…”);
const result2 = await executor.call({ input: “Qual è la radice quadrata di 625 moltiplicata per 3?” });
console.log(“Risultato agente 2:”, result2.output);
}
runAgent();
“`
Esegui con `node agentExample.js`.
Con `verbose: true`, vedrai i “pensieri” dell’agente: identifica che è necessario un calcolo, utilizza lo strumento `Calculator`, poi fornisce la risposta. È un modello potente per costruire applicazioni che vanno oltre la semplice generazione di testo. LangChain.js semplifica la configurazione di questi agenti.
Memoria: Mantenere il contesto della conversazione
Per impostazione predefinita, i LLM sono senza stato. Ogni chiamata API è indipendente. Per le applicazioni di conversazione, il LLM deve ricordare le interazioni passate. LangChain.js offre diversi tipi di memoria per raggiungere questo obiettivo.
`ConversationBufferMemory` è una scelta comune, che memorizza la cronologia grezza delle conversazioni.
“`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(“Avvio della conversazione…”);
let result1 = await chain.call({ input: “Ciao, mi chiamo Jake.” });
console.log(“Utente : Ciao, mi chiamo Jake.”);
console.log(“IA :”, result1.response);
let result2 = await chain.call({ input: “Qual è il mio nome ?” });
console.log(“Utente : Qual è il mio nome ?”);
console.log(“IA :”, result2.response);
let result3 = await chain.call({ input: “Raccontami un fatto divertente su JavaScript.” });
console.log(“Utente : Raccontami un fatto divertente su JavaScript.”);
console.log(“IA :”, result3.response);
}
conversationalApp();
“`
Esegui con `node memoryExample.js`.
`ConversationBufferMemory` mantiene la cronologia delle discussioni, consentendo al LLM di rispondere correttamente alla domanda « Qual è il mio nome ? » basandosi sul turno precedente. LangChain.js offre altri tipi di memoria per casi d’uso più avanzati, come il riassunto delle conversazioni o il salvataggio solo di parti specifiche.
Suggerimenti Pratici per lo Sviluppo con LangChain.js
1. **Inizia Semplice:** Non cercare di costruire un agente complesso con più strumenti e memorie subito. Inizia con semplici chiamate LLM e `LLMChain`.
2. **Usa `verbose: true`:** Durante il debug di catene o agenti, impostare `verbose: true` è utile. Mostra i passaggi intermedi, i prompt inviati e le risposte ricevute, aiutandoti a capire perché la tua catena potrebbe non comportarsi come previsto.
3. **Gestisci le Chiavi API in Modo Sicuro:** Usa sempre variabili d’ambiente per le tue chiavi API. Non immetterle mai nel controllo di versione.
4. **Comprendi le Finestre di Contesto:** Fai attenzione ai limiti di token del tuo LLM scelto. Prompt lunghi, una cronologia di conversazione estesa o molti documenti recuperati possono rapidamente superare questi limiti. LangChain.js ha strumenti per gestirli, ma è importante esserne consapevoli.
5. **Sperimenta con `temperature`:** Il parametro `temperature` controlla il grado di casualità dell’output del LLM. Valori più alti (ad esempio, 0.7-1.0) portano a risposte più creative e meno deterministiche. Valori più bassi (ad esempio, 0.0-0.3) sono più fattuali e coerenti. Regola questo in base alle esigenze della tua applicazione.
6. **Esplora la Documentazione:** La documentazione di LangChain.js è completa. Se stai cercando un caricatore, una catena o uno strumento specifico, è probabile che venga trattato.
7. **Considera gli Archivi di Vettori in Produzione:** Per applicazioni RAG in ambienti reali, sostituisci `HNSWLib` con un database di vettori persistente come Pinecone, Chroma o Weaviate. Questo ti permetterà di scalare e archiviare grandi quantità di dati.
8. **Gestione degli Errori:** Implementa una gestione degli errori solida per le chiamate API, specialmente in ambienti di produzione. Problemi di rete, limiti di frequenza o voci non valide possono portare a fallimenti.
Domande Frequenti su LangChain.js
Q1: LangChain.js è solo per modelli OpenAI?
No, LangChain.js supporta una vasta gamma di fornitori di LLM, tra cui OpenAI, i modelli Hugging Face (tramite `HuggingFaceHub` o `HuggingFaceInference`), Google (ad esempio, `GoogleGenerativeAI`), Anthropic e molti altri. Le astrazioni di base di LangChain.js ti consentono di sostituire i fornitori di LLM con un minimo di modifiche al codice.
Q2: Qual è la differenza tra `model.call()` e `chain.call()` o `chain.run()`?
`model.call()` è il modo più diretto per interagire con un’unica istanza di LLM, passando un prompt di catena grezza. `chain.call()` è utilizzato per le catene e prende un oggetto contenente variabili di input (che vengono poi formattate da un `PromptTemplate` se utilizzato nella catena). `chain.run()` è un metodo di comodità per le catene che hanno solo una variabile di input e restituisce direttamente il testo di output. Per catene più complesse con più input/output, è preferibile `chain.call()`.
Q3: Come posso integrare LangChain.js con il mio database o API esistente?
Puoi integrare LangChain.js con i tuoi sistemi esistenti creando strumenti personalizzati per gli agenti. Uno strumento è essenzialmente una funzione che prende una stringa di input e restituisce una stringa di output. Puoi definire uno strumento che interroga il tuo database, chiama un’API interna o esegue qualsiasi altra logica personalizzata, quindi rendere disponibile questo strumento al tuo agente LangChain.js. Per RAG, puoi creare implementazioni personalizzate di `DocumentLoader` per caricare dati dalle tue fonti specifiche.
Q4: Quando dovrei usare LangChain.js invece di chiamare direttamente le API LLM?
Usa LangChain.js quando devi costruire applicazioni che coinvolgono più di una semplice chiamata isolata a LLM. Se la tua applicazione richiede :
* Ragionamento multi-passaggio (catene).
* Integrazione di dati esterni (RAG).
* Dare al LLM accesso a strumenti esterni (agenti).
* Gestione della cronologia delle conversazioni (memoria).
* Cambiamento facile di fornitori di LLM.
* Codice più strutturato e mantenibile.
Se stai semplicemente facendo un prompt unico per test, le chiamate API dirette possono essere più semplici, ma per qualsiasi applicazione pratica, LangChain.js diventa rapidamente indispensabile.
Conclusione
LangChain.js è un framework potente per costruire applicazioni LLM sofisticate in JavaScript e TypeScript. Comprendendo i suoi componenti chiave—LLMs, Prompts, Chains, Retrieval, Agents e Memory—puoi costruire sistemi intelligenti che vanno oltre la semplice generazione di testo. Questa guida ha fornito un punto di partenza pratico e realizzabile. Dai semplici chiamate LLM ai pipeline RAG complessi e agli agenti, LangChain.js offre gli strumenti per dare vita alle tue idee di automazione AI. Inizia a sperimentare, costruisci piccoli progetti e scoprirai rapidamente il potenziale di LangChain.js nel tuo flusso di sviluppo.
🕒 Published: