Startseite / Artikel / LangChain 1.x in der Praxis: Ketten, RAG, Tools und Agenten lokal

LangChain 1.x in der Praxis: Ketten, RAG, Tools und Agenten lokal

Lernen Sie, mit LangChain 1.x und einer kostenlosen lokalen Ollama-Installation Ketten, retrieval-augmented Generation, Tools sowie agentebasierte RAG-Lösungen zu erstellen – ohne Notwendigkeit von API-Schlüsseln.

3586 Wörter

Dies ist der dritte Teil einer praktischen Serie, die auf früheren Arbeiten zur Erstellung von RAG-Systemen und Agenten direkt in Python aufbaut. Der Ansatz hier ist bewusst anders: Anstatt jedes Komponententeil selbst zusammenzustellen, sehen Sie, wie LangChain dieselben Funktionen in nur wenigen Zeilen bereitstellt. Da Sie die zugrundeliegenden Komponenten bereits selbst erstellt haben, sind Sie in einer guten Position, zu verstehen, was jede Abstraktion tatsächlich bewirkt – anstatt sie als Magie zu betrachten. Dieses Verständnis ist entscheidend: Es macht den Unterschied zwischen Entwicklern, die LangChain effektiv nutzen, und denen, die ständig dagegen ankämpfen müssen.

Alles in diesem Tutorial läuft lokal und kostenlos, wobei ein lokales Modell über Ollama zusammen mit lokalen Embeddings verwendet wird. Es sind weder API-Schlüssel noch Rate Limits erforderlich.

Hinweis zu den Versionen: Dieser Leitfaden richtet sich an LangChain 1.x und wurde mit langchain==1.3.11 sowie langchain-core==1.4.8 überprüft. LangChain 1.0 brachte erhebliche Umstrukturierungen mit sich – die aktuelle Agent-API konzentriert sich auf create_agent, während ältere Komponenten wie AgentExecutor und initialize_agent in ein separates Paket namens langchain-classic verschoben wurden. Viele im Internet verfügbare Leitfäden zeigen weiterhin die Vorgehensweisen vor Version 1.0; die hier angezeigten Importe sind aktuell und wurden alle überprüft, um sicherzustellen, dass sie korrekt funktionieren.

So gehen Sie vor: Öffnen Sie eine Datei mit dem Namen lc.py, führen Sie jeden Codeblock nacheinander aus und erledigen Sie die Übungen „Ihre Reihe“ sobald sie auftauchen. Wenn Sie den Satz „Sie haben das selbst gebaut“ sehen, verweist dieser auf die manuelle Implementierung aus den früheren Tutorials dieser Reihe.

Schritt 0 – Was LangChain eigentlich ist

LangChain lässt sich am besten als Menge standardisierter, austauschbarer Komponenten verstehen, mit denen LLM-basierte Anwendungen erstellt werden – beispielsweise Modell-Wrapper, Prompt-Vorlagen, Retriever, Vektorlagereinrichtungen, Tools und Agenten. All diese Komponenten entsprechen einer gemeinsamen Schnittstelle, sodass Sie sie miteinander verbinden und eine durch eine andere ersetzen können (zum Beispiel ein anderes Modell verwenden oder die Vektorlagereinrichtung wechseln), ohne die Logik Ihrer Anwendung neu schreiben zu müssen.

Das Konzept, das alles miteinander verbindet, ist Runnable. Jeder Komponente stellt dieselbe .invoke()-Methode zur Verfügung, und beliebige zwei Komponenten können mithilfe des |-Pipes operatoren miteinander verbunden werden. Dieses Piping-Mechanismus wird als LCEL bezeichnet, abgekürzt für LangChain Expression Language. Sobald jeder Teil Ihres Systems die Runnable-Schnittstelle verwendet, kann ein ganzes RAG-Pipeline oder Agent in nur wenigen Zeilen dargestellt werden.

