Zurück zum Katalog
Season 49 18 Episoden 1h 11m 2026

LangGraph

v1.1 — 2026 Edition. Ein umfassender Audiokurs über LangGraph, ein Framework zur Erstellung von zustandsbehafteten, langlaufenden Agenten-Workflows. Behandelt mentale Modelle, Graph vs Functional APIs, Memory, Time Travel, Human-in-the-Loop und Production Deployment.

LLM-Orchestrierung Multi-Agenten-Systeme AI/ML-Frameworks
LangGraph
Aktuelle Wiedergabe
Click play to start
0:00
0:00
1
Das Orchestrierungsproblem: Warum LangGraph?
Eine Einführung in die Kernprobleme, die LangGraph löst. Wir untersuchen den Übergang von einfachen linearen Workflows zur Orchestrierung langlaufender, zustandsbehafteter Agenten.
3m 54s
2
In LangGraph denken: Das mentale Modell
Lerne, wie man komplexe KI-Aufgaben in das mentale Modell von LangGraph übersetzt. Wir schlüsseln die grundlegenden Konzepte von Nodes, Edges und State auf.
4m 07s
3
Die Graph API: State und Reducers
Tauche in die Mechanik der Graph API ein. Wir erklären, wie TypedDict dein Schema definiert und wie Reducers State-Updates von mehreren Nodes verwalten.
3m 47s
4
Die Functional API: @entrypoint und @task
Entdecke die Functional API als Alternative zur Graph API. Wir diskutieren, wie man mit Standard-Python-Kontrollfluss Enterprise-Grade Persistenz erreicht.
4m 12s
5
Verwaltung des Chatverlaufs mit MessagesState
Verstehe die Herausforderungen von Chatverläufen bei KI-Agenten. Wir untersuchen MessagesState und den add_messages Reducer, um Bearbeitungen und Deduplizierung zu handhaben.
3m 53s
6
Die Wahl der Abstraktion: Graph vs Functional
Ein Framework zur Entscheidung, welche API verwendet werden soll. Wir stellen das explizite visuelle Routing der Graph API dem imperativen Ablauf der Functional API gegenüber.
4m 07s
7
Dynamisches Routing und Conditional Edges
Gehe über fest codierte Logik hinaus. Wir diskutieren, wie man LLMs mit strukturierten Ausgaben zusammen mit Conditional Edges nutzt, um Workflows dynamisch zu routen.
4m 13s
8
Map-Reduce Workflows mit der Send API
Meistere das Orchestrator-Worker-Pattern. Wir tauchen in die Send API ein, um parallele Worker-Nodes basierend auf Laufzeitplänen dynamisch zu verteilen.
4m 27s
9
Persistenz: Threads und Checkpoints
Entdecke die Grundlage der Zustandsbehaftung. Wir erklären Threads, Checkpoints und Super-steps und zeigen, wie LangGraph das Überleben bei Abstürzen garantiert.
3m 56s
10
Durable Execution und Idempotenz
Verstehe die Nuancen bei der Fortsetzung von Workflows. Wir behandeln, warum Seiteneffekte idempotent sein müssen und wie man Nodes für Durable Execution strukturiert.
4m 06s
11
Human-in-the-Loop: Interrupts
Lerne, wie man Agenten mitten in der Ausführung einfriert. Wir detaillieren die interrupt-Funktion und wie man Workflows mit externer menschlicher Freigabe fortsetzt.
4m 01s
12
Die Vergangenheit debuggen: Time Travel und Forking
Erkunde die Time-Travel-Fähigkeiten von LangGraph. Wir zeigen, wie man durch die State-Historie navigiert, vergangene Checkpoints abspielt und alternative Ausführungspfade forkt.
3m 43s
13
Langzeitgedächtnis: Stores über Threads hinweg
Gehe über isolierte Threads hinaus. Wir stellen das Store-Interface vor und erklären, wie du deinen Agenten ein persistentes, sitzungsübergreifendes Gedächtnis verleihst.
3m 46s
14
Streaming-Ausführung und das v2-Format
Verbessere die UX mit Echtzeit-Feedback. Wir schlüsseln Stream-Modi (values, updates, messages) und das vereinheitlichte v2 StreamPart-Format auf.
3m 59s
15
Komplexität zusammensetzen: Subgraphs
Skaliere deine Workflows, indem du kompilierte Graphen als Nodes behandelst. Wir diskutieren das Zusammensetzen von Subgraphs und die Verwaltung von geteilten gegenüber privaten State-Schemata.
3m 29s
16
Subgraph-Persistenz und Multi-Agenten-Patterns
Meistere das Memory-Scoping in Multi-Agenten-Systemen. Wir erklären den Unterschied zwischen per-invocation, per-thread und zustandsloser Subgraph-Persistenz.
3m 58s
17
Anwendungsstruktur und Deployment-Bereitschaft
Der Übergang von Prototypen zur Produktion. Wir untersuchen langgraph.json, die richtige Dateistruktur und das Dependency-Management für zustandsbehaftete Deployments.
4m 13s
18
Graph-Ausführung End-to-End testen
Lerne robuste Teststrategien für Graph-Workflows. Wir behandeln die pytest-Integration, isolierte Node-Ausführung und die Simulation von partiellem State.
3m 45s

Episoden

1

Das Orchestrierungsproblem: Warum LangGraph?

3m 54s

Eine Einführung in die Kernprobleme, die LangGraph löst. Wir untersuchen den Übergang von einfachen linearen Workflows zur Orchestrierung langlaufender, zustandsbehafteter Agenten.

Herunterladen
Hallo, hier ist Alex von DEV STORIES DOT EU. LangGraph, Folge 1 von 18. Die meisten Large Language Model-Skripte funktionieren perfekt für einen schnellen Prompt und eine schnelle Response. Aber wenn eine Task zwanzig Minuten läuft und die Netzwerkverbindung mittendrin abbricht, fällt alles in sich zusammen. Du verlierst deinen Fortschritt, deinen Kontext und dein API-Budget. Bei "The Orchestration Problem: Why LangGraph?" geht es genau darum, diese Anfälligkeit zu beheben. LangGraph ist ein Orchestration Framework, das für stateful Multi-Actor-Applications gebaut wurde. Vielleicht hörst du den Namen und denkst, es ist nur ein Feature von LangChain. Ist es aber nicht. LangGraph ist eine Low-Level Orchestration Engine, und du musst LangChain überhaupt nicht nutzen, um sie zu verwenden. Sie ist speziell dafür da, Agent-Workflows als stateful Graphen zu modellieren, statt als einfache lineare Skripte. Standard-Skripte laufen im Memory. Stoppt ein Skript unerwartet, gehen all diese Runtime-Daten verloren. Stell dir ein Szenario vor, in dem du einen Background Agent hast, der ein hundertseitiges Dokument durchsucht. Der Agent liest, extrahiert Fakten und verknüpft Informationen seit zwanzig Minuten ohne Unterbrechung. Tritt in Minute neunzehn ein Server-Timeout auf, verliert ein Standard-Skript diesen gesamten State. Du musst den kompletten Job von vorne anfangen. LangGraph löst dieses Orchestration Problem durch Durable Execution. Wenn du deinen Workflow als Graphen modellierst, wird jeder einzelne Schritt zu einer Node, und die logischen Verbindungen dazwischen sind Edges. Während die Application von einer Node zur nächsten wandert, speichert LangGraph automatisch den Fortschritt. Es behandelt langlaufende Prozesse als eine Reihe sicherer Checkpoints. Stürzt das System ab, nimmt LangGraph die Execution genau dort wieder auf, wo sie unterbrochen wurde. Dieser Checkpointing-Mechanismus basiert auf umfassendem Memory. Memory in LangGraph ist nicht nur eine fortlaufende Liste von Chat-Nachrichten. Es ist der gesamte State des Graphen. Sobald eine Node ihre Verarbeitung abgeschlossen hat, aktualisiert sie ein Shared-State-Objekt. Die nächste Node in der Sequenz liest ihren Input direkt aus diesem State. Das bedeutet, das Memory bleibt über den gesamten Lifecycle der Application erhalten. Der Background Agent, der dein Dokument durchsucht, vergisst die wichtigen Daten von Seite fünf nicht, wenn er endlich bei Seite neunzig ankommt, weil der Graph State sie sicher festhält. Hier ist der entscheidende Punkt: Da der Graph State zwischen den Schritten pausiert und sauber gespeichert wird, hast du die Möglichkeit, einen Human in the Loop einzubinden. Manchmal kommt ein autonomer Agent an einen Entscheidungspunkt, an dem er eine Freigabe braucht, bevor er weitermacht – etwa um eine finale E-Mail zu senden oder eine Finanztransaktion auszuführen. In einem Standard-Skript führt das Warten auf den Klick eines Users auf einen Button oft zu Connection Timeouts. In LangGraph konfigurierst du einfach eine bestimmte Node so, dass sie die Execution anhält. Das System geht in den Sleep-Modus und speichert den aktuellen State perfekt. Ein menschlicher Operator kann dann die gesammelten Daten überprüfen, die nächste Action freigeben oder sogar den Agent State manuell anpassen, bevor er auf Continue klickt. Nach der Freigabe wacht der Graph auf und setzt seine Arbeit mit dem aktualisierten Kontext fort. Das Core Takeaway ist: Die Entwicklung komplexer Agents hängt stark vom Management von State und Failures ab. LangGraph verlagert deine Architektur weg von fragilen, Memory-gebundenen Skripten, hin zu resilienten Graphen, die Unterbrechungen überstehen, sich an ihre Vergangenheit erinnern und geduldig auf menschliche Steuerung warten. Wenn du die Show unterstützen möchtest, kannst du auf Patreon nach DevStoriesEU suchen. Das war’s für diese Folge. Danke fürs Zuhören, und keep building!
2

In LangGraph denken: Das mentale Modell

4m 07s

Lerne, wie man komplexe KI-Aufgaben in das mentale Modell von LangGraph übersetzt. Wir schlüsseln die grundlegenden Konzepte von Nodes, Edges und State auf.

