Wahrscheinlich kennen Sie das Meme mit dem bekannten Zitat aus dem Film „The Wolf of Wall Street“: „Sell me this pen“. Die Antwort lautet: „It’s AI powered“. Mittlerweile ist es ja schon fast nicht mehr zum Lachen – alle wollen AI, aber was die praktische Umsetzung angeht, ist sich keiner so richtig einig. Hier kommt das Framework LangChain ins Spiel.
Ein entscheidender Vorteil des AI-Booms für uns Softwareentwickler ist, dass sich Probleme herauskristallisieren, die in der gleichen oder ähnlichen Form überall auftreten. Die Integration eines KI-Modells ist ein typisches Beispiel. Die meisten Plattformen wie das OpenAI API, Google Gemini oder auch das lokale Ollama bieten HTTP-Schnittstellen, über die Sie mit einem LLM interagieren können. Zusätzlich dazu stellt Ihnen jeder Anbieter ein eigenes Paket zur Integration in Ihre Applikation zur Verfügung.
Das Problem an dieser Stelle ist jedoch, dass sich die Anbieter in den Details ihrer APIs unterscheiden. Das macht einen leichtgewichtigen Austausch der Modelle schwierig. Was für die Modelle gilt, gilt auch für andere Komponenten einer KI-Applikation. Wäre es da nicht schön, wenn es ein Framework gäbe, das Ihnen die Arbeit mit dem unübersichtlichen und schnell wachsenden KI-Ökosystem erleichtert? Das Framework, nach dem Sie suchen, heißt LangChain.
LangChain ist modular aufgebaut und adressiert die wichtigsten Aspekte einer AI-Applikation von der Anbindung von Modellen über den Umgang mit Prompts und Templates und der Anbindung von Vektordatenbanken bis hin zur Verarbeitung der Ausgabe. Dabei arbeitet das Framework, wie der Name schon andeutet, mit Ketten. Diese sind vergleichbar mit dem Konzept von Pipes, die Sie beispielsweise aus Unix-Shell oder der Rx-Bibliothek kennen. Die einzelnen Glieder der Kette haben jeweils eine Ein- und Ausgabe und können zu beliebig langen Ketten zusammengefügt werden.
Das LangChain-Framework existiert in zwei verschiedenen Varianten: Python und JavaScript bzw. TypeScript. Die grundlegende Architektur und die Konzepte sind in beiden Versionen die gleichen. Dieser Artikel verwendet für die konkreten Umsetzungsbeispiele die JavaScript-Variante.
Aufbau und Konzepte
Das wichtigste Paket des LangChain-Frameworks ist das Core-Paket. Es enthält die grundlegenden Strukturen für alle weiteren Komponenten. In der JavaScript-Variante trägt dieses Paket den Namen @langchain/core. Es enthält alle Basisklassen, wie beispielsweise BaseLLM, von der die konkreten Model-Klassen für die LLM-Integration ableiten. Außerdem definiert dieses Paket die LangChain Expression Language (LCEL), eine Schnittstelle, mit der Sie auf einfache Art die Ketten für Ihre Applikation definieren können. Das zweite Paket ist das langchain-Paket, das die Strukturen für Ketten, Agents und Retrieval Strategien beisteuert.
Zu diesen beiden Paketen kommt noch eine Vielzahl zusätzlicher Integrationspakete für die verschiedenen Bestandteile der Applikation. So können Sie das @langchain/openai-Paket nutzen, um das OpenAI API in Ihre Applikation einzubinden, oder Sie nutzen @langchain/pinecore, um die Pinecore-Vektordatenbank zu integrieren.
Für beide Pakete gilt, dass Sie sich problemlos sowohl für JavaScript als auch für TypeScript entscheiden können. LangChain liefert immer die aktuellen Typdefinitionen selbst aus, da das gesamte Framework in TypeScript umgesetzt ist. Die wichtigsten Bestandteile des Frameworks lernen Sie im Folgenden am konkreten Beispiel kennen und sehen dabei, wie die einzelnen Komponenten ineinandergreifen.
Die erste Beispielapplikation: ein Chatbot mit LangChain
Wenn Sie von KI-Applikationen hören, kommt Ihnen wahrscheinlich sehr schnell die typische Chatbot-Anwendung in den Sinn. Und genau mit einer solchen wollen wir den Einstieg in LangChain beginnen. Das Beispiel ist vollständig lokal lauffähig und setzt dafür auf Ollama als Plattform, die ein Sprachmodell lokal ausführt. Als konkretes Modell kommt Llama in der Version 3.2 zum Einsatz. Neben den bereits vorgestellten Paketen benötigen Sie dafür noch das @langchain/ollama-Paket, um mit der Plattform zu kommunizieren.
Egal, ob Sie Ollama oder eine andere Plattform wie OpenAI nutzen, die HTTP-Schnittstellen sind in der Regel zustandslos. Das bedeutet, dass das Modell kein Gedächtnis hat, die Konversation nach der Antwort also sofort wieder vergisst. Dieses Problem können Sie lösen, indem Sie eine Nachrichtenhistorie integrieren. Auch für diesen Fall bietet Ihnen LangChain eine passende Struktur. Doch bevor es an die konkrete Umsetzung geht, werfen wir einen Blick auf die Komponenten, die Sie für die Umsetzung eines solchen Chatbots benötigen:
-
Prompt: Der Prompt ist die Nachricht, die Sie an das Sprachmodell senden, um eine Antwort zu erhalten.
-
Prompt-Template: Mit dem Prompt-Template können Sie zum Prompt noch weitere Strukturen hinzufügen, um dem Model zusätzlichen Kontext zu bieten.
-
Model: Das Model steht für das Sprachmodell, mit dem Ihre Applikation interagiert. Dieser Klasse übergeben Sie im Fall von Ollama das konkrete Modell, das Sie verwenden möchten, oder bei OpenAI zusätzlich noch Ihren API-Key.
-
OutputParser: Mit dem OutputParser können Sie die Antwort des Modells manipulieren und den Inhalt der Nachricht extrahieren.
Der Prompt ist die Eingabe eines Menschen. Sie kann in ein Prompt-Template eingefügt werden. Diese Eingabe wird an das Modell weitergegeben, das dann die Antwort erzeugt. Der OutputParser liefert Ihnen schließlich die Antwort des Modells. In Listing 1 sehen Sie den Code des Beispiels.
Listing 1: Chatbot mit Historie in LangChain
import { InMemoryChatMessageHistory } from '@langchain/core/chat_history';
import { AIMessage, HumanMessage } from '@langchain/core/messages';
import { StringOutputParser } from '@langchain/core/output_parsers';
import { ChatPromptTemplate } from '@langchain/core/prompts';
import {
RunnableSequence,
RunnableWithMessageHistory,
} from '@langchain/core/runnables';
import { ChatOllama } from '@langchain/ollama';
const messageHistories: Record<string, InMemoryChatMessageHistory> = {};
const prompt = ChatPromptTemplate.fromMessages([
[
'system',
'You are a helpful assistant who remembers all details the user shares with you.',
],
['placeholder', '{chat_history}'],
['human', '{input}'],
]);
const model = new ChatOllama({
model: 'llama3.2',
});
const parser = new StringOutputParser();
const chain = RunnableSequence.from([prompt, model, parser]);
const withMessageHistory = new RunnableWithMessageHistory({
runnable: chain,
getMessageHistory: async (sessionId) => {
if (messageHistories[sessionId] === undefined) {
messageHistories[sessionId] = new InMemoryChatMessageHistory();
}
return messageHistories[sessionId];
},
inputMessagesKey: 'input',
historyMessagesKey: 'chat_history',
});
const config = {
configurable: {
sessionId: 'theSession',
},
};
const messages = [
new HumanMessage('Hey AI, I need help with my JavaScript project.'),
new AIMessage("Sure! What's the project about, and what's the issue?"),
];
const stream = await withMessageHistory.stream(
{
chat_history: messages,
input:
"I'm building a task list app, but the date filtering isn't working right.",
},
config
);
for await (const chunk of stream) {
console.log(chunk);
}
Die erste Komponente der Implementierung ist das Prompt-Template. Dieses erstellen Sie mit der fromMessages-Methode der ChatPromptTemplate-Klasse. Ihr übergeben Sie ein Array aus Nachrichten, wobei jede Nachricht aus einem Tupel aus Nachrichtentyp und Nachricht gebildet wird. Es gibt drei verschiedene Typen von Nachrichten: system steht für den System-Prompt, mit dem Sie das Verhalten des Systems beeinflussen können. Den Typ ai nutzen Sie, um eine Antwort des Modells zu speichern und human steht schließlich für eine menschliche Eingabe. Eine Sonderrolle nimmt der Typ placeholder ein. Hier platzieren Sie später die Einträge der Chat-Historie.
Als Modell nutzt das Beispiel eine Instanz der ChatOllama. Hier wird nur der Wert für das zu verwendende Modell überschrieben, sodass das Llama-3.2-Modell verwendet wird. Diese Konfiguration sorgt dafür, dass die Applikation mit dem Ollama API auf http://localhost:11434 kommuniziert. Als Output Parser kommt im Beispiel der StringOutputParser zum Einsatz. Dieser nimmt ein Objekt vom Typ AIMessage entgegen und extrahiert den Textinhalt, da das ursprüngliche Objekt neben diesem Inhalt noch eine ganze Reihe von Metainformationen enthält, die für das Beispiel nicht relevant sind.
Prompt-Template, Modell und Output Parser fasst die from-Methode der RunnableSequence-Klasse zu einer Kette zusammen. Diese können Sie bereits verwenden, indem Sie die invoke– oder stream-Methode des zurückgegebenen Objekts verwenden. In diesem Fall hat Ihre Applikation jedoch noch kein Gedächtnis. Dieses erhält sie erst mit dem withMessageHistory-Objekt. Dafür instanziieren Sie die RunnableWithMessageHistory-Klasse. Dem Konstruktor übergeben Sie ein Objekt, das die Kette mit Prompt, Modell und Parser in der runnable-Eigenschaft enthält. Außerdem definieren Sie die getMessageHistory-Methode. Sie arbeitet mit einer SessionId, um verschiedene User-Sessions verwalten zu können. Jede Session erhält ihr eigenes InMemoryChatMessageHistory-Objekt, das im globalen messageHistories-Objekt festgehalten wird. Außerdem definieren Sie mit inputMessagesKey und historyMessagesKey zwei Platzhalter, die für den User-Prompt und die Chat-Historie stehen und auf die Sie im Prompt-Template zugreifen können.
Mit dem withMessageHistory-Objekt können Sie mit dem Modell interagieren. Im Beispielcode sehen Sie zwei verschiedene Arten: die stream– und die invoke-Methode. Die stream-Methode stellt Ihnen die Antworten als Stream aus Tokens zur Verfügung. Das hat den Vorteil, dass Sie mit der Anzeige der Ergebnisse schon beginnen können, bevor die Antwort vollständig vorliegt. Die einfachste Variante, die einzelnen Tokens zu konsumieren, ist die Verwendung der asynchronen for-of-Schleife. Im Gegensatz dazu erhalten Sie das Ergebnis bei der invoke-Methode erst, wenn das Modell die Antwort vollständig generiert hat. Egal, welche der beiden Methoden Sie nutzen, in jedem Fall übergeben Sie ein Objekt mit dem Prompt und optional können Sie zusätzlich noch eine Chat-Historie angeben. Im zweiten Argument geben Sie die Session-ID für die korrekte Zuordnung der Chat-Session an. Diese Implementierung können Sie im nächsten Schritt in ein beliebiges Web Application Framework, wie beispielsweise Express oder Nest, integrieren und eine Clientapplikation anbinden.
Einen Aspekt der Chat-Historie müssen Sie jedoch noch beachten: Die Historie darf nicht beliebig groß werden. Die LLMs, mit denen Sie hier arbeiten, verfügen über einen Kontext. Das ist Text, den Sie dem Modell für eine Interaktion übergeben können, um passende Antworten zu erzeugen. Dadurch kann beispielsweise der bisherige Gesprächsverlauf zur Verfügung gestellt werden. Die Größe des Kontexts ist jedoch limitiert. Sie liegt bei Llama 3.2 beispielsweise bei 128 000 Tokens. Sie sollten darauf achten, dass Ihre Konversation diese Größe nicht überschreitet, und wenn dies der Fall ist, entweder die Historie entsprechend limitieren und Ihre User darüber in Kenntnis setzen oder ältere Nachrichten einfach kontrolliert abschneiden. In diesem Fall kommt es zu Informationsverlust.
Neben dem typischen Chatbot können Sie mit LangChain noch viele weitere spannende Probleme lösen. In den folgenden Abschnitten erfahren Sie, wie Sie LangChain nutzen können, um sich Texte zusammenfassen zu lassen.

