AI-Entwicklung & LangChain

LangChain.js mit Claude Code: LLM-Anwendungen 2026

ChatModels, LCEL, Prompt Templates, RAG, Agents und LangGraph — der vollständige Praxis-Guide für produktionsreife KI-Apps in TypeScript.

6. Mai 2026 · 12 min Lesezeit · TypeScript / LangChain 0.3

LangChain.js hat sich in 2026 zur führenden Framework-Lösung für produktionsreife LLM-Anwendungen in JavaScript und TypeScript entwickelt. In Kombination mit Claude Code — Anthropics leistungsstarkem CLI-Agenten — entstehen Workflows, die früher Wochen an Entwicklungszeit erfordert hätten, jetzt in Stunden. Dieser Guide zeigt dir die wichtigsten Bausteine: von der ersten ChatModel-Integration bis hin zu komplexen State-Maschinen mit LangGraph.

Die Beispiele basieren auf LangChain 0.3.x, dem aktuellen Stable Release, und nutzen durchgängig TypeScript für maximale Typsicherheit. Alle Snippets sind direkt ausführbar — ohne Boilerplate, ohne Ablenkung.

Voraussetzungen

Node.js ≥ 20, TypeScript ≥ 5.4, ein OpenAI- oder Anthropic-API-Key. Claude Code als lokaler Dev-Agent empfohlen.

1. LangChain Setup & ChatModels

Der Einstieg in LangChain.js beginnt mit der Installation der relevanten Pakete. Das Kernpaket langchain stellt alle abstrakten Klassen bereit; die Provider-Pakete (@langchain/openai, @langchain/anthropic) liefern die konkreten Implementierungen.

Setup Pakete installieren
bash
# Basis-Installation npm install langchain @langchain/core # Provider (je nach Bedarf) npm install @langchain/openai # GPT-4o, Embeddings npm install @langchain/anthropic # Claude 3.5, Claude 3.7 npm install @langchain/google-genai # Gemini 1.5 # TypeScript Dev-Dependencies npm install -D typescript ts-node @types/node

ChatModel: Das universelle Interface

Alle LangChain-Provider implementieren das BaseChatModel-Interface. Das bedeutet: du kannst zwischen GPT-4o, Claude 3.7 und Gemini wechseln, ohne deinen Business-Code anzufassen — nur die Initialisierung ändert sich.

src/models/chat-models.ts
import { ChatOpenAI } from "@langchain/openai"; import { ChatAnthropic } from "@langchain/anthropic"; import { HumanMessage, SystemMessage, AIMessage } from "@langchain/core/messages"; // GPT-4o mit Streaming const gpt4o = new ChatOpenAI({ model: "gpt-4o", temperature: 0.7, streaming: true, apiKey: process.env.OPENAI_API_KEY, }); // Claude 3.7 Sonnet — aktuell stärkstes Anthropic-Modell const claude37 = new ChatAnthropic({ model: "claude-3-7-sonnet-20250219", temperature: 0.5, maxTokens: 8192, apiKey: process.env.ANTHROPIC_API_KEY, }); // Einfacher Invoke-Call mit Messages-Array async function basicChat() { const response = await claude37.invoke([ new SystemMessage("Du bist ein präziser TypeScript-Experte."), new HumanMessage("Erkläre Generics in TypeScript in 3 Sätzen."), ]); console.log(response.content); console.log("Tokens verwendet:", response.usage_metadata?.total_tokens); } // Streaming für lange Antworten async function streamChat() { const stream = await gpt4o.stream([ new HumanMessage("Schreibe einen Blogbeitrag über LangChain.js"), ]); for await (const chunk of stream) { process.stdout.write(chunk.content as string); } }

Model-Auswahl 2026: Welches Modell für welchen Use-Case?

Modell Stärken Typischer Use-Case Kosten/1M Tokens
Claude 3.7 Sonnet Reasoning, Code, Long Context Komplexe Agents, Code-Review ~$3 In / $15 Out
GPT-4o Multimodal, Schnelligkeit Chat-UIs, Image-Input ~$2.50 In / $10 Out
Claude 3.5 Haiku Geschwindigkeit, niedrige Latenz Klassifikation, schnelle NLP ~$0.25 In / $1.25 Out
Gemini 1.5 Flash 1M Context Window Dokumenten-Analyse, Long RAG ~$0.075 In / $0.30 Out
Tipp: Provider-Wechsel ohne Code-Änderungen