Herunterladen
Hallo, hier ist Alex von DEV STORIES DOT EU. LangGraph, Folge 2 von 18. Du baust einen KI-Agenten, indem du Anweisungen, Edge Cases und Beispiele in einen einzigen riesigen Prompt packst, ihn dann abschickst und hoffst, dass er das Richtige tut. Das funktioniert, bis es fehlschlägt, und das Debugging der Ergebnisse ist frustrierend. Um das zu beheben, musst du aufhören, monolithische Prompts zu schreiben, und anfangen, Systeme zu entwerfen. Das bringt uns zum Thema: Thinking in LangGraph – das mentale Modell. LangGraph zwingt dich, von linearen Skripten wegzukommen. Es verlangt von dir, deine Anwendung als State Machine zu betrachten. Der allererste Schritt ist, einfach deinen Prozess zu definieren. Bevor du Code schreibst, schau dir an, was das System erreichen soll, und brich es in einzelne Aktionen herunter. Stell dir ein Triage-System für den Kundensupport vor. Ein User sendet eine Nachricht. Ein menschlicher Mitarbeiter würde die E-Mail lesen, entscheiden, ob es ein Abrechnungsproblem oder technischer Support ist, und dann eine passende Antwort entwerfen. Diese Abfolge ist dein Prozess. Du bildest diesen Prozess auf einen LangGraph-Workflow ab, und zwar mit drei Kernkomponenten: State, Nodes und Edges. Fangen wir mit dem State an. Der State ist die Shared Memory deines Graphen. Er ist eine Struktur, die den Kontext deiner gesamten Operation zu jedem Zeitpunkt hält. Jeder Schritt in deinem Workflow liest aus diesem State und schreibt Updates dorthin zurück. Zuhörer machen hier oft einen bestimmten Fehler. Sie versuchen, vollständig formatierte Prompt-Strings im State zu speichern. Mach das nicht. Der State sollte nur Rohdaten enthalten. Er enthält den originalen E-Mail-Text des Kunden, die extrahierte Kategorie oder eine rohe Liste vorheriger Nachrichten. Die Formatierung passiert on-demand, später, genau in dem Schritt, in dem sie gebraucht wird. Als Nächstes haben wir die Nodes. Wenn der State der Speicher ist, sind die Nodes die Worker, die die eigentlichen Aufgaben erledigen. Ein Node ist einfach eine Python-Funktion, die einen einzelnen logischen Schritt deines Prozesses ausführt. Er empfängt den aktuellen State, führt eine Aktion aus und gibt ein Update zurück. In unserem Triage-Beispiel würdest du drei separate Nodes erstellen. Der erste ist ein Read-Node. Er nimmt die eingehende E-Mail und speichert den Rohtext im State. Der zweite ist ein Classify-Node. Er schaut sich den Rohtext im State an, bittet ein Language Model, ihn als Abrechnung oder Technisch zu kategorisieren, und speichert die resultierende Kategorie zurück in den State. Der dritte ist ein Draft-Node. Er liest sowohl die E-Mail als auch die Kategorie aus dem State, formatiert sie lokal zu einem Prompt und generiert eine Response. Jeder Node macht genau einen Job. Schließlich brauchst du einen Weg, diese Worker zu verbinden. Das ist die Rolle der Edges. Edges repräsentieren die Routing-Logik. Sie diktieren, was passiert, nachdem ein Node seine Arbeit beendet hat. Eine Standard-Edge besagt einfach: Sobald der Read-Node fertig ist, gehe immer zum Classify-Node. Aber LangGraph nutzt auch Conditional Edges. Hier wird es interessant. Nach dem Classify-Node kannst du eine Conditional Edge nutzen, um den State zu überprüfen. Wenn die Kategorie Abrechnung ist, routet die Edge den Flow zu einem spezifischen Abrechnungs-Draft-Node. Ist sie technisch, routet sie zu einem technischen Draft-Node. Edges treffen Traffic-Entscheidungen basierend auf den Daten, die deine Nodes gerade produziert haben. Du beginnst mit dem Prozess, isolierst die Daten in einem gemeinsamen State, definierst die Worker als Nodes und diktierst den Flow mit Edges. Indem du das Problem aufteilst, isolierst du Fehler. Betrachte deine Anwendung nicht als einen einzigen Textgenerator, sondern als ein koordiniertes Fließband, auf dem sich Rohdaten systematisch von einem spezialisierten Worker zum nächsten bewegen. Danke fürs Zuhören, Happy Coding zusammen!
3

Die Graph API: State und Reducers

3m 47s

Tauche in die Mechanik der Graph API ein. Wir erklären, wie TypedDict dein Schema definiert und wie Reducers State-Updates von mehreren Nodes verwalten.

Herunterladen
Hallo, hier ist Alex von DEV STORIES DOT EU. LangGraph, Folge 3 von 18. Zwei parallele Funktionen beenden ihre Ausführung in exakt derselben Millisekunde und versuchen, in exakt dieselbe gemeinsame Liste zu schreiben. Normalerweise überschreibt eine die andere, und Daten gehen verloren. Um dies zu verhindern, verwendet LangGraph die Graph API: State und Reducer. Die Grundlage jedes LangGraph-Workflows ist sein State. Den definierst du mit einem Standard-Python-TypedDict. Dieses Dictionary legt die exakten Keys fest, die dein Graph trackt, und die Datentypen für jeden Key. Stell es dir als das Schema vor, das von Node zu Node weitergegeben wird, während dein Graph läuft. Hier ist der entscheidende Punkt. Nodes in LangGraph verändern den State nicht direkt. Ein Node erhält eine Kopie des aktuellen States, macht seine Arbeit und gibt ein Dictionary mit Updates zurück. Ein häufiger Fehler ist die Annahme, dass die Rückgabe eines Dictionarys das gesamte State-Objekt ersetzt. Das ist nicht der Fall. Wenn dein State fünf Keys hat und dein Node ein Dictionary mit nur einem Key zurückgibt, lässt LangGraph die anderen vier unangetastet und wendet nur dein spezifisches Update an. Wie wendet LangGraph dieses Update an? Hier kommen Reducer ins Spiel. Ein Reducer ist einfach eine Funktion, die festlegt, wie ein zurückgegebener Wert mit dem vorhandenen Wert für einen bestimmten Key zusammengeführt wird. Standardmäßig verwendet LangGraph einen Overwrite-Reducer. Wenn dein Node einen neuen String für einen Status-Key zurückgibt, ist der alte String weg und wird komplett durch den neuen ersetzt. Manchmal ist Überschreiben genau das, was du vermeiden willst. Stell dir einen parallelen Data-Fetching-Workflow vor. Du hast einen Shared-State-Key namens results, der eine Liste ist. Du startest zwei Nodes, die gleichzeitig laufen, um unterschiedliche Daten-Batches abzurufen. Wenn beide Nodes ein Dictionary zurückgeben, das den results-Key aktualisiert, führt das Standard-Overwrite-Verhalten dazu, dass der Node, der als Letztes fertig wird, die Arbeit des anderen löscht. Um das zu beheben, annotierst du den results-Key in deinem TypedDict mit einem spezifischen Reducer, wie zum Beispiel Pythons eingebautem operator dot add. Wenn nun die beiden Nodes ihre Listen zurückgeben, fungiert der Reducer wie ein Verkehrspolizist. Er nimmt die bestehende Liste und hängt die Outputs von beiden Nodes sicher an. Es geht nichts verloren. Es gibt jedoch einen Edge Case. Was ist, wenn du einen Append-Style-Reducer auf deinem results-Key hast, aber an einen Punkt in deinem Graph kommst, an dem du die Liste wirklich komplett leeren und von vorne anfangen musst? Wenn dein Node eine leere Liste zurückgibt, fügt der Reducer der bestehenden einfach eine leere Liste hinzu, und die alten Daten bleiben intakt. Für dieses Szenario bietet LangGraph einen speziellen Overwrite-Typ. Wenn dein Node sein Update in ein Overwrite-Objekt verpackt, erkennt LangGraph das und umgeht den Reducer komplett. Die alte Liste wird weggeworfen und ein Hard Reset erzwungen. Der State in einem komplexen Graph ist keine fragile globale Variable, die ständig mutiert wird, sondern ein Append-Only-Log aus kontrollierten Updates, das von klaren Reducer-Regeln gesteuert wird. Das war's für diese Folge. Danke fürs Zuhören und viel Spaß beim Entwickeln!
4

Die Functional API: @entrypoint und @task

4m 12s

Entdecke die Functional API als Alternative zur Graph API. Wir diskutieren, wie man mit Standard-Python-Kontrollfluss Enterprise-Grade Persistenz erreicht.

Herunterladen
Hallo, hier ist Alex von DEV STORIES DOT EU. LangGraph, Folge 4 von 18. Manchmal möchtest du einfach nur ein Standard-Python-Skript mit normalen if-Statements und for-Loops schreiben, brauchst aber trotzdem Enterprise-Grade State Persistence. Du willst keine explizite State Machine bauen, nur um ein paar Language Model Calls nacheinander auszuführen. Genau hier löst die Functional API, insbesondere die Entrypoint- und Task-Decorators, dieses Problem. Wenn du Anwendungen mit expliziten Nodes und Edges baust, musst du manuell definieren, wie Daten von einem Schritt zum nächsten geroutet werden. Diese Struktur bietet zwar immense Kontrolle, kann sich aber schwerfällig anfühlen, wenn deine Logik stark sequenziell ist oder auf Standard-Loops basiert. Die Functional API erlaubt es dir, normalen Top-to-Bottom-Python-Code zu schreiben und gleichzeitig die integrierten Streaming- und Recovery-Features beizubehalten. Anstatt ein Graph-Objekt zu instanziieren, wendest du Decorators auf deine bestehenden Python-Funktionen an. Du beginnst mit dem Task-Decorator. Diesen wendest du auf die einzelnen Arbeitseinheiten deiner Anwendung an. Stell dir einen Task als einen diskreten Schritt vor, der etwas Bestimmtes tut, wie zum Beispiel eine Datenbank abzufragen, eine Metrik zu berechnen oder ein Model zu prompten. Wenn eine Funktion den Task-Decorator hat, wrappt das Framework sie in einen Tracking-Layer, um ihre Ausführung zu überwachen. Als Nächstes nutzt du den Entrypoint-Decorator. Diesen platzierst du auf der Haupt-Orchestrierungsfunktion, die den gesamten Flow steuert. Innerhalb dieser Entrypoint-Funktion rufst du deine dekorierten Tasks mithilfe des Standard-Python Control Flows auf. Du weist den Output eines Tasks einer Variable zu und übergibst diese Variable dann an den nächsten Task. Du kannst try-except-Blöcke, List Comprehensions oder while-Loops verwenden. Die Orchestrierungslogik verhält sich genau so, wie sich natives Python verhält. Da der Code völlig standardmäßig aussieht, könntest du annehmen, dass ihm das Memory einer formalen State-Struktur fehlt. Das ist ein häufiges Missverständnis. Die Functional API erstellt hinter den Kulissen weiterhin automatisch Checkpoints für deinen Fortschritt. Jedes Mal, wenn ein Task abgeschlossen ist, fängt LangGraph den Return Value ab und speichert ihn in einem Persistent Store. Das Framework protokolliert die Inputs und Outputs jeder dekorierten Funktion sicher, während sie passieren. Stell dir ein automatisiertes Skript zum Schreiben von Essays vor. Du definierst drei dekorierte Tasks: eine Funktion zum Generieren einer Gliederung, eine Funktion zum Schreiben eines Absatzes und eine Funktion zum Reviewen des Entwurfs. In deiner Haupt-Entrypoint-Funktion rufst du zuerst den Outline-Generator auf. Als Nächstes schreibst du einen Standard-for-Loop, der über die Abschnitte dieser Gliederung iteriert und für jeden den Write-Paragraph-Task aufruft. Du hängst die Ergebnisse an eine lokale Liste an. Schließlich führst du den Review-Task aus. Du nutzt ein einfaches if-Statement, um den resultierenden Score zu prüfen. Wenn der Score schlecht ist, triggert dein Code einfach einen while-Loop, um bestimmte Absätze so lange neu zu schreiben, bis sich der Score verbessert. Hier ist der entscheidende Punkt. Wegen des versteckten Checkpointings geht deine Arbeit nicht verloren, falls dein Skript beim Schreiben des dritten Absatzes auf ein Netzwerk-Timeout stößt. Wenn du den Prozess mit demselben Thread Identifier neu startest, weiß LangGraph, dass die Gliederung und die ersten beiden Absätze bereits fertiggestellt sind. Es überspringt die Ausführung dieser Tasks komplett, ruft ihre gecachten Outputs aus dem State Store ab und setzt die Ausführung exakt beim dritten Absatz fort. Die Functional API verlagert den Cognitive Load von der Visualisierung abstrakter Routing-Topologien zurück zum Top-to-Bottom-Lesen von Code und gibt dir die Resilience einer State Machine mit der Einfachheit eines Plain Scripts. Das war’s für diese Folge. Vielen Dank fürs Zuhören und keep building!
5