Ein ehrlicher Kompromiss, den man gleich zu Beginn erwähnen sollte: LangChain reduziert wiederholenden Code und gibt Ihnen Zugang zu einem umfangreichen Katalog an fertigen Integrationen. Im Gegenzug führt es Abstraktionsschichten ein, die das Debuggen erschweren können – es wird Momente geben, in denen man lieber den einfachen Loop betrachtet, den man selbst geschrieben hat. Zu wissen, wann diese Abstraktion die Kosten lohnt, ist die eigentliche Fähigkeit, und wir werden diesen Kompromiss in Schritt 7 erneut besprechen.

Einrichtung (der kostenlose, lokale Stack)

pip install langchain langchain-core langchain-ollama langchain-huggingface langchain-text-splitters sentence-transformers

Auch Ollama muss installiert sein (es ist kostenlos und läuft lokal), danach sollten Sie ein Modell herunterladen, das Toolaufrufe ermöglicht:

ollama pull llama3.2     # ~2 GB; needs ~8 GB RAM. qwen2.5 also works well.

Falls Sie Ollama überspringen möchten, können Sie die Abschnitte chain und RAG dennoch mithilfe eines lokalen Hugging Face-Modells über langchain-huggingface ausführen. Die Abschnitte agent hängen jedoch von zuverlässigem Tool-Aufrufverhalten ab, was kleine, CPU-intensive Modelle in der Regel schlecht bewältigen. Für die Schritte 4 bis 6 wird dringend empfohlen, Ollama zu verwenden.

Schritt 1 – Der Kernschritt: eine Kette mit dem |-Pfeil

In dem früheren RAG-Tutorial haben Sie eine Anfrage manuell mit einer f-String erstellt, sie an das Modell übergeben und die Ausgabe mit .strip() aufbereitet. LangChain erfasst diese identische Abfolge als Pfeilexpression. Fügen Sie dies zu lc.py hinzu:

from langchain_ollama import ChatOllama
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser

llm = ChatOllama(model="llama3.2", temperature=0)

prompt = ChatPromptTemplate.from_template(
    "Explain {topic} in exactly one sentence."
)

# The chain: prompt -> model -> plain-string parser
chain = prompt | llm | StrOutputParser()

print(chain.invoke({"topic": "retrieval-augmented generation"}))

Lesen des Pipelines von links nach rechts: prompt wandelt Ihr Eingabedictionary in eine ordnungsgemäß formatierte Nachricht um, llm verwandelt diese Nachricht in eine Antwort des Modells, und StrOutputParser() extrahiert den reinen Text aus dem Antwortobjekt.

Sie haben das bereits erstellt. Dieser Pipeline ist funktional identisch mit f"Explain {topic}..." gefolgt von generator(prompt) und anschließend von [0]["generated_text"].strip() aus der früheren RAG-Einführung – drei manuelle Schritte, die nun als drei miteinander verbundene Runnables dargestellt werden. Die Logik hat sich nicht geändert; nur die Schnittstelle wurde standardisiert.

Ihre Aufgabe: Jeder Runnable unterstützt standardmäßig auch .batch() und .stream(). Probieren Sie das aus:

for piece in chain.stream({"topic": "vector embeddings"}):
    print(piece, end="", flush=True)   # tokens arrive as they're generated
print()
print(chain.batch([{"topic": "agents"}, {"topic": "chunking"}]))  # two at once

Beachten Sie, dass Streaming und Batching einfach kostenlos mitgeliefert wurden, weil Sie die Standard-Runnable-Schnittstelle verwendet haben. Dieser „kostenlose“ Aspekt ist im Grunde die gesamte Begründung für den Einsatz von LangChain.

Schritt 2 – RAG auf die LangChain-Art

Es ist an der Zeit, Ihren manuellen RAG-Pipeline mit den Bausteinen von LangChain neu aufzubauen. Jeder Bestandteil entspricht direkt etwas, was Sie bereits manuell geschrieben haben.

from langchain_huggingface import HuggingFaceEmbeddings
from langchain_core.vectorstores import InMemoryVectorStore
from langchain_text_splitters import RecursiveCharacterTextSplitter