Setze LANGCHAIN_MODEL als Umgebungsvariable und lade das Modell dynamisch. So kannst du in der Entwicklung günstigere Modelle nutzen und in Produktion auf leistungsstärkere umschalten.

2. LCEL: LangChain Expression Language

Die LangChain Expression Language (LCEL) ist das Herzstück von LangChain 0.3. Statt imperativer Verkettung nutzt LCEL den Pipe-Operator (|) um Komponenten deklarativ zu verbinden — ähnlich wie Unix-Pipes oder RxJS-Streams.

LCEL Das Pipe-Prinzip

Jede LCEL-Komponente implementiert das Runnable-Interface mit invoke(), stream() und batch(). Der Pipe-Operator (pipe() oder |) gibt eine neue RunnableSequence zurück.

src/chains/lcel-basics.ts
import { ChatOpenAI } from "@langchain/openai"; import { ChatPromptTemplate } from "@langchain/core/prompts"; import { StringOutputParser } from "@langchain/core/output_parsers"; import { RunnableSequence, RunnableParallel } from "@langchain/core/runnables"; const model = new ChatOpenAI({ model: "gpt-4o-mini", temperature: 0 }); const parser = new StringOutputParser(); // ---- Einfache LCEL-Chain ---- const simplePrompt = ChatPromptTemplate.fromTemplate( "Erkläre {thema} in {niveau}-Niveau auf Deutsch." ); // pipe() ist der LCEL-Operator — gibt RunnableSequence zurück const erklaerungsChain = simplePrompt.pipe(model).pipe(parser); const antwort = await erklaerungsChain.invoke({ thema: "Vektorräume", niveau: "Anfänger", }); // ---- RunnableParallel: parallele Ausführung ---- const parallelChain = RunnableParallel.from({ zusammenfassung: ChatPromptTemplate.fromTemplate( "Fasse zusammen: {text}" ).pipe(model).pipe(parser), stimmung: ChatPromptTemplate.fromTemplate( "Welche Stimmung hat dieser Text? {text} Antworte: positiv/neutral/negativ" ).pipe(model).pipe(parser), }); // Beide Ketten laufen PARALLEL — spart ~50% Latenz const ergebnis = await parallelChain.invoke({ text: "LangChain.js revolutioniert die KI-Entwicklung in 2026.", }); // ergebnis.zusammenfassung, ergebnis.stimmung // ---- withFallbacks: Robustheit gegen API-Fehler ---- const primaerModel = new ChatOpenAI({ model: "gpt-4o" }); const fallbackModel = new ChatAnthropic({ model: "claude-3-5-haiku-20241022" }); const robustModel = primaerModel.withFallbacks({ fallbacks: [fallbackModel], });

RunnableSequence manuell aufbauen

Für komplexere Workflows — z.B. wenn du Zwischenergebnisse transformieren willst — baust du RunnableSequence explizit:

src/chains/manual-sequence.ts
import { RunnableSequence, RunnableLambda } from "@langchain/core/runnables"; // RunnableLambda: beliebige Funktion als Runnable const textBereinigerRunnable = RunnableLambda.from((input: string) => { return input.trim().toLowerCase().replace(/\s+/g, " "); }); const vollChain = RunnableSequence.from([ textBereinigerRunnable, // 1. Eingabe bereinigen simplePrompt, // 2. Prompt befüllen model, // 3. LLM aufrufen parser, // 4. Output parsen ]); // Batch-Verarbeitung: mehrere Inputs parallel const themen = [ { thema: "Embeddings", niveau: "Experte" }, { thema: "RAG", niveau: "Anfänger" }, { thema: "LangGraph", niveau: "Fortgeschrittene" }, ]; // Alle 3 parallel ausführen const ergebnisse = await erklaerungsChain.batch(themen, { maxConcurrency: 3, });
LCEL-Gotcha: Input-Typen

Der erste Schritt in einer Chain bestimmt den Input-Typ. Wenn dein Prompt {"{"}{"{thema}"}, {"{niveau}"} erwartet, musst du ein Objekt übergeben — kein String. TypeScript warnt hier zuverlässig.

3. Prompt Templates & OutputParsers

