Zurück zum Katalog
Season 2 15 Episoden 58 min 2026

Learning DSPy (v3.1 - 2026 Edition)

Ein umfassender, schrittweiser Lehrplan zum Erlernen von DSPy, der sich von fragilen, string-basierten Prompts hin zu modularer, strukturierter Programmierung und automatisierter Optimierung bewegt.

AI/ML-Frameworks Prompt Engineering
Learning DSPy (v3.1 - 2026 Edition)
Aktuelle Wiedergabe
Click play to start
0:00
0:00
1
Programmieren statt Prompting
Diese Episode behandelt die grundlegende Philosophie von DSPy: die Abkehr von fragilen, string-basierten Prompts hin zu modularer, strukturierter Programmierung. Die Zuhörer erfahren, warum die Trennung der Systemarchitektur von den Anweisungen für das Sprachmodell zu robusteren KI-Anwendungen führt.
3m 47s
2
Sprachmodelle konfigurieren
Lerne, wie man Sprachmodelle in DSPy konfiguriert und verwaltet. Diese Episode behandelt das Festlegen von Standardmodellen, den Umgang mit Caching, das Überschreiben von Generierungseinstellungen und den Zugriff auf verschiedene Modellanbieter über LiteLLM.
4m 04s
3
Deklaratives Prompting mit Signatures
Entdecke, wie DSPy Signatures das traditionelle Prompting ersetzen. Diese Episode erklärt, wie man das Eingabe- und Ausgabeverhalten eines Moduls deklarativ definiert, indem man sowohl Inline-Strings als auch klassenbasierte Definitionen mit strenger Typisierung verwendet.
4m 04s
4
Bausteine mit Modules
Erkunde DSPy Modules, die zentralen Bausteine für Sprachmodell-Programme. Diese Episode behandelt dspy.Predict, dspy.ChainOfThought und wie man mehrere Modules zu einer größeren, zusammenhängenden Pipeline zusammensetzt.
4m 05s
5
Modelle mit Adapters verbinden
Verstehe die Rolle von Adapters in DSPy. Diese Episode erklärt, wie ChatAdapter und JSONAdapter die Lücke zwischen abstrakten DSPy Signatures und den tatsächlichen Multi-Turn-Nachrichten schließen, die an Sprachmodell-APIs gesendet werden.
4m 33s
6
Datenverwaltung mit Examples
Lerne, wie DSPy Datensätze für maschinelles Lernen handhabt. Diese Episode behandelt das dspy.Example-Objekt, die Unterscheidung zwischen Input-Keys und Labels sowie die Vorbereitung von Daten für die Evaluierung und Optimierung.
3m 37s
7
Erfolg definieren mit Metriken
Entdecke, wie man DSPy-Programme mithilfe von Metriken evaluiert. Diese Episode zeigt dir, wie du benutzerdefinierte Python-Funktionen schreibst, um Ausgaben zu bewerten, das trace-Argument verwendest und sogar AI-as-a-Judge für ausführliche Evaluierungen nutzt.
4m 22s
8
Eine Einführung in Optimizers
Tauche ein in die wahre Magie von DSPy: Optimizers. Diese Episode bietet einen Überblick darüber, was Optimizers tun, über den iterativen Optimierungszyklus und die ungewöhnliche 20/80-Datenaufteilungsstrategie für die Prompt-Optimierung.
3m 31s
9
Automatisches Few-Shot Learning
Lerne, wie DSPy Few-Shot Prompting automatisiert. Diese Episode konzentriert sich auf BootstrapFewShot und BootstrapFewShotWithRandomSearch und erklärt, wie sie hochwertige Beispiele synthetisieren, filtern und in deine Prompts injizieren.
3m 36s
10
Instruction Optimization mit MIPROv2
Tauche ein in das automatische Instruction Tuning. Diese Episode untersucht MIPROv2 und COPRO und zeigt, wie DSPy Bayesian Optimization und Coordinate Ascent verwendet, um überlegene, kontraintuitive Prompt-Anweisungen zu entdecken.
3m 38s
11
Finetuning mit BootstrapFinetune
Entdecke, wie man massive Sprachmodelle in kleinere, effiziente Modelle destilliert. Diese Episode behandelt BootstrapFinetune und erklärt, wie man ein prompt-basiertes DSPy-Programm in ein maßgeschneidertes Modell mit aktualisierten Gewichten umwandelt.
3m 14s
12
Automatisierte Tool-Nutzung mit ReAct
Lerne, wie du Sprachmodellen Zugriff auf externe Tools gibst. Diese Episode behandelt das dspy.ReAct-Modul und demonstriert, wie man autonome Agenten baut, die dynamisch schlussfolgern und mit APIs interagieren.
4m 07s
13
Manuelles Tool Handling für mehr Kontrolle
Übernimm die volle Kontrolle über die Tool-Ausführung. Diese Episode behandelt manuelles Tool Handling in DSPy unter Verwendung von dspy.Tool, dspy.ToolCalls und nativem Function Calling für latenzempfindliche Anwendungen.
3m 56s
14
Tools integrieren mit MCP
Verbinde deine Agenten mit universellen Tool-Servern. Diese Episode erklärt, wie man das Model Context Protocol (MCP) in DSPy verwendet, um standardisierte Tools über verschiedene Frameworks hinweg mit minimalem Einrichtungsaufwand zu nutzen.
3m 57s
15
Ensembles und Meta-Optimization
Bringe DSPy an seine Grenzen. Die letzte Episode behandelt Programmtransformationen via dspy.Ensemble und den experimentellen BetterTogether Meta-Optimizer, der Prompt Tuning mit Weight Finetuning für maximale Leistung kombiniert.
3m 51s

Episoden

1

Programmieren statt Prompting

3m 47s

Diese Episode behandelt die grundlegende Philosophie von DSPy: die Abkehr von fragilen, string-basierten Prompts hin zu modularer, strukturierter Programmierung. Die Zuhörer erfahren, warum die Trennung der Systemarchitektur von den Anweisungen für das Sprachmodell zu robusteren KI-Anwendungen führt.

Herunterladen
Hallo, hier ist Alex von DEV STORIES DOT EU. Learning DSPy, Folge 1 von 15. Du verbringst drei Stunden damit, den perfekten Prompt zu bauen, damit dein Sprachmodell die richtigen Outputs generiert. Dann veröffentlicht ein Provider ein neues Modell, du tauschst den API Key aus, und deine gesamte Pipeline bricht zusammen. Du bist ständig damit beschäftigt, fragile Strings zu warten, anstatt Software zu bauen. Genau dieses Problem löst eine Philosophie namens Programming, Not Prompting. Viele hören von DSPy und denken, es ist nur ein weiteres Prompt-Templating-Tool, um Variablen in Textblöcke einzufügen. Ist es aber nicht. DSPy ist ein Framework zum Kompilieren und Optimieren von Control Flow. Nehmen wir mal ein Standard-Setup für ein System, das Dokumente liest und einen Report mit Citations generiert. Beim traditionellen Ansatz schreibst du einen riesigen Textblock. Du erklärst die Task, fügst die Dokumente ein, ergänzt manuelle Instructions wie "think step by step" und spezifizierst genau, wie die Citations aussehen müssen. Dieser Ansatz koppelt deine Systemarchitektur eng an beiläufige Entscheidungen. Deine Architektur ist die Core-Logik. Das bedeutet: Fakten extrahieren, eine Summary entwerfen und Citations anhängen. Die beiläufigen Entscheidungen sind die spezifischen Wörter, die du benutzt hast, um ein bestimmtes Sprachmodell dazu zu bringen, dir zu gehorchen. Genau diese Wörter werden bei einem anderen Modell, oder sogar einer anderen Version desselben Modells, nicht optimal funktionieren. Wenn sich die Daten auch nur leicht ändern, bricht der Prompt. DSPy trennt deine Systemarchitektur von diesen beiläufigen Prompt-Entscheidungen. Du hörst auf, lange Instruction-Strings zu schreiben. Stattdessen definierst du deine Task rein über Inputs und Outputs. Für den Report-Generator deklarierst du, dass der Input eine Liste von Text-Snippets ist, und der Output ein Textentwurf und ein Set von Citations. Sobald die Inputs und Outputs definiert sind, verdrahtest du sie mit Standard-Code. Du erstellst eine Extraktions-Komponente, übergibst ihr die Dokumente und sammelst die Fakten. Dann übergibst du diese Fakten an eine Drafting-Komponente. Zum Schluss nutzt du vielleicht einen einfachen Loop, um den Entwurf mit den ursprünglichen Fakten gegenzuchecken. Es gibt keine manuellen Strings mehr, die das Modell anbetteln, seinen Output richtig zu formatieren. Es gibt nur noch strukturierte Logik. Hier ist die wichtigste Erkenntnis: Weil deine Architektur als modularer Code definiert ist, kann ein Compiler diese Struktur automatisch in die tatsächlichen Prompts übersetzen, die von dem Sprachmodell benötigt werden, das du gerade nutzt. DSPy behandelt die Model-Instructions, die Reasoning-Steps und die Few-Shot-Examples als interne Parameter. Das sind Variablen, die vom Framework optimiert werden, und kein statischer Text, den du von Hand eintippst. Du baust die Pipeline, definierst die Data Shapes und schreibst die Execution-Steps in Standard-Python. Das Framework übernimmt die unvorhersehbare Aufgabe herauszufinden, wie man das Sprachmodell am besten bittet, diese Schritte zuverlässig auszuführen. Das verändert die Developer Experience grundlegend. Du verbringst deine Zeit damit, die Logik deiner Applikation zu debuggen, und rätst nicht mehr herum, welches Adjektiv das Modell dazu bringt, auf das Ende eines Satzes zu achten. Deine Systemarchitektur sollte die Eigenheiten des Sprachmodells überdauern, an das du heute zufällig deine Requests routest. Danke fürs Zuhören und Happy Coding zusammen!
2