Verwaltung des Chatverlaufs mit MessagesState

3m 53s

Verstehe die Herausforderungen von Chatverläufen bei KI-Agenten. Wir untersuchen MessagesState und den add_messages Reducer, um Bearbeitungen und Deduplizierung zu handhaben.

Herunterladen
Hallo, hier ist Alex von DEV STORIES DOT EU. LangGraph, Folge 5 von 18. Du baust eine Chat-App, und ein User entdeckt einen Tippfehler in seinem Prompt. Er klickt auf Bearbeiten, korrigiert ihn und drückt auf Senden. Aber anstatt die alte Message zu ersetzen, hängt dein Backend die korrigierte Version einfach ans Ende der History an, sodass der ursprüngliche Tippfehler als Geister-Duplikat genau dort bleibt, wo er war. Genau das verhinderst du, indem du deine Conversation History mit MessagesState managst. Wenn Entwickler ihren ersten Graph bauen, definieren sie normalerweise ein Custom State Dictionary, um ihre Chat History zu speichern. Ein häufiger Fehler ist es, ein Standard List Append zu verwenden, um diese History zu managen. Sie hängen die Standardfunktion operator dot add an ihre Messages-Liste an. Das sagt dem Graph, dass er neue Messages einfach nehmen und ans Ende des bestehenden Arrays kleben soll. Dieser Append-Only-Ansatz funktioniert super für einen einfachen Ping-Pong-Bot, bei dem ein User spricht, die AI antwortet und die History sequenziell wächst. Aber er bricht komplett zusammen, wenn der State mutable sein muss. Wenn ein Mensch einen vergangenen Prompt editiert oder ein Agent beschließt, seine letzte Response neu zu generieren, kommt eine Standard-Addition damit nicht klar. Am Ende hast du Duplikate. LangGraph bietet eine Built-in State-Struktur namens MessagesState, um das zu lösen. Sie enthält einen einzigen Key namens messages. Hier ist die entscheidende Erkenntnis: Die Power von MessagesState ist nicht der Key selbst, sondern die spezifische Reducer-Funktion, die daran hängt, namens add messages. Der add messages Reducer hängt Daten nicht einfach blind an. Er trackt Message-IDs. Jedes Mal, wenn eine neue Message in den State kommt, checkt der Reducer ihre eindeutige ID. Wenn diese ID schon irgendwo in der Conversation History existiert, überschreibt der Reducer die alte Message mit der neuen. Wenn die ID neu ist, oder wenn die Message noch keine ID hat, hängt der Reducer sie ans Ende der Liste an. Denk zurück an unser Tippfehler-Szenario. Der menschliche User sendet einen Prompt. Das System gibt dieser Message die ID 123. Der User bemerkt seinen Fehler, editiert den Text und schickt die Korrektur ab. Das Frontend sendet den neuen Text und taggt ihn explizit mit der ID 123. Wenn diese Daten auf den Graph treffen, scannt der add messages Reducer die History, findet die ursprüngliche Message mit der ID 123 und tauscht den Text direkt aus. Das Geister-Duplikat ist weg. Die Conversation verläuft genau wie beabsichtigt. Neben dem Managen von IDs übernimmt der add messages Reducer auch die Daten-Deserialisierung. In einer Production-App kommen deine Messages oft in verschiedenen Formaten an. Dein Frontend sendet vielleicht rohe JSON-Dictionaries, die Role- und Content-Strings enthalten. Deine internen Graph Nodes generieren vielleicht native LangChain Message-Objekte. Der Reducer fungiert als universeller Übersetzer für diese Inputs. Wenn du eine Liste von einfachen Python-Dictionaries in den State übergibst, konvertiert die add messages Funktion sie automatisch in die korrekten LangChain Message-Klassen. Du musst keinen Boilerplate-Code schreiben, um ein Dictionary in eine HumanMessage oder AIMessage zu parsen, bevor du den State updatest. Das normalisiert die Daten für dich. Wenn du Chat Agents baust, ist die State History kein Append-Only-Log, sie ist ein lebendiges Dokument. Und deine Updates an eindeutige Message-IDs zu binden, ist das, was dieses Dokument akkurat hält. Danke fürs Einschalten. Bis zum nächsten Mal!
6

Die Wahl der Abstraktion: Graph vs Functional

4m 07s

Ein Framework zur Entscheidung, welche API verwendet werden soll. Wir stellen das explizite visuelle Routing der Graph API dem imperativen Ablauf der Functional API gegenüber.

Herunterladen
Hallo, hier ist Alex von DEV STORIES DOT EU. LangGraph, Folge 6 von 18. Wenn du frühzeitig das falsche Design-Paradigma wählst, schreibst du entweder hunderte Zeilen Boilerplate für ein einfaches Script, oder du strickst einen Spaghetti-Code-Albtraum aus simplen Python-Funktionen. Heute geht es um die Wahl deiner Abstraktion: Graph versus Functional. Man nimmt leicht an, dass eine dieser APIs von Natur aus mächtiger oder mehr production-ready ist als die andere. Das ist falsch. Unter der Haube kompilieren sowohl die Graph API als auch die Functional API auf exakt dieselbe Runtime Engine. Beide unterstützen Persistence, Streaming und Execution Control auf exakt dieselbe Weise. Die Wahl dazwischen hängt allein von deinem mentalen Modell ab und davon, wie du deine Logik ausdrücken willst. Schauen wir uns zuerst die Functional API an. Diese verlässt sich auf den standardmäßigen, imperativen Python Control Flow. Du schreibst ganz normale Python-Funktionen, markierst sie mit einem Decorator und routest deine Execution mit Standard-If-Statements und Loops. Das State Management ist hier komplett function-scoped. Daten fließen strikt vom Return Value der einen Funktion in die Arguments der nächsten. Es gibt kein geteiltes, globales Memory-Objekt, das im Hintergrund herumschwirrt. Wenn dein Workflow linear ist oder eine vorhersehbare, tightly scoped Logik hat, hält die Functional API deinen Code schlank und vertraut. Du sparst dir den kompletten Overhead, Graph-Strukturen zu definieren. Die Graph API erfordert ein anderes Mindset. Anstatt Funktionen direkt aufzurufen, definierst du ein geteiltes, globales State Schema. Dann schreibst du Nodes. Das sind kleine Funktionen, deren einziger Job es ist, diesen geteilten State zu lesen und zu mutieren. Zum Schluss verdrahtest du diese Nodes explizit über Edges miteinander. Das Routing wird nicht von einem Conditional Statement übernommen, das tief in einem Function Body versteckt ist. Stattdessen wird die Logik, die diktiert, wohin die Application als Nächstes geht, in explizite Conditional Edges herausgezogen, die auf dem Top-Level des Graphen deklariert sind. Hier ist die wichtigste Erkenntnis: Du wählst zwischen den beiden danach aus, wie dein System State und Routing im Laufe der Zeit handhabt. Stell dir einen Developer vor, der ein einfaches Data Extraction Tool baut. Es führt ein einzelnes Language Model aus, parst den Output und speichert ihn. Die Functional API ist perfekt dafür. Sie ist schnell geschrieben und leicht zu lesen. Aber spulen wir mal drei Monate vor. Das simple Script wird in ein komplexes Multi-Agent System refactored. Jetzt hast du einen Researcher Agent, der Daten an einen Writer Agent übergibt, einen Critic Agent, der mit Corrections gegensteuert, und eine Execution Pause, die darauf wartet, dass ein menschlicher Manager den finalen Draft approved. Wenn du versuchst, diesen asynchronen Multi-Agent Workflow mit der Functional API zu bauen, bricht der imperative Ansatz zusammen. Du endest damit, massive Data Payloads in tiefen Function Call Stacks hoch und runter zu reichen. Deine Routing-Logik wird in tief verschachtelten Conditionals begraben. Das ist genau der Moment, in dem du zur Graph API migrierst. Die Graph-Abstraktion glänzt hier, weil sie State von Execution entkoppelt. Weil der State global und geteilt ist, müssen deine einzelnen Agent Nodes keine schweren Data Structures aneinander weitergeben. Ein Node liest einfach den geteilten State, updatet den spezifischen Key, für den er verantwortlich ist, und ist fertig. Die expliziten Edges übernehmen und machen das Routing extrem sichtbar. Du kannst dir die Graph Definition ansehen und sofort den kompletten Workflow nachvollziehen, ohne eine einzige Zeile Business Logic zu lesen. Du nutzt die Functional API, wenn der Control Flow einfach genug ist, um ihn von oben nach unten zu lesen. Aber du wechselst zur Graph API, wenn das Routing so komplex wird, dass du es auf ein Whiteboard zeichnen musst. Danke fürs Zuhören. Macht's gut, Leute.
7

Dynamisches Routing und Conditional Edges

4m 13s