# Same Nimbus knowledge base from the RAG tutorial
DOCUMENTS = [
    "Nimbus is a fictional note-taking app. The free plan, Nimbus Lite, allows up to 50 notes and 1 GB of storage.",
    "Nimbus Pro costs 8 dollars per month billed annually, or 10 dollars billed monthly. It includes 50 GB of storage and collaboration for up to 5 people.",
    "Nimbus stores notes encrypted at rest with AES-256. End-to-end encryption is Pro-only and must be enabled in Settings > Security.",
    "Nimbus offers a 30-day refund policy on all paid plans. Refunds reach the original payment method within 5 business days.",
    "Nimbus live chat support is staffed for Pro customers, Monday to Friday, 9am-6pm UTC. Free users get email support with a 48-hour response time.",
]

# 1. Split (↔ your chunk_text function)
splitter = RecursiveCharacterTextSplitter(chunk_size=200, chunk_overlap=40)
chunks = splitter.create_documents(DOCUMENTS)

# 2. Embed locally (↔ your sentence-transformers model)
embeddings = HuggingFaceEmbeddings(model_name="sentence-transformers/all-MiniLM-L6-v2")

# 3. Store + index (↔ your numpy array of vectors). No server needed.
vectorstore = InMemoryVectorStore.from_documents(chunks, embeddings)

# 4. Retriever (↔ your retrieve() with cosine top-k)
retriever = vectorstore.as_retriever(search_kwargs={"k": 3})

for doc in retriever.invoke("How much does Pro cost?"):
    print("-", doc.page_content[:70], "...")

↔ Sie haben das hier gebaut – den gesamten Aufbau. RecursiveCharacterTextSplitter übernimmt die Rolle des Teilungswerkzeugs, ist dabei jedoch sorgfältiger: Es teilt den Text anhand von Absatz- und Satzgrenzen auf, anstatt nur Wörter zu zählen. HuggingFaceEmbeddings ist ein Wrapper um dasselbe all-MiniLM-L6-v2-Modell, das Sie bereits zuvor verwendet haben. InMemoryVectorStore ersetzt Ihren numpy-Array mit Vektoren, und seine Methode .as_retriever() führt dieselbe Suche nach den top-k Elementen mit kosinusähnlicher Ähnlichkeit durch, wie Sie sie manuell programmiert haben. Vier Zeilen hier umfassen alles, was Sie in den Schritten 2 bis 4 zuvor entwickelt haben.

Danach verbinden Sie die Suche mit der Generierung mithilfe von LCEL:

from langchain_core.runnables import RunnablePassthrough
from langchain_core.output_parsers import StrOutputParser

rag_prompt = ChatPromptTemplate.from_template(
    "Answer using only the context. If it's not there, say you don't know.\n\n"
    "Context:\n{context}\n\nQuestion: {question}\nAnswer:"
)

def format_docs(docs):
    return "\n\n".join(d.page_content for d in docs)

rag_chain = (
    {"context": retriever | format_docs, "question": RunnablePassthrough()}
    | rag_prompt
    | llm
    | StrOutputParser()
)

print(rag_chain.invoke("How much does Nimbus Pro cost per month?"))

Das Dictionary am Anfang der Kette führt zwei parallele Abläufe aus: question leitet die Eingabe unverändert weiter, während context dieselbe Eingabe über den Retriever leitet und die Ergebnisse formatiert. Beide Ausgaben fließen anschließend in den Prompt. ↔ du hast das gebaut ist im Grunde deine alte rag_answer()-Funktion – Daten abrufen, in einen Prompt einfügen, generieren – komprimiert in einer einzigen Ausdrucksform.