Sprachmodelle konfigurieren

4m 04s

Lerne, wie man Sprachmodelle in DSPy konfiguriert und verwaltet. Diese Episode behandelt das Festlegen von Standardmodellen, den Umgang mit Caching, das Überschreiben von Generierungseinstellungen und den Zugriff auf verschiedene Modellanbieter über LiteLLM.

Herunterladen
Hallo, hier ist Alex von DEV STORIES DOT EU. DSPy lernen, Folge 2 von 15. Ein einzelner API Call an ein Language Model ist einfach. Aber mehrere Provider zu managen, Responses zu cachen und Prompt Histories zu tracken, zwingt dich normalerweise dazu, einen eigenen Wrapper zu bauen und zu maintainen. Die Konfiguration von Language Models in DSPy erspart dir diese nervige Arbeit. Du denkst vielleicht, du brauchst das OpenAI SDK für GPT-Modelle, das Anthropic SDK für Claude und eine separate Library für lokale Modelle. Brauchst du aber nicht. DSPy regelt das einheitlich über eine einzige LM Class, die unter der Haube von LiteLLM angetrieben wird. Um ein Modell zu nutzen, instanziierst du ein DSPy LM Object und übergibst einen String, der den Provider-Namen, einen Slash und den Modellnamen enthält. Zum Beispiel übergibst du open ai slash gpt-4o-mini. Wenn du dieses Object erstellst, kannst du auch Standardparameter wie Temperature oder Max Tokens übergeben. Dank des einheitlichen Backends bleiben diese Parameternamen konsistent, egal welchen Provider du eigentlich aufrufst. Du kannst direkt mit diesem LM Object interagieren. Ruf es wie eine normale Python-Funktion auf und übergib entweder einen einfachen Text-String oder eine Liste von Chat Dictionaries. Es verarbeitet den Input und gibt eine Liste von generierten Strings zurück. Standardmäßig gibt es einen einzelnen String in dieser Liste zurück, aber du kannst auch mehrere Completions anfordern. Diese direkte Nutzung ist unkompliziert, aber ein Model Object manuell an jede Funktion in einer großen Codebase zu übergeben, wird schnell unübersichtlich. Um das zu lösen, nutzt DSPy ein globales Konfigurationssystem. Du definierst dein Default Language Model einmalig, indem du dspy dot configure aufrufst und dein instanziiertes LM Object als Target zuweist. Jede nachfolgende DSPy-Operation wird automatisch über dieses Modell geroutet. Aber was ist, wenn du Outputs von verschiedenen Providern vergleichen willst? Angenommen, du willst testen, wie Claude 3.5 Sonnet eine bestimmte Funktion im Vergleich zu deinem Default-GPT-Modell verarbeitet. Anstatt den globalen State zu überschreiben, nutzt du dspy dot context. Das erstellt einen temporären Scope. Du öffnest einen Python with block mit dspy dot context, weist Claude als lokalen Default zu und führst deinen Code aus. Wenn der Block endet, wechselt DSPy automatisch zu deinem globalen GPT-4o-mini Modell zurück. Das deckt das Routing von Requests ab. Wie sieht es mit der Performance aus? DSPy cached standardmäßig jede Model Generation, um Zeit und API-Kosten zu sparen. Wenn du exakt denselben Prompt mit exakt denselben Parametern ausführst, liefert es sofort die gecachte Response. Hier ist der entscheidende Punkt. Manchmal brauchst du eine frische Generation, ohne deinen Prompt zu ändern oder die Temperature anzupassen. Dafür nutzt DSPy einen Parameter namens rollout id. Wenn du eine neue rollout id übergibst, zum Beispiel einen eindeutigen Integer, behandelt DSPy das als separaten Request und umgeht den Cache. Das zwingt das Modell, eine neue Sequence zu generieren, und gibt dir die Kontrolle über die Generation Diversity, während die Core Inputs statisch bleiben. Schließlich musst du beim Experimentieren genau sehen, was über die Leitung ging. Jedes LM Object führt ein eigenes Interaction Log. Du kannst über das history Attribut des Model Objects auf die Rohdaten zugreifen. Für eine menschenlesbare Zusammenfassung rufst du die inspect history Methode auf. Sie printet den exakten Prompt, der an den Provider gesendet wurde, und die exakte Response, die empfangen wurde. Der wahre Wert dieser Configuration Layer ist, dass sie deine Application Logic komplett von Provider-Eigenheiten entkoppelt und Model Selection und Caching zu einfachen deklarativen Switches macht. Danke fürs Zuhören und Happy Coding zusammen!
3

Deklaratives Prompting mit Signatures

4m 04s

Entdecke, wie DSPy Signatures das traditionelle Prompting ersetzen. Diese Episode erklärt, wie man das Eingabe- und Ausgabeverhalten eines Moduls deklarativ definiert, indem man sowohl Inline-Strings als auch klassenbasierte Definitionen mit strenger Typisierung verwendet.

Herunterladen
Hallo, hier ist Alex von DEV STORIES DOT EU. DSPy lernen, Folge 3 von 15. Standard-Python-Function-Signatures sagen deinem Code, welche Datentypen er erwarten soll. Aber was wäre, wenn eine Signature die eigentliche Logik der Funktion selbst diktieren könnte, ohne dass du die Anweisungen explizit schreiben musst? Das ist die Prämisse von Declarative Prompting with Signatures. Du könntest eine DSPy-Signature natürlich leicht mit einer Standard-Python-Signature verwechseln. Sie sehen ähnlich aus, aber ihre Rollen sind grundlegend verschieden. Eine Standard-Python-Signature definiert ein striktes Daten-Interface. Eine DSPy-Signature deklariert und initialisiert tatsächlich das Verhalten des Language Models. Du schreibst keinen Prompt. Du schreibst eine deklarative Spezifikation dessen, was passieren muss. Das Framework nimmt diese Spezifikation, schaut sich die Input- und Output-Erwartungen an und konstruiert den zugrundeliegenden Prompt für dich. Der einfachste Weg, eine Signature zu definieren, ist inline, mit einem kurzen String. Du gibst deine Input-Variablen an, schreibst einen Richtungspfeil und gibst deine Output-Variablen an. Zum Beispiel sagt der String "question arrow answer" DSPy, dass das Modell eine Frage erhält und eine Antwort generieren muss. Du kannst mehrere Inputs übergeben, wie "context comma question arrow answer". Hier wird es interessant. Die Variablennamen, die du wählst, haben echtes semantisches Gewicht. DSPy verwendet genau diese String-Namen, um Rollen im Prompt zuzuweisen. Wenn du einen Input "context" nennst, interpretiert das Modell ihn als Hintergrundinformation. Over-engineere diese Namen nicht und versuche nicht, die Keywords mit cleveren Prompt-Tricks zu hacken. Verwende klare, beschreibende englische Wörter, um die Rollen zu definieren. Wenn Inline-Strings nicht ausdrucksstark genug sind, wechselst du zu Class-based Signatures. Du erstellst eine neue Klasse, die von der DSPy Signature Base Class erbt. Innerhalb dieser Klasse definierst du deine Inputs und Outputs als Attribute. Du weist diese Attribute mit den Input Field und Output Field Funktionen zu, die von DSPy bereitgestellt werden. Dieser Ansatz gibt dir feingranulare Kontrolle über das Verhalten des Modells. Der Docstring der Klasse selbst wird zur Kernanweisung für das Language Model und definiert die Gesamtaufgabe. Stell dir ein multimodales Image-Classification-Szenario vor. Du möchtest ein Bild und eine Textfrage an ein Vision Model übergeben und die spezifische Hunderasse extrahieren. Du erstellst eine Klasse namens ClassifyDogBreed. Ganz oben in der Klasse schreibst du einen Docstring, der besagt: "Identify the breed of the dog based on the provided image and question." Als Nächstes definierst du deine Inputs. Du erstellst ein Attribut namens "image" und weist es als Input Field zu. Du erstellst ein zweites Attribut namens "question" und weist es als Input Field zu. Schließlich definierst du ein Attribut namens "breed" und weist es als Output Field zu. Innerhalb dieses Output Fields kannst du ein Description-Argument übergeben, das besagt: "The exact name of the dog breed, with no extra text." Class-based Signatures übernehmen auch die Type Resolution. Du kannst Standard-Python Type Hints für deine Felder angeben. Wenn du für dein Output Field einen Boolean als Type Hint angibst, versteht DSPy, dass das Modell einen True- oder False-Wert zurückgeben muss. Das Framework verarbeitet diese Type Annotations und injiziert automatisch Constraints in die Prompt-Struktur, um das Modell zum korrekten Output-Format zu führen. Die Struktur deiner Daten und die Namen deiner Variablen sind die eigentlichen Anweisungen. Ein klar benanntes Field und ein präziser Docstring in einer deklarativen Signature diktieren das Modellverhalten weitaus zuverlässiger als endlose Absätze von handgemachtem Prompt Engineering. Danke fürs Zuhören, Happy Coding zusammen!
4