Gute Prompts sind strukturiert, wiederverwendbar und typsicher. LangChain bietet ChatPromptTemplate, StructuredOutputParser und JsonOutputParser für alle gängigen Patterns.

Prompts ChatPromptTemplate mit System & Human Messages
src/prompts/structured-prompts.ts
import { ChatPromptTemplate, MessagesPlaceholder } from "@langchain/core/prompts"; import { StructuredOutputParser, JsonOutputParser } from "@langchain/core/output_parsers"; import { z } from "zod"; // Komplexes Template mit System + Human + MessagesPlaceholder const chatTemplate = ChatPromptTemplate.fromMessages([ [ "system", `Du bist {persona}. Deine Stärken: {staerken}. Antworte IMMER auf Deutsch. Sei präzise, nicht länger als {max_saetze} Sätze.`, ], new MessagesPlaceholder("chat_history"), // dynamische History ["human", "{user_input}"], ]); // partial(): Werte vorausfüllen — nützlich für fixe Konfiguration const codeReviewTemplate = await chatTemplate.partial({ persona: "Senior TypeScript-Entwickler", staerken: "Clean Code, SOLID, Performance", max_saetze: "5", }); // StructuredOutputParser mit Zod-Schema const reviewSchema = z.object({ qualitaet: z.enum(["sehr gut", "gut", "verbesserungswürdig", "schlecht"]), probleme: z.array(z.string()).describe("Liste der gefundenen Probleme"), empfehlungen: z.array(z.string()).describe("Konkrete Verbesserungsvorschläge"), score: z.number().min(0).max(10).describe("Code-Qualitäts-Score 0-10"), }); const strukturierterParser = StructuredOutputParser.fromZodSchema(reviewSchema); // Format-Anweisungen automatisch generieren und in Prompt einfügen const formatAnweisungen = strukturierterParser.getFormatInstructions(); const reviewPrompt = ChatPromptTemplate.fromTemplate( `Reviewe folgenden TypeScript-Code:\n\n{code}\n\n{format_anweisungen}` ); const reviewChain = reviewPrompt .pipe(model) .pipe(strukturierterParser); const reviewErgebnis = await reviewChain.invoke({ code: `function add(a, b) { return a + b; }`, format_anweisungen: formatAnweisungen, }); // reviewErgebnis ist vollständig typisiert dank Zod! // reviewErgebnis.score, reviewErgebnis.probleme usw.

JsonOutputParser: Flexibel ohne Schema

src/parsers/json-parser.ts
import { JsonOutputParser } from "@langchain/core/output_parsers"; // Wenn du kein fixes Schema willst — z.B. für dynamische Strukturen const jsonParser = new JsonOutputParser(); const extraktionPrompt = ChatPromptTemplate.fromTemplate( `Extrahiere alle Entitäten aus diesem Text als JSON-Array. Format: [{{"name": "...", "typ": "Person|Ort|Organisation", "kontext": "..."}}] Text: {text}` ); const entitaetenChain = extraktionPrompt .pipe(model) .pipe(jsonParser); // Streaming JSON — für Echtzeit-Updates in UI const stream = await entitaetenChain.stream({ text: "Elon Musk gründete SpaceX in Hawthorne, Kalifornien.", }); for await (const chunk of stream) { // Partial JSON-Objekte als sie ankommen console.log(chunk); }

4. RAG mit Vectorstore

Retrieval Augmented Generation (RAG) ist das meistgenutzte LangChain-Pattern in Produktionsanwendungen. Das Prinzip: Eigene Dokumente werden in Embeddings umgewandelt, in einem Vectorstore gespeichert und bei Anfragen semantisch gesucht — der LLM bekommt nur relevante Kontext-Chunks.

RAG Vollständige RAG-Pipeline

Document Loading → Text Splitting → Embedding → Vectorstore → Retrieval → LLM