Gehe über fest codierte Logik hinaus. Wir diskutieren, wie man LLMs mit strukturierten Ausgaben zusammen mit Conditional Edges nutzt, um Workflows dynamisch zu routen.

Herunterladen
Hallo, hier ist Alex von DEV STORIES DOT EU. LangGraph, Folge 7 von 18. Hardcoded Conditionals bringen dich nur bedingt weiter, wenn du einen Agent baust. Wenn ein User eine komplexe Frage stellt, kann eine einfache Keyword-Suche nicht zuverlässig entscheiden, was deine Applikation als Nächstes tun soll. Was wäre, wenn die AI selbst den Pfad deines Workflows bestimmen könnte? Hier kommen dynamisches Routing und Conditional Edges ins Spiel. In einem Standard-Graph-Setup verbindest du Node A mit Node B. Das ist ein statischer, garantierter Pfad. Aber wenn du einen intelligenten Routing-Mechanismus baust, muss sich der Pfad basierend auf den eingehenden Daten ändern. Du nimmst vielleicht an, dass Edges in LangGraph nur hardcoded String-Verbindungen akzeptieren. Das ist nicht der Fall. Eine Edge kann eine Python-Funktion sein, die den aktuellen State deines Graphen liest und den Namen des nächsten Nodes dynamisch berechnet. Du fügst diese Logik deinem Graphen mit der Methode add conditional edges hinzu. Diese Methode benötigt drei Komponenten. Erstens, den Start-Node. Zweitens, eine Routing-Funktion. Drittens, ein Dictionary, das die möglichen String-Outputs deiner Routing-Funktion den tatsächlichen Ziel-Nodes in deinem Graphen zuordnet. Hier ist die wichtigste Erkenntnis. Die zuverlässigste Methode, eine Conditional Edge zu steuern, ist, sie mit einem Large Language Model zu kombinieren, das strukturierte Daten generiert. Du willst nicht, dass die Routing-Funktion selbst komplexe Evaluierungen oder Natural Language Processing durchführt. Stattdessen hast du einen vorgelagerten Node, in dem das Modell gezwungen wird, eine strikte Struktur zurückzugeben, wie zum Beispiel ein Pydantic-Modell. Stell dir einen Customer Service Router vor. Ein User sendet eine Nachricht. Der erste Node in deinem Graphen ist ein Intent Classifier. Innerhalb dieses Nodes übergibst du die User-Nachricht an ein Language Model und verlangst, dass es einen strukturierten Output mit einem einzigen Feld namens intent zurückgibt. Das Modell wertet den Text aus und füllt dieses Feld mit einem bestimmten Wert, zum Beispiel billing, tech support oder sales. Diese strukturierte Response wird dann im Graph State gespeichert. Jetzt übernimmt die Conditional Edge. Die Edge ist an den Classifier Node angehängt. Wenn der Classifier Node fertig ist, triggert die Conditional Edge eine kurze Python-Funktion. Diese Funktion nimmt den Graph State als Input, schaut in den State und extrahiert den Intent-Wert, den das Modell gerade generiert hat. Wenn der Intent billing ist, gibt die Funktion den String billing zurück. Die Conditional Edge schaut in ihr Mapping Dictionary, sieht, dass der String billing deinem Billing Node entspricht, und übergibt die Ausführung an genau diesen Node. Wenn der Intent tech support ist, gibt sie einen anderen String zurück und routet den Flow zum Tech Support Node. Du nutzt das Language Model für seine Reasoning-Fähigkeiten, um den Input zu kategorisieren, aber du hältst die eigentliche Routing-Logik deterministisch. Die Python-Funktion in der Conditional Edge liest einfach nur eine Variable und gibt einen String zurück. Das ist extrem berechenbar und leicht zu testen. Das wichtigste Takeaway hier ist, dass du die Entscheidung immer von der Richtung entkoppeln solltest. Lass das Language Model den Intent entscheiden und in den State schreiben, und nutze dann eine reine Python Conditional Edge, um diesen State zu lesen und den Graphen zu steuern. Bevor wir zum Schluss kommen: Wenn dir diese Folgen gefallen und du die Show unterstützen möchtest, kannst du auf Patreon nach DevStoriesEU suchen – das hilft uns wirklich sehr. Das war’s für diese Folge. Danke fürs Zuhören und viel Spaß beim Entwickeln!
8

Map-Reduce Workflows mit der Send API

4m 27s

Meistere das Orchestrator-Worker-Pattern. Wir tauchen in die Send API ein, um parallele Worker-Nodes basierend auf Laufzeitplänen dynamisch zu verteilen.

Herunterladen
Hallo, hier ist Alex von DEV STORIES DOT EU. LangGraph, Folge 8 von 18. Du kannst deine Execution Paths nicht hardcoden, wenn du keine Ahnung hast, wie viele Sub-Tasks dein Agent erstellen wird, bis er tatsächlich läuft. Wenn dein System on the fly entscheidet, dass es drei oder dreißig Items verarbeiten muss, wird das standardmäßige statische Routing fehlschlagen. Um das zu fixen, brauchst du Map-Reduce Workflows mit der Send API. Ein sehr häufiger Fehler beim Bauen mit LangGraph ist der Versuch, standardmäßige Conditional Edges für dynamisches Fan-Out zu nutzen. Conditional Edges sind perfekt, wenn du basierend auf einem Logik-Check zwischen bekannten, vordefinierten Pfaden wählen willst. Sie versagen jedoch, wenn du zur Runtime eine unbekannte Anzahl identischer paralleler Tasks spawnen musst. Standard-Parallelisierung erlaubt es dir, zu mehreren festen Nodes zu routen. Du benennst die Nodes, und der Graph triggert sie. Aber was passiert, wenn du exakt dieselbe Node mehrmals gleichzeitig ausführen musst, jeweils mit anderen Daten? Das kannst du mit basic Routing nicht machen. Das bringt uns zum Orchestrator-Worker Pattern. In dieser Architektur schaut sich eine zentrale Node die eingehenden Daten an, berechnet, wie viele separate Tasks benötigt werden, und dispatcht dynamische Worker, um sie concurrent abzuarbeiten. LangGraph ermöglicht dieses Pattern speziell durch die Send API. Stell dir einen Agent vor, der die Aufgabe hat, einen umfassenden Research Report zu schreiben. Die erste Node fungiert als Orchestrator. Sie liest den User Prompt und generiert eine Outline. Je nach Komplexität des Themas kann diese Outline drei Sections enthalten, oder auch zwölf. Du willst, dass eine separate Worker Node jede Section zur exakt gleichen Zeit draftet. Um das zu erreichen, definierst du eine Conditional Edge Funktion direkt nach deiner Orchestrator Node. Anstatt einen einfachen String zurückzugeben, der auf die nächste statische Node im Graph zeigt, gibt diese Edge Funktion eine Liste von Send Objects zurück. Hier ist die entscheidende Erkenntnis. Ein Send Object verpackt eine Destination und ihre Daten zusammen. Es nimmt zwei Argumente entgegen. Das erste Argument ist der Name der Worker Node, die du triggern willst. Das zweite Argument ist die spezifische Payload für diesen isolierten Worker. In unserem Report-Szenario iteriert die Orchestrator-Funktion durch die generierte Outline. Für jedes Section-Topic, das sie findet, erstellt sie ein neues Send Object, das auf eine einzelne Node namens draft_section zeigt, und übergibt den individuellen Topic String als Payload. Wenn LangGraph diese Edge Funktion evaluiert, empfängt es die Liste der Send Objects. Es startet dann dynamisch eine parallele Instanz der draft_section Node für jedes einzelne Item in dieser Liste. Wenn der Orchestrator eine Outline mit sieben Sections generiert hat, launcht LangGraph sieben parallele Drafting Nodes. Jede Node führt identischen Code aus, operiert aber auf ihrer eigenen, einzigartigen Payload. Das Generieren dieser dynamischen Worker ist die Map Phase. Das Sammeln ihrer unabhängigen Outputs ist die Reduce Phase. Weil diese Worker Nodes concurrent laufen, können sie nicht sicher einen einzelnen String in deinem Graph State überschreiben. Dein gesamter State muss so konfiguriert sein, dass er mehrere eingehende Updates gleichzeitig sammeln kann. Das handhabst du, indem du eine Reducer Funktion an das spezifische State Field hängst, das deine gedrafteten Sections halten wird, und sie anweist, neue Items an eine Liste anzuhängen, anstatt den vorherigen Wert zu überschreiben. Sobald jeder parallele Draft Worker mit dem Schreiben fertig ist, gibt er seinen Textblock zurück. LangGraph fängt diese Responses ab und nutzt deinen Reducer, um jeden Textblock sicher in das gemeinsame Array zu stacken. Sobald jeder dynamische Worker seine Execution abgeschlossen hat, wird der gesamte parallele Schritt resolved. Der Workflow bewegt sich dann weiter und trägt eine komplette, befüllte Liste aller gedrafteten Sections mit sich. Die Send API nimmt die parallele Execution aus deiner statischen Graph Definition heraus und legt sie direkt in die Hände deiner Runtime Data. Danke fürs Zuhören. Ich hoffe, du hast etwas Neues mitgenommen.
9

Persistenz: Threads und Checkpoints

3m 56s

Entdecke die Grundlage der Zustandsbehaftung. Wir erklären Threads, Checkpoints und Super-steps und zeigen, wie LangGraph das Überleben bei Abstürzen garantiert.