Bausteine mit Modules

4m 05s

Erkunde DSPy Modules, die zentralen Bausteine für Sprachmodell-Programme. Diese Episode behandelt dspy.Predict, dspy.ChainOfThought und wie man mehrere Modules zu einer größeren, zusammenhängenden Pipeline zusammensetzt.

Herunterladen
Hallo, hier ist Alex von DEV STORIES DOT EU. DSPy lernen, Folge 4 von 15. Normalerweise, wenn du willst, dass ein Sprachmodell ein Problem durchdenkt, fängst du an, Strings zusammenzuhacken und Phrasen wie think step by step planlos an deinen Prompt anzuhängen. Du vermischst deine Anwendungslogik mit fragilen Textanweisungen. In DSPy sind Prompting-Techniken keine Strings. Sie sind strukturierte, austauschbare Komponenten, die Modules genannt werden. Es ist leicht, Modules mit Signatures zu verwechseln. Stell dir eine Signature wie einen Vertrag vor. Sie definiert das Was und mappt spezifische Input-Felder auf spezifische Output-Felder. Ein Module definiert das Wie. Es ist ein parametrisiertes, aufrufbares Objekt, das deine Signature nimmt und eine spezifische Prompting-Strategie anwendet, um diesen Vertrag zu erfüllen. Das einfachste Module ist das Predict-Module. Du initialisierst es, indem du deine Signature als Argument übergibst. Wenn deine Signature verlangt, ein Dokument in eine Zusammenfassung zu verwandeln, übernimmt das Predict-Module das Prompt-Formatting und ruft das Sprachmodell auf. Aber vielleicht ist die Aufgabe komplex und erfordert Zwischenlogik. Du kannst Predict ganz einfach gegen das Chain of Thought-Module austauschen. Du änderst deine Signature nicht. Du übergibst sie stattdessen einfach an das Chain of Thought-Module. Unter der Haube modifiziert dieses Module automatisch die Prompt-Architektur. Es weist das Sprachmodell an, einen schrittweisen Reasoning-Trace zu generieren, bevor es die finalen Output-Felder produziert, die du definiert hast. Wenn du das Chain of Thought-Module mit deinen Input-Daten aufrufst, gibt es ein Objekt zurück, das deine angeforderten Outputs enthält. Weil du Chain of Thought verwendet hast, enthält dieses Objekt auch ein neues Feld mit der Rationale des Modells. Du kannst genau inspizieren, wie das Sprachmodell zu seiner Antwort gekommen ist, komplett getrennt vom finalen extrahierten Wert. Hier ist die entscheidende Erkenntnis. Du kannst diese Built-in-Modules verschachteln, um komplexe Programme zu bauen, ähnlich wie du Layer eines neuronalen Netzes in PyTorch stapeln würdest. Wir können eine Multi-Hop-Retrieval-Pipeline bauen, um das in Aktion zu sehen. Du fängst an, indem du eine Custom Class definierst. In ihrer Initialisierungsphase deklarierst du die kleineren Modules, die du brauchst. Für eine Multi-Hop-Architektur könntest du ein Query-Generator-Module mit Chain of Thought und ein Answer-Synthesis-Module mit Standard-Predict deklarieren. Dann definierst du eine Forward-Methode, um Daten zwischen ihnen zu routen. Die Forward-Methode nimmt eine anfängliche User-Frage entgegen. Sie übergibt diese Frage an dein Query-Generator-Module, das eine Search-Query ausgibt. Du führst diese Suche gegen deine Datenbank aus, um ein Dokument abzurufen. Wenn du einen zweiten Hop brauchst, übergibst du das abgerufene Dokument und die ursprüngliche Frage zurück in das Query-Generator-Module, um eine verfeinerte Search-Query zu produzieren. Schließlich übergibst du alle abgerufenen Dokumente und die User-Frage in das Answer-Synthesis-Module, um die finale Response zu generieren. Du hast gerade einen eigenen, ausführbaren Graphen aus modularen Komponenten gebaut. Wenn du mehrere Calls so aneinander chainst, ist es extrem wichtig, genau zu sehen, was hinter den Kulissen an das Modell gesendet wird. DSPy trackt die Nutzung deines Sprachmodells global. Du kannst den inspect history Befehl auf deinem Sprachmodell-Objekt aufrufen, um die letzten Interaktionen zu printen. Das rendert den exakten String, den das Modell empfangen hat, und den exakten String, den es generiert hat. So stellst du sicher, dass deine zusammengesetzte Pipeline den Kontext korrekt zusammenbaut. Indem du deine Task-Definition in Signatures und deine Ausführungsstrategie in parametrisierte Modules aufteilst, verwandelst du Prompting von einer lästigen Textbearbeitungsaufgabe in eine Architekturentscheidung. So kannst du die Reasoning-Fähigkeit deiner Pipeline ganz einfach durch den Austausch eines Klassennamens verbessern. Danke fürs Zuhören und Happy Coding zusammen!
5

Modelle mit Adapters verbinden

4m 33s

Verstehe die Rolle von Adapters in DSPy. Diese Episode erklärt, wie ChatAdapter und JSONAdapter die Lücke zwischen abstrakten DSPy Signatures und den tatsächlichen Multi-Turn-Nachrichten schließen, die an Sprachmodell-APIs gesendet werden.

Herunterladen
Hallo, hier ist Alex von DEV STORIES DOT EU. DSPy lernen, Folge 5 von 15. Du schreibst eine saubere, strongly-typed Signature in deinem Code, übergibst sie an ein Sprachmodell und bekommst irgendwie ein perfekt strukturiertes Python-Objekt zurück. Zwischen deinem sauberen Code und der Raw Text API des Modells sitzt eine chaotische Übersetzungsschicht. Diese verborgene Schicht ist das Konzept von Connecting Models with Adapters. Bevor wir uns anschauen, wie sie funktionieren, lass uns ein häufiges Missverständnis klären. Vielleicht verwechselst du Adapters mit Modules. Modules verwalten die Reasoning-Strategie. Sie entscheiden, ob das Modell Chain of Thought verwendet oder sich auf externe Tools verlässt. Adapters kümmern sich nicht um die Strategie. Ein Adapter ist rein die Übersetzungsschicht. Er übernimmt den Raw String und die JSON Serialization, die tatsächlich übers Netzwerk an die Model API gesendet wird. Sprachmodelle verstehen keine deklarativen Signatures. Sie erwarten Multi-Turn Message Arrays, die spezifische Roles und Textblöcke enthalten. Der Adapter schließt diese Lücke. Das Default-Tool dafür in DSPy ist der ChatAdapter. Wenn du ein Module aufrufst, fängt der ChatAdapter deine Signature ab und formatiert sie in eine Standard Chat History. Die Hauptanweisung der Signature wird direkt in den System Prompt gemappt. Deine Input-Felder werden gesammelt und in die User Message gepackt. Hier ist der entscheidende Punkt. Der ChatAdapter verwendet spezielle Text-Marker, um deine Inputs streng zu organisieren. Er umschließt jeden Feldnamen mit doppelten eckigen Klammern und doppelten Rautezeichen. Wenn dein Input-Feld context heißt, sieht das Sprachmodell einen Marker mit Klammern und Rauten um das Wort context, unmittelbar gefolgt von den eigentlichen context-Daten. Diese visuelle Grenze verhindert, dass das Modell deine System Instructions versehentlich mit dem User Input Text verwechselt. Er wiederholt dieses Muster für die erwarteten Output-Felder und promptet das Modell, genau dieselben Marker in seiner Response zu generieren. Stell dir ein Szenario vor, in dem du Science-News extrahierst. Dein Input ist ein Raw Text Artikel, und dein Output muss einer Pydantic-Klasse mit spezifischen Feldern für die Headline und die zentrale wissenschaftliche Entdeckung entsprechen. Wenn du diese Anforderung durch den ChatAdapter übergibst, inspiziert er deine Pydantic-Klasse, generiert ein komplettes JSON Schema und injiziert dieses Schema direkt in den System Prompt. Er teilt dem Sprachmodell explizit mit, wie es seine Text Response formatieren soll. Wenn das Modell schließlich antwortet, fängt der ChatAdapter den Raw Text String ab. Er sucht nach den erwarteten Output-Markern, extrahiert den Textblock dazwischen und parst diese Daten zurück in die exakten Python-Objekte, die deine Anwendung benötigt. Das deckt Inputs und das Parsen für textbasierte Interaktionen ab. Aber moderne Sprachmodelle haben oft nativen Support für Structured Output. Hier kommt der JSONAdapter ins Spiel. Anstatt den System Prompt stark zu modifizieren und sich auf Text-Marker zu verlassen, nimmt der JSONAdapter einen direkteren Weg. Er delegiert die Formatierungs-Constraints an den nativen JSON Mode oder die Structured Outputs API des Model-Providers. Das Modell wird auf Protokollebene gezwungen, ein valides JSON-Objekt zurückzugeben, das alle deine angeforderten Output-Felder enthält. Weil die Model API die Struktur nativ handhabt, muss der Adapter den Raw Text nicht mehr nach String-Markern durchsuchen. Wenn dein Target Model diese Fähigkeit unterstützt, führt die Umstellung deiner Pipeline auf den JSONAdapter normalerweise zu geringerer Latenz und deutlich zuverlässigerem Parsen. Der Adapter ist die strikte Grenze zwischen deiner deterministischen Anwendungslogik und der unstrukturierten Text Generation des Sprachmodells. Indem sie genau kontrollieren, wie Inputs serialisiert und Outputs geparst werden, stellen Adapters sicher, dass deine Pipeline niemals wegen eines schlecht formatierten Strings abbricht. Danke fürs Zuhören, Happy Coding zusammen!
6