src/rag/pipeline.ts
import { OpenAIEmbeddings } from "@langchain/openai"; import { MemoryVectorStore } from "langchain/vectorstores/memory"; import { RecursiveCharacterTextSplitter } from "langchain/text_splitter"; import { PDFLoader } from "@langchain/community/document_loaders/fs/pdf"; import { CheerioWebBaseLoader } from "@langchain/community/document_loaders/web/cheerio"; import { createRetrievalChain } from "langchain/chains/retrieval"; import { createStuffDocumentsChain } from "langchain/chains/combine_documents"; // 1. Dokumente laden const pdfLoader = new PDFLoader("./dokumente/handbuch.pdf"); const webLoader = new CheerioWebBaseLoader("https://agentic-movers.com/docs"); const rawDocs = [ ...await pdfLoader.load(), ...await webLoader.load(), ]; // 2. Text aufteilen — kritisch für Retrieval-Qualität const splitter = new RecursiveCharacterTextSplitter({ chunkSize: 1000, // Zeichen pro Chunk chunkOverlap: 200, // Überlappung für Kontext-Kontinuität separators: ["\n\n", "\n", " ", ""], }); const splitDocs = await splitter.splitDocuments(rawDocs); // 3. Embeddings + Vectorstore erstellen const embeddings = new OpenAIEmbeddings({ model: "text-embedding-3-small", // billig + gut genug dimensions: 1536, }); // MemoryVectorStore für Entwicklung; in Produktion: Pinecone, Supabase, Chroma const vectorStore = await MemoryVectorStore.fromDocuments( splitDocs, embeddings ); // 4. Retriever konfigurieren const retriever = vectorStore.asRetriever({ k: 4, // Top-4 Chunks zurückgeben searchType: "similarity", // oder "mmr" für Diversität }); // 5. RAG-Chain zusammenbauen const ragPrompt = ChatPromptTemplate.fromMessages([ ["system", `Du bist ein hilfreicher Assistent. Antworte NUR basierend auf dem Kontext: Kontext: {context}`], ["human", "{input}"], ]); const dokumentenChain = await createStuffDocumentsChain({ llm: model, prompt: ragPrompt }); const ragChain = await createRetrievalChain({ retriever: retriever, combineDocsChain: dokumentenChain, }); // 6. Abfrage const antwort = await ragChain.invoke({ input: "Wie richte ich die Authentifizierung ein?", }); console.log(antwort.answer); // LLM-Antwort console.log(antwort.context); // Verwendete Dokument-Chunks

Produktions-Vectorstores

Für Produktionsanwendungen brauchst du persistente, skalierbare Vectorstores. LangChain unterstützt alle gängigen Optionen:

RAG Supabase pgvector als Vectorstore
src/rag/supabase-vectorstore.ts
import { SupabaseVectorStore } from "@langchain/community/vectorstores/supabase"; import { createClient } from "@supabase/supabase-js"; const supabase = createClient( process.env.SUPABASE_URL!, process.env.SUPABASE_ANON_KEY! ); // Dokumente in Supabase einfügen const store = await SupabaseVectorStore.fromDocuments( splitDocs, embeddings, { client: supabase, tableName: "documents", queryName: "match_documents", } ); // Existierenden Store laden const existingStore = new SupabaseVectorStore(embeddings, { client: supabase, tableName: "documents", queryName: "match_documents", });
RAG-Qualität verbessern

Nutze HyDE (Hypothetical Document Embeddings): Lass das LLM erst eine hypothetische Antwort generieren, embedde diese und nutze sie für die Suche. Das verbessert die Recall-Rate um oft 20–40%.

5. Agents & Tools

Agents sind das mächtigste Muster in LangChain: Das LLM entscheidet dynamisch, welche Tools es in welcher Reihenfolge aufruft, um eine Aufgabe zu lösen. LangChain bietet createReactAgent als Standard-Implementierung des ReAct-Patterns (Reason + Act).