Herunterladen
Hallo, hier ist Alex von DEV STORIES DOT EU. LangGraph, Folge 9 von 18. Dein Server stürzt mitten im Gedankengang ab, während ein Agent ein riesiges Dataset verarbeitet. Er sollte nicht wieder bei null anfangen müssen. Er sollte genau da weitermachen, wo er aufgehört hat. Genau um diese Ausfallsicherheit geht es heute beim Thema Persistence, genauer gesagt um Threads und Checkpoints. Um einer LangGraph-App Persistence hinzuzufügen, musst du das Konzept eines Threads verstehen. Ein Thread repräsentiert eine einzelne, isolierte Execution Sequence oder eine spezifische User Conversation. Er hält den Working State des Graphen, während er von Node zu Node wandert. Lass mich gleich mal etwas klarstellen. Leute verwechseln Thread Memory oft mit langfristigem Cross-Session Memory. Ein Thread ist keine globale Datenbank, in der sich dein Agent Fakten über verschiedene Tasks hinweg für immer merkt. Er ist das kurzfristige Working Memory, das strikt an eine laufende Sequenz gebunden ist. Du aktivierst dieses Memory, indem du beim Compilen deines Graphen einen Checkpointer übergibst. Ein Checkpointer ist ein Objekt, das das Speichern und Laden des Graph States in ein Storage Backend übernimmt. Sobald dein Graph mit einem Checkpointer compiled ist, triggerst du die Persistence, indem du bei jedem Invoke des Graphen ein Configuration Object mit einer Thread ID übergibst. Diese ID ist der Unique Key, den der Checkpointer nutzt, um die History dieses spezifischen Runs zu tracken. Wenn du den Graphen mit dieser Thread ID ausführst, speichert der Checkpointer den State automatisch. Aber er speichert nicht kontinuierlich. Er speichert an bestimmten Grenzen, den sogenannten Super-Steps. Ein Super-Step ist ein einzelner Execution Cycle im Graphen. Wenn dein Graph Node A und danach Node B ausführt, sind das zwei Super-Steps. Wenn dein Graph branched und Node C und Node D gleichzeitig ausführt, wird diese parallele Execution zu einem einzigen Super-Step zusammengefasst. Der Checkpointer unterbricht keinen Node, während er arbeitet. Er wartet auf die Super-Step Boundary. Sobald alle für diesen Super-Step eingeplanten Nodes ihre Execution beendet haben und ihre Updates zurückgeben, erstellt LangGraph einen Checkpoint. Dieser Checkpoint enthält einen State Snapshot, der exakt festhält, wie die Graph State Variables in genau diesem Moment aussehen. Schauen wir uns an, wie das in der Praxis aussieht. Angenommen, du hast einen Agent, der ein riesiges Dataset analysiert. Der Graph hat vier Steps. Step eins fetched die Daten. Step zwei bereinigt sie. Step drei führt eine rechenintensive Analyse aus. Step vier formatiert die Summary. Du startest den Run und übergibst eine Configuration mit der Thread ID eins zwei drei. Der Graph schließt die Fetch-, Clean- und Analysis-Steps erfolgreich ab. Am Ende von Step drei speichert der Checkpointer einen State Snapshot. Dann, bevor Step vier beendet werden kann, stürzt dein Server ab. Weil du einen Checkpointer und eine Thread ID verwendet hast, ist der State sicher. Wenn dein Server neu startet, invokest du den Graphen einfach erneut und übergibst exakt dieselbe Thread ID eins zwei drei. Der Checkpointer sucht den neuesten Checkpoint heraus. Er findet den State Snapshot, der direkt nach dem Analysis-Step gespeichert wurde. Der Graph lädt diesen State und setzt die Execution sofort bei Step vier fort. Die Fetch-, Clean- und Analysis-Nodes werden komplett übersprungen, weil ihre Outputs bereits sicher im Thread Checkpoint gespeichert sind. Hier ist die wichtigste Erkenntnis. Indem du deinen Graphen mit einem Checkpointer compilest und deine Execution an eine Thread ID bindest, verwandelst du fehleranfällige In-Memory-Operations in robuste Workflows, die Unterbrechungen automatisch überstehen. Das war's für diese Folge. Danke fürs Zuhören, und keep building!
10

Durable Execution und Idempotenz

4m 06s

Verstehe die Nuancen bei der Fortsetzung von Workflows. Wir behandeln, warum Seiteneffekte idempotent sein müssen und wie man Nodes für Durable Execution strukturiert.

Herunterladen
Hallo, hier ist Alex von DEV STORIES DOT EU. LangGraph, Folge 10 von 18. Dein Workflow verarbeitet eine Zahlung, stößt im nächsten Schritt auf ein Rate Limit und stürzt ab. Wenn das System sich erholt und den Workflow fortsetzt, wird deinem Kunden ein zweites Mal etwas berechnet. Dein Code hat sich nicht geändert, aber deine Annahme darüber, wie der Graph fortgesetzt wird, war falsch. Der Fix erfordert ein Verständnis von Durable Execution und Idempotenz. Viele Entwickler gehen davon aus, dass ein langlaufender Prozess, wenn er pausiert oder fehlschlägt, beim Fortsetzen genau bei der Zeile Python-Code weitermacht, an der er gestoppt hat. Sie erwarten, dass die Runtime sich mitten in der Funktion magisch an lokale Variablen erinnert. Aber genau das passiert nicht. LangGraph friert den Python-Interpreter nicht einfach ein. Der State wird nur an den Grenzen zwischen den Nodes gespeichert. Durable Execution in LangGraph bedeutet, dass das System deinen Fortschritt trackt, indem es den Graph State persistiert, nachdem eine Node ihre Arbeit abgeschlossen hat und zurückkehrt. Wenn eine Node auf halbem Weg durch ihre Logik fehlschlägt, hat das System keine Aufzeichnung über diesen teilweisen Fortschritt. Der letzte bekannte gute State ist der, der der Node beim Start übergeben wurde. Wenn du den Graphen neu startest oder einen Retry machst, wird die Execution fortgesetzt, indem diese komplette fehlgeschlagene Node ab ihrer allerersten Zeile neu ausgeführt wird. Denk mal an das Payment-Szenario. Angenommen, du schreibst eine einzige Node, die zwei Aktionen ausführt. Erstens ruft sie eine externe API auf, um eine Kreditkarte zu belasten. Zweitens updatet sie eine Remote-Datenbank, um die Transaktion zu speichern. Die Kreditkartenbelastung ist erfolgreich, aber die Datenbankverbindung hat einen Timeout, wodurch die Node abstürzt. Der Graph State wird nicht aktualisiert. Wenn der Workflow fortgesetzt wird, übergibt er den alten State wieder an dieselbe Node. Die Node fängt von vorne an. Sie ruft die externe API auf und belastet die Kreditkarte ein zweites Mal. Hier ist die entscheidende Erkenntnis: Weil Nodes von vorne neu starten können, muss jeder Side Effect innerhalb einer Node idempotent sein. Idempotenz ist eine Eigenschaft, bei der die mehrmalige Ausführung einer Operation exakt dasselbe Ergebnis liefert wie die einmalige Ausführung. Wenn deine Node mit der Außenwelt interagiert, musst du den Code unter der Annahme schreiben, dass er für denselben Schritt mehrmals ausgeführt wird. Wie stellst du diese Sicherheit her? Du hast zwei praktische Ansätze. Der erste ist die Nutzung von Idempotency Keys bei deinen externen Services. Wenn du die Payment-API aufrufst, übergibst du einen Unique Identifier, der aus dem aktuellen Graph State abgeleitet ist. Wenn die Node abstürzt und neu läuft, sendet sie denselben Unique Identifier. Der externe Service erkennt den doppelten Request und gibt eine Success-Response zurück, ohne tatsächlich noch mal Geld zu bewegen. Der zweite Ansatz ist strukturelles Graph-Design. Wenn eine bestimmte Operation nicht von Haus aus idempotent ist, gruppiere sie nicht mit anderen Schritten, die fehlschlagen könnten. Pack die gefährliche Operation in ihre eigene, dedizierte Node. Sorge dafür, dass das das Einzige ist, was diese Node tut. Wenn du die Zahlungsabbuchung in Node A und das Datenbank-Update in Node B packst, lässt ein Datenbank-Timeout nur Node B abstürzen. Der Graph wird bei Node B fortgesetzt. Die Zahlungsabbuchung in Node A ist komplett sicher, weil Node A fertig war und der Graph seinen State gespeichert hat, bevor er weitergegangen ist. Du kontrollierst, wo das System seinen Fortschritt speichert, indem du festlegst, wie du deine Node-Grenzen ziehst. Pack niemals eine irreversible, nicht-idempotente Aktion in dieselbe Node wie etwas, das zufällig fehlschlagen könnte. Das war's für dieses Mal. Danke fürs Zuhören und keep building!
11

Human-in-the-Loop: Interrupts

4m 01s

Lerne, wie man Agenten mitten in der Ausführung einfriert. Wir detaillieren die interrupt-Funktion und wie man Workflows mit externer menschlicher Freigabe fortsetzt.

Herunterladen
Hallo, hier ist Alex von DEV STORIES DOT EU. LangGraph, Folge 11 von 18. Manchmal sollte eine KI nicht das letzte Wort haben. Vielleicht möchtest du einen Agenten mitten im Gedankengang einfrieren, einen Menschen um Freigabe bitten und seine Antwort direkt wieder in die laufende Logik einfügen. Genau das bewirken Human-in-the-Loop Interrupts. Wenn du einen Menschen brauchst, der innerhalb eines LangGraph-Workflows eine Entscheidung trifft, nutzt du eine spezielle Funktion namens interrupt. Es ist extrem wichtig zu verstehen, was das unter der Haube eigentlich macht. Zuhörer könnten das mit einem Standard-Python Input Prompt verwechseln. Das ist es aber nicht. Ein Standard Input Prompt blockiert einen aktiven Thread und belegt System Memory, während er darauf wartet, dass ein User eine Taste drückt. In LangGraph verhält sich der Aufruf von interrupt ganz anders. Er serialisiert den kompletten Graph State, speichert ihn in deiner Checkpointer Database und pausiert die Ausführung komplett. Der Graph geht schlafen. Er kann unbegrenzt auf eine Antwort warten, ohne aktive Rechenressourcen zu verbrauchen. Der Flow passiert in zwei klaren Phasen: Pausieren und Fortsetzen. Schauen wir uns zuerst das Pausieren an. In einem deiner Nodes erreicht dein Agent einen Punkt, an dem er menschliche Autorisierung braucht. Genau in dieser Codezeile rufst du die interrupt Funktion auf. Du übergibst dieser Funktion einen Payload, was normalerweise ein JSON-Objekt ist, das den Kontext enthält, den der Mensch braucht. Stell dir einen Agenten vor, der automatisierten Customer Support übernimmt. Er beschließt, eine Rückerstattung über fünfhundert Dollar vorzubereiten. Bevor er die Zahlung verarbeitet, ruft der Agent Node interrupt auf. Er übergibt einen Payload, der angibt, dass die vorgeschlagene Aktion eine Rückerstattung ist und der Betrag fünfhundert beträgt. In dem Moment, in dem diese Funktion aufgerufen wird, stoppt der Graph. Die LangGraph Runtime fängt dieses Event ab und reicht den JSON Payload nach oben an deine Client Application durch. Der Graph-Prozess fährt herunter und lässt den Payload auf einem Web UI auf das menschliche Review warten. Nun zur zweiten Phase: den Graphen wieder aufwecken. Ein externer Prozess, wie dein Backend-Server, der einen API Call vom Web UI empfängt, ist dafür verantwortlich, den Graphen neu zu starten. Der menschliche Manager klickt auf seinem Dashboard auf Approve. Dein Backend nimmt dieses Approval und startet den Graphen wieder, indem es eine spezielle Anweisung namens Command nutzt. Wenn du dieses Command sendest, packst du ein resume Argument dazu, das die Antwort des Menschen enthält. In unserem Szenario ist diese Antwort ein einfacher Boolean-Wert von true. Hier ist die wichtigste Erkenntnis. Wenn der Graph aufwacht, führt er den pausierten Node nicht noch einmal von ganz vorne aus. Er setzt die Ausführung genau in der Codezeile fort, in der er gestoppt hat. Die interrupt Funktion, die den Graphen ursprünglich pausiert hat, beendet ihre Ausführung und gibt genau den Wert zurück, den du über das resume Command gesendet hast. Die Boolean-Antwort des Menschen wird direkt in die Variable eingefügt, die auf das interrupt Ergebnis wartet. Der Agent liest dann diesen true Wert, besteht seinen Conditional Check und schließt die fünfhundert Dollar Rückerstattung ab. Diese Architektur schafft eine saubere Trennung. Die Graph Logic muss keine Webhooks, E-Mails oder User Interfaces handhaben. Sie ruft einfach eine Funktion auf, die einen Payload über die Mauer wirft und auf einen Return Value wartet. Das externe System übernimmt die komplette User Interaction und pusht die Antwort einfach wieder rein. Indem du die menschliche Antwort direkt in den Function Return einfügst, vermeidest du es, deinen Main Graph State mit temporären Interaktionsdaten zu verschmutzen. Die Stärke der interrupt Funktion liegt darin, menschliches Feedback nicht als komplexen architektonischen Umweg zu behandeln, sondern als Standard Function Call, der das Universum sicher pausieren kann, bis er eine Antwort bekommt. Das war's für diese Folge. Danke fürs Zuhören und keep building!
12