Datenverwaltung mit Examples

3m 37s

Lerne, wie DSPy Datensätze für maschinelles Lernen handhabt. Diese Episode behandelt das dspy.Example-Objekt, die Unterscheidung zwischen Input-Keys und Labels sowie die Vorbereitung von Daten für die Evaluierung und Optimierung.

Herunterladen
Hallo, hier ist Alex von DEV STORIES DOT EU. DSPy lernen, Folge 6 von 15. Du führst einen Optimizer aus, um deine Language Model Pipeline zu verbessern, und sie erzielt auf Anhieb einen perfekten Score. Wenn du genauer hinschaust, stellst du fest, dass du die Zielantworten versehentlich direkt in den Prompt gepackt hast. Um Language Models wie traditionelle Machine Learning Komponenten zu behandeln, brauchst du einen wasserdichten Weg, um deine Training-, Dev- und Test-Sets zu verwalten, ohne Antworten zu leaken. Das Verwalten von Daten mit Examples in DSPy übernimmt genau das. In DSPy ist die grundlegende Datenstruktur das Example-Objekt. Du nutzt es, um alle deine Datasets zu bauen. Oberflächlich betrachtet verhält es sich sehr wie ein Standard-Python-Dictionary. Du erstellst eins, indem du Key-Value-Paare übergibst, die deine Daten repräsentieren. Nehmen wir einen Summarization-Task. Du erstellst ein neues Example-Objekt und gibst ihm zwei Felder. Du weist einem Feld namens report einen langen String zu, und einem Feld namens summary einen kurzen String. Du kannst diese Werte jederzeit über die Standard-Dot-Notation wieder auslesen, indem du das report-Feld oder das summary-Feld direkt vom Objekt abfragst. Es ist üblich, das Example-Objekt einfach als Dictionary-Wrapper zu behandeln, aber wenn du ein normales Dictionary verwendest, bricht der Compilation-Prozess ab. Wenn du ein Dataset an einen DSPy-Optimizer übergibst, muss der Compiler trennen, was in die Pipeline reingeht und was genutzt wird, um die Pipeline zu scoren. Er braucht explizite Grenzen zwischen den Input-Daten und den erwarteten Antworten. Hier wird es interessant. Das Example-Objekt steuert diese Grenzen über eine spezielle Methode namens with_inputs. Wenn du dein Example mit dem report und der summary instanziierst, chainst du die with_inputs-Methode ganz am Ende dran. Du übergibst ihr den String report. Das taggt das report-Feld explizit als Input-Daten. Jedes Feld, das du in dieser Methode nicht angibst, wird automatisch zu einem Label. Der Optimizer weiß jetzt, dass er nur den report an deine Pipeline senden darf. Die summary bleibt während der Inference komplett verborgen. Sobald du ein einzelnes Example konfiguriert hast, gruppierst du mehrere Examples in Standard-Python-Listen, um deine Training-, Dev- und Test-Sets zu bilden. Weil DSPy Prompt Engineering als Machine Learning Optimierungsproblem betrachtet, sind diese klar partitionierten Datasets eine strikte Voraussetzung. Wenn der Optimizer deine Pipeline über das Training-Set laufen lässt, verarbeitet er ein Example nach dem anderen. Er entfernt die Labels, feedet die Inputs, fängt den generierten Output auf und evaluiert dann das Ergebnis. Wenn du Custom Evaluation Metrics schreibst, musst du auf diese getrennten Felder zugreifen. Das Example-Objekt bietet zwei Methoden für diese Extraktion. Der Aufruf der inputs-Methode gibt ein Dictionary zurück, das nur die Daten enthält, die du als Inputs markiert hast. Der Aufruf der labels-Methode gibt ein Dictionary mit den versteckten Target-Daten zurück. Deine Evaluation-Funktion ruft die labels-Methode auf, um die Target-Summary abzurufen, vergleicht sie mit dem generierten Text und vergibt einen Score, basierend darauf, wie gut sie übereinstimmen. Das richtige Konfigurieren deiner Example-Objekte garantiert, dass deine Pipeline tatsächlich lernt, Inputs auf Outputs zu mappen. Die strikte Trennung von Inputs und Labels verhindert Data Leakage während der Optimierung und stellt sicher, dass sich dein System verbessert, anstatt nur vorgegebene Antworten auswendig zu lernen. Danke fürs Zuhören und Happy Coding zusammen!
7

Erfolg definieren mit Metriken

4m 22s

Entdecke, wie man DSPy-Programme mithilfe von Metriken evaluiert. Diese Episode zeigt dir, wie du benutzerdefinierte Python-Funktionen schreibst, um Ausgaben zu bewerten, das trace-Argument verwendest und sogar AI-as-a-Judge für ausführliche Evaluierungen nutzt.

Herunterladen
Hallo, hier ist Alex von DEV STORIES DOT EU. Learning DSPy, Folge 7 von 15. Was du nicht messen kannst, kannst du nicht kontinuierlich verbessern. Und wenn du mit Language Models arbeitest, skaliert es einfach nicht, dich bei der Messung der Output-Qualität auf menschliche Intuition zu verlassen. Um deine Prompts automatisch umzuschreiben oder dein System zu tunen, braucht der Compiler einen mathematischen Leitstern, und genau das bietet das Definieren von Erfolg durch Metrics. In DSPy ist eine Metric eine Standard-Python-Funktion. Sie nimmt zwei primäre Argumente entgegen. Das erste ist ein Example, das den Goldstandard für Input und erwarteten Output aus deinem Dataset repräsentiert. Das zweite ist eine Prediction, also die tatsächliche Response, die von deinem DSPy-Programm generiert wird. Die Metric-Funktion vergleicht die Prediction mit dem Example und gibt einen Score zurück. Dieser Score ist normalerweise ein Float, ein Integer oder ein einfacher Boolean-Wert wie true oder false. Für einfache Classification-Tasks kann deine Metric aus ganz simpler Python-Logik bestehen. Du könntest eine Exact-Match-Funktion schreiben, die prüft, ob der predicted String exakt dem erwarteten String entspricht. Um diese Messung systematisch über deine Daten auszuführen, bietet DSPy ein Built-in-Utility namens Evaluate. Du übergibst diesem Utility dein Development-Dataset, deine Metric-Funktion und Execution-Parameter wie die Anzahl der parallelen Threads. Das Evaluate-Utility lässt deine Metric über jede Prediction laufen, aggregiert die Results und gibt einen einzelnen numerischen Score zurück, der deine gesamte System-Performance repräsentiert. Exact Matching ist jedoch für generative Language-Tasks fast immer zu starr. An diesem Punkt wechselst du von einfachen String-Checks zur Nutzung von AI-Feedback, einem Pattern, das allgemein als LLM-as-a-judge bekannt ist. Da DSPy-Module einfach nur Python-Code sind, kann deine Metric-Funktion ein kleineres, separates DSPy-Programm instanziieren und aufrufen, um komplexe semantische Outputs zu bewerten. Schauen wir uns ein konkretes Szenario an. Du baust ein System, das einen Promo-Tweet generiert, um eine User-Frage zu beantworten. Eine Exact-Match-Metric versagt hier völlig, da ein guter Tweet auf unzählige gültige Arten formuliert werden kann. Stattdessen sollte deine Metric-Funktion mehrere Dimensionen des Outputs evaluieren. Zuerst nutzt sie einen simplen Python-Length-Check, um sicherzustellen, dass der generierte Text unter zweihundertachtzig Zeichen lang ist. Zweitens checkt sie, ob der Text die faktische Antwort enthält, die vom Example gefordert wird. Schließlich übergibt sie den generierten Text an eine spezialisierte DSPy-Signature, die ein kleineres Language Model bittet zu bewerten, ob der Tweet engaging ist. Deine Metric-Funktion kombiniert dann den Length-Check, den Fact-Check und den Engagement-Score des Language Models zu einem finalen mathematischen Wert. Wenn du schließlich anfängst, diese Programme zu kompilieren und zu optimieren, muss deine Metric-Funktion ein drittes, optionales Argument akzeptieren. Es nennt sich Trace. Zuhörer verwechseln das Trace-Argument oft mit einem Debugging-Log, das Console-Errors oder die Execution-History ausgibt. Genau das ist es nicht. Das Trace-Argument ist ein spezifisches Objekt, das vom DSPy-Compiler während der Optimierung genutzt wird, um dazwischenliegende Reasoning-Steps zu validieren. Wenn dein Programm mehrere Language-Model-Calls aneinanderreiht, enthält der Trace den spezifischen Reasoning-Path, den das Model genommen hat, um ans Ende zu gelangen. Indem du auf den Trace innerhalb deiner Metric zugreifst, kann deine Funktion nicht nur verifizieren, dass der finale Tweet gut war, sondern auch, dass die Intermediate-Steps, die zu seinem Entwurf genutzt wurden, logisch sinnvoll waren. Das ist der Teil, auf den es ankommt. Deine Metric definiert strikt, wie Erfolg aussieht, und der DSPy-Compiler wird dein System gnadenlos optimieren, um genau diesen Score zu maximieren. Wenn deine Metric fehlerhaft ist, wird dein kompiliertes Programm auf exakt dieselbe Weise fehlerhaft sein. Danke fürs Zuhören, Happy Coding zusammen!
8

