Un percorso formativo completo e passo dopo passo per imparare DSPy, passando da fragili prompt basati su stringhe a una programmazione modulare e strutturata, fino all'ottimizzazione automatizzata.
Questo episodio tratta la filosofia fondamentale di DSPy: allontanarsi dai fragili prompt basati su stringhe verso una programmazione modulare e strutturata. Gli ascoltatori scopriranno perché separare l'architettura di sistema dalle istruzioni per i language models crea applicazioni AI più robuste.
3m 38s
2
Configurare i Language Models
Impara a configurare e gestire i language models in DSPy. Questo episodio spiega come impostare i modelli predefiniti, gestire la cache, sovrascrivere le impostazioni di generazione e accedere a diversi provider di modelli tramite LiteLLM.
3m 57s
3
Prompting Dichiarativo con le Signatures
Scopri come le Signatures di DSPy sostituiscono il prompting tradizionale. Questo episodio spiega come definire il comportamento di input e output di un modulo in modo dichiarativo, utilizzando sia stringhe inline che definizioni basate su classi con tipizzazione rigorosa.
4m 06s
4
Mattoni Fondamentali con i Modules
Esplora i Modules di DSPy, i mattoni fondamentali per i programmi basati su language models. Questo episodio tratta dspy.Predict, dspy.ChainOfThought e come comporre più moduli in una pipeline più grande e coesa.
3m 54s
5
Connettere i Modelli con gli Adapters
Comprendi il ruolo degli Adapters in DSPy. Questo episodio spiega come ChatAdapter e JSONAdapter colmano il divario tra le astratte signatures di DSPy e gli effettivi messaggi multi-turno inviati alle API dei language models.
4m 05s
6
Gestire i Dati con gli Examples
Impara come DSPy gestisce i dataset per il machine learning. Questo episodio tratta l'oggetto dspy.Example, la distinzione tra chiavi di input e label, e la preparazione dei dati per la valutazione e l'ottimizzazione.
3m 41s
7
Definire il Successo con le Metriche
Scopri come valutare i programmi DSPy utilizzando le metriche. Questo episodio ti insegna a scrivere funzioni Python personalizzate per assegnare un punteggio agli output, a utilizzare l'argomento trace e persino a sfruttare l'AI-as-a-judge per valutazioni di testi lunghi.
3m 52s
8
Un'Introduzione agli Optimizers
Entra nella vera magia di DSPy: gli Optimizers. Questo episodio fornisce una panoramica di cosa fanno gli optimizers, del ciclo di ottimizzazione iterativo e dell'insolita strategia di data splitting 20/80 per l'ottimizzazione dei prompt.
3m 42s
9
Few-Shot Learning Automatico
Impara come DSPy automatizza il few-shot prompting. Questo episodio si concentra su BootstrapFewShot e BootstrapFewShotWithRandomSearch, spiegando come sintetizzano, filtrano e iniettano esempi di alta qualità nei tuoi prompt.
3m 21s
10
Ottimizzazione delle Istruzioni con MIPROv2
Immergiti nell'instruction tuning automatico. Questo episodio esplora MIPROv2 e COPRO, mostrando come DSPy utilizzi la Bayesian Optimization e la coordinate ascent per scoprire istruzioni di prompt superiori e controintuitive.
3m 41s
11
Finetuning con BootstrapFinetune
Scopri come distillare language models enormi in modelli più piccoli ed efficienti. Questo episodio tratta BootstrapFinetune, spiegando come convertire un programma DSPy basato su prompt in un modello personalizzato con pesi aggiornati.
3m 37s
12
Uso Automatizzato dei Tool con ReAct
Impara a dare ai language models l'accesso a tool esterni. Questo episodio tratta il modulo dspy.ReAct, dimostrando come costruire agenti autonomi che ragionano e interagiscono dinamicamente con le API.
3m 39s
13
Gestione Manuale dei Tool per un Maggiore Controllo
Prendi il pieno controllo sull'esecuzione dei tool. Questo episodio tratta la gestione manuale dei tool in DSPy utilizzando dspy.Tool, dspy.ToolCalls e il native function calling per applicazioni sensibili alla latenza.
3m 42s
14
Integrare i Tool con MCP
Connetti i tuoi agenti a server di tool universali. Questo episodio spiega come utilizzare il Model Context Protocol (MCP) in DSPy per sfruttare tool standardizzati su diversi framework con una configurazione minima.
4m 11s
15
Ensembles e Meta-Ottimizzazione
Spingi DSPy ai suoi limiti. L'episodio finale tratta le trasformazioni dei programmi tramite dspy.Ensemble e il meta-optimizer sperimentale BetterTogether, che combina il prompt tuning con il weight finetuning per ottenere le massime prestazioni.
3m 43s
Episodi
1
Programmazione, non Prompting
3m 38s
Questo episodio tratta la filosofia fondamentale di DSPy: allontanarsi dai fragili prompt basati su stringhe verso una programmazione modulare e strutturata. Gli ascoltatori scopriranno perché separare l'architettura di sistema dalle istruzioni per i language models crea applicazioni AI più robuste.
Ciao, sono Alex di DEV STORIES DOT EU. Imparare DSPy, episodio 1 di 15.
Passi tre ore a perfezionare un prompt in modo che il tuo modello linguistico generi gli output corretti. Poi un provider rilascia un nuovo modello, cambi la chiave API e l'intera pipeline va in pezzi. Ti ritrovi a mantenere stringhe fragili invece di sviluppare software. Questo è il problema risolto da una filosofia chiamata Programming, Not Prompting.
Spesso si sente parlare di DSPy e si pensa che sia solo un altro tool di prompt templating per inserire variabili in blocchi di testo. Non è così. DSPy è un framework per compilare e ottimizzare il control flow.
Prendi una configurazione standard per un sistema che legge documenti e genera un report con citazioni. In un approccio tradizionale, scrivi un enorme blocco di testo. Spieghi il task, inietti i documenti, aggiungi istruzioni manuali come think step by step, e specifichi esattamente come devono apparire le citazioni. Questo approccio accoppia strettamente l'architettura del tuo sistema a scelte incidentali. La tua architettura è la logica di base. Questo significa estrarre fatti, redigere un riassunto e aggiungere citazioni. Le scelte incidentali sono le parole specifiche che hai usato per convincere un particolare modello linguistico a obbedirti. Quelle stesse parole non funzioneranno in modo ottimale su un modello diverso, o persino su una versione diversa dello stesso modello. Quando i dati cambiano leggermente, il prompt si rompe.
DSPy separa l'architettura del tuo sistema da queste scelte incidentali di prompt. Smetti di scrivere lunghe stringhe di istruzioni. Invece, definisci il tuo task puramente in termini di input e output. Per il generatore di report, dichiari che l'input è una lista di snippet di testo, e l'output è una bozza di testo e un set di riferimenti per le citazioni.
Una volta definiti gli input e gli output, li colleghi usando codice standard. Crei un componente di estrazione, gli passi i documenti e raccogli i fatti. Poi passi questi fatti a un componente di redazione. Infine, potresti usare un semplice loop per fare un controllo incrociato della bozza con i fatti originali. Non ci sono stringhe manuali che implorano il modello di formattare correttamente il suo output. C'è solo logica strutturata.
Ecco il punto chiave. Dato che la tua architettura è definita come codice modulare, un compilatore può tradurre automaticamente quella struttura nei prompt effettivi richiesti da qualsiasi modello linguistico tu stia usando. DSPy tratta le istruzioni del modello, i passaggi di ragionamento e gli esempi few-shot come parametri interni. Queste sono variabili da far ottimizzare al framework, invece di testo statico che scrivi a mano. Tu costruisci la pipeline, definisci le shape dei dati e scrivi i passaggi di esecuzione in Python standard. Il framework gestisce il task imprevedibile di scoprire il modo migliore per chiedere al modello linguistico di eseguire quei passaggi in modo affidabile. Questo cambia radicalmente la developer experience. Passi il tuo tempo a fare il debug della logica della tua applicazione, non a indovinare quale aggettivo farà sì che il modello presti attenzione alla fine di una frase.
L'architettura del tuo sistema dovrebbe sopravvivere alle stranezze di qualsiasi modello linguistico a cui stai facendo routing delle richieste oggi.
Grazie per l'ascolto, happy coding a tutti!
2
Configurare i Language Models
3m 57s
Impara a configurare e gestire i language models in DSPy. Questo episodio spiega come impostare i modelli predefiniti, gestire la cache, sovrascrivere le impostazioni di generazione e accedere a diversi provider di modelli tramite LiteLLM.
Ciao, sono Alex di DEV STORIES DOT EU. Imparare DSPy, episodio 2 di 15. Fare una singola chiamata API a un language model è facile. Ma gestire più provider, fare il caching delle risposte e tracciare la history dei prompt di solito ti costringe a creare e mantenere un custom wrapper. Configurare i language model in DSPy elimina tutto questo lavoro extra.
Magari pensi di aver bisogno dell'SDK di OpenAI per i modelli GPT, dell'SDK di Anthropic per Claude e di una libreria separata per i modelli locali. Invece no. DSPy gestisce tutto questo in modo uniforme tramite un'unica classe LM, che sotto il cofano usa LiteLLM. Per usare un modello, istanzi un oggetto LM di DSPy e passi una stringa che contiene il nome del provider, uno slash e il nome del modello. Ad esempio, passi open ai slash gpt-4o-mini. Quando crei questo oggetto, puoi anche passare parametri standard come temperature o max tokens. Grazie al backend unificato, i nomi di questi parametri restano coerenti indipendentemente dal provider che chiami effettivamente.
Puoi interagire direttamente con questo oggetto LM. Chiamalo come una normale funzione Python, passandogli una semplice stringa di testo o una lista di dizionari di chat. Elabora l'input e restituisce una lista di stringhe generate. Di default, restituisce una singola stringa in quella lista, ma puoi richiedere completamenti multipli. Questo utilizzo diretto è semplice, ma passare manualmente un oggetto modello a ogni funzione in una codebase di grandi dimensioni diventa subito caotico.
Per risolvere questo problema, DSPy usa un sistema di configurazione globale. Definisci il tuo language model di default una volta sola chiamando dspy dot configure, assegnando il tuo oggetto LM istanziato come target. Ogni successiva operazione di DSPy fa routing automaticamente su quel modello. Ma cosa succede se vuoi confrontare gli output tra provider diversi? Mettiamo che tu voglia testare come Claude 3.5 Sonnet gestisce una funzione specifica rispetto al tuo modello GPT di default. Invece di sovrascrivere lo stato globale, usi dspy dot context. Questo crea uno scope temporaneo. Apri un blocco with in Python usando dspy dot context, assegni Claude come default locale ed esegui il tuo codice. Quando il blocco finisce, DSPy torna automaticamente al tuo modello globale GPT-4o-mini.
Questo copre il routing delle richieste. E per quanto riguarda le performance? DSPy fa il caching di ogni generazione del modello di default, per farti risparmiare tempo e costi delle API. Se esegui esattamente lo stesso prompt con gli stessi parametri, ti restituisce all'istante la risposta in cache. Qui c'è il punto chiave. A volte ti serve una nuova generazione senza cambiare il tuo prompt o regolare la temperature. Per farlo, DSPy usa un parametro chiamato rollout id. Quando passi un nuovo rollout id, come un intero univoco, DSPy lo tratta come una richiesta distinta e bypassa la cache. Questo forza il modello a generare una nuova sequenza, dandoti il controllo sulla diversità della generazione pur mantenendo statici gli input principali.
Infine, quando fai esperimenti, devi vedere esattamente cosa è passato sulla rete. Ogni oggetto LM mantiene il proprio log delle interazioni. Puoi accedere ai dati grezzi tramite l'attributo history sull'oggetto modello. Per un riassunto human-readable, chiami il metodo inspect history. Stampa il prompt esatto inviato al provider e la risposta esatta ricevuta.
Il vero valore di questo layer di configurazione è che scollega completamente la tua logica applicativa dalle stranezze dei provider, trasformando la selezione del modello e il caching in semplici switch dichiarativi.
Grazie per l'ascolto, happy coding a tutti!
3
Prompting Dichiarativo con le Signatures
4m 06s
Scopri come le Signatures di DSPy sostituiscono il prompting tradizionale. Questo episodio spiega come definire il comportamento di input e output di un modulo in modo dichiarativo, utilizzando sia stringhe inline che definizioni basate su classi con tipizzazione rigorosa.
Ciao, sono Alex di DEV STORIES DOT EU. Imparare DSPy, episodio 3 di 15. Le signature delle funzioni Python standard dicono al tuo codice quali tipi di dati aspettarsi. Ma cosa succederebbe se una signature potesse dettare la logica effettiva della funzione stessa, senza che tu debba scriverne esplicitamente le istruzioni? Questa è la premessa del Declarative Prompting con le signature.
Potresti facilmente confondere una signature di DSPy con la signature di una funzione Python standard. Sembrano simili, ma i loro ruoli sono fondamentalmente diversi. Una signature Python standard definisce una rigorosa interfaccia dati. Una signature DSPy, invece, dichiara e inizializza il comportamento del language model. Non stai scrivendo un prompt. Stai scrivendo una specifica dichiarativa di ciò che deve accadere. Il framework prende questa specifica, analizza gli input e gli output attesi, e costruisce il prompt sottostante per te.
Il modo più semplice per definire una signature è inline, usando una breve string. Specifichi le tue variabili di input, scrivi una freccia direzionale e specifichi le tue variabili di output. Per esempio, la string question freccia answer dice a DSPy che il modello riceverà una question e dovrà generare una answer. Puoi passare input multipli, come context virgola question freccia answer. Ed è qui che la cosa si fa interessante. I nomi delle variabili che scegli hanno un vero e proprio peso semantico. DSPy utilizza esattamente i nomi di quelle string per assegnare i ruoli nel prompt. Se chiami un input context, il modello lo interpreta come informazione di background. Non fare over-engineering su questi nomi e non cercare di hackerare le keyword con trucchetti di prompt. Usa parole inglesi chiare e descrittive per definire i ruoli.
Quando le string inline non sono abbastanza espressive, passi alle signature basate su classi. Crei una nuova classe che eredita dalla classe base Signature di DSPy. All'interno di questa classe, definisci i tuoi input e output come attributi. Assegni questi attributi usando le funzioni Input Field e Output Field fornite da DSPy. Questo approccio ti dà un controllo granulare sul comportamento del modello. La docstring della classe stessa diventa l'istruzione principale per il language model, definendo il task complessivo.
Considera uno scenario di classificazione di immagini multimodale. Vuoi passare un'immagine e una domanda testuale a un vision model, ed estrarre la razza specifica di un cane. Crei una classe chiamata ClassifyDogBreed. In cima alla classe, scrivi una docstring che dice: Identifica la razza del cane in base all'immagine e alla domanda fornite. Poi, definisci i tuoi input. Crei un attributo chiamato image e lo assegni come Input Field. Crei un secondo attributo chiamato question e lo assegni come Input Field. Infine, definisci un attributo chiamato breed e lo assegni come Output Field. All'interno di quell'Output Field, puoi passare un argomento description che dice: Il nome esatto della razza del cane, senza testo aggiuntivo.
Le signature basate su classi gestiscono anche la type resolution. Puoi specificare dei type hint Python standard per i tuoi field. Se assegni un type hint boolean al tuo output field, DSPy capisce che il modello deve restituire un valore true o false. Il framework elabora queste type annotation e inietta automaticamente dei vincoli nella struttura del prompt, guidando il modello verso il formato di output corretto.
La struttura dei tuoi dati e i nomi delle tue variabili sono le vere e proprie istruzioni. Un field con un nome chiaro e una docstring precisa in una signature dichiarativa detteranno il comportamento del modello in modo molto più affidabile rispetto a paragrafi e paragrafi di prompt engineering fatto a mano.
Grazie per l'ascolto, happy coding a tutti!
4
Mattoni Fondamentali con i Modules
3m 54s
Esplora i Modules di DSPy, i mattoni fondamentali per i programmi basati su language models. Questo episodio tratta dspy.Predict, dspy.ChainOfThought e come comporre più moduli in una pipeline più grande e coesa.
Ciao, sono Alex di DEV STORIES DOT EU. Imparare DSPy, episodio 4 di 15. Di solito, quando vuoi che un language model ragioni su un problema, ricorri a mettere insieme string alla bell'e meglio, aggiungendo a caso frasi come think step by step al tuo prompt. Finisci per intrecciare la logica della tua applicazione con fragili istruzioni testuali. In DSPy, le tecniche di prompting non sono string. Sono componenti strutturati e intercambiabili chiamati module.
È facile confondere i module con le signature. Pensa a una signature come al contratto. Definisce il cosa, mappando specifici campi di input a specifici campi di output. Un module definisce il come. È un oggetto parametrizzato e callable che prende la tua signature e applica una specifica strategia di prompting per soddisfare quel contratto.
Il module più basilare è il module Predict. Lo inizializzi passando la tua signature come argomento. Se la tua signature richiede di trasformare un documento in un riassunto, il module Predict gestisce la formattazione del prompt e chiama il language model. Ma forse il task è complesso e richiede una logica intermedia. Puoi facilmente sostituire Predict con il module Chain of Thought. Non cambi la tua signature. La passi semplicemente al module Chain of Thought. Dietro le quinte, questo module modifica automaticamente l'architettura del prompt. Istruisce il language model a generare una traccia di ragionamento passo passo prima di produrre i campi di output finali che hai definito.
Quando chiami il module Chain of Thought con i tuoi dati di input, restituisce un oggetto che contiene gli output richiesti. Dato che hai usato Chain of Thought, quell'oggetto include anche un nuovo campo che contiene il rationale del modello. Puoi ispezionare esattamente come il language model è arrivato alla sua risposta, in modo completamente separato dal valore finale estratto.
Ecco il punto chiave. Puoi annidare questi module integrati per creare programmi complessi, proprio come impileresti i layer di una rete neurale in PyTorch. Possiamo costruire una pipeline di multi-hop retrieval per vederlo in azione. Inizi definendo una classe custom. Nella sua fase di inizializzazione, dichiari i module più piccoli di cui avrai bisogno. Per un'architettura multi-hop, potresti dichiarare un module di generazione di query usando Chain of Thought, e un module di sintesi delle risposte usando il Predict standard. Poi, definisci un metodo forward per instradare i dati tra di loro.
Il metodo forward prende una domanda iniziale dell'utente. Passa questa domanda al tuo module di generazione di query, che restituisce in output una query di ricerca. Esegui questa ricerca sul tuo database per recuperare un documento. Se hai bisogno di un secondo hop, passi il documento recuperato e la domanda originale di nuovo nel module di generazione di query per produrre una query di ricerca più raffinata. Infine, passi tutti i documenti recuperati e la domanda dell'utente nel module di sintesi delle risposte per generare la risposta finale. Hai appena costruito un grafo custom ed eseguibile a partire da componenti modulari.
Quando metti in chain più chiamate in questo modo, è cruciale vedere esattamente cosa viene inviato al modello dietro le quinte. DSPy tiene traccia dell'utilizzo del tuo language model a livello globale. Puoi chiamare il comando inspect history sull'oggetto del tuo language model per stampare le interazioni più recenti. Questo mostra l'esatta string che il modello ha ricevuto e l'esatta string che ha generato, assicurando che la tua pipeline composta stia assemblando il contesto correttamente.
Separando la definizione del tuo task in signature e la tua strategia di esecuzione in module parametrizzati, trasformi il prompting da un noioso lavoro di text-editing a una decisione architetturale, permettendoti di fare l'upgrade della capacità di ragionamento della tua pipeline semplicemente scambiando il nome di una classe.
Grazie per aver ascoltato, happy coding a tutti!
5
Connettere i Modelli con gli Adapters
4m 05s
Comprendi il ruolo degli Adapters in DSPy. Questo episodio spiega come ChatAdapter e JSONAdapter colmano il divario tra le astratte signatures di DSPy e gli effettivi messaggi multi-turno inviati alle API dei language models.
Ciao, sono Alex di DEV STORIES DOT EU. Imparare DSPy, episodio 5 di 15. Scrivi una signature pulita e fortemente tipizzata nel tuo codice, la passi a un modello linguistico, e in qualche modo ottieni in cambio un oggetto Python perfettamente strutturato. Tra il tuo codice pulito e le API di raw text del modello c'è un layer di traduzione caotico. Questo layer nascosto è il concetto di connettere i modelli con gli Adapter.
Prima di vedere come funzionano, chiariamo un equivoco comune. Potresti confondere gli Adapter con i Module. I Module gestiscono la strategia di ragionamento. Decidono se il modello deve usare Chain of Thought o affidarsi a tool esterni. Agli Adapter non importa nulla della strategia. Un adapter è puramente il layer di traduzione. Gestisce la raw string e la serializzazione JSON che viene effettivamente inviata sulla rete alle API del modello.
I modelli linguistici non capiscono le signature dichiarative. Si aspettano array di messaggi multi-turn contenenti ruoli e blocchi di testo specifici. L'adapter colma questo gap. Il tool predefinito per fare questo in DSPy è il ChatAdapter. Quando richiami un module, il ChatAdapter intercetta la tua signature e la formatta in una chat history standard. L'istruzione principale della signature viene mappata direttamente nel system prompt. I tuoi campi di input vengono raccolti e inseriti nello user message.
Ecco il punto chiave. Il ChatAdapter utilizza specifici marker di testo per mantenere i tuoi input rigorosamente organizzati. Racchiude ogni nome del campo tra doppie parentesi quadre e doppi cancelletti. Se il tuo campo di input si chiama context, il modello linguistico vede un marker con parentesi e cancelletti che circondano la parola context, immediatamente seguito dai dati effettivi del context. Questo confine visivo impedisce al modello di confondere accidentalmente le tue system instruction con il testo di input dell'utente. Ripete questo pattern per i campi di output attesi, spingendo il modello a generare esattamente gli stessi marker nella sua risposta.
Considera uno scenario in cui stai estraendo notizie scientifiche. Il tuo input è un articolo in raw text, e il tuo output deve corrispondere a una classe Pydantic con campi specifici per il titolo e la scoperta scientifica principale. Quando passi questo requisito al ChatAdapter, ispeziona la tua classe Pydantic, genera un JSON schema completo e inietta quello schema direttamente nel system prompt. Dice esplicitamente al modello linguistico come formattare la sua risposta testuale. Quando il modello finalmente risponde, il ChatAdapter intercetta la stringa di raw text. Cerca i marker di output attesi, estrae il blocco di testo compreso tra di essi, e fa il parsing di quei dati per riportarli negli esatti oggetti Python che la tua applicazione richiede.
Questo copre gli input e il parsing per le interazioni basate sul testo. Ma i modelli linguistici moderni spesso hanno un supporto nativo per la structured output. È qui che entra in gioco il JSONAdapter. Invece di modificare pesantemente il system prompt e basarsi sui marker di testo, il JSONAdapter prende una strada più diretta. Delega i vincoli di formattazione alla JSON mode nativa del provider del modello o alle API di structured output. Il modello viene forzato a livello di protocollo a restituire un oggetto JSON valido che contiene tutti i tuoi campi di output richiesti. Dato che le API del modello gestiscono la struttura nativamente, l'adapter non ha più bisogno di cercare gli string marker all'interno del raw text. Se il tuo modello di destinazione supporta questa funzionalità, passare la tua pipeline all'uso del JSONAdapter di solito si traduce in una latenza inferiore e in un parsing significativamente più affidabile.
L'adapter è il confine rigido tra la logica deterministica della tua applicazione e la generazione di testo non strutturato del modello linguistico. Controllando esattamente come gli input vengono serializzati e gli output vengono parsati, gli adapter assicurano che la tua pipeline non si rompa mai a causa di una stringa formattata male.
Grazie per l'ascolto, happy coding a tutti!
6
Gestire i Dati con gli Examples
3m 41s
Impara come DSPy gestisce i dataset per il machine learning. Questo episodio tratta l'oggetto dspy.Example, la distinzione tra chiavi di input e label, e la preparazione dei dati per la valutazione e l'ottimizzazione.
Ciao, sono Alex di DEV STORIES DOT EU. Imparare DSPy, episodio 6 di 15. Lanci un optimizer per migliorare la pipeline del tuo language model, ma ottiene uno score perfetto al primo colpo. Guardi meglio e ti rendi conto di aver accidentalmente passato le risposte target direttamente nel prompt. Per trattare i language model come componenti tradizionali di machine learning, ti serve un modo a tenuta stagna per gestire i tuoi training, dev e test set senza leak di risposte. Gestire i dati con gli Example in DSPy serve esattamente a questo.
In DSPy, la struttura dati fondamentale è l'oggetto Example. Lo usi per costruire tutti i tuoi dataset. All'apparenza, si comporta in modo molto simile a un dizionario Python standard. Lo crei passando coppie chiave-valore che rappresentano i tuoi dati. Prendi un task di summarization. Crei un nuovo oggetto Example e gli assegni due campi. Assegni una string lunga a un campo chiamato report, e una string corta a un campo chiamato summary. Puoi rileggere questi valori in qualsiasi momento usando la standard dot notation, richiedendo il campo report o il campo summary direttamente dall'oggetto.
È comune trattare l'oggetto Example come un semplice wrapper di un dizionario, ma usare un dizionario normale farà fallire il processo di compilazione. Quando passi un dataset a un optimizer DSPy, il compiler deve separare ciò che entra nella pipeline da ciò che viene usato per fare lo score della pipeline. Richiede confini espliciti tra i dati di input e le risposte attese.
È qui che la cosa si fa interessante. L'oggetto Example controlla questi confini usando un metodo specifico chiamato with_inputs. Quando istanzi il tuo Example contenente il report e il summary, metti in chain il metodo with_inputs alla fine. Gli passi la string "report". Questo tagga esplicitamente il campo report come dato di input. Qualsiasi campo che non specifichi in questo metodo diventa automaticamente una label. L'optimizer ora sa che deve inviare solo il report alla tua pipeline. Il summary rimane completamente nascosto durante l'inference.
Una volta configurato un singolo Example, raggruppi più Example in liste Python standard per formare i tuoi training, dev e test set. Dato che DSPy inquadra il prompt engineering come un problema di ottimizzazione di machine learning, avere questi dataset chiaramente partizionati è un requisito fondamentale. Quando l'optimizer fa girare la tua pipeline sul training set, processa un Example alla volta. Rimuove le label, passa gli input in forward, cattura l'output generato e poi valuta il risultato.
Quando scrivi metriche di valutazione custom, avrai bisogno di accedere a questi campi separati. L'oggetto Example fornisce due metodi per questa estrazione. Chiamare il metodo inputs restituisce un dizionario che contiene solo i dati che hai marcato come input. Chiamare il metodo labels restituisce un dizionario che contiene i dati target nascosti. La tua funzione di valutazione chiama il metodo labels per recuperare il summary target, lo confronta con il testo generato e assegna uno score in base a quanto corrispondono.
Configurare correttamente i tuoi oggetti Example garantisce che la tua pipeline impari effettivamente a mappare gli input agli output. La rigorosa separazione tra input e label previene il data leakage durante l'ottimizzazione, assicurando che il tuo sistema migliori anziché limitarsi a memorizzare le risposte fornite.
Grazie per aver ascoltato, happy coding a tutti!
7
Definire il Successo con le Metriche
3m 52s
Scopri come valutare i programmi DSPy utilizzando le metriche. Questo episodio ti insegna a scrivere funzioni Python personalizzate per assegnare un punteggio agli output, a utilizzare l'argomento trace e persino a sfruttare l'AI-as-a-judge per valutazioni di testi lunghi.
Ciao, sono Alex di DEV STORIES DOT EU. Imparare DSPy, episodio 7 di 15. Non puoi migliorare costantemente ciò che non puoi misurare e, quando hai a che fare con i language model, affidarti all'intuizione umana per misurare la qualità dell'output semplicemente non scala. Per riscrivere automaticamente i tuoi prompt o ottimizzare il tuo sistema, il compiler ha bisogno di una stella polare matematica, ed è esattamente ciò che ti offre definire il successo tramite le metriche.
In DSPy, una metrica è una funzione Python standard. Prende due argomenti principali. Il primo è un example, che rappresenta l'input di riferimento e l'output atteso dal tuo dataset. Il secondo è una prediction, ovvero la risposta effettiva generata dal tuo programma DSPy. La funzione metrica confronta la prediction con l'example e restituisce uno score. Questo score di solito è un float, un integer o un semplice valore boolean come true o false.
Per task di classificazione di base, la tua metrica potrebbe essere della semplice logica Python. Potresti scrivere una funzione di exact match che verifica se la string prevista è perfettamente uguale alla string attesa. Per eseguire questa misurazione in modo sistematico sui tuoi dati, DSPy fornisce una utility integrata chiamata Evaluate. Passi a questa utility il tuo dataset di sviluppo, la tua funzione metrica e i parametri di esecuzione, come il numero di thread paralleli. La utility Evaluate esegue la tua metrica su ogni prediction, aggrega i risultati e restituisce un singolo score numerico che rappresenta le performance complessive del tuo sistema.
Tuttavia, l'exact matching è quasi sempre troppo rigido per i task di linguaggio generativo. È qui che passi dai semplici controlli di string all'utilizzo del feedback dell'IA, un pattern comunemente noto come LLM-as-a-judge. Poiché i moduli DSPy sono semplicemente codice Python, la tua funzione metrica può istanziare e chiamare un programma DSPy separato e più piccolo per valutare output semantici complessi.
Considera uno scenario concreto. Stai costruendo un sistema che genera un tweet promozionale per rispondere a una domanda dell'utente. Una metrica di exact match fallisce completamente in questo caso, perché un buon tweet può essere formulato in innumerevoli modi validi. Invece, la tua funzione metrica dovrebbe valutare diverse dimensioni dell'output. Primo, utilizza un semplice controllo di lunghezza in Python per assicurarsi che il testo generato sia sotto i duecentottanta caratteri. Secondo, verifica se il testo contiene la risposta fattuale richiesta dall'example. Infine, passa il testo generato a una signature DSPy specializzata che chiede a un language model più piccolo di valutare se il tweet è coinvolgente. La tua funzione metrica combina quindi il controllo di lunghezza, il fact check e lo score di coinvolgimento del language model in un unico valore matematico finale.
Quando inizierai a compilare e ottimizzare questi programmi, la tua funzione metrica dovrà accettare un terzo argomento opzionale. Si chiama trace. Spesso gli ascoltatori confondono l'argomento trace con un log di debug che stampa gli errori in console o la cronologia di esecuzione. Non è niente di tutto questo. L'argomento trace è un oggetto specifico utilizzato dal compiler DSPy durante l'ottimizzazione per validare gli step di ragionamento intermedi. Se il tuo programma concatena più chiamate al language model, il trace contiene lo specifico percorso di ragionamento che il modello ha seguito per arrivare alla fine. Accedendo al trace all'interno della tua metrica, la tua funzione può verificare non solo che il tweet finale fosse buono, ma anche che gli step intermedi usati per scriverlo fossero logicamente sensati.
Questa è la parte che conta. La tua metrica definisce rigorosamente l'aspetto del successo, e il compiler DSPy ottimizzerà il tuo sistema in modo spietato per massimizzare quello specifico score. Se la tua metrica è difettosa, il tuo programma compilato sarà difettoso esattamente nello stesso modo. Grazie per l'ascolto, happy coding a tutti!
8
Un'Introduzione agli Optimizers
3m 42s
Entra nella vera magia di DSPy: gli Optimizers. Questo episodio fornisce una panoramica di cosa fanno gli optimizers, del ciclo di ottimizzazione iterativo e dell'insolita strategia di data splitting 20/80 per l'ottimizzazione dei prompt.
Ciao, sono Alex di DEV STORIES DOT EU. Imparare DSPy, episodio 8 di 15. Immagina di scrivere un software e, invece di modificare manualmente la sua logica interna per superare i test, un compilatore riscrive automaticamente le istruzioni in modo che il programma funzioni meglio da solo. Non tocchi il codice. Fornisci solo il test set. Questo è esattamente ciò che fa DSPy attraverso un concetto chiamato Optimizer.
Gli optimizer, che nelle versioni precedenti del framework erano chiamati teleprompter, sono algoritmi che calibrano i parametri del tuo programma. Nel machine learning tradizionale, i parametri rappresentano i pesi della rete neurale. In DSPy, i parametri rappresentano principalmente le stringhe dei prompt e le istruzioni inviate al language model, anche se possono includere i pesi. Il compito dell'optimizer è regolare questi parametri per massimizzare una metrica che hai già definito. Questo processo avviene prima di fare il deploy della tua applicazione. È puro compute in fase di pre-inference. Spendi potenza di calcolo in anticipo per trovare le istruzioni migliori, in modo che la tua applicazione giri in modo preciso in seguito.
Quando senti la parola optimizer, potresti pensare che servano dataset enormi, come faresti per il fine-tuning di un modello tradizionale. Non è così. I prompt optimizer sono estremamente efficienti. In genere richiedono solo da trenta a trecento esempi. Poiché il dataset è così piccolo, DSPy consiglia un approccio insolito per splittare i tuoi dati. Invece del classico split ottanta-venti, in cui la maggior parte dei dati va al training, lo inverti. Usi il venti percento per il training e l'ottanta percento per la validation. Se hai cinquanta esempi, ne dai dieci all'optimizer per costruire i prompt e usi i restanti quaranta per valutare se quei prompt generalizzano davvero. Questo reverse split impedisce all'optimizer di fare overfitting dei prompt generati su un set di input minuscolo.
Ecco il punto chiave. Il ciclo di sviluppo iterativo in DSPy ruota attorno all'esecuzione di questo loop di ottimizzazione. Vediamo uno scenario concreto. Stai creando un semplice bot di question answering. Per prima cosa, definisci il tuo programma DSPy e la tua metrica. Successivamente, raccogli il tuo dataset di cinquanta domande senza label. Fai uno split di questi dati, passando la piccola porzione di training a un oggetto optimizer. Dici all'optimizer di compilare il tuo programma usando i tuoi dati di training e la tua metrica. L'optimizer gira, sperimentando diverse strutture di prompt dietro le quinte. Confronta gli output con la tua metrica, impara cosa funziona e perfeziona i prompt.
Quando l'optimizer finisce, restituisce una nuova versione compilata del tuo programma. Questo programma compilato contiene i parametri appena calibrati. Non hai bisogno di eseguire questo step di ottimizzazione ogni volta che la tua applicazione si avvia. Invece, chiami il metodo save sul programma compilato, fornendo un file path. Questo scrive tutti i prompt e le configurazioni ottimizzate in un file JSON standard. Quando fai il deploy della tua applicazione in produzione, il tuo codice istanzia semplicemente il programma base e chiama il metodo load, puntandolo a quell'esatto file JSON. Il tuo bot è immediatamente pronto a rispondere alle domande usando le istruzioni ottimizzate. Il vero potere degli optimizer di DSPy è che disaccoppiano la logica della tua applicazione dal phrasing esatto dei tuoi prompt, lasciando che sia il compute a trovare le parole migliori per te. Grazie per l'ascolto, e happy coding a tutti!
9
Few-Shot Learning Automatico
3m 21s
Impara come DSPy automatizza il few-shot prompting. Questo episodio si concentra su BootstrapFewShot e BootstrapFewShotWithRandomSearch, spiegando come sintetizzano, filtrano e iniettano esempi di alta qualità nei tuoi prompt.
Ciao, sono Alex di DEV STORIES DOT EU. Imparare DSPy, episodio 9 di 15. Selezionare manualmente gli esempi migliori da inserire in un prompt è un'operazione noiosa e soggetta a bias. Devi indovinare quali esempi contino, li inserisci hardcoded nelle tue stringhe e speri che il modello ci faccia attenzione. DSPy sintetizza, testa e seleziona attivamente le dimostrazioni perfette per te. Questo processo è il few-shot learning automatico, e DSPy lo gestisce tramite tre optimizer specifici.
L'approccio più semplice è LabeledFewShot. Fornisci un set di esempi di training etichettati. L'optimizer seleziona casualmente un sottoinsieme di queste coppie di input e output e le inserisce direttamente nei tuoi prompt come dimostrazioni. Dà al modello un pattern di base da seguire. Questo funziona bene se i tuoi dati di training corrispondono esattamente agli step intermedi di cui ha bisogno il tuo programma. Di solito, però, non è così.
Questo ci porta a BootstrapFewShot. Un malinteso comune è pensare che BootstrapFewShot scelga semplicemente a caso degli esempi dal tuo training set. Non lo fa. Genera attivamente degli step di ragionamento intermedi che non sono mai esistiti nei tuoi dati grezzi.
Ecco come si svolge il processo di bootstrapping. L'optimizer richiede un programma teacher. Di default, si tratta semplicemente della versione non ottimizzata e zero-shot del tuo programma. Il teacher passa in rassegna i tuoi esempi di training. Per ogni esempio, tenta di generare una risposta. DSPy passa quindi quella risposta alla tua metrica di valutazione. Se la metrica dice che l'output è corretto, DSPy salva l'intera trace di quell'esecuzione andata a buon fine. Questa trace include l'input, l'output e, cosa fondamentale, qualsiasi lavoro intermedio che il programma ha fatto per arrivarci.
Prendi ad esempio un classificatore di sentiment. Il tuo dataset grezzo contiene solo recensioni dei clienti e una label positiva o negativa. Il tuo programma DSPy chiede al language model di usare il ragionamento chain-of-thought prima di restituire in output il sentiment. Durante il bootstrapping, il teacher legge una recensione e scrive un paragrafo di ragionamento prima di indovinare il sentiment. Se l'ipotesi finale corrisponde alla label corretta, quel ragionamento generato viene considerato un successo. L'optimizer raccoglie queste trace di successo. Ne prende quattro e le inietta nei prompt futuri. Il tuo classificatore zero-shot è ora un classificatore four-shot esperto, completo di step di ragionamento sintetizzati.
BootstrapFewShot si ferma una volta che trova abbastanza trace di successo. Ma le prime trace di successo non sono sempre le migliori.
Ecco l'intuizione chiave. BootstrapFewShotWithRandomSearch risolve questo problema eseguendo l'intero processo di bootstrap più volte. Ogni volta, estrae un sottocampione casuale dei tuoi dati di training. Questo crea diversi set candidati di dimostrazioni few-shot. L'optimizer prende quindi tutti questi set candidati e li valuta sui tuoi dati di validazione. Testa quale combinazione specifica di dimostrazioni produce il punteggio complessivo più alto. Scarta i set deboli e mantiene quello matematicamente vincente.
Il vero potere del few-shot learning automatico non è solo farti risparmiare tempo nello scrivere prompt, ma scoprire path di ragionamento intermedi di successo che il tuo dataset non ha mai contenuto esplicitamente.
Grazie per l'ascolto, happy coding a tutti!
10
Ottimizzazione delle Istruzioni con MIPROv2
3m 41s
Immergiti nell'instruction tuning automatico. Questo episodio esplora MIPROv2 e COPRO, mostrando come DSPy utilizzi la Bayesian Optimization e la coordinate ascent per scoprire istruzioni di prompt superiori e controintuitive.
Ciao, sono Alex di DEV STORIES DOT EU. Imparare DSPy, episodio 10 di 15. A volte l'istruzione più efficace per un language model sembra priva di senso per un essere umano. Passi ore a creare meticolosamente la frase perfetta, solo per scoprire che un prompt generato automaticamente e un po' sconnesso supera di gran lunga il tuo lavoro. Ecco perché dovresti lasciare che siano gli algoritmi a scrivere i tuoi prompt. Oggi parliamo di Instruction Optimization con MIPROv2.
Per prima cosa, libera la mente dal prompt templating. Non si tratta di sostituire variabili in una string statica. La Instruction Optimization in realtà riscrive le istruzioni di sistema che governano la tua pipeline. Algoritmi precedenti come COPRO e SIMBA affrontavano questo problema generando varianti step-by-step dei prompt e perfezionandole nel tempo. MIPROv2 porta questo concetto molto più in là, trattando le istruzioni e gli esempi few-shot come un unico search space.
MIPROv2 opera in tre fasi distinte. La prima fase è il bootstrapping. L'optimizer esegue il tuo programma non ottimizzato sui tuoi training data per creare un pool di execution trace. Queste trace contengono gli input effettivi, gli step intermedi e gli output che attraversano il tuo sistema.
La seconda fase è la grounded proposal. L'optimizer non ipotizza nuove istruzioni alla cieca. Utilizza un language model separato, chiamato prompter, per analizzare quelle trace generate. Analizzando dove la tua pipeline ha avuto successo e dove ha fallito, il prompter elabora un set di nuove istruzioni candidate. Questi candidati sono basati direttamente sul comportamento effettivo del tuo programma, non su template generici.
La terza fase è la discrete search. MIPROv2 valuta le nuove istruzioni insieme a diverse combinazioni di few-shot trace. Per farlo in modo efficiente, si affida alla Bayesian Optimization. Invece di provare in brute-force ogni possibile combinazione, MIPROv2 crea un surrogate model. Questo surrogate model funge da proxy leggero. Prevede quali combinazioni di istruzioni e trace produrranno lo score più alto sulla tua specifica metrica di valutazione.
La Bayesian Optimization permette al surrogate model di mappare lo spazio dei prompt e delle demonstration. Calcola il miglioramento atteso testando una nuova combinazione. Questo bilancia sistematicamente l'esplorazione di istruzioni non testate con lo sfruttamento delle combinazioni che ottengono già un buono score. L'optimizer individua la configurazione ottimale senza eseguire migliaia di network call ridondanti.
Considera uno scenario concreto. Costruisci un agente ReAct per rispondere a query complesse. Inizialmente, la sua accuracy è al 24 percento. Passi questo agente a MIPROv2, lo configuri per l'esecuzione in light mode e fornisci un dataset di 500 domande. L'optimizer fa il bootstrap delle trace, propone grounded instruction e cerca nello spazio utilizzando il surrogate model. Quando finisce, l'accuracy del tuo agente salta dal 24 percento al 51 percento. Il prompt finale che guida questo salto di performance conterrà probabilmente istruzioni e selezioni di trace che un essere umano non avrebbe mai scritto.
Ecco il punto chiave. MIPROv2 elimina il collo di bottiglia dell'intuizione umana. Tratta le tue istruzioni in linguaggio naturale esattamente come pesi regolabili in un modello matematico, trasformando la creazione dei prompt da una forma d'arte imprevedibile a un problema di ottimizzazione deterministico. Grazie per l'ascolto, happy coding a tutti!
11
Finetuning con BootstrapFinetune
3m 37s
Scopri come distillare language models enormi in modelli più piccoli ed efficienti. Questo episodio tratta BootstrapFinetune, spiegando come convertire un programma DSPy basato su prompt in un modello personalizzato con pesi aggiornati.
Ciao, sono Alex di DEV STORIES DOT EU. Imparare DSPy, episodio 11 di 15. Fare prompting su modelli enormi è ottimo per far decollare un prototipo. Ma quando questa logica va in produzione, la latenza e il costo di un modello da settanta miliardi di parametri diventano rapidamente un problema. Hai bisogno della potenza di ragionamento del modello grande, ma della velocità e del prezzo di un modello da otto miliardi di parametri. Questo è esattamente ciò di cui si occupa BootstrapFinetune.
BootstrapFinetune compila il tuo programma DSPy in un modello fine-tuned. Rappresenta l'ottimizzazione definitiva per l'efficienza. Aggiorna i pesi effettivi di un modello target più piccolo per replicare esattamente il comportamento della tua pipeline pesante.
Un malinteso comune è che per fare fine-tuning di un modello, devi raccogliere manualmente migliaia di esempi, formattarli in noiosi file JSONL e sorvegliare un training loop. BootstrapFinetune automatizza completamente questo processo. Gestisce la generazione del dataset, la formattazione e l'aggiornamento dei pesi interamente attraverso le trace di esecuzione del tuo programma.
Prendi uno scenario concreto che coinvolge un classificatore di intenti bancari. Il programma prende un messaggio confuso di un cliente e lo categorizza. All'inizio, costruisci un modulo DSPy usando un modello molto capace come GPT-4o-mini, configurato per usare il ragionamento chain-of-thought. Il modello pensa passo dopo passo alla frase del cliente prima di restituire in output l'intento. Ottiene le risposte giuste, ma è troppo lento e costoso per una chat in tempo reale.
Per ottimizzare tutto questo, inizializzi BootstrapFinetune. Gli passi la tua metrica di valutazione per misurare il successo, e specifichi il modello target più piccolo ed economico di cui vuoi fare il deploy. Poi compili il programma.
Quando avvii la compilazione, DSPy esegue il tuo programma non ottimizzato sui tuoi dati di training. Usa il modello teacher pesante per generare gli output. L'optimizer osserva questa esecuzione. Ogni volta che il modello teacher ottiene la risposta giusta secondo la tua metrica, BootstrapFinetune cattura la trace. Registra gli input, il ragionamento passo passo e l'output finale. Mappa la logica interna del modello enorme in un formato che il piccolo modello target può assimilare.
Una volta raccolte abbastanza trace di successo, BootstrapFinetune le struttura automaticamente in un training dataset. A quel punto fa partire il processo di fine-tuning sul tuo modello target. Il modello piccolo viene addestrato direttamente sui percorsi di ragionamento di alta qualità generati dal modello grande.
Ecco il punto chiave. Il modello più piccolo impara la distribuzione specifica del task e lo stile di ragionamento richiesto per risolverlo, senza dover eseguire il pesante chain-of-thought a tempo di inference. Nel nostro esempio del classificatore bancario, un piccolo modello standard potrebbe raggiungere solo il 66% di accuratezza out of the box. Ma dopo la compilazione con BootstrapFinetune, quello stesso piccolo modello salta all'87% di accuratezza.
Il fine-tuning non è più un progetto infrastrutturale separato; è semplicemente un altro passaggio di compilazione che trasforma una costosa pipeline di ragionamento in un asset di produzione veloce ed economico.
Grazie per aver ascoltato, buon coding a tutti!
12
Uso Automatizzato dei Tool con ReAct
3m 39s
Impara a dare ai language models l'accesso a tool esterni. Questo episodio tratta il modulo dspy.ReAct, dimostrando come costruire agenti autonomi che ragionano e interagiscono dinamicamente con le API.
Ciao, sono Alex di DEV STORIES DOT EU. Imparare DSPy, episodio 12 di 15. Dare a un LLM l'accesso ad API esterne lo rende incredibilmente potente, ma scrivere il loop per gestire il suo ragionamento, fare il parsing degli output e riprendersi dagli errori di esecuzione è un vero grattacapo. La soluzione è l'uso completamente automatizzato dei tool con il modulo ReAct di DSPy.
Spesso si confonde ReAct con il semplice function calling. Il function calling è solamente il meccanismo API che permette a un language model di formattare il suo output come una richiesta di dati strutturati. ReAct è uno specifico paradigma comportamentale. Sta per Reason and Act. È un execution loop in cui il modello passa attraverso tre fasi distinte: un Thought, un'Action e un'Observation.
Il modulo ReAct di DSPy gestisce completamente questa orchestrazione per te. Non devi scrivere tu l'execution loop. Non devi fare il catch manuale delle eccezioni API. ReAct fa il wrap di una signature di DSPy e di una lista di tool, trasformando un prompt statico in un agente autonomo.
Per usarlo, prima di tutto definisci i tuoi tool. In DSPy, i tool sono semplicemente delle funzioni Python standard. Scrivi una funzione, definisci i suoi parametri di input e fornisci una docstring chiara. Quella docstring è fondamentale. DSPy estrae il nome della funzione e la docstring, passandoli al language model in modo che sappia esattamente cosa fa il tool e quando farne il deploy.
Immagina uno scenario in cui costruisci un semplice agente di ricerca e meteo. Scrivi una funzione Python chiamata get weather che accetta il nome di una città come string e fa una query a un'API per restituire la temperatura attuale. Istanzi il modulo dspy dot ReAct, passandogli una signature standard di question and answer insieme a una lista che contiene la tua funzione get weather.
Quando chiedi al modulo che tempo fa a Tokyo, inizia il loop di ReAct. Per prima cosa, il modello genera un Thought. Ragiona sul fatto che ha bisogno di dati meteorologici aggiornati per Tokyo. Successivamente, genera un'Action. Decide di chiamare il tuo tool get weather, passando Tokyo come argomento.
Questo è il punto chiave. Non sei tu a eseguire quella funzione. Il modulo ReAct di DSPy intercetta l'Action del modello, esegue la tua funzione Python dietro le quinte e ne cattura l'output. Se la funzione ha successo, DSPy restituisce i dati della temperatura al modello come Observation. Se il modello allucina un parametro o la funzione lancia un errore Python, DSPy fa il catch di quell'errore e restituisce il messaggio di errore come Observation. Il modello legge l'errore, genera un nuovo Thought per correggere il suo sbaglio e prova una nuova Action.
Una volta che il modello osserva i dati corretti della temperatura, riconosce che il suo obiettivo è stato raggiunto. Esce dal loop e formatta la risposta finale per l'utente.
Per evitare esecuzioni incontrollate, questo ciclo è strettamente limitato da un parametro chiamato max iters, che sta per maximum iterations. Questo parametro stabilisce quanti cicli di Thought, Action e Observation il modulo è autorizzato a eseguire. Se il modello fa fatica a trovare i dati corretti e raggiunge il limite di iterazioni, ReAct lo forza a interrompere la ricerca e a generare una risposta finale usando solo le informazioni che ha raccolto con successo.
Il vero potere di questo modulo è che astrae il control flow fragile e soggetto a errori dei loop degli agenti, permettendoti di trattare il ragionamento complesso aumentato dai tool come un altro componente prevedibile nella tua pipeline.
Grazie per l'ascolto, happy coding a tutti!
13
Gestione Manuale dei Tool per un Maggiore Controllo
3m 42s
Prendi il pieno controllo sull'esecuzione dei tool. Questo episodio tratta la gestione manuale dei tool in DSPy utilizzando dspy.Tool, dspy.ToolCalls e il native function calling per applicazioni sensibili alla latenza.
Ciao, sono Alex di DEV STORIES DOT EU. Imparare DSPy, episodio 13 di 15. Gli agent automatizzati sono ottimi quando hai un task flessibile e aperto. Ma quando ti serve un controllo assoluto e deterministico su come, quando e se una funzione esterna viene eseguita, lasciare il volante completamente al language model è troppo rischioso. Devi aprire il cofano e gestire l'esecuzione in prima persona. Ed è proprio qui che entra in gioco la gestione manuale dei tool per avere il controllo.
Usare un loop di agent automatizzati astrae il layer di esecuzione, il che può causare una latenza imprevedibile o nascondere errori di runtime. La gestione manuale è l'alternativa per i power user. Ti restituisce il controllo sull'error recovery, sui limiti di timeout e sull'esatto ordine di esecuzione. Per creare tutto questo in DSPy, inizi wrappando una funzione Python standard usando la classe dspy.Tool. Immagina di avere una funzione Python che fa da calcolatrice per moltiplicare due numeri. Passi questa funzione a dspy.Tool. Se la tua funzione gestisce query al database o network request, puoi anche wrappare funzioni asincrone, e la classe Tool gestirà l'esecuzione async in modo nativo.
Una volta che il tuo tool calcolatrice è pronto, devi esporlo al language model. Lo fai passando una lista che contiene il tuo tool direttamente a un modulo Predict di DSPy. Definisci un parametro chiamato tools nella tua chiamata a Predict. Quando il modello elabora l'input, valuta il prompt e decide se ha bisogno della calcolatrice per generare la risposta finale.
Quando il modello decide di usare il tuo tool, si affida a un meccanismo sottostante chiamato Adapter. Di default, DSPy usa un JSONAdapter. Questo adapter traduce automaticamente il tuo tool Python nel formato di function calling nativo richiesto dalla specifica API del language model che stai usando. Questo garantisce che il modello produca un output JSON affidabile e strutturato quando richiede un tool. Fai attenzione a questa parte. È facile dare per scontato che usare il tool calling nativo produca automaticamente output di qualità superiore. La documentazione di DSPy avverte esplicitamente che si tratta di un'idea sbagliata. Il tool calling nativo offre una maggiore affidabilità per la sintassi della request, ma non garantisce una qualità di ragionamento superiore rispetto al parsing text-based standard. Il modello non diventa improvvisamente più intelligente solo perché sta formattando in JSON.
Dato che stai gestendo questo processo manualmente, il modello non esegue fisicamente la calcolatrice. Si ferma e restituisce una response che contiene il suo intento. Puoi accedere a questo intento ispezionando response outputs punto tool calls. Questa proprietà restituisce un oggetto dspy.ToolCalls, che si comporta come una lista di istruzioni. Ogni elemento di questa lista specifica quale tool il modello vuole usare e gli esatti argomenti che ha generato, come cinque e dieci per la calcolatrice.
Poi, scrivi un loop Python standard per iterare attraverso queste tool call richieste. Per ogni call, invochi manualmente il suo metodo execute. Invocare execute innesca il tuo vero codice Python usando gli argomenti generati dal modello e restituisce il risultato. Se gli argomenti non sono validi, o se la calcolatrice lancia un errore, il tuo loop Python lo intercetta. Gestisci il fallimento alle tue condizioni, invece di sperare che un loop automatizzato si riprenda da solo.
La gestione manuale dei tool separa nettamente la decisione del modello di richiedere un'azione dall'esecuzione fisica di quell'azione, dando alla tua applicazione l'affidabilità deterministica che gli ambienti di produzione richiedono. Grazie per l'ascolto, happy coding a tutti!
14
Integrare i Tool con MCP
4m 11s
Connetti i tuoi agenti a server di tool universali. Questo episodio spiega come utilizzare il Model Context Protocol (MCP) in DSPy per sfruttare tool standardizzati su diversi framework con una configurazione minima.
Ciao, sono Alex di DEV STORIES DOT EU. Imparare DSPy, episodio 14 di 15. Ogni volta che adotti un nuovo framework AI, in genere finisci per riscrivere gli stessi wrapper Python custom per le tue query a database, ricerche web e lettori di file. Invece di mantenere infiniti wrapper API duplicati, cosa succederebbe se i tuoi agenti potessero connettersi all'istante a tool server universali e standardizzati? Questa è la promessa del Model Context Protocol, e oggi vedremo come integrare i tool con MCP.
Il Model Context Protocol è uno standard aperto introdotto da Anthropic. Fornisce un modo universale per connettere i modelli AI a data source e tool esterni. Adottando questo standard, gli sviluppatori scrivono un tool una sola volta, lo ospitano su un server MCP e lo usano su qualsiasi framework supportato. DSPy lo supporta nativamente. Non hai bisogno di scrivere complesse classi adapter per portare questi tool esterni nei tuoi programmi DSPy.
Un malinteso comune è che DSPy gestisca da solo le connessioni server sottostanti a questi tool. Non è così. DSPy si affida interamente al package Python ufficiale mcp per gestire il networking e stabilire la connessione. DSPy interviene solo alla fine per convertire l'oggetto tool MCP attivo in un formato tool nativo di DSPy.
Per vedere come funziona, vediamo come connettere un tool server locale a un agente DSPy. Per prima cosa, hai bisogno di un server MCP in esecuzione. Nel tuo script Python, importi la classe dei parametri del server dal package MCP. Se stai eseguendo un processo locale, definisci i parametri server standard IO e li punti all'eseguibile del tuo server. In alternativa, se i tuoi tool si trovano su un server remoto, configuri una connessione HTTP client. Entrambi i metodi stabiliscono come la tua applicazione parlerà con il tool provider.
Successivamente, usi la libreria MCP per aprire una sessione client. All'interno di questo context di sessione, inizializzi la connessione. A questo punto, DSPy è ancora completamente fuori dai giochi. Chiedi alla sessione MCP attiva di elencare i suoi tool disponibili. Il server risponde con una lista di oggetti tool. Ogni oggetto contiene il nome del tool, una descrizione di cosa fa e gli argomenti di input attesi.
Ora colmi il divario. È qui che la cosa si fa interessante. Per ogni tool restituito dal server, chiami il metodo from mcp tool sulla classe base DSPy Tool. Passi a questo metodo due argomenti specifici: l'oggetto tool grezzo e la sessione client attiva. Questo singolo comando legge lo schema fornito dal server MCP e lo wrappa all'istante in un'interfaccia compatibile. Ora hai a disposizione una lista pronta all'uso di tool nativi DSPy.
Infine, passi questa lista di tool appena convertita a un agente. Inizializzi un modulo ReAct e gli passi il tuo array di tool DSPy. Quando esegui l'agente, ora può chiamare senza problemi i tool MCP esterni. Gli argomenti fluiscono dal modulo ReAct, attraverso il wrapper DSPy convertito, passano per la sessione client MCP fino al server, e il risultato torna indietro per informare lo step di ragionamento successivo.
Il vero potere di questa integrazione è il disaccoppiamento. I tuoi moduli DSPy possono accedere in modo sicuro a database aziendali o file system locali con zero codice wrapper custom, garantendo al contempo che lo stesso identico tool server rimanga completamente utilizzabile da framework del tutto diversi.
Grazie per l'ascolto, happy coding a tutti!
15
Ensembles e Meta-Ottimizzazione
3m 43s
Spingi DSPy ai suoi limiti. L'episodio finale tratta le trasformazioni dei programmi tramite dspy.Ensemble e il meta-optimizer sperimentale BetterTogether, che combina il prompt tuning con il weight finetuning per ottenere le massime prestazioni.
Ciao, sono Alex di DEV STORIES DOT EU. Imparare DSPy, episodio 15 di 15. Cosa succede quando combini la prompt optimization bayesiana con il weight fine-tuning in deep learning? Smetti di trattare il prompt engineering e il model training come step isolati, e inizi a trattarli come una pipeline continua. È qui che raggiungi la frontiera dell'AI engineering automatizzata, basandoti su Ensemble e Meta-Optimization.
Dobbiamo chiarire subito un punto. Quando senti la parola ensemble, probabilmente pensi a fare query a cinque diversi foundation model contemporaneamente. In DSPy, un ensemble è qualcosa di completamente diverso. Qui un ensemble significa eseguire più programmi ottimizzati sullo stesso language model sottostante. Combina diverse strutture di prompt e diverse reasoning traces per aggregare i loro output.
La logica qui è semplice. Durante una run di deep optimization, diverse configurazioni spesso scoprono reasoning path distinti e ugualmente validi per arrivare alla risposta corretta. Mettiamo che tu abbia appena lanciato l'optimizer MIPROv2. Valuta centinaia di configurazioni e mantiene uno storico dei best performer. Invece di scegliere il singolo programma con lo score più alto e scartare il resto, estrai i cinque programmi candidati migliori. Li passi alla trasformazione Ensemble di DSPy. Quando arriva un nuovo input, l'ensemble esegue tutti e cinque i programmi. Aggrega i loro output, di solito tramite majority voting, e restituisce una risposta finale estremamente robusta. In sostanza, stai scalando il tuo compute a tempo di inference per garantire un risultato di qualità superiore.
Eseguire un ensemble di cinque programmi su un foundation model enorme ti dà un'accuratezza incredibile, ma è costoso e lento. È qui che entrano in gioco i meta-optimizer. Un meta-optimizer gestisce l'esecuzione di altri optimizer, mettendoli in sequenza per combinare i loro benefici. L'esempio principale in DSPy è BetterTogether.
BetterTogether stratifica i miglioramenti in modo sistematico. Ti permette di prendere l'enorme capacità di reasoning del tuo ensemble e distillarla in un modello veloce e fine-tuned. Per prima cosa, configuri BetterTogether per usare la prompt optimization per generare reasoning traces di altissima qualità dal tuo ensemble pesante. Successivamente, passa automaticamente queste traces a un weight optimizer. Il weight optimizer usa quei dati per fare il fine-tuning dei parametri di uno student model molto più piccolo ed economico. Infine, BetterTogether può lanciare un secondo round di prompt optimization, questa volta adattando le istruzioni specificamente ai pesi appena aggiornati dello student model.
Stai passando dalla prompt optimization, alla weight optimization, e di nuovo alla prompt optimization. L'output è un modello veloce e altamente specializzato che ha catturato i diversi reasoning path dell'ensemble originale, senza l'enorme costo di inference. Stratificare le tecniche di ottimizzazione in modo sequenziale è il modo in cui colmi il divario tra un reasoning pesante e costoso e un'inference veloce e production-ready.
Questo ci porta alla fine della serie. Ti incoraggio vivamente a esplorare la documentazione ufficiale di DSPy, a provare a costruire queste pipeline hands-on, o a visitare devstories dot eu per suggerire gli argomenti che vorresti vedere trattati in futuro. Grazie per l'ascolto, buon coding a tutti!
Tap to start playing
Browsers block autoplay
Share this episode
Episode
—
Copy this episode in another language:
Questo sito non utilizza cookie. Il nostro fornitore di hosting potrebbe registrare il tuo indirizzo IP a fini statistici. Scopri di più.