Die Vergangenheit debuggen: Time Travel und Forking

3m 43s

Erkunde die Time-Travel-Fähigkeiten von LangGraph. Wir zeigen, wie man durch die State-Historie navigiert, vergangene Checkpoints abspielt und alternative Ausführungspfade forkt.

Herunterladen
Hallo, hier ist Alex von DEV STORIES DOT EU. LangGraph, Folge 12 von 18. Dein Agent gerät außer Kontrolle, führt eine Aktion aus, die du nicht wolltest, oder generiert eine furchtbare Response. Normalerweise musst du den gesamten Prozess von Grund auf neu starten und hoffen, dass er sich beim zweiten Mal besser verhält. Was wäre, wenn du die Execution buchstäblich bis zum exakten Moment vor dem Fehler zurückspulen, den State manuell ändern und das Ganze auf einer alternativen Timeline weiterlaufen lassen könntest? Genau darum geht es heute bei Debugging the Past: Time Travel und Forking. Um die Vergangenheit zu manipulieren, musst du sie zunächst sehen. Dies machst du mit einer Methode namens get state history, der du deine Thread-ID übergibst. Diese Methode gibt einen Iterator zurück, der jeden State enthält, den der Graph während der Execution dieses Threads durchlaufen hat. Jeder einzelne dieser historischen States besitzt einen Unique Identifier, die sogenannte Checkpoint-ID. Du kannst dir diese ID wie deine genauen Koordinaten in der Zeit vorstellen. Wenn du den Graph einfach ab einem bestimmten Punkt replayen möchtest, holst du dir die Ziel-Checkpoint-ID aus dieser History. Anschließend rufst du die invoke-Methode deines Graphen auf und übergibst ein Configuration-Object, das sowohl die Thread-ID als auch genau diese Checkpoint-ID enthält. Der Graph setzt die Execution sofort ab genau diesem State fort. Er führt keine der vorherigen Nodes erneut aus, was Compute und Zeit spart. Replaying ist nützlich, aber die wahre Stärke liegt darin, die Vergangenheit zu ändern, um die Execution zu forken. Schauen wir uns ein praktisches Szenario an. Angenommen, dein Agent hatte die Aufgabe, einen Witz zu schreiben, und hat einen furchtbaren Witz über einen Hund generiert. Du checkst die State-History und findest die Checkpoint-ID für den State, kurz bevor der Generation-Step stattfand. Anstatt einfach von diesem Punkt aus zu replayen, nutzt du die update state Methode. Du übergibst die Thread-ID, die spezifische historische Checkpoint-ID und die neuen State-Values, die du injecten möchtest. In diesem Fall updatest du die Topic-Variable manuell und änderst sie von einem Hund zu Hühnern. Hier ist die entscheidende Erkenntnis. Developer denken oft, dass das Updaten eines vergangenen States die Execution zurückrollt und die originale History überschreibt oder löscht. Das tut es nicht. LangGraph arbeitet mit einer Append-Only-Architektur. Wenn du update state auf einem historischen Checkpoint aufrufst, erstellt das System sicher einen brandneuen Checkpoint, der von diesem alten abzweigt. Deine originale Timeline, komplett mit dem furchtbaren Hundewitz, bleibt vollständig intakt und zugänglich. Du hast die Vergangenheit nicht gelöscht; du hast eine neue Realität geforkt. Sobald du dieses Update anwendest, befindet sich der Graph an einem neu erstellten Checkpoint mit dem geänderten State. Um auf dieser neuen Timeline weiterzumachen, rufst du den Graph einfach erneut mit der Thread-ID auf und lässt jede spezifische Checkpoint-ID weg. Der Graph nutzt standardmäßig den neuesten State auf diesem neu geforkten Branch und setzt die Execution fort. Dein Agent liest den geupdateten State und generiert stattdessen einen Witz über Hühner. Wenn du diese technischen Breakdowns hilfreich findest und die Show unterstützen möchtest, kannst du auf Patreon nach DevStoriesEU suchen. Time Travel verwandelt Debugging vom bloßen Raten, was schiefgelaufen ist, in das präzise Manipulieren der Graph-History, um alternative Outcomes zu erkunden, ohne auch nur eine einzige Spur des originalen Runs zu verlieren. Das war's für diese Folge. Danke fürs Zuhören und keep building!
13

Langzeitgedächtnis: Stores über Threads hinweg

3m 46s

Gehe über isolierte Threads hinaus. Wir stellen das Store-Interface vor und erklären, wie du deinen Agenten ein persistentes, sitzungsübergreifendes Gedächtnis verleihst.

Herunterladen
Hallo, hier ist Alex von DEV STORIES DOT EU. LangGraph, Folge 13 von 18. Du baust einen Agent, und ein User sagt ihm, dass er seinen Code immer in Python 3.11 haben will. Morgen startet er eine neue Konversation, und der Agent vergisst das komplett und gibt stattdessen Python 3.9 aus. Thread Memory ist auf eine einzelne Konversation isoliert. Wenn dein Agent Fakten über komplett separate Sessions hinweg behalten muss, brauchst du Long-Term Memory mit Stores über Threads hinweg. Ein häufiger Fehler ist der Versuch, das zu lösen, indem man langfristige Fakten in den Checkpointer State stopft. Checkpointer sind Short-Term Memory. Sie sind strikte Per-Thread State Snapshots, die dafür gedacht sind, eine einzelne Konversation zu pausieren, fortzusetzen oder zu replayen. Wenn ein User in Thread A eine Preference angibt, hat Thread B absolut keine Möglichkeit, sie zu sehen. Um Wissen über mehrere Threads hinweg zu teilen, bietet LangGraph das Store Interface. Ein Store ist ein Key-Value Memory Layer, der außerhalb der einzelnen Thread States liegt. Du richtest ihn ein, indem du beim Kompilieren deines Graphs ein Store Object, wie einen PostgresStore, als Argument übergibst. Sobald er kompiliert ist, wird dieser Store an die Graph Execution Environment angehängt. Innerhalb deines Graphs greifen Nodes über das Runtime Object auf diesen Memory Layer zu. Wenn du eine Node definierst, kannst du auf den Runtime Context zugreifen, der den Store bereitstellt. Du greifst einfach auf runtime dot store zu, um mit deinem Long-Term Memory zu interagieren. Hier ist der entscheidende Punkt. Daten in einem Store werden mithilfe von Namespaces organisiert. Ein Namespace ist eine hierarchische Liste von Strings, die deine Daten partitioniert, ähnlich wie ein Ordnerpfad auf deinem Computer. Für eine Multi-Tenant Application könntest du einen Namespace definieren, der mit dem String users beginnt, gefolgt von einer spezifischen User ID, und mit preferences endet. Denk mal an das Coding Assistant Szenario. Ein User startet am Montag eine Session. Während des Chats erwähnt er, dass er Python 3.11 und den Dark Mode bevorzugt. Eine Node in deinem Graph erkennt das als permanente Preference. Sie ruft die put Methode auf runtime dot store auf. Sie übergibt den Namespace für diesen spezifischen User, einen eindeutigen Key für das Item und ein Dictionary, das die Preferences enthält. Die Daten sind nun außerhalb des Threads gespeichert. Am Freitag öffnet derselbe User deine Application und startet einen brandneuen Thread. Der Checkpointer State für diesen neuen Thread ist komplett leer. Dein Graph enthält jedoch eine Setup Node, die zuerst läuft. Diese Node ruft die search Methode auf runtime dot store auf und übergibt das User Namespace Prefix. Der Store gibt die gespeicherten Preferences zurück. Die Node packt diese Preferences dann in den aktuellen Thread State. Ab diesem Punkt weiß der Agent, dass er für diese neue Konversation Python 3.11 und den Dark Mode verwenden soll. Das Store Interface bietet drei Hauptoperationen. Du nutzt put, um ein Item zu speichern oder zu überschreiben. Du nutzt get, um ein einzelnes Item abzurufen, wenn du seinen genauen Namespace und Key kennst. Du nutzt search, um mehrere Items abzurufen, die sich ein Namespace Prefix teilen. Searching ist besonders nützlich, wenn du im Laufe der Zeit mehrere verschiedene Memory Fragments für einen User gespeichert hast und sie alle in den aktuellen Context ziehen musst. Indem du den Short-Term State von einem Cross-Thread Store trennst, entkoppelst du die Lebensdauer deines Agent-Wissens von der Lebensdauer einer einzelnen Konversation. Danke fürs Zuhören – bis zum nächsten Mal.
14

Streaming-Ausführung und das v2-Format

3m 59s

Verbessere die UX mit Echtzeit-Feedback. Wir schlüsseln Stream-Modi (values, updates, messages) und das vereinheitlichte v2 StreamPart-Format auf.