Ihre Reihe: Versuchen Sie, rag_chain.invoke("Können freie Benutzer den Live-Chat nutzen?") aufzurufen, und folgen Sie diesem Aufruf mit einer unverwandten Frage wie rag_chain.invoke("Welche ist die Hauptstadt Frankreichs?"). Achten Sie auf die Antwort „Ich weiß es nicht“ – das ist derselbe Grundüberprüfungsmechanismus aus dem früheren RAG-Tutorial, der denselben Punkt betont: Die Qualität der Informationsbeschaffung bestimmt die Qualität der Antwort. Danach rufen Sie retriever.invoke(...) einzeln auf, um genau zu sehen, welche Informationen abgerufen wurden, wenn die Antwort unpassend erscheint. Diese Trennung – die Überprüfung der Informationsbeschaffung unabhängig von der Erstellung der Antwort – ist eine Fehlersuchungspraxis, die Sie bereits zuvor angewandt haben, und LangChain bewahrt sie, indem es die beiden Schritte als separate Ausführbarkeiten beibehält.

Schritt 3 – Werkzeuge

In der Agenten-Tutorial haben Sie Werkzeuge als TOOLS-Wörterbuch definiert und selbst einen auf regulären Ausdrücken basierenden Parser geschrieben, um den Namen des Werkzeugs sowie seine Eingabedaten aus der rohen Textausgabe des Modells zu extrahieren. LangChain beseitigt den Bedarf an einem solchen Parser vollständig durch native Toolaufrufe: Sie beschreiben, was ein Werkzeug tut, das Modell antwortet mit einem strukturierten Aufruf, und LangChain kümmert sich um die Weiterleitung. Die Definition eines Werkzeugs sieht so aus, unter Verwendung des @tool-Decorators:

from langchain_core.tools import tool
import ast, operator, datetime

_OPS = {ast.Add: operator.add, ast.Sub: operator.sub, ast.Mult: operator.mul,
        ast.Div: operator.truediv, ast.Pow: operator.pow, ast.USub: operator.neg}
def _ev(n):
    if isinstance(n, ast.Constant): return n.value
    if isinstance(n, ast.BinOp):   return _OPS[type(n.op)](_ev(n.left), _ev(n.right))
    if isinstance(n, ast.UnaryOp): return _OPS[type(n.op)](_ev(n.operand))
    raise ValueError("unsupported")

@tool
def calculator(expression: str) -> str:
    """Evaluate a basic arithmetic expression like '8 * 12'."""
    return str(_ev(ast.parse(expression, mode="eval").body))

@tool
def get_today(_: str = "") -> str:
    """Return today's date in ISO format."""
    return datetime.date.today().isoformat()

Hier sind die Details, bei denen man langsamer vorgehen sollte. Sie können genau prüfen, was LangChain aus Ihrer Funktion erzeugt hat:

print(calculator.name)         # 'calculator'
print(calculator.description)  # the docstring
print(calculator.args)         # {'expression': {'title': 'Expression', 'type': 'string'}}

Jene letzte Zeile ist echter, überprüfter Ausgabeinhalt. LangChain hat Ihren Typhinweis (expression: str) zusammen mit der Dokumentation untersucht und daraus ein Schema erstellt – dieses Schema ist genau das, was das Modell zur Entscheidung darüber liest, ob und wie das Tool aufgerufen werden soll. ↔ Sie haben dieses selbst erstellt; früher schrieben Sie die Toolbeschreibungen manuell in Ihren SYSTEM_PROMPT und analysierten die Ausgabe des Modells selbst. Jetzt wird die Dokumentation selbst zur Beschreibung, und die Analyse erfolgt automatisch. Das erklärt, warum Dokumentationen und Typhinweise tatsächlich eine große Bedeutung haben – sie sind nicht nur Dokumentation, sondern bestimmen, wie das Modell das Tool versteht und verwendet. Eine nachlässige Dokumentation führt dazu, dass das Modell das Tool falsch aufruft.

Ihre Reihe: Ersetzen Sie die Dokumentation der Rechenmaschine durch etwas Unnützes, wie """führt Mathematik durch""", und prüfen Sie anschließend erneut .description. In Schritt 4 werden Sie selbst sehen, wie eine schwächere Dokumentation zu schlechteren Entscheidungen des Modells bei der Auswahl von Werkzeugen führt. Die von Ihnen verfasste Beschreibung dient als Steuerung für das Verhalten des Modells.