Eine Einführung in Optimizers

3m 31s

Tauche ein in die wahre Magie von DSPy: Optimizers. Diese Episode bietet einen Überblick darüber, was Optimizers tun, über den iterativen Optimierungszyklus und die ungewöhnliche 20/80-Datenaufteilungsstrategie für die Prompt-Optimierung.

Herunterladen
Hallo, hier ist Alex von DEV STORIES DOT EU. DSPy lernen, Folge 8 von 15. Angenommen, du schreibst eine Software, und anstatt die interne Logik manuell anzupassen, um deine Tests zu bestehen, schreibt ein Compiler die Anweisungen automatisch um, sodass das Programm von selbst besser läuft. Du fasst den Code gar nicht an. Du stellst einfach nur das Test-Set bereit. Genau das macht DSPy durch ein Konzept namens Optimizers. Optimizers, die in älteren Versionen des Frameworks noch Teleprompter hießen, sind Algorithmen, die die Parameter deines Programms tunen. Im traditionellen Machine Learning bedeuten Parameter die Weights von neuronalen Netzen. In DSPy sind Parameter primär die eigentlichen Prompt-Strings und Instructions, die an das Language Model gesendet werden, obwohl sie auch Weights umfassen können. Die Aufgabe des Optimizers ist es, diese Parameter so anzupassen, dass eine von dir bereits definierte Metrik maximiert wird. Dieser Prozess passiert, bevor du deine Anwendung deployest. Es ist reine Pre-Inference-Time Compute. Du investierst im Vorfeld Rechenleistung, um die besten Instructions zu finden, damit deine Anwendung später präzise läuft. Wenn du das Wort Optimizer hörst, denkst du vielleicht, du brauchst riesige Datasets, wie beim Fine-Tuning eines traditionellen Models. Brauchst du aber nicht. Prompt-Optimizers sind hocheffizient. Sie brauchen typischerweise nur dreißig bis dreihundert Beispiele. Weil das Dataset so klein ist, empfiehlt DSPy einen ungewöhnlichen Ansatz beim Splitten deiner Daten. Statt des üblichen Achtzig-Zwanzig-Splits, bei dem die meisten Daten ins Training gehen, drehst du es um. Du nutzt zwanzig Prozent fürs Training und achtzig Prozent für die Validation. Wenn du fünfzig Beispiele hast, gibst du zehn an den Optimizer, um die Prompts zu bauen, und nutzt die restlichen vierzig, um zu evaluieren, ob diese Prompts tatsächlich generalisieren. Dieser Reverse-Split verhindert, dass der Optimizer die generierten Prompts an ein winziges Set von Inputs overfittet. Hier ist die wichtigste Erkenntnis. Der iterative Development-Cycle in DSPy dreht sich darum, diesen Optimization-Loop auszuführen. Lass uns ein konkretes Szenario durchgehen. Du baust einen einfachen Question-Answering-Bot. Zuerst definierst du dein DSPy-Programm und deine Metrik. Als Nächstes sammelst du dein Dataset aus fünfzig ungelabelten Fragen. Du splittest diese Daten und übergibst den kleinen Trainingsteil an ein Optimizer-Objekt. Du sagst dem Optimizer, dass er dein Programm mit deinen Trainingsdaten und deiner Metrik kompilieren soll. Der Optimizer läuft und experimentiert unter der Haube mit verschiedenen Prompt-Strukturen. Er checkt die Outputs gegen deine Metrik, lernt, was funktioniert, und verfeinert die Prompts. Wenn der Optimizer fertig ist, gibt er eine neue, kompilierte Version deines Programms zurück. Dieses kompilierte Programm enthält die neu getunten Parameter. Du musst diesen Optimization-Step nicht bei jedem Start deiner Anwendung ausführen. Stattdessen rufst du die Save-Methode auf dem kompilierten Programm auf und übergibst einen Dateipfad. Das schreibt alle optimierten Prompts und Konfigurationen in eine Standard-JSON-Datei. Wenn du deine Anwendung in Production deployest, instanziiert dein Code einfach das Basisprogramm und ruft die Load-Methode auf, die auf genau diese JSON-Datei zeigt. Dein Bot ist sofort bereit, Fragen mit den optimierten Instructions zu beantworten. Die wahre Stärke der DSPy-Optimizers liegt darin, dass sie deine Application-Logic von der genauen Formulierung deiner Prompts entkoppeln und Compute die besten Wörter für dich finden lassen. Danke fürs Zuhören, Happy Coding zusammen!
9

Automatisches Few-Shot Learning

3m 36s

Lerne, wie DSPy Few-Shot Prompting automatisiert. Diese Episode konzentriert sich auf BootstrapFewShot und BootstrapFewShotWithRandomSearch und erklärt, wie sie hochwertige Beispiele synthetisieren, filtern und in deine Prompts injizieren.

Herunterladen
Hallo, hier ist Alex von DEV STORIES DOT EU. DSPy lernen, Folge 9 von 15. Manuell die besten Beispiele für einen Prompt auszuwählen, ist mühsam und anfällig für Bias. Du rätst, welche Beispiele relevant sind, hardcodest sie in deine Strings und hoffst, dass das Modell darauf achtet. DSPy synthetisiert, testet und wählt aktiv die perfekten Demonstrationen für dich aus. Dieser Prozess ist automatisches Few-Shot-Learning, und DSPy nutzt dafür drei spezielle Optimizer. Der einfachste Ansatz ist LabeledFewShot. Du stellst ein Set an gelabelten Trainingsbeispielen bereit. Der Optimizer wählt zufällig eine Teilmenge dieser Input- und Output-Paare aus und fügt sie direkt als Demonstrationen in deine Prompts ein. Dadurch erhält das Modell ein grundlegendes Muster, dem es folgen kann. Das funktioniert gut, wenn deine Trainingsdaten exakt den Zwischenschritten entsprechen, die dein Programm benötigt. Normalerweise ist das aber nicht der Fall. Das bringt uns zu BootstrapFewShot. Ein häufiges Missverständnis ist, dass BootstrapFewShot einfach zufällig Beispiele aus deinem Trainings-Set auswählt. Das tut es nicht. Es generiert aktiv Reasoning-Zwischenschritte, die in deinen Rohdaten nie existiert haben. So läuft der Bootstrapping-Prozess ab: Der Optimizer benötigt ein Teacher-Programm. Standardmäßig ist das einfach die unoptimierte Zero-Shot-Version deines eigenen Programms. Der Teacher durchläuft deine Trainingsbeispiele. Für jedes Beispiel versucht er, eine Antwort zu generieren. DSPy übergibt diese Antwort dann an deine Evaluationsmetrik. Wenn die Metrik sagt, dass der Output korrekt ist, speichert DSPy den gesamten Trace dieser erfolgreichen Ausführung. Dieser Trace umfasst den Input, den Output und – ganz wichtig – alle Zwischenschritte, die das Programm gemacht hat, um dorthin zu gelangen. Nehmen wir mal einen Sentiment Classifier. Dein Rohdatensatz enthält nur Kundenrezensionen und ein positives oder negatives Label. Dein DSPy-Programm fordert das Language Model auf, Chain-of-Thought Reasoning zu verwenden, bevor es das Sentiment ausgibt. Beim Bootstrapping liest der Teacher eine Rezension und verfasst einen Absatz mit Reasoning, bevor er das Sentiment errät. Stimmt der finale Tipp mit dem echten Label überein, gilt dieses generierte Reasoning als erfolgreich. Der Optimizer sammelt diese erfolgreichen Traces. Er nimmt vier davon und fügt sie in zukünftige Prompts ein. Dein Zero-Shot Classifier ist nun ein echter Experte: ein Four-Shot Classifier, komplett mit synthetisierten Reasoning-Schritten. BootstrapFewShot stoppt, sobald es genug erfolgreiche Traces gefunden hat. Aber die ersten erfolgreichen Traces sind nicht immer die besten. Hier ist die entscheidende Erkenntnis: BootstrapFewShotWithRandomSearch löst dieses Problem, indem es den gesamten Bootstrap-Prozess mehrmals ausführt. Jedes Mal zieht es ein zufälliges Sub-Sample deiner Trainingsdaten. Dadurch entstehen mehrere verschiedene Kandidaten-Sets von Few-Shot-Demonstrationen. Der Optimizer nimmt dann all diese Kandidaten-Sets und evaluiert sie gegen deine Validierungsdaten. Er testet, welche spezifische Kombination von Demonstrationen den höchsten Gesamt-Score erzielt. Er verwirft die schwachen Sets und behält den mathematischen Gewinner. Die wahre Stärke von automatischem Few-Shot-Learning liegt nicht nur darin, dir Zeit beim Schreiben von Prompts zu sparen, sondern auch darin, erfolgreiche Reasoning-Zwischenschritte zu entdecken, die dein Datensatz nie explizit enthielt. Danke fürs Zuhören und Happy Coding zusammen!
10