Herunterladen
Hallo, hier ist Alex von DEV STORIES DOT EU. LangGraph, Folge 14 von 18. User hassen es, dreißig Sekunden lang auf einen statischen Loading Spinner zu starren, während dein System im Hintergrund arbeitet. Du willst ihnen den Denkprozess des Systems in Echtzeit zeigen, aber das Erfassen dieser internen Signale erfordert oft das Einrichten komplexer Custom Callbacks. Streaming Execution und das v2-Format lösen dieses Problem, indem sie jedes interne Event in einen einzigen, vorhersehbaren Flow zusammenfassen. Klären wir zuerst ein häufiges Missverständnis auf. Entwickler verwechseln oft das Streamen von Language Model Tokens mit dem Streamen von Application State. Das sind völlig unterschiedliche Informationsebenen. Ein Token Stream ist einfach Text, der Wort für Wort erscheint. Ein State Stream verfolgt den übergeordneten Fortschritt deines Workflows von einem Task zum nächsten. LangGraph verarbeitet beides gleichzeitig. Du greifst auf dieses Verhalten zu, indem du einen Stream anforderst und das Argument version v2 an deine Execution Method übergibst. Das standardisiert den Output. Anstatt mit gemischten Datentypen zu arbeiten, wird jedes einzelne Event, das deinen Graph verlässt, zu einem einheitlichen Dictionary mit genau drei Feldern: type, ns und data. Das type-Feld definiert die Kategorie des Events. Das ns-Feld steht für Namespace und gibt den genauen Pfad in deiner Graph-Hierarchie an, von dem das Event ausging. Das wird extrem wichtig, wenn du verschachtelte Subgraphen hast und genau wissen musst, welche Sub-Component das Event gefeuert hat. Das data-Feld enthält schließlich den eigentlichen Payload. Du steuerst genau, was in diesen Stream gelangt, indem du einen oder mehrere Stream Modes auswählst. Der values-Mode pusht dir den kompletten, aktualisierten Graph State jedes Mal, wenn irgendein Node seine Arbeit abschließt. Das ist nützlich, wenn deine Application in jedem Schritt das Gesamtbild braucht. Der updates-Mode ist deutlich schlanker. Er streamt nur die spezifischen Daten, die von einem Node zurückgegeben werden, und repräsentiert nur das Delta oder die Änderung am gesamten State. Der messages-Mode arbeitet auf einer granulareren Ebene und streamt die einzelnen Chunks einer generierten Chat Message, während sie von einem zugrunde liegenden Language Model produziert werden. Stell dir ein Frontend Interface vor. Du willst einen leuchtenden Status Indicator, der hervorhebt, welcher Schritt gerade aktiv ist – vielleicht Context fetchen, dann Dokumente evaluieren, dann draften – während gleichzeitig der Draft-Text Token für Token angezeigt wird. Um das zu bauen, startest du deine Graph Execution mit den Stream Modes auf updates und messages gesetzt, und stellst sicher, dass du das v2 Version Flag übergibst. Dein Frontend empfängt nun einen kontinuierlichen, einheitlichen Stream dieser Dictionaries. Sobald ein Dictionary eintrifft, bei dem der type auf updates gesetzt ist, liest du das Namespace-Feld. Das sagt dir genau, welcher Node gerade seine Arbeit abgeschlossen hat. Du nutzt dieses Signal, um deinen leuchtenden Status Indicator auf dem User Interface auf den nächsten Schritt zu schieben. Millisekunden später liefert der Stream ein neues Dictionary, bei dem der type auf messages gesetzt ist. Du ziehst den Raw Text Token aus dem data-Feld und hängst ihn direkt an den Absatz an, den dein User gerade liest. Sowohl High-Level State Changes als auch Low-Level Text Generation kommen über exakt dieselbe Pipe an. Hier ist die entscheidende Erkenntnis. Indem Tokens, State Changes und Node-Fortschritt in eine einzige Dictionary-Struktur mit drei Feldern gezwungen werden, entfernt das v2-Format komplett die Notwendigkeit, separate Handling Logic oder komplexe asynchrone Callbacks für verschiedene Arten von Real-Time Events zu schreiben. Das war's für diese Folge. Bis zum nächsten Mal!
15

Komplexität zusammensetzen: Subgraphs

3m 29s

Skaliere deine Workflows, indem du kompilierte Graphen als Nodes behandelst. Wir diskutieren das Zusammensetzen von Subgraphs und die Verwaltung von geteilten gegenüber privaten State-Schemata.

Herunterladen
Hallo, hier ist Alex von DEV STORIES DOT EU. LangGraph, Folge 15 von 18. Wenn dein AI-Agent komplexer wird, landest du oft bei einem riesigen, unlesbaren Mega-Graph, bei dem eine einzige Änderung alles zerschießt. Du musst nicht so bauen – du kannst stattdessen spezialisierte Mini-Graphen bauen und sie zusammenstecken. Wir sprechen über Composing Complexity: Subgraphs. Mit Subgraphs kannst du Logik wiederverwenden und die Entwicklung auf verschiedene Teams verteilen. Anstatt jeden einzelnen Schritt deiner Application in eine Datei zu packen, erstellst du kleinere, eigenständige Graphen. Sobald ein Graph kompiliert ist, verhält er sich genau wie eine ganz normale Callable Function. Das bedeutet, du kannst einen kompletten kompilierten Graphen nehmen und ihn direkt als einzelnen Node in einen anderen Graphen einfügen. Stell dir ein Master-Routing-System für einen Enterprise-Assistant vor. Der Main-Graph verarbeitet den User-Input, checkt die Security und entscheidet, was als Nächstes passiert. Wenn ein User eine tiefe technische Frage stellt, muss der Router komplexes Data Gathering durchführen. Anstatt diese Logik direkt in den Router zu coden, delegierst du sie an einen dedizierten Research-Subgraph. Ein komplett anderes Engineering-Team kann diesen Research-Graph isoliert bauen, testen und optimieren. Dem Parent-Graph ist es egal, wie die Research gemacht wird. Er ruft einfach nur den Node auf. Man neigt oft dazu, die Datenübergabe zwischen diesen Graphen unnötig zu verkomplizieren. Wenn der Parent-Graph und der Subgraph genau dasselbe State Schema verwenden – also genau dieselben State Keys teilen –, brauchst du keine speziellen Adapter. Du übergibst den kompilierten Research-Subgraph einfach direkt in die add node-Funktion deines Master-Graphs. Die Engine füttert den Parent State automatisch in den Subgraph, führt die Logik aus und mergt die Ergebnisse nach Abschluss wieder zurück in den Parent State. Was passiert nun, wenn sich die Teams nicht perfekt abstimmen? Angenommen, dein Parent-Router verwendet einen State Key namens user query, aber das andere Engineering-Team hat den Research-Subgraph so gebaut, dass er einen Key namens search term erwartet. Du kannst den kompilierten Subgraph nicht direkt in den Parent-Graph einfügen. Die Keys matchen nicht, und die Execution schlägt fehl. Hier ist der entscheidende Punkt. Du überbrückst diesen Mismatch mit einer einfachen Wrapper-Funktion. In deinem Parent-Graph definierst du eine Standard-Node-Funktion, die den Parent State akzeptiert. Innerhalb dieser Funktion extrahierst du den user query-Wert. Dann rufst du den kompilierten Research-Subgraph manuell auf und übergibst ihm einen Payload, in dem du diese user query auf den search term-Key mappst. Der Subgraph führt seine interne Logik aus und gibt seinen Final State zurück. Deine Wrapper-Funktion nimmt diesen Output, übersetzt die Ergebnisse zurück in die spezifischen Keys, die der Parent erwartet, und gibt sie zurück. Für den Parent-Graph sieht dieser Wrapper wie ein ganz normaler Node aus. Er hat keine Ahnung, dass darin gerade ein massiver, komplexer Subgraph ausgeführt wurde. Er hat einfach Daten hineingegeben und einen aktualisierten State zurückbekommen. Dieses Pattern gibt dir strikte Modularität, ohne die Kontrolle aufzugeben. Einen kompilierten Graphen einfach als eine weitere Callable Function hinter einem simplen Wrapper zu behandeln, ist der absolut mächtigste Weg, eine AI-Architektur zu skalieren, ohne unter deinem eigenen Code zusammenzubrechen. Das war's für diese Folge. Danke fürs Zuhören und keep building!
16

Subgraph-Persistenz und Multi-Agenten-Patterns

3m 58s

Meistere das Memory-Scoping in Multi-Agenten-Systemen. Wir erklären den Unterschied zwischen per-invocation, per-thread und zustandsloser Subgraph-Persistenz.

Herunterladen
Hallo, hier ist Alex von DEV STORIES DOT EU. LangGraph, Folge 16 von 18. Wenn ein Expert Subagent in einer einzigen Conversation zweimal aufgerufen wird, soll er sich dann an den ersten Aufruf erinnern oder mit völliger Amnesie von vorne beginnen? Diese Entscheidung verändert alles daran, wie sich Multi-Agent-Systeme verhalten, und sie wird komplett durch Subgraph Persistence und Multi-Agent Patterns gesteuert. Wenn du einen Parent Graph baust, der Tasks an Subgraphs routet, wird das State Management kompliziert. Stell dir einen primären Customer Service Bot vor, der den allgemeinen Chat übernimmt. Wenn ein User eine komplexe Frage zu einer Rechnung stellt, routet der primäre Bot den Request an einen dedizierten Billing Expert Subgraph. Standardmäßig sind Subgraphs komplett stateless. Wenn du diesen Billing Expert compilest, ohne einen Checkpointer anzugeben, operiert er strikt auf einer Per-Invocation-Basis. Der primäre Bot übergibt ihm die benötigten Inputs, der Experte führt seine internen Steps aus, gibt ein Result zurück und verwirft sofort seinen internen State. Wenn der User fünf Minuten später eine Follow-up-Frage zur Rechnung stellt, callt der primäre Bot den Experten erneut. Der Experte hat keine Erinnerung an den vorherigen Austausch. Er fängt komplett von vorne an. Für einen simplen Data-Extraction Subgraph ist diese Amnesie völlig in Ordnung. Für einen interaktiven, spezialisierten Agent ist das für den User allerdings extrem frustrierend. Um das zu fixen, braucht der Experte sein eigenes Memory über mehrere Turns hinweg. Ein sehr häufiger Fehler hierbei ist, eine brandneue Checkpointer-Instanz, wie ein Memory Saver Object, direkt in die compile-Methode des Subgraphs zu übergeben. Mach das auf keinen Fall, es sei denn, du willst, dass der Subgraph exakt denselben State über komplett verschiedene User und Sessions hinweg teilt. Wenn User A und User B gleichzeitig mit dem System sprechen, bedeutet die Übergabe einer expliziten Checkpointer-Instanz an den Subgraph, dass ihre Daten zu einem einzigen globalen State zusammengemischt werden. Das erzeugt massiven Cross-Talk zwischen isolierten Parent Threads. Stattdessen übergibst du beim Compilen des Subgraphs einfach den Boolean-Wert True an das Checkpointer-Argument. Hier wird es interessant. Ihn auf True zu setzen, sagt dem Subgraph, dass er sich auf den Checkpointer-Mechanismus des Parent Graphs verlassen soll, aber eine komplett isolierte Multi-Turn History speziell für sich selbst pflegen soll. Im Hintergrund kümmert sich das Framework um das Namespacing. Es erstellt automatisch eine unique Thread-ID für den Subgraph, die permanent an die Thread-ID des Parents gebunden ist. Schau dir nun das Billing Expert Szenario mit dieser Konfiguration noch mal an. Der User stellt eine Frage zu einer Rechnung. Der primäre Bot routet sie an den Experten. Der Experte antwortet und geht in den Ruhezustand. Später in derselben Conversation stellt der User ein Follow-up. Der primäre Bot routet wieder zum Experten. Da er mit dem Checkpointer auf True compiled wurde, wacht der Experte auf, checkt seinen dedizierten Sub-Thread und lädt den Context der Rechnung aus dem früheren Turn. Er agiert wie ein persistenter Teilnehmer in der Conversation. Und weil dieser Sub-Thread strikt auf den Parent Thread gescoped ist, bekommt ein anderer User, der mit dem System spricht, seine eigene, komplett cleane Instanz des Billing Experts. Wie du den Checkpointer eines Subgraphs konfigurierst, diktiert seine komplette Identität in deinem System: Ihn leer zu lassen, erzeugt eine disposable, stateless Utility Function, während ihn auf True zu setzen einen kontinuierlichen, context-aware Collaborator erschafft. Danke, dass du dir ein paar Minuten für mich Zeit genommen hast. Bis zum nächsten Mal, mach's gut.
17