Bist du up-to-date bei JavaScript?
Erleben Sie die Trends von morgen vom 20. - 24. Oktober 2025
Textzusammenfassungen mit LangChain
Grundsätzlich können Sie einem LLM einen beliebigen Text übergeben und es mit einem Prompt auffordern, ihn für sich zusammenfassen zu lassen. Die einzige Limitierung ist hier das Kontextfenster. Bei modernen LLMs liegt seine Größe normalerweise bei 128 000 Tokens. Hier fangen die Probleme aber schon an: Ist das viel oder wenig? Gehen wir von einer normalen 12-Punkt-Schriftart und einem einfachen Zeilenabstand und etwas Seitenrändern aus, passen etwa 500 Wörter auf eine DIN-A4-Seite. Allerdings können Sie Wörter nicht einfach in Tokens umrechnen, sondern sollten eher von 0,75 Wörtern pro Token ausgehen. Damit ergeben die 128 000 Tokens etwa 190 bis 200 Seiten, also schon eine ganze Menge.
Irgendwann stoßen Sie jedoch selbst mit einem großen Kontext an eine Grenze, und auch sonst können Sie es einem LLM mit einem Trick leichter machen, große Mengen von Text zu verarbeiten. Dieser besteht darin, dass Sie den Text in kleinere Einheiten herunterbrechen und diese verarbeiten lassen. LangChain kann Sie dabei mit der SummarizationChain unterstützten. Der grundlegende Aufbau sieht dabei folgendermaßen aus:
-
Das gesamte Dokument laden
-
Unterteilen des Dokuments in kleinere Bestandteile
-
Erzeugen der SummarizationChain
-
Ausführen der Summarization Chain
Wie die Implementierung konkret im Code aussieht, sehen Sie in Listing 2.
Listing 2: Textzusammenfassung mit Langchain
import { Ollama } from '@langchain/ollama';
import { loadSummarizationChain } from 'langchain/chains';
import { RecursiveCharacterTextSplitter } from 'langchain/text_splitter';
import * as fs from 'fs';
import { PDFLoader } from '@langchain/community/document_loaders/fs/pdf';
const filePath = './docs/Ch2.pdf';
const loader = new PDFLoader(filePath);
const pdf = await loader.load();
const text = pdf.map((doc) => doc.pageContent).join('\n\n');
const model = new Ollama({ model: 'llama3.2' });
const textSplitter = new RecursiveCharacterTextSplitter({ chunkSize: 1000 });
const docs = await textSplitter.createDocuments([text]);
const chain = loadSummarizationChain(model);
const res = await chain.invoke({
input_documents: docs,
});
console.log({ res });
Für das Beispiel gehen wir von einer PDF-Datei als Eingabe aus. Diese müssen Sie zunächst in Text umwandeln. Da es sich hierbei um ein Problem handelt, das sehr häufig auftritt, gibt es in der LangChain-Community ein Paket, das eine Lösung für das Lesen von PDF-Dateien bietet. Sie finden es unter dem Namen PDFLoader im @langchain/community-Paket. Zusätzlich zu diesem Paket müssen Sie noch das pdf-parse-Paket installieren. Anschließend können Sie den PDFLoader mit dem Pfad zur PDF-Datei instanziieren und das Dokument mit der load-Methode des Loader laden.
Die wichtigsten Komponenten neben der SummarizationChain sind in diesem Beispiel das Modell, das auch in diesem Fall wieder Llama 3.2 ist, und der TextSplitter. Der TextSplitter zerlegt, wie der Name vermuten lässt, den Text in annähernd gleichgroße Teile. Der RecursiveCharacterTextSplitter ist am besten für generische Texte geeignet und zerlegt den Text anhand einer bestimmten Zeichensequenz. Bei der Instanziierung geben Sie die gewünschte Blockgröße, hier 1000 Zeichen, an. Als Trennzeichen für die Zerlegung nutzt dieser TextSplitter die Zeichenfolgen „\n\n“, „\n“ und „ “. Zuerst versucht das Werkzeug einen 1000-Zeichen-Block anhand der zwei Zeilenumbrüche abzutrennen. Ist das nicht möglich, wird das Gleiche mit einem Zeilenumbruch versucht und schließlich beim nächstgelegenen Leerzeichen. Damit soll der TextSplitter sicherstellen, dass möglichst zusammengehörige Textblöcke erzeugt werden.
Der createDocuments-Methode können Sie einen, aber auch mehrere Texte in Form eines String-Arrays übergeben. Die daraus resultierenden Dokumente nutzen Sie beim invoke-Aufruf der SummarizationChain als Eingabe. Um diese Kette zu erstellen, nutzen Sie die loadSummarizationChain-Funktion. Dieser müssen Sie beim Aufruf lediglich das Modell übergeben. Führen Sie den Code aus, erhalten Sie die Zusammenfassung des übergebenen Texts.
Sie können noch weiter Einfluss auf die Zusammenfassung nehmen, indem Sie beim Aufruf der loadSummarizationChain-Funktion ein Konfigurationsobjekt übergeben. Hier können Sie über die type-Eigenschaft den Typ der Zusammenfassung festlegen. Die SummarizationChain unterstützt hier drei verschiedene Werte:
-
map_reduce: Dieser Typ arbeitet nach dem Map-Reduce-Muster und erzeugt für jeden Textblock eine Zusammenfassung. Diese werden dann zu einer Gesamtzusammenfassung reduziert. Der map_reduce-Typ ist der Standard von LangChain, wenn Sie keinen Typ angeben.
-
refine: Mit diesem Typ bildet die SummarizationChain eine initiale Zusammenfassung über den ersten Block und verfeinert sie mit allen folgenden Blöcken, bis am Ende eine Gesamtzusammenfassung entstanden ist. Diese Strategie eignet sich besonders für sehr lange Texte.
-
stuff: Der stuff-Typ ist die einfachste Art der Zusammenfassung. Alle Textblöcke werden zu einem großen Block zusammengefasst und eine Zusammenfassung darüber gebildet. Diese Strategie ist nicht für Texte geeignet, die länger als das Kontextfenster sind.
Je nachdem, welchen Typ Sie auswählen, können Sie zusätzlich noch Prompt-Templates für die Zusammenfassung angeben und so die Zusammenfassung anpassen. Die Qualität des Ergebnisses hängt sowohl vom verwendeten Modell als auch vom gewählten Typ und zu einem kleinen Teil von den Prompt-Templates ab. Sie sollten mit verschiedenen Kombinationen experimentieren, um ein optimales Ergebnis für Ihre Anforderungen zu erreichen.
Die bisherigen beiden Beispiele waren auf Ihr lokales System beschränkt. LangChain ist jedoch nicht nur auf die Arbeit mit lokalen Interaktionen und Datenquellen beschränkt, wie Sie im nächsten Beispiel sehen werden.
Webseiten analysieren lassen
Sie können LangChain auch dazu verwenden, Webseiten einzulesen und Fragen dazu zu stellen. Sie benötigen dazu ein Modell, Embeddings und das WebBrowser-Werkzeug. Mit dem WebBrowser ist natürlich nicht der Browser auf Ihrem System gemeint, sondern eine Klasse aus dem tools-Modul von LangChain, das die gleiche Rolle wie ein Browser spielt. In Listing 3 sehen Sie, wie Sie mit Hilfe von LangChain und Wikipedia herausfinden können, wann JavaScript erfunden wurde.
Listing 3: Webseiten mit LangChain auswerten
import { WebBrowser } from 'langchain/tools/webbrowser';
import { Ollama, OllamaEmbeddings } from '@langchain/ollama';
const model = new Ollama({
model: 'llama3.2',
});
const embeddings = new OllamaEmbeddings({
model: 'llama3.2',
});
const browser = new WebBrowser({ model, embeddings });
const result = await browser.invoke(
`"https://en.wikipedia.org/wiki/JavaScript", "Wann wurde JavaScript erfunden?"`
);
console.log(result);
Die Grundlage des Beispiels bildet wieder ein Modell wie das Llama 3.2. Außerdem benötigen Sie ein Embeddings-Modell. Dieses erzeugt eine Vektorrepräsentation aus einem Stück Text, die Sie mit LangChain weiterverarbeiten können. LangChain bietet Ihnen eine ganze Reihe von Embeddings wie OpenAI, MistralAI, Nomic oder die im Beispiel verwendeten Ollama Embeddings. Geben Sie nichts weiter an, nutzt die OllamaEmbeddings-Klasse das mxbai-embed-large-Modell. Das Problem mit diesem Modell ist, dass es einen verhältnismäßig kleinen Kontext hat, was schon bei einfachen Webseiten dazu führen kann, dass Sie die Fehlermeldung „Input length exceeds maximum context length“ erhalten. Die einfachste Lösung für dieses Problem ist, dass Sie ein anderes Modell angeben. Mit dem Modell und den Embeddings können Sie schließlich eine Instanz der WebBrowser-Klasse erzeugen. Diese stellt Ihnen die invoke-Methode zur Verfügung, die Sie mit einer Zeichenkette aufrufen. Zuerst geben Sie den URL der Webseite an, die Sie analysieren möchten, in unserem Beispiel ist das die Wikipedia-Seite über JavaScript. Anschließend formulieren Sie einen Prompt. Das WebBrowser-Werkzeug nutzt Axios, um eine Anfrage an den URL zu stellen und wertet die Antwort mit Hilfe des HTML-Parsers cheerio aus. Diese Daten wandelt das Embeddings-Modell in eine Vektorrepräsentation um und stellt sie dem Modell als Kontext zur Verfügung. Führen Sie diesen Code aus, erhalten Sie die Antwort, dass Brendan Eich JavaScript 1995 erfunden hat und zusätzlich dazu noch eine Reihe weiterführender Links.
Stay tuned
Bei neuen Artikel & Eventupdates informiert werden:
Websuche mit LangChain
Neben dem WebBrowser gibt es noch einige weitere Tools für Langchain. Eine Kategorie, die für Applikationen besonders spannend sein können, sind Suchmaschinenschnittstellen, mit denen Sie Ihrer Applikation das Tor zum Internet noch weiter öffnen können. Beim WebBrowser müssen Sie einen einzelnen URL angeben. Mit den Suchmaschinenintegrationen können Sie eine Suchmaschine befragen und erhalten aktuelle Informationen aus dem Internet, die Sie in Ihrer LangChain-Applikation weiterverwenden können. Beispiele für solche Suchmaschinenintegrationen sind die DuckDuckGoSearch oder die TavilySearch. Im folgenden Beispiel sehen Sie, wie Sie die TavilySearch nutzen können. Im einfachsten Fall erzeugen Sie eine Instanz des Werkzeugs und rufen die invoke-Methode mit Ihrer Suchanfrage auf. In Listing 4 sehen Sie jedoch, wie Sie es in eine Kette einbinden können.
Listing 4: Integration der Web-Suche in eine LangChain-Applikation
import { HumanMessage } from '@langchain/core/messages';
import { ChatPromptTemplate } from '@langchain/core/prompts';
import { RunnableLambda } from '@langchain/core/runnables';
import { TavilySearchResults } from '@langchain/community/tools/tavily_search';
import { ChatOllama } from '@langchain/ollama';
const tool = new TavilySearchResults({
maxResults: 2,
});
const model = new ChatOllama({
model: 'llama3.2',
});
const llmWithTools = model.bindTools([tool]);
const prompt = ChatPromptTemplate.fromMessages([
['system', 'You are a helpful assistant.'],
[
'system',
`You have access to the following tool:\n\n${tool.description}\n\nUse this tool whenever you need additional information to answer the user's question.`,
],
['placeholder', '{messages}'],
]);
const chain = prompt.pipe(llmWithTools);
const toolChain = RunnableLambda.from(async (userInput, config) => {
const humanMessage = new HumanMessage(userInput);
const aiMsg = await chain.invoke(
{
messages: [humanMessage],
},
config
);
if (aiMsg.tool_calls && aiMsg.tool_calls.length > 0) {
const toolMsgs = await tool.batch(aiMsg.tool_calls, config);
return chain.invoke(
{
messages: [humanMessage, aiMsg, ...toolMsgs],
},
config
);
} else {
return aiMsg;
}
});
const toolChainResult = await toolChain.invoke(
'What is the current weather in Seattle?'
);
console.log(toolChainResult.content);
Für die Verwendung der Tavily-Suchmaschine benötigen Sie einen API-Key. Die Plattform bietet Ihnen verschiedene Verträge an. Mit dem kostenlosen Einstieg können Sie 1000 Suchanfragen pro Monat stellen. Den API-Key geben Sie über die Umgebungsvariable TAVILY_API_KEY an. Mit der TavilySearchResults-Klasse können Sie eine Instanz erzeugen, die Sie als Werkzeug an das LLM binden können. Dies erfolgt, wie Sie im Code sehen, über die bindTools-Methode der Modell-Klasse. Im Prompt-Template teilen Sie dem LLM mit, dass es auf die Suchmaschinenintegration zugreifen kann. Die Werkzeugintegration bedeutet jedoch nicht, dass das LLM von selbst auf das Such-API zugreifen kann. Es erzeugt lediglich die Anfragen für die Suche, die Sie dann selbst ausführen müssen. Wie das funktioniert, sehen Sie im Aufruf der from-Methode der RunnableLambda-Klasse. Hier kommunizieren Sie zunächst mit dem LLM. Enthält dessen Antwort ein Element in der tool_calls-Eigenschaft, bedeutet das, dass Sie mit dem Tavily API kommunizieren sollten. Dies erfolgt mit der batch-Methode. Das Resultat geben Sie in einem invoke-Aufruf wiederum an das LLM, das die finale Antwort erzeugt. Mit dieser Konfiguration können Sie über das toolChain-Objekt und dessen invoke-Methode so über einen kleinen Umweg Ihr LLM an eine Suchmaschine anschließen.
Mit den Bausteinen, die Sie bis jetzt kennengelernt haben, können Sie noch viele weitere Anwendungsfälle umsetzen. Eines der wohl populärsten Beispiele einer KI-Anwendung ist nach dem Chatbot aktuell wohl RAG. Mit LangChain können Sie eine solche Applikation mit nur wenigen Zeilen umsetzen.
RAG mit LangChain
RAG steht für Retrieval Augmented Generation und ist eine Technik, mit deren Hilfe Sie die Möglichkeiten eines LLMs noch deutlich erweitern können. Sie können der Applikation aktuelle und spezialisierte Informationen zur Verfügung stellen, ohne dass Sie das LLM modifizieren müssen. RAG besteht aus zwei Teilen:
-
Retriever: Der Retriever sammelt die passenden Informationen, meist liegen diese in Form von Vektoren vor.
-
Generator: Die Informationen des Retrievers nutzt der Generator, um die Antwort auf einen Prompt zu erzeugen.
Das folgende Beispiel soll eine PDF-Datei zum Thema Python einlesen, verarbeiten und es Ihnen ermöglichen, Fragen zu stellen. Den Code für die RAG-Kette finden Sie in Listing 5.
Listing 5: Implementierung einer RAG-Applikation mit LangChain
import { Ollama, OllamaEmbeddings } from '@langchain/ollama';
import { PDFLoader } from '@langchain/community/document_loaders/fs/pdf';
import { RecursiveCharacterTextSplitter } from 'langchain/text_splitter';
import { MemoryVectorStore } from 'langchain/vectorstores/memory';
import { ChatPromptTemplate } from '@langchain/core/prompts';
import {
RunnablePassthrough,
RunnableSequence,
} from '@langchain/core/runnables';
import { formatDocumentsAsString } from 'langchain/util/document';
import { StringOutputParser } from '@langchain/core/output_parsers';
const filePath = './python.pdf';
const loader = new PDFLoader(filePath);
const docs = await loader.load();
const textSplitter = new RecursiveCharacterTextSplitter({
chunkSize: 1000,
chunkOverlap: 200,
});
const splits = await textSplitter.splitDocuments(docs);
const embeddings = new OllamaEmbeddings();
const vectorStore = await MemoryVectorStore.fromDocuments(splits, embeddings);
const retriever = vectorStore.asRetriever();
const prompt = ChatPromptTemplate.fromMessages([
`You are an assistant for question-answering tasks. Use the following pieces of retrieved context to answer the question.
If you don't know the answer, just say that you don't know. Use three sentences maximum and keep the answer concise.
Question: {question}
Context: {context}
Answer:`,
]);
const model = new Ollama({
model: 'llama3.2',
});
const ragChain = RunnableSequence.from([
{
context: (...args) => retriever.pipe(formatDocumentsAsString),
question: new RunnablePassthrough(),
},
prompt,
model,
new StringOutputParser(),
]);
const result = await ragChain.invoke(
'Was sind die Unterschiede zwischen list und tuple in Python'
);
console.log(result);
Der Code gliedert sich in zwei Teile. Im ersten Teil verarbeiten Sie die Informationen aus der PDF-Datei und im zweiten Teil arbeiten Sie mit dieser Datenquelle. Zunächst nutzen Sie den PDFLoader, um die Datei zu laden. Anschließend nutzen Sie den RecursiveCharacterTextSplitter, um Textblöcke mit 1000 Zeichen zu erzeugen. Die Blöcke überlappen sich dabei um 200 Zeichen. Die Überlappung erhöht die Genauigkeit und Relevanz der Antworten und stellt sicher, dass keine Informationen verloren gehen, wenn der Text beliebig unterteilt wird.
Die Textblöcke werden im nächsten Schritt in einen Vektorspeicher eingefügt. Für die Umwandlung des Texts in Vektoren kommen wieder die OllamaEmbeddings zum Einsatz. In dieser Variante können Sie das Standard-Embeddings-Modell mxbai-mebed-large verwenden, da die Textblöcke problemlos in dessen Kontext passen. Als Vektorspeicher nutzt das Beispiel den MemoryVectorStore von LangChain. Sie können an dieser Stelle aber auch eine persistente Vektordatenbank, wie beispielsweise FAISS oder ChromaDB, verwenden. Der größte Vorteil dieser Implementierung mit dem MemoryVectorStore ist, dass die Vektoren bei jedem Durchlauf erneut aufgebaut werden müssen, was unnötig Zeit in Anspruch nimmt. Arbeiten Sie also an einer Produktivapplikation, sollten Sie in jedem Fall auf eine Vektordatenbank zurückgreifen.
Sobald Sie den Datenspeicher aufgebaut haben, können Sie sich dem eigentlichen Teil der RAG-Applikation widmen und die RAG-Kette aufbauen. Die Kette besteht aus der Eingabe, einem Prompt-Template, dem Modell und einem Output-Parser. Die Besonderheit im Vergleich zu den bisherigen Implementierungen liegt hier im Einstieg in die Kette. Das Prompt-Template arbeitet mit zwei Platzhaltern: context und question. Die question, also die eigentliche Prompt-Eingabe aus dem Aufruf der invoke-Methode, wird unverändert an das Modell weitergereicht, dafür sorgt die RunnablePassthrough-Instanz. Beim Kontext sucht der Retriever nach relevanten Informationen aus dem Vektorspeicher. Die Resultate werden mit der formatDocumentsAsString in einen Stringwert umgewandelt, den das Modell als Kontext verwenden kann.
Die übrige Kette arbeitet dann ganz ähnlich wie die Kette im ersten Beispiel des Chatbots. Rufen Sie die invoke-Methode mit einer beliebigen Frage auf, liefert der Retriever den Kontext, fügt ihn ins Prompt-Template ein und übergibt es ans Modell. Der OutputParser extrahiert die Antwort und liefert sie als Zeichenkette aus.
Die Laufzeit der Applikation hängt von zahlreichen Faktoren ab. Entscheidend sind vor allem die Ressourcen des Systems, auf dem Sie sie ausführen. Steht eine leistungsstarke Grafikkarte zur Verfügung, beschleunigt das den Ablauf erheblich. Auch die Größe der PDF-Datei hat Einfluss auf die Laufzeit, je größer die Datei, desto mehr Textblöcke müssen gebildet, in Vektoren umgewandelt und in der Vektordatenbank gespeichert werden. Damit Sie einen Eindruck über die Verteilung der Laufzeit bekommen, sehen Sie hier eine Messung der Ausführung der einzelnen Applikationsteile:
-
Laden der PDF-Datei: 1 Sekunde
-
Aufteilen der Datei in Textblöcke: 0,2 Sekunden
-
Erzeugen und Speichern der Vektoren: 2,5 Minuten
-
Erzeugen der RAG-Kette: 0,3 Sekunden
-
Aufruf der invoke-Methode: 3,5 Sekunden
Wie Sie sehen, nimmt die Umwandlung der Textblöcke in Vektoren und deren Speicherung den mit Abstand größten Teil der gesamten Operation in Anspruch. Da sich aber weder der Inhalt des Dokuments noch die daraus abgeleiteten Vektoren verändern, müssen Sie diese Operation auch nur einmalig durchführen. Der übrige Prozess läuft verhältnismäßig schnell ab und kann durch ein leistungsstärkeres System oder ein kleineres Modell noch beschleunigt werden.
Fazit
LangChain erfindet das Rad der KI-Applikation nicht neu, sondern stellt Ihnen vielmehr eine Plattform mit einheitlichen Schnittstellen und einem mächtigen Baukastensystem zur Verfügung. Da es die Plattform sowohl für Python als auch für JavaScript bzw. TypeScript gibt, ist die Community auch entsprechend groß und die vorgefertigten und beschriebenen Lösungsansätze sind vielfältig. LangChain schreibt Ihnen nicht vor, welches Sprachmodell Sie für Ihre Applikation nutzen sollen, durch die nahtlose Integration vieler verschiedener Modelle von dem kommerziellen OpenAI API bis hin zu lokal gehosteten LLMs über Plattformen wie Ollama haben Sie die maximale Flexibilität für Ihre Lösung.
Die möglichen Anwendungsfälle reichen von den hier gezeigten typischen Applikationen wie Chatbots oder RAG-Applikationen bis hin zu komplexen Applikationen mit sehr langen Ketten.
LangChain ist so aufgebaut, dass Sie es als Standalone-Framework nutzen können und in jede beliebige Webapplikation integrieren können. Zusätzlich dazu gibt es um LangChain noch ein leistungsstarkes Ökosystem, das aus verschiedenen Open-Source- und kommerziellen Komponenten besteht:
-
LangChain: Ein Open-Source-Framework, das die Integration von Sprachmodellen, Prompts und Datenbanken erleichtert. Mit ihm können Sie komplexe Ketten für LLM-basierte Applikationen erzeugen.
-
LangGraph: Mit diesem Framework aus dem LangChain-Ökosystem können Sie Multi-Agenten-Systeme als gerichtete Graphen verwalten. Dabei definieren Sie die Logik der Agenten und können Themen wie Skalierbarkeit und Fehlertoleranz abdecken. LangGraph ist wie LangChain ebenfalls ein Open-Source-Projekt, das Sie ohne zusätzliche Kosten in Ihre Applikation integrieren können.
-
LangGraph Cloud: Eine kostenpflichtige Cloudumgebung für LangGraph-Applikationen. Durch die Infrastruktur können Sie Ihre Applikation einfacher skalieren und ausfallsicher gestalten.
-
LangSmith: Diese Entwicklungsplattform unterstützt Sie in allen Schritten des Entwicklungsprozesses einer LLM-Applikation. Der Fokus liegt auf dem Tracing, das Ihnen Einblicke in jede Stufe der Ketten und Agenten einer Applikation gibt.
Hinter diesem Ökosystem steht das Unternehmen LangChain, Inc., das sich aktiv um die Weiterentwicklung der einzelnen Komponenten als auch um die Erbringung der Dienste kümmert. Der Vorteil an diesem Ökosystem ist, dass Sie ohne Einschränkungen mit den Open-Source-Komponenten arbeiten und selbst entscheiden können, ob Sie die kostenpflichtigen Dienste in Anspruch nehmen möchten.
🔍 Frequently Asked Questions (FAQ)
1. Was ist LangChain?
LangChain ist ein Open-Source-Framework, das die Integration von LLMs (Large Language Models) wie OpenAI, Ollama oder Google Gemini erleichtert. Es bietet modulare Bausteine für Prompts, Vektordatenbanken und Ketten.
2. Was kann man mit LangChain machen?
Mit LangChain lassen sich Chatbots, RAG-Anwendungen, Textzusammenfassungen und sogar Websuchen entwickeln. Das Framework eignet sich für viele praxisnahe KI-Szenarien.
3. Wie funktioniert die Integration von LLMs mit LangChain?
LangChain stellt einheitliche Schnittstellen und Tools bereit, um LLMs flexibel anzubinden. Entwickler können Modelle austauschen, Prompts gestalten und Datenquellen wie PDFs oder Webseiten einbinden.
4. Wer nutzt LangChain?
LangChain wird vor allem von Softwareentwicklern und KI-Teams genutzt, die Anwendungen mit JavaScript, TypeScript oder Python entwickeln. Durch seine Community und Dokumentation ist es für Einsteiger und Profis interessant.
5. Welche Vorteile bietet LangChain gegenüber direkter API-Nutzung?
Statt jede API einzeln zu integrieren, liefert LangChain eine einheitliche Architektur. Das spart Zeit, erhöht die Flexibilität und macht komplexe LLM-Workflows einfacher umsetzbar.
6. Welche Rolle spielt RAG in LangChain?
RAG (Retrieval Augmented Generation) ermöglicht es, externe Informationen in die Antworten eines Modells einzubeziehen. LangChain bietet fertige Bausteine, um RAG-Anwendungen mit Vektordatenbanken schnell umzusetzen.
7. In welchen Sprachen kann man LangChain nutzen?
LangChain steht für JavaScript, TypeScript und Python zur Verfügung. Alle Varianten folgen denselben Konzepten und sind mit vielen Integrationen kompatibel.