Instruction Optimization mit MIPROv2

3m 38s

Tauche ein in das automatische Instruction Tuning. Diese Episode untersucht MIPROv2 und COPRO und zeigt, wie DSPy Bayesian Optimization und Coordinate Ascent verwendet, um überlegene, kontraintuitive Prompt-Anweisungen zu entdecken.

Herunterladen
Hallo, hier ist Alex von DEV STORIES DOT EU. DSPy lernen, Folge 10 von 15. Manchmal liest sich die effektivste Instruction für ein Language Model für einen Menschen wie völliger Unsinn. Du verbringst Stunden damit, akribisch die perfekte Formulierung zu finden, nur um festzustellen, dass ein automatisierter, leicht zusammenhangloser Prompt deine Arbeit komplett übertrifft. Deshalb solltest du Algorithmen deine Prompts schreiben lassen. Wir schauen uns Instruction Optimization mit MIPROv2 an. Zuerst einmal: Vergiss die Idee von Prompt Templating. Es geht hier nicht darum, Variablen in einen statischen String einzufügen. Instruction Optimization schreibt tatsächlich die systemischen Instructions neu, die deine Pipeline steuern. Frühere Algorithmen wie COPRO und SIMBA haben das gelöst, indem sie schrittweise Variationen von Prompts generiert und diese mit der Zeit verfeinert haben. MIPROv2 geht noch einen Schritt weiter, indem es Instructions und Few-Shot Examples als einheitlichen Search Space behandelt. MIPROv2 arbeitet in drei verschiedenen Phasen. Die erste Phase ist das Bootstrapping. Der Optimizer führt dein unoptimiertes Programm über deine Trainingsdaten aus, um einen Pool von Execution Traces aufzubauen. Diese Traces enthalten die tatsächlichen Inputs, Zwischenschritte und Outputs, die durch dein System fließen. Die zweite Phase ist das Grounded Proposal. Der Optimizer rät neue Instructions nicht einfach blind. Er nutzt ein separates Language Model, den sogenannten Prompter, um sich diese generierten Traces anzuschauen. Indem er analysiert, wo deine Pipeline erfolgreich war und wo sie fehlgeschlagen ist, entwirft der Prompter eine Reihe neuer Instruction-Kandidaten. Diese Kandidaten basieren direkt auf dem tatsächlichen Verhalten deines Programms und nicht auf generischen Templates. Die dritte Phase ist die Discrete Search. MIPROv2 evaluiert die neuen Instructions zusammen mit verschiedenen Kombinationen von Few-Shot Traces. Um das effizient zu machen, setzt es auf Bayesian Optimization. Anstatt jede mögliche Kombination per Brute-Force durchzuprobieren, baut MIPROv2 ein Surrogate Model. Dieses Surrogate Model fungiert als leichtgewichtiger Proxy. Es sagt voraus, welche Kombinationen aus Instructions und Traces den höchsten Score bei deiner spezifischen Evaluation Metric erzielen werden. Bayesian Optimization ermöglicht es dem Surrogate Model, den Prompt- und Demonstration-Space abzubilden. Es berechnet die erwartete Verbesserung, wenn eine neue Kombination getestet wird. Das wägt systematisch die Exploration ungetesteter Instructions gegen die Exploitation von Kombinationen ab, die bereits gut scoren. Der Optimizer findet zielgenau die optimale Konfiguration, ohne Tausende redundante Network Calls auszuführen. Schauen wir uns ein konkretes Szenario an. Du baust einen ReAct-Agenten, um komplexe Queries zu beantworten. Anfangs liegt seine Accuracy bei 24 Prozent. Du übergibst diesen Agenten an MIPROv2, konfigurierst ihn für den Light Mode und stellst ein Dataset mit 500 Fragen bereit. Der Optimizer bootstrappt Traces, schlägt Grounded Instructions vor und durchsucht den Space mithilfe des Surrogate Models. Wenn er fertig ist, springt die Accuracy deines Agenten von 24 Prozent auf 51 Prozent. Der finale Prompt, der diesen Performance-Sprung bewirkt, wird wahrscheinlich Instructions und Trace-Selections enthalten, die ein Mensch niemals entworfen hätte. Hier ist die entscheidende Erkenntnis: MIPROv2 beseitigt den Flaschenhals der menschlichen Intuition. Es behandelt deine Natural Language Instructions genau wie anpassbare Weights in einem mathematischen Modell und verwandelt so die Prompt-Erstellung von einer unvorhersehbaren Kunstform in ein deterministisches Optimierungsproblem. Danke fürs Zuhören und Happy Coding zusammen!
11

Finetuning mit BootstrapFinetune

3m 14s

Entdecke, wie man massive Sprachmodelle in kleinere, effiziente Modelle destilliert. Diese Episode behandelt BootstrapFinetune und erklärt, wie man ein prompt-basiertes DSPy-Programm in ein maßgeschneidertes Modell mit aktualisierten Gewichten umwandelt.

Herunterladen
Hallo, hier ist Alex von DEV STORIES DOT EU. DSPy lernen, Folge 11 von 15. Das Prompting massiver Modelle ist super, um einen Prototyp an den Start zu bringen. Aber wenn diese Logik in Production geht, werden die Latenz und die Kosten eines Modells mit 70 Milliarden Parametern schnell zum Problem. Du brauchst die Reasoning-Power des großen Modells, aber den Speed und den Preis eines Modells mit acht Milliarden Parametern. Genau das übernimmt BootstrapFinetune. BootstrapFinetune kompiliert dein DSPy-Programm in ein fine-tuned Modell. Es ist die ultimative Optimierung für mehr Effizienz. Es updatet die tatsächlichen Weights eines kleineren Target-Modells, um das exakte Verhalten deiner schweren Pipeline nachzuahmen. Ein weit verbreiteter Irrglaube ist, dass man zum Fine-Tunen eines Modells Tausende von Beispielen manuell sammeln, sie in nervige JSONL-Files formatieren und einen Training-Loop babysitten muss. BootstrapFinetune automatisiert das komplett. Es übernimmt die Dataset-Generierung, das Formatting und die Weight-Updates komplett über die Execution-Traces deines Programms. Nehmen wir ein konkretes Szenario mit einem Banking-Intent-Classifier. Das Programm nimmt eine unstrukturierte Kundennachricht und kategorisiert sie. Am Anfang baust du ein DSPy-Modul mit einem extrem fähigen Modell wie GPT-4o-mini, das so konfiguriert ist, dass es Chain-of-Thought-Reasoning nutzt. Das Modell denkt die Formulierung des Kunden Schritt für Schritt durch, bevor es den Intent ausgibt. Es liefert die richtigen Antworten, ist aber zu langsam und zu teuer für einen Real-Time-Chat. Um das zu optimieren, initialisierst du BootstrapFinetune. Du übergibst deine Evaluation-Metric, um den Erfolg zu messen, und spezifizierst das kleinere, günstigere Target-Modell, das du deployen willst. Dann kompilierst du das Programm. Wenn du auf Kompilieren drückst, lässt DSPy dein unoptimiertes Programm über deine Trainingsdaten laufen. Es nutzt das schwere Teacher-Modell, um Outputs zu generieren. Der Optimizer beobachtet diese Execution. Jedes Mal, wenn das Teacher-Modell laut deiner Metric die richtige Antwort liefert, erfasst BootstrapFinetune den Trace. Es speichert die Inputs, das Step-by-Step-Reasoning und den finalen Output. Es mappt die interne Logik des riesigen Modells in ein Format, das das kleine Target-Modell verarbeiten kann. Sobald genug erfolgreiche Traces gesammelt wurden, strukturiert BootstrapFinetune sie automatisch zu einem Training-Dataset. Dann triggert es den Fine-Tuning-Prozess für dein Target-Modell. Das kleine Modell wird direkt auf den hochwertigen Reasoning-Paths trainiert, die vom großen Modell generiert wurden. Hier ist der entscheidende Punkt: Das kleinere Modell lernt die spezifische Task-Distribution und den Reasoning-Style, der zur Lösung nötig ist, ohne die schwere Chain-of-Thought zur Inference-Time ausführen zu müssen. In unserem Banking-Classifier-Beispiel erreicht ein kleines Standardmodell out of the box vielleicht nur 66 Prozent Accuracy. Aber nach dem Kompilieren mit BootstrapFinetune springt dasselbe kleine Modell auf 87 Prozent Accuracy. Fine-Tuning ist kein separates Infrastrukturprojekt mehr; es ist einfach ein weiterer Kompilierungsschritt, der eine teure Reasoning-Pipeline in ein schnelles, günstiges Production-Asset verwandelt. Danke fürs Zuhören und Happy Coding zusammen!
12