Agent Eigene Tools definieren mit DynamicTool
src/agents/tools.ts
import { DynamicTool, DynamicStructuredTool } from "@langchain/core/tools"; import { createReactAgent } from "langchain/agents"; import { AgentExecutor } from "langchain/agents"; import { z } from "zod"; // Einfaches Tool — Input ist immer ein String const wetterTool = new DynamicTool({ name: "get_weather", description: "Gibt aktuelle Wetterdaten für eine Stadt zurück. Input: Stadtname.", func: async (stadtName: string) => { // Echter API-Call const response = await fetch( `https://api.openweathermap.org/data/2.5/weather?q=${stadtName}&appid=${process.env.OWM_KEY}&units=metric&lang=de` ); const data = await response.json(); return `${data.name}: ${data.main.temp}°C, ${data.weather[0].description}`; }, }); // StructuredTool — mehrere typisierte Parameter via Zod const datenbankTool = new DynamicStructuredTool({ name: "query_database", description: "Führt eine Datenbankabfrage durch und gibt die Ergebnisse zurück.", schema: z.object({ tabelle: z.string().describe("Name der Tabelle"), limit: z.number().default(10).describe("Maximale Anzahl Ergebnisse"), filter: z.string().optional().describe("WHERE-Klausel (optional)"), }), func: async ({ tabelle, limit, filter }) => { // Sanitize und Query ausführen const sql = `SELECT * FROM ${tabelle} ${filter ? `WHERE ${filter}` : ""} LIMIT ${limit}`; // ... DB-Call hier return JSON.stringify({ rows: [], sql }); }, }); // Calculator Tool (eingebaut) import { Calculator } from "@langchain/community/tools/calculator"; const calculator = new Calculator(); // Agent erstellen — ReAct-Pattern const tools = [wetterTool, datenbankTool, calculator]; const agent = await createReactAgent({ llm: model, tools: tools, }); const agentExecutor = new AgentExecutor({ agent: agent, tools: tools, verbose: true, // Thought-Action-Observation loggen maxIterations: 10, // Endlosschleifen verhindern returnIntermediateSteps: true, }); // Agent aufrufen const ergebnis = await agentExecutor.invoke({ input: "Wie ist das Wetter in Berlin? Und wieviel ist 847 * 293?", }); console.log(ergebnis.output); console.log("Schritte:", ergebnis.intermediateSteps.length);

Agent mit Memory: Gesprächsverlauf merken

src/agents/agent-with-memory.ts
import { ChatMessageHistory } from "langchain/stores/message/in_memory"; import { RunnableWithMessageHistory } from "@langchain/core/runnables"; // In-Memory History (für Produktion: Redis, Supabase etc.) const messageStore: Record<string, ChatMessageHistory> = {}; function getSessionHistory(sessionId: string): ChatMessageHistory { if (!messageStore[sessionId]) { messageStore[sessionId] = new ChatMessageHistory(); } return messageStore[sessionId]; } // Agent mit persistenter History wrappen const agentMitMemory = new RunnableWithMessageHistory({ runnable: agentExecutor, getMessageHistory: getSessionHistory, inputMessagesKey: "input", historyMessagesKey: "chat_history", }); // Session-basierter Aufruf const config = { configurable: { sessionId: "user-42" } }; await agentMitMemory.invoke({ input: "Mein Name ist Max." }, config); await agentMitMemory.invoke({ input: "Wie heiße ich?" }, config); // → "Du heißt Max." ✓ (History wird automatisch mitgegeben)

6. LangGraph: State Machines für komplexe Workflows

LangGraph ist die nächste Evolutionsstufe nach einfachen Agents. Statt linearer Ketten baut LangGraph gerichtete Graphen mit zustandsbehafteten Nodes — ideal für Multi-Step-Reasoning, Human-in-the-Loop und Fehlerbehandlung.