Schritt 4 – Agenten in einem Aufruf

Hier zeigt sich, dass die vorherige Arbeit Früchte trägt. Ihr selbst erstellter Agent benötigte einen Schleifenmechanismus, ein Zwischenspeicherfeld, einen Parser, Fehlerbehandlung, eine Obergrenze für die Anzahl der Schritte sowie einen Systemprompt, der dem Modell das ReAct-Format beibrachte. In LangChain 1.x lässt sich all das auf einen einzigen Funktionsaufruf zusammenfassen: create_agent.

from langchain.agents import create_agent

agent = create_agent(
    model=llm,                                   # your ChatOllama from Step 1
    tools=[calculator, get_today],               # the @tool functions from Step 3
    system_prompt="You are a helpful assistant. Use tools for math and dates.",
)

result = agent.invoke(
    {"messages": [{"role": "user", "content": "What is 8 times 12, and what is today's date?"}]}
)
print(result["messages"][-1].content)

Das ist der gesamte Agent, von Anfang bis Ende. ↔ Sie haben das alles selbst gebaut. Die Funktion create_agent führt intern den Zyklus „Gründen – Handeln – Beobachten“ aus, leitet die Aufgaben an das richtige Tool weiter, gibt die Beobachtungen wieder in das Modell ein, überprüft die Stoppsbedingung und achtet auf die Schrittanzahl – alles, was Sie in run_agent manuell zusammengesetzt haben. Im Hintergrund setzt sie auf LangGraph, weshalb das Schleifenverhalten so zuverlässig ist.

Falls Sie mitverfolgen möchten, wie das Denken abläuft – genauso wie bei der Verwendung von verbose=True – sollten Sie die Zwischenschritte streamen anstatt einfach auf eine endgültige Antwort zu warten:

inputs = {"messages": [{"role": "user", "content": "How much is a year of Nimbus Pro?"}]}
for chunk in agent.stream(inputs, stream_mode="updates"):
    print(chunk)

Während es läuft, werden jede Entscheidung, die das Modell trifft, sowie jedes Ergebnis, das ein Tool zurückgibt, nacheinander ausgegeben. Es handelt sich um dasselbe Thought/Action/Observation-Muster wie bei der manuellen Nachverfolgung, nur dass die Informationen als strukturierte Aktualisierungsobjekte bereitgestellt werden anstelle von Rohtext, den man selbst auswerten musste.

Ihre Reihe: Versuchen Sie eine Frage, die das Modell zwingt, zwei Tools nacheinander zu verwenden – fragen Sie beispielsweise danach, wann das 30-tägige Rückgabefenster endet, wenn ein Test heute begonnen hat. Beobachten Sie, ob es korrekt zuerst get_today und anschließend calculator in dieser Reihenfolge aufruft. Danach kehren Sie zu Schritt 7 des Agentenmaterials zurück – jede Fehlerart, die Sie dort dokumentiert haben (Abweichungen im Ausgabeformat, erfundene Toolnamen, endlose Schleifen), kann auch hier auftreten. Das Framework verbessert das Denkvermögen eines schwachen Modells nicht; es versteckt lediglich die dahinterliegenden Mechanismen. Genau dieses Verständnis ist der Grund, warum Sie bei der Fehlersuche an diesen Agenten schneller vorankommen werden als jemand, der direkt zum Framework greift, ohne eines erst selbst zu entwickeln.

Schritt 5 – Alles zusammenfügen: Ein Agent, der Daten abruft (agentic RAG)

Hier treffen alle drei vorherigen Lektionen aufeinander. Nehmen Sie Ihren Retriever und verwenden Sie ihn als Werkzeug, geben Sie dieses Werkzeug anschließend dem Agenten. Ab diesem Punkt entscheidet der Agent selbst wann eine Dokumentensuche erforderlich ist – und er kann nach Belieben mehrmals suchen oder Suchvorgänge mit Berechnungen kombinieren.