Automatisierte Tool-Nutzung mit ReAct

4m 07s

Lerne, wie du Sprachmodellen Zugriff auf externe Tools gibst. Diese Episode behandelt das dspy.ReAct-Modul und demonstriert, wie man autonome Agenten baut, die dynamisch schlussfolgern und mit APIs interagieren.

Herunterladen
Hallo, hier ist Alex von DEV STORIES DOT EU. DSPy lernen, Folge 12 von 15. Einem LLM Zugriff auf externe APIs zu geben, macht es unglaublich leistungsfähig, aber den Loop zu schreiben, um sein Reasoning zu steuern, die Outputs zu parsen und sich von Execution Errors zu erholen, ist ein riesiges Kopfzerbrechen. Die Lösung ist vollautomatischer Tool Use mit dem DSPy ReAct Modul. Leute verwechseln ReAct oft mit einfachem Function Calling. Function Calling ist lediglich der API-Mechanismus, der es einem Sprachmodell ermöglicht, seinen Output als strukturierten Data Request zu formatieren. ReAct ist ein spezifisches Verhaltensparadigma. Es steht für Reason und Act. Es ist ein Execution Loop, in dem das Modell drei verschiedene Schritte durchläuft: einen Thought, eine Action und eine Observation. Das DSPy ReAct Modul übernimmt diese Orchestrierung komplett für dich. Du schreibst den Execution Loop nicht selbst. Du fängst API Exceptions nicht manuell ab. ReAct wrappt eine DSPy Signature und eine Liste von Tools und verwandelt so einen statischen Prompt in einen autonomen Agenten. Um es zu nutzen, definierst du zuerst deine Tools. In DSPy sind Tools einfach Standard-Python-Funktionen. Du schreibst eine Funktion, definierst ihre Input-Parameter und schreibst einen klaren Docstring. Dieser Docstring ist extrem wichtig. DSPy extrahiert den Funktionsnamen und den Docstring und übergibt sie an das Sprachmodell, damit es genau weiß, was das Tool macht und wann es deployt werden soll. Stell dir ein Szenario vor, in dem du einen einfachen Wetter- und Such-Agenten baust. Du schreibst eine Python-Funktion namens get weather, die einen Stadtnamen als String akzeptiert und eine API abfragt, um die aktuelle Temperatur zurückzugeben. Du instanziierst das dspy dot ReAct Modul und übergibst ihm eine Standard Question and Answer Signature zusammen mit einer Liste, die deine get weather Funktion enthält. Wenn du das Modul fragst, wie das Wetter in Tokio ist, beginnt der ReAct Loop. Zuerst generiert das Modell einen Thought. Es schlussfolgert, dass es aktuelle meteorologische Daten für Tokio braucht. Als Nächstes generiert es eine Action. Es entscheidet sich, dein get weather Tool aufzurufen und übergibt Tokio als Argument. Hier ist die wichtigste Erkenntnis. Du führst diese Funktion nicht selbst aus. Das DSPy ReAct Modul fängt die Action des Modells ab, führt deine Python-Funktion hinter den Kulissen aus und fängt den Output ab. Wenn die Funktion erfolgreich ist, füttert DSPy die Temperaturdaten als Observation zurück an das Modell. Wenn das Modell einen Parameter halluziniert oder die Funktion einen Python Error wirft, fängt DSPy diesen Error ab und füttert die Error Message als Observation zurück. Das Modell liest den Error, generiert einen neuen Thought, um seinen Fehler zu korrigieren, und versucht eine neue Action. Sobald das Modell die korrekten Temperaturdaten beobachtet, erkennt es, dass sein Ziel erreicht ist. Es bricht aus dem Loop aus und formatiert die finale Antwort für den User. Um Runaway Executions zu verhindern, ist dieser Zyklus durch einen Parameter namens max iters streng begrenzt, was für Maximum Iterations steht. Dieser Parameter legt fest, wie viele Thought, Action und Observation Zyklen das Modul durchführen darf. Wenn das Modell Schwierigkeiten hat, die korrekten Daten zu finden, und das Iteration Limit erreicht, zwingt ReAct es, die Suche zu beenden und eine finale Response zu generieren, wobei es nur die Informationen nutzt, die es erfolgreich gesammelt hat. Die wahre Stärke dieses Moduls ist, dass es den fragilen, fehleranfälligen Control Flow von Agent Loops abstrahiert, sodass du komplexes Tool-Augmented Reasoning einfach als eine weitere vorhersehbare Komponente in deiner Pipeline behandeln kannst. Danke fürs Zuhören, Happy Coding zusammen!
13

Manuelles Tool Handling für mehr Kontrolle

3m 56s

Übernimm die volle Kontrolle über die Tool-Ausführung. Diese Episode behandelt manuelles Tool Handling in DSPy unter Verwendung von dspy.Tool, dspy.ToolCalls und nativem Function Calling für latenzempfindliche Anwendungen.

Herunterladen
Hallo, hier ist Alex von DEV STORIES DOT EU. Learning DSPy, Folge 13 von 15. Automated Agents sind super, wenn du eine vage, offene Aufgabe hast. Aber wenn du absolute, deterministische Kontrolle darüber brauchst, wie, wann und ob eine externe Funktion ausgeführt wird, ist es zu riskant, das Steuer komplett dem Language Model zu überlassen. Du musst unter die Haube schauen und die Execution selbst managen. Genau da kommt manuelles Tool Handling für die Kontrolle ins Spiel. Wenn du einen Automated Agent Loop nutzt, wird der Execution Layer wegabstrahiert, was zu unvorhersehbarer Latency führen oder Runtime Errors verstecken kann. Manuelles Handling ist die Alternative für Power-User. Es gibt dir die Kontrolle über Error Recovery, Timeout Limits und die genaue Execution Order zurück. Um das in DSPy zu bauen, fängst du damit an, eine Standard-Python-Funktion mit der dspy.Tool Klasse zu wrappen. Stell dir vor, du hast eine Python-Funktion, die als Calculator fungiert, um zwei Zahlen zu multiplizieren. Du übergibst diese Funktion an dspy.Tool. Wenn deine Funktion Database Queries oder Network Requests verarbeitet, kannst du auch asynchrone Funktionen wrappen, und die Tool-Klasse managt die Async-Execution nativ. Sobald dein Calculator-Tool fertig ist, musst du es dem Language Model zugänglich machen. Das machst du, indem du eine Liste, die dein Tool enthält, direkt an ein DSPy Predict-Modul übergibst. Du definierst einen Parameter namens tools in deinem Predict Call. Wenn das Modell den Input verarbeitet, evaluiert es den Prompt und entscheidet, ob es den Calculator braucht, um die finale Antwort zu generieren. Wenn das Modell entscheidet, dein Tool zu nutzen, verlässt es sich auf einen zugrundeliegenden Mechanismus namens Adapter. Standardmäßig nutzt DSPy einen JSONAdapter. Dieser Adapter übersetzt dein Python-Tool automatisch in das native Function Calling Format, das von der spezifischen Language Model API, die du nutzt, benötigt wird. Das stellt sicher, dass das Modell zuverlässiges, strukturiertes JSON ausgibt, wenn es ein Tool anfragt. Pass an dieser Stelle gut auf. Man geht leicht davon aus, dass die Nutzung von nativem Tool Calling automatisch Outputs von höherer Qualität produziert. Die DSPy-Dokumentation warnt ausdrücklich davor, dass das ein Irrglaube ist. Natives Tool Calling bietet eine bessere Reliability für die Syntax des Requests, aber es garantiert keine bessere Reasoning-Qualität als standardmäßiges textbasiertes Parsing. Das Modell ist nicht plötzlich schlauer, nur weil es JSON formatiert. Weil du diesen Prozess manuell managst, führt das Modell den Calculator nicht wirklich aus. Es stoppt und gibt eine Response zurück, die seinen Intent enthält. Du greifst auf diesen Intent zu, indem du response Punkt outputs Punkt tool calls inspizierst. Diese Property gibt ein dspy.ToolCalls Objekt zurück, das sich wie eine Liste von Instructions verhält. Jedes Item in dieser Liste spezifiziert, welches Tool das Modell nutzen möchte und die genauen Arguments, die es generiert hat, wie fünf und zehn für den Calculator. Als Nächstes schreibst du einen Standard-Python-Loop, um durch diese angefragten Tool Calls zu iterieren. Für jeden Call rufst du manuell seine execute-Methode auf. Das Aufrufen von execute triggert deinen eigentlichen Python-Code mit den vom Modell generierten Arguments und gibt das Result zurück. Wenn die Arguments invalid sind oder der Calculator einen Error wirft, fängt dein Python-Loop ihn ab. Du behandelst den Fehler nach deinen eigenen Regeln, anstatt zu hoffen, dass ein Automated Loop sich von selbst erholt. Manuelles Tool Handling trennt die Entscheidung des Modells, eine Action anzufragen, sauber von der physischen Execution dieser Action. Das gibt deiner Application die deterministische Reliability, die Production Environments erfordern. Danke fürs Zuhören, Happy Coding zusammen!
14