Anwendungsstruktur und Deployment-Bereitschaft

4m 13s

Der Übergang von Prototypen zur Produktion. Wir untersuchen langgraph.json, die richtige Dateistruktur und das Dependency-Management für zustandsbehaftete Deployments.

Herunterladen
Hallo, hier ist Alex von DEV STORIES DOT EU. LangGraph, Folge 17 von 18. Ein Python-Skript, das auf deinem Laptop erfolgreich läuft, ist noch keine Production Application. Wenn du versuchst, stateful Agents durch die Ausführung von Standalone-Skripten zu betreiben, stößt du beim Skalieren unweigerlich an eine Wand. Um das zu lösen, schauen wir uns Application Structure und Deployment Readiness an. Wenn du zum ersten Mal einen LangGraph Agent baust, erstellst du wahrscheinlich einen Prototyp in einem Jupyter Notebook oder einem einzelnen Python-File. Du definierst die Nodes, verbindest die Edges, kompilierst den Graph und rufst die invoke-Methode direkt in demselben File auf, um zu sehen, ob es funktioniert. Das ist für Tests völlig in Ordnung. Ein Production-Server kann jedoch nicht deine Gedanken lesen. Er braucht einen standardisierten Weg, um diesen Graph als API zu serven, die erforderlichen Packages zu installieren und Environment Variables zu injecten. Um deinen Prototyp deployment-ready zu machen, musst du deinen Code in einer sauberen Directory Structure organisieren. Sagen wir, du erstellst einen neuen Ordner namens my-app. Du verschiebst deinen Python-Code aus dem Notebook in ein sauberes File in diesem Ordner. Als Nächstes fügst du ein Dependency-File hinzu, typischerweise requirements dot txt. Schließlich erstellst du ein Configuration-File namens langgraph dot json im Root des my-app-Ordners. Das langgraph dot json File ist der zentrale Blueprint für deine Anwendung. Wenn du das LangGraph Command Line Interface nutzt oder in ein Production Environment deployest, sagt dieses Configuration-File dem zugrunde liegenden System genau, wie dein Projekt gebaut und ausgeführt werden soll. Es benötigt drei Hauptinformationen bezüglich Dependencies, Environment Variables und den Graph Entry Points. Zuerst deklarierst du deine Dependencies. Das ist einfach ein Path String im JSON-File, der auf dein Requirements-File zeigt. Er stellt sicher, dass der Deployment-Server genau die Python-Packages installiert, auf die dein Agent angewiesen ist, und verhindert Missing Module Errors in Production. Als Nächstes definierst du den Environment String. Dieser zeigt auf dein dot env File. Stateful Agents brauchen immer Secrets, wie Database Credentials oder Model API Keys. Der Verweis auf das Environment-File stellt sicher, dass die Runtime diese Keys sicher lädt, bevor sie versucht, den Graph zu starten. Das ist der Teil, auf den es ankommt. Die dritte Anforderung im Configuration-File ist das Graphs Mapping. Das sagt dem Server genau, wo dein kompilierter Graph im Source Code lebt. Es funktioniert wie ein Dictionary. Du weist deinem Graph eine ID zu, die zu seinem offiziellen Namen in der generierten API wird. Dann mappst du diese ID auf ein bestimmtes Python-Module und einen Variable Name. Zum Beispiel könntest du die ID customer-support-agent auf den String agent dot py colon compiled-graph mappen. Der Server schaut sich das agent dot py File an, findet die Variable namens compiled-graph und lädt sie in den Memory. Diese Struktur erfordert ein bewusstes Umdenken dabei, wie du deinen Code schreibst. Anfänger führen Graphen oft über Standalone-Python-Skripte aus, die Actions ausführen, sobald sie gestartet werden. Aber die LangGraph-Runtime verlässt sich auf langgraph dot json, um den Graph dynamisch als Web Service zu exposen. Sie führt dein Skript nicht von oben nach unten aus. Sie importiert nur das Compiled Graph Object, das du im Configuration-File angegeben hast. Deswegen sollte dein Python-File nur die Nodes definieren, sie verbinden und den kompilierten Graph einer Variable zuweisen. Du musst jeglichen übrig gebliebenen Testing-Code am Ende entfernen, der den Graph manuell invoket. Wenn du Testing-Code im File lässt, wird er während der Import Phase auf dem Server ausgeführt, was zu Deployment Failures oder ungewollten API Calls führt, nur weil der Service gestartet wird. Indem du deine Dependencies, das Environment und die Graph Paths explizit in einem zentralen JSON-File deklarierst, trennst du die Definition deines Agents von seiner Ausführung und machst aus einem lokalen Skript einen robusten, deploybaren Service. Das war’s für diese Folge. Vielen Dank fürs Zuhören und keep building!
18

Graph-Ausführung End-to-End testen

3m 45s

Lerne robuste Teststrategien für Graph-Workflows. Wir behandeln die pytest-Integration, isolierte Node-Ausführung und die Simulation von partiellem State.

Herunterladen
Hallo, hier ist Alex von DEV STORIES DOT EU. LangGraph, Folge 18 von 18. Du hast einen komplexen Multi-Agent Workflow und musst in Schritt vier einen bestimmten Routing Edge Case testen. Du solltest nicht das gesamte System von Anfang bis Ende ausführen müssen, nur um diese Condition zu triggern. Mit dem End-to-End Testing der Graph Execution kannst du genau die Logik gezielt testen, die du brauchst. Wenn Entwickler Teile eines Graphen für das Testing isolieren wollen, greifen sie oft auf komplexe Mock-Objekte zurück. Sie versuchen, die umgebende Graph-Struktur zu mocken oder alle vorhergehenden Nodes zu stubben. In LangGraph musst du das nicht tun. Die Architektur dreht sich komplett um den State. Da Nodes einfach nur Funktionen sind, die den State lesen und schreiben, kannst du manuell einen spezifischen State Payload injecten und isolierte Node-Fragmente nativ testen. Genau hier werden State Injection und Breakpoints in deiner Test Suite unglaublich nützlich. Du brauchst nur zwei Tools, um direkt in die Mitte eines Graphen zu springen. Das erste ist die update state Methode. Das zweite ist ein Configuration Parameter namens interrupt after. Wenn du diese in einem Standard Testing Framework wie pytest verwendest, kannst du exakte Conditions simulieren, ohne die ganze Application auszuführen. Lass uns das auf ein konkretes Szenario anwenden. Angenommen, du hast einen Graphen, in dem Node drei einen externen API Call macht und Node vier das Ergebnis prüft. Du willst verifizieren, dass Node vier den Execution Flow korrekt an deinen Error Handler Node routet, wenn der API Payload einen bestimmten Failure Code enthält. Anstatt Node eins und zwei auszuführen, um das zu triggern, isolierst du das Problem. Du initialisierst den Graphen mit einem Thread Identifier. Dann nutzt du update state, um einen simulierten, fehlgeschlagenen API Payload direkt in den Thread State einzufügen. Du tust so, als ob Node drei gleich mit diesen spezifischen Daten ausgeführt wird. Als Nächstes rufst du den Graphen auf, aber du übergibst ein Configuration Dictionary, das interrupt after auf Node vier setzt. Wenn du den Graphen startest, beginnt die Execution sofort bei Node drei mit deinem injected Failure State. Node drei verarbeitet den fehlerhaften Payload und übergibt den resultierenden State an Node vier. Node vier evaluiert die Logik und entscheidet, zum Error Handler zu routen. Weil du einen Breakpoint gesetzt hast, pausiert der Graph die Execution in dem Moment, in dem Node vier fertig ist. Jetzt kann dein Test das Ergebnis evaluieren. Du ziehst dir den aktuellen State aus dem Graphen. Du kannst deine Assertions schreiben, um sicherzustellen, dass Node vier die State Variables korrekt geupdatet hat. Noch wichtiger ist, dass du den Graph Execution Plan inspecten kannst. Indem du dir den nächsten pending Node in den State Metadata ansiehst, kannst du bestätigen, dass die Routing-Logik perfekt funktioniert hat und der Error Handler gequeued ist. Hier ist die wichtigste Erkenntnis. Indem du den State direkt manipulierst, verwandelst du eine stark vernetzte, unvorhersehbare Chain von Agents in einen deterministischen, schrittweisen Test Case. Du verifizierst genau, wie der Graph von einem Node zum nächsten übergeht, ohne in den vorhergehenden Schritten auf Language Models oder Network Calls warten zu müssen. Da dies die letzte Folge der Serie ist, ermutige ich dich, die offizielle Documentation zu erkunden und diese Workflows hands-on zu bauen. Wenn du Ideen hast, was wir als Nächstes behandeln sollten, besuche devstories dot eu und schlag ein Thema vor. Das war's für diese Folge. Danke fürs Zuhören und keep building!