@tool
def search_nimbus_docs(query: str) -> str:
    """Search the Nimbus product documentation for facts about plans, pricing, refunds, security, and support."""
    docs = retriever.invoke(query)
    return "\n\n".join(d.page_content for d in docs)

smart_agent = create_agent(
    model=llm,
    tools=[search_nimbus_docs, calculator, get_today],
    system_prompt=(
        "You answer questions about the Nimbus app. "
        "Use search_nimbus_docs for any product facts, and calculator for arithmetic. "
        "Base answers only on retrieved facts."
    ),
)

q = "How much would Nimbus Pro cost a team of 4 for a full year?"
result = smart_agent.invoke({"messages": [{"role": "user", "content": q}]})
print(result["messages"][-1].content)

Um eine solche Frage richtig zu beantworten, muss der Agent zunächst den Preis für die monatliche Mitgliedschaft suchen und erst danach 8 * 12 * 4 berechnen. Das ist die Suchfunktion (aus dem ersten Tutorial) als Werkzeug (aus dem dritten) genutzt, gesteuert von einem Agenten (aus dem zweiten) – drei getrennte Konzepte, die als ein System zusammenarbeiten. Dem Agenten die Entscheidung darüber zu überlassen, wann Daten abgerufen werden sollen, ist weitaus flexibler als der fest vorgegebene Pfad rag_chain aus Schritt 2, und es handelt sich dabei um ein Muster, das häufig in echten Produktionsystemen vorkommt.

Ihre Reihe: Streamen Sie auch die Ausführung dieses Agenten mithilfe von smart_agent.stream(..., stream_mode="updates") und überprüfen Sie die Reihenfolge der Operationen – die Suche findet vor der Berechnung statt. Wenn Ihr lokales Modell versucht, die Arithmetik im Kopf zu lösen anstatt das Rechenwerkzeug aufzurufen (ein häufiges Verhalten bei kleineren Modellen), verschärfen Sie den Systemprompt mit einer Aussage wie "Sie MÜSSEN für jeden arithmetischen Schritt den Rechner verwenden." Das ist derselbe Ansatz, der im Agententutorial funktioniert hat.

Schritt 6 – Eine kurze Übersicht über weitere Funktionen