Tools integrieren mit MCP

3m 57s

Verbinde deine Agenten mit universellen Tool-Servern. Diese Episode erklärt, wie man das Model Context Protocol (MCP) in DSPy verwendet, um standardisierte Tools über verschiedene Frameworks hinweg mit minimalem Einrichtungsaufwand zu nutzen.

Herunterladen
Hallo, hier ist Alex von DEV STORIES DOT EU. DSPy lernen, Folge 14 von 15. Jedes Mal, wenn du ein neues AI Framework einsetzt, schreibst du am Ende meistens wieder dieselben Custom Python Wrapper für deine Database Queries, Web Searches und File Reader. Anstatt endlos redundante API Wrapper zu maintainen, wie wäre es, wenn sich deine Agents direkt mit universellen, standardisierten Tool Servern verbinden könnten? Genau das verspricht das Model Context Protocol, und heute schauen wir uns die Integration von Tools mit MCP an. Das Model Context Protocol ist ein offener Standard, der von Anthropic eingeführt wurde. Es bietet eine universelle Möglichkeit, AI Models mit externen Datenquellen und Tools zu verbinden. Wenn Entwickler diesen Standard nutzen, schreiben sie ein Tool nur einmal, hosten es auf einem MCP Server und nutzen es in jedem unterstützenden Framework. DSPy unterstützt das nativ. Du musst keine komplexen Adapter Classes schreiben, um diese externen Tools in deine DSPy Programme zu bringen. Ein weit verbreiteter Irrtum ist, dass DSPy die zugrundeliegenden Server Connections zu diesen Tools selbst managt. Das tut es nicht. DSPy verlässt sich komplett auf das offizielle Python mcp Package, um das Networking zu managen und die Connection aufzubauen. DSPy schaltet sich erst ganz am Ende ein, um das aktive MCP Tool Object in ein natives DSPy Tool Format zu konvertieren. Um zu sehen, wie das funktioniert, gehen wir mal durch, wie man einen lokalen Tool Server mit einem DSPy Agent verbindet. Zuerst brauchst du einen laufenden MCP Server. In deinem Python Script importierst du die Server Parameters Class aus dem MCP Package. Wenn du einen lokalen Process ausführst, definierst du Standard IO Server Parameters und verweist auf deine Server Executable. Alternativ, wenn deine Tools auf einem Remote Server liegen, konfigurierst du eine HTTP Client Connection. Beide Methoden legen fest, wie deine Application mit dem Tool Provider kommuniziert. Als Nächstes nutzt du die MCP Library, um eine Client Session zu öffnen. Innerhalb dieses Session Contexts initialisierst du die Connection. Zu diesem Zeitpunkt ist DSPy noch komplett außen vor. Du bittest die aktive MCP Session, ihre verfügbaren Tools aufzulisten. Der Server antwortet mit einer Liste von Tool Objects. Jedes Object enthält den Tool-Namen, eine Beschreibung, was es macht, und die erwarteten Input Arguments. Jetzt schließt du die Lücke. Hier wird es interessant. Für jedes Tool, das vom Server zurückgegeben wird, rufst du die from mcp tool Method auf der Base DSPy Tool Class auf. Du übergibst dieser Method zwei spezifische Arguments: das rohe Tool Object und die aktive Client Session. Dieser einzige Command liest das Schema, das vom MCP Server bereitgestellt wird, und wrappt es sofort in ein kompatibles Interface. Du hast jetzt eine sofort einsatzbereite Liste von nativen DSPy Tools. Zum Schluss übergibst du diese neu konvertierte Liste von Tools an einen Agent. Du initialisierst ein ReAct Module und übergibst dein Array von DSPy Tools. Wenn du den Agent ausführst, kann er nun nahtlos die externen MCP Tools aufrufen. Die Arguments fließen vom ReAct Module durch den konvertierten DSPy Wrapper, über die MCP Client Session zum Server, und das Result fließt zurück, um den nächsten Reasoning Step zu informieren. Die wahre Power dieser Integration ist das Decoupling. Deine DSPy Modules können sicher auf Enterprise Databases oder lokale File Systems zugreifen, mit null Custom Wrapper Code, während sichergestellt ist, dass exakt derselbe Tool Server für komplett andere Frameworks vollständig nutzbar bleibt. Danke fürs Zuhören, Happy Coding zusammen!
15

Ensembles und Meta-Optimization

3m 51s

Bringe DSPy an seine Grenzen. Die letzte Episode behandelt Programmtransformationen via dspy.Ensemble und den experimentellen BetterTogether Meta-Optimizer, der Prompt Tuning mit Weight Finetuning für maximale Leistung kombiniert.

Herunterladen
Hallo, hier ist Alex von DEV STORIES DOT EU. Learning DSPy, Folge 15 von 15. Was passiert, wenn du Bayesian Prompt Optimization mit Deep Learning Weight Fine-Tuning kombinierst? Du betrachtest Prompt Engineering und Model Training nicht mehr als isolierte Schritte, sondern als eine kontinuierliche Pipeline. Hier erreichst du die Cutting Edge des Automated AI Engineering, basierend auf Ensembles und Meta-Optimization. Wir müssen gleich vorweg eine Sache klarstellen. Wenn du das Wort Ensemble hörst, denkst du wahrscheinlich daran, fünf verschiedene Foundation Models gleichzeitig abzufragen. In DSPy ist ein Ensemble aber etwas völlig anderes. Ein Ensemble bedeutet hier, mehrere optimierte Programme auf demselben zugrunde liegenden Language Model auszuführen. Es kombiniert verschiedene Prompt Structures und unterschiedliche Reasoning Traces, um deren Outputs zu aggregieren. Die Logik dahinter ist ziemlich simpel. Während eines Deep Optimization Runs entdecken verschiedene Konfigurationen oft unterschiedliche, aber gleichermaßen gültige Reasoning Paths, um zur richtigen Antwort zu gelangen. Nehmen wir an, du hast gerade den MIPROv2 Optimizer laufen lassen. Er evaluiert hunderte von Konfigurationen und behält eine History der besten Performer. Anstatt nur das eine Programm mit dem höchsten Score auszuwählen und den Rest zu verwerfen, extrahierst du die Top-Fünf der Candidate Programs. Diese übergibst du dann an die DSPy Ensemble Transformation. Wenn ein neuer Input reinkommt, führt das Ensemble alle fünf Programme aus. Es aggregiert deren Outputs, meistens durch Majority Voting, und gibt eine finale, extrem robuste Antwort zurück. Du skalierst im Grunde deine Compute zur Inference Time, um ein Resultat mit höherer Qualität zu garantieren. Ein Fünf-Programm-Ensemble auf einem riesigen Foundation Model laufen zu lassen, gibt dir zwar eine unglaubliche Accuracy, aber es ist teuer und langsam. Hier kommen Meta-Optimizer ins Spiel. Ein Meta-Optimizer managt die Execution anderer Optimizer und reiht sie so aneinander, dass sich ihre Vorteile potenzieren. Das Paradebeispiel in DSPy ist BetterTogether. BetterTogether schichtet Verbesserungen systematisch übereinander. Es erlaubt dir, die massive Reasoning Capability deines Ensembles zu nehmen und sie in ein schnelles, fine-tuned Model zu destillieren. Zuerst konfigurierst du BetterTogether so, dass es Prompt Optimization nutzt, um extrem hochwertige Reasoning Traces aus deinem schweren Ensemble zu generieren. Als Nächstes übergibt es diese Traces automatisch an einen Weight Optimizer. Der Weight Optimizer nutzt diese Daten, um die Parameter eines viel kleineren, günstigeren Student Models zu fine-tunen. Zum Schluss kann BetterTogether eine zweite Runde Prompt Optimization laufen lassen und die Instructions dieses Mal speziell auf die neu geupdateten Weights des Student Models zuschneiden. Du bewegst dich also von Prompt Optimization zu Weight Optimization und wieder zurück zu Prompt Optimization. Der Output ist ein hochspezialisiertes, schnelles Model, das die diversen Reasoning Paths des ursprünglichen Ensembles eingefangen hat, und zwar ohne die massiven Inference Costs. Indem du Optimization Techniques sequenziell übereinanderlegst, schließt du die Lücke zwischen schwerem, teurem Reasoning und schneller, production-ready Inference. Damit sind wir am Ende der Serie angekommen. Ich kann dir nur wärmstens empfehlen, die offizielle DSPy Documentation zu erkunden, diese Pipelines mal hands-on selbst zu bauen oder devstories dot eu zu besuchen, um Themen vorzuschlagen, die du als Nächstes sehen möchtest. Danke fürs Zuhören und Happy Coding zusammen!