LangGraph StateGraph: Kernkonzepte
src/langgraph/research-agent.ts
import { StateGraph, Annotation, END, START } from "@langchain/langgraph"; import { MemorySaver } from "@langchain/langgraph"; import { BaseMessage, HumanMessage, AIMessage } from "@langchain/core/messages"; // State-Definition: Was wissen alle Nodes? const StateAnnotation = Annotation.Root({ messages: Annotation<BaseMessage[]>({ reducer: (x, y) => x.concat(y), // Messages werden akkumuliert default: () => [], }), recherche_ergebnis: Annotation<string>({ default: () => "" }), qualitaets_check: Annotation<boolean>({ default: () => false }), iterationen: Annotation<number>({ reducer: (x, y) => x + y, default: () => 0, }), }); type GraphState = typeof StateAnnotation.State; // NODE 1: Recherche durchführen async function rechercheNode(state: GraphState) { const letzteMessage = state.messages.at(-1)!; const recherchePrompt = `Recherchiere zu: ${letzteMessage.content} Gib eine strukturierte Zusammenfassung mit Quellen.`; const antwort = await model.invoke([new HumanMessage(recherchePrompt)]); return { recherche_ergebnis: antwort.content as string, messages: [antwort], iterationen: 1, }; } // NODE 2: Qualitätsprüfung async function qualitaetsNode(state: GraphState) { const pruefPrompt = `Prüfe diese Recherche auf Vollständigkeit und Korrektheit. Antworte NUR mit "BESTANDEN" oder "NACHBESSERN: [Grund]" Recherche: ${state.recherche_ergebnis}`; const pruefErgebnis = await model.invoke([new HumanMessage(pruefPrompt)]); const bestanden = (pruefErgebnis.content as string).includes("BESTANDEN"); return { qualitaets_check: bestanden }; } // CONDITIONAL EDGE: Soll nachgebessert werden? function sollNachbessern(state: GraphState): string { if (state.qualitaets_check) return "fertig"; if (state.iterationen >= 3) return "fertig"; // Max 3 Versuche return "nachbessern"; } // Graph zusammenbauen const graph = new StateGraph(StateAnnotation) .addNode("recherche", rechercheNode) .addNode("qualitaet", qualitaetsNode) .addEdge(START, "recherche") .addEdge("recherche", "qualitaet") .addConditionalEdges("qualitaet", sollNachbessern, { fertig: END, nachbessern: "recherche", // Schleife zurück zur Recherche }); // Checkpointer für Human-in-the-Loop und Persistenz const checkpointer = new MemorySaver(); const app = graph.compile({ checkpointer }); // Graph ausführen const finalState = await app.invoke( { messages: [new HumanMessage("LangGraph State Machines in 2026")] }, { configurable: { thread_id: "thread-1" } } ); console.log("Iterationen:", finalState.iterationen); console.log("Ergebnis:", finalState.recherche_ergebnis);

Streaming in LangGraph

src/langgraph/streaming.ts
// Echtzeit-Updates aus dem Graph streamen const streamConfig = { configurable: { thread_id: "stream-1" } }; for await (const event of await app.stream( { messages: [new HumanMessage("Analysiere KI-Trends 2026")] }, { ...streamConfig, streamMode: "updates" } )) { // Jeder Node-Output wird sofort gestreamt console.log("Node:", Object.keys(event)[0]); console.log("Output:", Object.values(event)[0]); } // Human-in-the-Loop: Graph an Breakpoint pausieren const appMitHITL = graph.compile({ checkpointer, interruptBefore: ["qualitaet"], // Vor Qualitäts-Node pausieren }); // Erster Run bis Breakpoint await appMitHITL.invoke( { messages: [new HumanMessage("Recherchiere Quantencomputer")] }, { configurable: { thread_id: "hitl-1" } } ); // Mensch prüft State, gibt Freigabe const currentState = await appMitHITL.getState({ configurable: { thread_id: "hitl-1" } }); // → currentState.values.recherche_ergebnis anzeigen, Mensch entscheidet // Resume: null als Input = weiterlaufen lassen await appMitHITL.invoke(null, { configurable: { thread_id: "hitl-1" } });

LangSmith Tracing: Production Observability

.env
# LangSmith Tracing aktivieren (kostenlos bis 100k Traces/Mo) LANGCHAIN_TRACING_V2=true LANGCHAIN_API_KEY=lsv2_pt_xxxxx LANGCHAIN_PROJECT=mein-langchain-projekt

Sobald diese Umgebungsvariablen gesetzt sind, werden alle LangChain- und LangGraph-Aufrufe automatisch zu LangSmith übertragen — inkl. Tokens, Latenz, Errors und Zwischenschritte. Kein Code-Change nötig.

LangGraph vs. einfache Agents: Wann was?

Nutze AgentExecutor für einfache Tool-Nutzung mit klarem Ende. Nutze LangGraph wenn du Schleifen, Fehlerbehandlung, parallele Branches, Human-in-the-Loop oder persistenten State zwischen mehreren Gesprächen brauchst.

LangChain.js TypeScript LCEL RAG LangGraph Claude Code AI Agents Vectorstore OpenAI Anthropic

LangChain.js mit KI-Agenten automatisieren

Starte deinen kostenlosen Trial und entdecke, wie Claude Code deine LangChain-Entwicklung beschleunigt — von der ersten Chain bis zur produktionsreifen Agent-Pipeline.

Kostenlos starten — kein Kreditkarte

← Alle Blogbeiträge