Zu diesem Zeitpunkt haben Sie den grundlegenden Rahmen bereits erstellt. Einige zusätzliche LangChain-Bausteine sind erwähnenswert, wobei jeder von ihnen auf etwas zurückgeht, was Sie bereits selbst erstellt haben:

  • Dokumentlader (langchain-community) – sie nehmen PDFs, Webseiten, Notion-Seiten sowie ähnliche Quellen direkt in dieselben Document-Objekte auf, die bereits von Ihrem Textzerlegungsmodul verwendet werden. Dadurch wird das manuelle Einfügen von Text in eine Liste durch eine echte, strukturierte Aufnahme ersetzt.
  • Produktionsreife Vektorlagereinrichtungen – ersetzen Sie den In-Memory-Speicher durch Chroma oder FAISS (importiert über from langchain_chroma import Chroma), um Embeddings auf der Festplatte zu speichern und die Leistungsfähigkeit über die Grenzen des Arbeitsspeichers hinaus zu erweitern. Da beide dieselbe .as_retriever()-Schnittstelle bieten, muss sich nichts in Ihrem Workflow ändern – genau diese Konsistenz ist der Sinn dieser Ersetzung.
  • Gedächtnis und Nachrichtenverlauf – ein Kettenprozess wird so umschlossen, dass er den Kontext früherer Schritte beibehält und somit ein einmaliger Ablauf in ein fortlaufendes Gespräch verwandelt wird.
  • Ausgabe-Parser jenseits einfacher Zeichenketten – die Antwort des Modells wird in JSON oder ein validiertes Pydantic-Objekt umgewandelt, anstatt darauf zu hoffen, dass der Rohtext zufällig gut strukturiert ist.
  • LangGraph – wenn die in create_agent integrierte Schleifenlogik nicht ausreicht (verzweigte Pfade, Zustimmungsschritte mit menschlicher Beteiligung, mehrere kooperierende Agenten), wird auf LangGraph zurückgegriffen, den untergeordneten Graphen-Engine, auf der create_agent selbst basiert.
  • Schritt 7 – Wann LangChain sinnvoll ist und wann nicht (eine ehrliche Einschätzung)

    Zu diesem Zeitpunkt haben Sie bereits zweimal dasselbe System erstellt – einmal von Grund auf, einmal mit dem Framework – was Sie in eine gute Position bringt, diesen Aufruf selbst durchzuführen. Das war der Zweck daran, beide Versionen zu durchgehen.

    LangChain lohnt sich, wenn Sie viele bestehende Integrationen zusammenfügen – einige Dokumentladegeräte, mehrere Vektordatenspeicher, mehr als einen Modellanbieter – und nicht für jede von ihnen eigene Logik für Streaming, Batchen, Wiederholungsversuche und Tracking entwickeln möchten. Es lohnt sich auch, wenn Sie damit rechnen, die Komponenten häufig auszutauschen und eine stabile Schnittstelle dafür benötigen, oder wenn Sie einen Agenten entwickeln und den Überlegungsprozess lieber nicht selbst verwalten möchten.

    Das Manuell-Schreiben ist oft die bessere Wahl, wenn die Anwendung klein genug ist, sodass das Erlernen der Abstraktionen von LangChain länger dauern würde als das Schreiben der fünfzig oder so Zeilen, die man bereits beherrscht. Es ist auch die bessere Wahl, wenn man eine vollständige Sicht darauf benötigt, was ausgeführt wird – das Durchgehen der Framework-Ebenen zur Fehlerbehebung stellt eine echte Hürde dar, und dieser Kritikpunkt ist berechtigt – oder wenn das Hinzufügen einer Indirektionsstufe Logik verbergen würde, die in reinem Python eigentlich klarer ist. Der RAG-Pipeline sowie der Agent, den man in früheren Tutorials manuell erstellt hat, eignen sich voll und ganz für den Produktivgebrauch; die Verwendung eines Frameworks macht kodiertes Handgeschriebenes keineswegs minderwertig.

    Es gibt hier keine einzige richtige Antwort. Der Grund, warum Sie zunächst die manuelle Version gelernt haben, ist, dass die Entscheidung für dieses Framework eine bewusste Wahl unter voller Kenntnis dessen ist, was es ersetzt – und nicht eine Standardlösung, auf die man zurückgreift, weil die Interna unklar sind.

    Schritt 8 – Was als Nächstes?

    • Falls Ihre lokale Einrichtung zu langsam ist, stehen kostenlose Hosting-Optionen von Groq und Google Gemini zur Verfügung. Der Wechsel erfordert nur eine einzige Änderung – ersetzen Sie ChatOllama durch ChatGroq oder verwenden Sie init_chat_model("gemini-...", model_provider="google_genai") – denn alles andere basiert auf derselben Standard-Schnittstelle. Für beide Optionen benötigen Sie eine API-Schlüssel, doch ihre kostenlosen Tarife kosten nichts.
  • LangSmith ist das Tracing- und Debugging-Tool von LangChain. Wenn eine Kette oder ein Agent sich seltsam verhält, ermöglicht es Ihnen, jeden Schritt, jede Eingabe und jedes Ausgabe zu überprüfen. Es gibt eine kostenlose Version, und es ist die direkte Lösung für den Vorwurf „Ich kann nicht ins Innere des Frameworks blicken“.
  • LangGraph lohnt es, es auszuprobieren, wenn Sie stateful Workflows, mehrere kooperierende Agenten oder Checkpoints mit menschlicher Beteiligung benötigen, die über einen einfachen Tool-Aufruf hinausgehen.
  • Die offizielle Dokumentation befindet sich unter docs.langchain.com. Überprüfen Sie unbedingt, ob das, was Sie lesen, sich auf Version 1.x bezieht – alles, was vor 1.0 geschrieben wurde, bezieht sich auf AgentExecutor, initialize_agent oder LLMChain, die alle seitdem verschoben oder veraltet wurden.
  • Das mentale Modell, das man beibehalten sollte

    LangChain sind im Grunde Ihre eigenen selbst erstellten Komponenten, die hinter einer einzigen Schnittstelle – Runnable – standardisiert sind und mithilfe von | miteinander verbunden werden. Sobald Sie die einzelnen Bestandteile selbst erstellt haben, enthält es nichts wirklich Neues:

    • Eine Kette ist der von Ihnen bereits geschriebene Ablauf von Prompt über Modell bis zu Parser, nur miteinander verbunden.
    • Ein Retriever ist Ihre Logik für Einbettung und kosinusbasierte Suche, in einer gemeinsamen Schnittstelle verpackt.
    • Ein Tool ist eine von Ihnen geschriebene Funktion zusammen mit einem automatisch generierten Schema, das es dem Modell ermöglicht, sie direkt aufzurufen, anstatt dass Sie deren Textausgabe parsen müssen.
    • Ein Agent über create_agent ist Ihr gesamter Reason-Act-Observe-Zyklus, der in einen einzigen Aufruf zusammengefasst wird.

    Wenn etwas schiefgeht, beheben Sie den Fehler auf die gleiche Weise wie immer: Isolieren Sie den fehlerhaften Komponenten. Testen Sie den Retriever einzeln, geben Sie die Werte von .args eines Tools aus oder überwachen Sie die Zwischenschritte des Agents. Das Framework ändert nur die Menge an Code, die Sie eingeben müssen – es verändert nicht das, was tatsächlich geschieht, und Sie verstehen bereits, was vor sich geht.

    Fehlerbehebung

    • Falls Sie bei create_agent oder langchain_ollama einen ImportError erhalten, verwenden Sie vermutlich eine Version vor 1.0 oder fehlt ein Paket. Führen Sie pip install -U langchain langchain-ollama aus und überprüfen Sie, ob langchain.__version__ mit 1. beginnt.
  • Falls ein Tutorial, das Sie lesen, AgentExecutor oder initialize_agent verwendet, handelt es sich um die ältere API. In Version 1.x wurde sie in langchain-classic verlegt; neuer Code sollte stattdessen create_agent verwenden.
  • Ein „Connection refused“-Fehler von Ollama bedeutet, dass der Server nicht läuft – starten Sie ihn mit ollama serve oder öffnen Sie die App und überprüfen Sie, ob Ihr Modell unter ollama list angezeigt wird.
  • Falls der Agent ein Tool ignoriert oder versucht, eigenständig arithmetische Operationen durchzuführen, liegt das wahrscheinlich daran, dass das Modell die Toolaufrufe nicht zuverlässig durchführt – was eine häufige Einschränkung kleinerer Modelle ist – oder die Dokumentation ist zu vage. Verfeinern Sie den Systemprompt sowie die Dokumentation des Tools oder versuchen Sie ein leistungsstärkeres Modell wie qwen2.5.
  • HuggingFaceEmbeddings wirkt beim ersten Gebrauch langsam, da es das Embedding-Modell (etwa 80 MB) herunterlädt und lokal speichert. Die Abfrage selbst verläuft danach schnell.
  • Der allererste Aufruf eines Modells über Ollama ist langsam, weil das Modell in den RAM geladen wird; anschließende Aufrufe sind schnell.
  • Sie haben nun RAG, Agenten sowie das Framework, das beide umfasst, erst manuell und anschließend mit LangChain entwickelt. Sie verstehen die Schicht, auf die die meisten Menschen nur von außen zugreifen. Viel Spaß beim Einsatz.

    Verwandte Literatur

  • Das Management von LLM-as-a-Judge als lebendiges Produktionsystem — Erfahren Sie, wie Netflix’ vierstufiger Lebenszyklus – Daten mit gesicherter Richtigkeit, auf Skalen angepasste Schulung, sicheres Rollout sowie kontinuierliche Überwachung – sicherstellt, dass ein LLM-Judge in großem Maßstab präzise bleibt.