Esplorare DSPy e la sua integrazione con Milvus per creare pipeline RAG altamente efficienti
Introduzione
I modelli linguistici di grandi dimensioni (LLMs) possiedono capacità generative trasformative e sono potenziati con strumenti come basi di conoscenza e retriever, alimentando applicazioni GenAI avanzate come chatbot e agenti. Al centro dell’interazione con gli LLM ci sono i prompt, ovvero istruzioni che guidano questi modelli nell’esecuzione di compiti specifici. Tuttavia, creare prompt efficaci è un processo sfumato e complesso, che spesso richiede tecniche sofisticate come Chain-of-Thought e ReAct. Con questa aggiunta, i prompt stanno diventando sempre più complessi. Inoltre, anche prompt identici possono produrre risultati divergenti tra diversi LLM, come GPT-4 e Gemini, a causa delle variazioni nei loro metodi e dataset di pre-addestramento. Questa sfida ha stimolato un’impennata di interesse per il Prompt Engineering—un’attività laboriosa incentrata sulla modifica e ottimizzazione dei prompt per ottenere risultati migliori e personalizzati.
Sebbene la creazione manuale dei prompt funzioni bene per applicazioni LLM semplici, diventa frustrante e dispendiosa in termini di tempo per pipeline complesse basate su LLM che coinvolgono più componenti. DSPy rappresenta un cambio di paradigma nel modo in cui gli sviluppatori interagiscono con i modelli linguistici, introducendo un’interfaccia programmabile che consente l’ottimizzazione algoritmica dei prompt e dei pesi dei modelli, portando a uno sviluppo dei modelli linguistici più efficiente. DSPy ha integrato perfettamente il database vettoriale Milvus, automatizzando l’ottimizzazione delle applicazioni di Retrieval Augmented Generation (RAG) usando un approccio programmatico.
Nelle sezioni seguenti, esploreremo l’essenza di DSPy e i suoi meccanismi operativi, e forniremo un esempio pratico che dimostra come costruire e ottimizzare un’applicazione RAG utilizzando DSPy e il database vettoriale Milvus.
Che cos’è DSPy?
DSPy, introdotto dallo Stanford NLP Group, è un framework programmatico progettato per ottimizzare prompt e pesi nei modelli linguistici, particolarmente utile quando gli LLM sono integrati in più fasi di una pipeline. Fornisce vari moduli componibili e dichiarativi per istruire gli LLM con una sintassi Pythonica.
A differenza delle tecniche tradizionali di prompt engineering, che si basano sulla creazione e modifica manuale dei prompt, DSPy apprende esempi di domande-risposte e imita questo apprendimento per generare prompt ottimizzati per risultati più personalizzati. Questo approccio consente il riassemblaggio dinamico dell’intera pipeline, adattato esplicitamente alle sfumature del tuo compito, eliminando così la necessità di continui aggiustamenti manuali dei prompt.
Concetti chiave e componenti fondamentali
In DSPy, tre elementi fondamentali—Signatures, Modules e Optimizers (precedentemente indicati come Teleprompters)—costituiscono i blocchi costitutivi principali per l’ottimizzazione automatica dei prompt e il fine-tuning dei modelli.
Signatures
Le Signatures sono specifiche dichiarative che definiscono il comportamento di input/output di un modulo DSPy. Indicano al modello linguistico quali compiti deve eseguire, piuttosto che come dovremmo formulare il prompt per il modello linguistico.
- Una signature comprende tre elementi essenziali:
- Una descrizione concisa del sotto-compito che il modello linguistico mira a risolvere.
- Una descrizione di uno o più campi di input (ad es., domande di input) che forniamo al modello linguistico.
- Una descrizione di uno o più campi di output (ad es., risposte alle domande) che ci aspettiamo dal modello linguistico.
Ecco esempi di signature per attività LLM popolari:
- Question Answering:
"question -> answer" - Sentiment Classification:
"sentence -> sentiment" - Retrieval Augmented Question Answering:
"context, question -> answer" - Multiple-Choice Question Answering with Reasoning:
"question, choices -> reasoning, selection"
Queste signature guidano DSPy nell'orchestrare in modo efficiente le operazioni LLM all'interno di vari moduli, favorendo un'esecuzione dei compiti semplificata e precisa.
Moduli
I moduli DSPy astraggono le tecniche di prompting convenzionali all'interno di una pipeline LLM. Hanno tre caratteristiche chiave:
- Ogni modulo integrato astrae una specifica tecnica di prompting (come Chain of Thoughts o ReAct) e gestisce le DSPy Signatures.
- I moduli DSPy hanno parametri apprendibili, inclusi componenti dei prompt e pesi LLM, che consentono loro di elaborare input e generare output.
- I moduli DSPy possono essere combinati per creare moduli più grandi e complessi.
- DSPy fornisce sette moduli integrati per vari scopi, tra cui
dspy.ReAct,dspy.ChainofThought,dspy.Predict,dspy.ProgramOfThought,dspy.ReAct,dspy.MultiChainComparisonedspy.Retrieve.
Ottimizzatori
Gli ottimizzatori DSPy, precedentemente Teleprompters, sono algoritmi progettati per perfezionare i parametri di un programma DSPy—come prompt e pesi LLM—per massimizzare metriche specificate come l'accuratezza. Un tipico ottimizzatore DSPy richiede tre input:
Il tuo programma DSPy: può essere un singolo modulo (ad es.,
dspy.Predict) o un programma complesso multi-modulo.La metrica scelta: una funzione che valuta l'output del programma e gli assegna un punteggio (punteggi più alti indicano risultati migliori).
Un piccolo insieme di input di addestramento: spesso solo da 5 a 10 esempi.
Una volta definiti i dati di addestramento, i moduli e le metriche, l'ottimizzatore ottimizza i pesi LLM, le istruzioni dei prompt e le dimostrazioni few-shot per migliorare l'efficienza del programma. Ad esempio, l'ottimizzatore BootstrapFewShot genera risposte allineate alla metrica specificata, mentre moduli come COT (Chain of Thought) generano ragionamenti strutturati per arrivare a risposte accurate. DSPy registra queste istanze riuscite e le relative motivazioni come dimostrazioni few-shot per gestire future query di test.
Oltre ai blocchi fondamentali menzionati sopra, DSPy incorpora dati, metriche e asserzioni come componenti supplementari, arricchendone la funzionalità e l'adattabilità. Consulta la documentazione DSPy per maggiori dettagli.
Flussi di lavoro DSPy: costruire pipeline LLM efficienti
Come funziona esattamente DSPy nella costruzione di pipeline LLM? Per chiarezza, possiamo suddividere il processo in diversi passaggi chiave.
- Per prima cosa, devi definire il tuo compito e preparare alcuni input di esempio, spesso senza etichette (o solo con etichette per gli output finali, se la tua metrica le richiede).
- In secondo luogo, costruisci la tua pipeline scegliendo tra i moduli integrati, assegnando a ciascun modulo una signature (specifica di input/output) e incorporando perfettamente questi moduli nel tuo codice Python.
- In terzo luogo, definisci la logica di validazione per la tua pipeline, inclusi quali metriche e input di esempio usare per valutare la qualità dei prompt e dei risultati finali.
- In quarto luogo, compila il tuo codice usando un ottimizzatore DSPy, che genera istruzioni di alta qualità, esempi few-shot automatici o pesi LLM aggiornati.
- Infine, intraprendi un processo iterativo di affinamento del tuo dataset, programma o logica di validazione per raggiungere il livello di prestazioni desiderato per la tua pipeline. Valuta e migliora continuamente per soddisfare requisiti in evoluzione e ottimizzare i risultati.
DSPy vs. LlamaIndex/LangChain/AutoGPT
DSPy si distingue per il suo approccio rispetto a molti altri framework AI popolari come LangChain, LlamaIndex e AutoGPT. Ecco uno sguardo più approfondito a come differiscono e si allineano:
LangChain è un toolkit per creare applicazioni personalizzate. Sfrutta vari modelli linguistici e pacchetti di utilità, consentendo agli sviluppatori di adattare le applicazioni a esigenze specifiche.
LlamaIndex è un framework di orchestrazione progettato per semplificare l'integrazione di varie fonti di dati private con modelli linguistici. Semplifica le attività di gestione ed elaborazione dei dati.
AutoGPT è un agente AI avanzato basato su GPT-4 e GPT3.5. È programmato per prendere decisioni e compiere azioni in base a regole e obiettivi predefiniti. Enfatizza l'autonomia e le capacità decisionali.
Caratteristiche distintive di DSPy:
- DSPy ottimizza e automatizza la costruzione dei prompt per un'interazione migliorata con i modelli linguistici.
- A differenza di LangChain e LlamaIndex, che si rivolgono allo sviluppo di applicazioni di alto livello con moduli predefiniti, DSPy fornisce moduli potenti e general-purpose capaci di imparare a creare prompt o a effettuare il fine-tuning degli LLM all'interno di pipeline personalizzate. La forza di DSPy risiede nella sua capacità di adattare dinamicamente i prompt ed effettuare il fine-tuning degli LLM in base ai cambiamenti dei dati, agli aggiustamenti del flusso di controllo del programma o alle variazioni della lingua di destinazione. Questo processo di ottimizzazione automatizzato può portare a output di qualità superiore con uno sforzo minimo, soprattutto quando gli sviluppatori sono aperti a scalare i loro programmi semplici o di prototipazione in soluzioni più sofisticate per scopi di produzione.
- DSPy è ideale per casi d'uso che richiedono un modello di programmazione leggero ma a ottimizzazione automatica, anziché affidarsi a prompt e integrazioni predefiniti offerti da librerie come LangChain e LlamaIndex.
Integrazione di DSPy con il database vettoriale Milvus
Milvus è un database vettoriale open-source, cloud-native, altamente flessibile, affidabile e rapidissimo. Alimenta la ricerca di similarità vettoriale ed è particolarmente vantaggioso nella creazione di varie applicazioni GenAI e Retrieval Augmented Generation (RAG).
Milvus è stato integrato nel workflow DSPy come modulo di retrieval sotto forma di client MilvusRM, rendendo più semplice implementare una pipeline RAG rapida ed efficiente.
Creazione di un'applicazione RAG con DSPy e Milvus
La Retrieval Augmented Generation (RAG) è un metodo che consente agli LLM di accedere a repository di conoscenza esterni, cercare in questi repository informazioni contestuali rilevanti per le query degli utenti e generare risposte perfezionate.
In questa dimostrazione, creeremo una semplice applicazione RAG utilizzando GPT-3.5 (gpt-3.5-turbo) per la generazione delle risposte. Usiamo Milvus come vector store tramite MilvusRM e DSPy per configurare e ottimizzare la pipeline RAG.
Prerequisiti
Prima di creare l'app RAG, installa il client MilvusRM e Milvus.
- Per installare MilvusRM, esegui il codice seguente.
pip install dspy-ai[milvus]
- Installa Milvus. Consulta la documentazione di Milvus per istruzioni dettagliate.
Caricamento del dataset
In questo esempio, usiamo HotPotQA, una raccolta di coppie domanda-risposta complesse, come nostro dataset di training. Possiamo caricarle tramite la classe HotPotQA.
from dspy.datasets import HotPotQA
# Load the dataset.
dataset = HotPotQA(train_seed=1, train_size=20, eval_seed=2023, dev_size=50, test_size=0)
# Tell DSPy that the 'question' field is the input. Any other fields are labels and/or metadata.
trainset = [x.with_inputs('question') for x in dataset.train]
devset = [x.with_inputs('question') for x in dataset.dev]
Importare i dati nel database vettoriale Milvus
Inserisci le informazioni di contesto nella collection Milvus per il recupero vettoriale. Questa collection dovrebbe avere un campo embedding e un campo text. In questo caso utilizziamo il modello text-embedding-3-small di OpenAI come funzione predefinita di embedding delle query.
import requests
MILVUS_URI = "http://localhost:19530"
MILVUS_TOKEN = ""
from pymilvus import MilvusClient, DataType, Collection
from dspy.retrieve.milvus_rm import openai_embedding_function
client = MilvusClient(
uri=MILVUS_URI,
token=MILVUS_TOKEN
)
if 'dspy_example' not in client.list_collections():
client.create_collection(
collection_name="dspy_example",
overwrite= True,
dimension=1536,
primary_field_name="id",
vector_field_name="embedding",
id_type="int",
metric_type="IP",
max_length=65535,
enable_dynamic=True
)
text = requests.get('https://raw.githubusercontent.com/wxywb/dspy_dataset_sample/master/sample_data.txt').text
for idx, passage in enumerate(text.split('\n')):
if len(passage) == 0:
continue
client.insert(collection_name="dspy_example", data = [{"id": idx , "embedding": openai_embedding_function(passage)[0], "text": passage}])
Definire MilvusRM.
Ora devi definire MilvusRM.
from dspy.retrieve.milvus_rm import MilvusRM
import os
import dspy
os.environ["OPENAI_API_KEY"] = "<YOUR_OPENAI_API_KEY>"
retriever_model = MilvusRM(
collection_name="dspy_example",
uri=MILVUS_URI,
token=MILVUS_TOKEN, # ignore this if no token is required for Milvus connection
embedding_function = openai_embedding_function
)
turbo = dspy.OpenAI(model='gpt-3.5-turbo')
dspy.settings.configure(lm=turbo)
Creazione delle signature
Ora che abbiamo caricato i dati, iniziamo a definire le signature per i sotto-task della nostra pipeline. Possiamo identificare il nostro semplice input question e output answer, ma poiché stiamo creando una pipeline RAG, recupereremo informazioni contestuali da Milvus. Quindi definiamo la nostra signature come context, question --> answer.
class GenerateAnswer(dspy.Signature):
"""Answer questions with short factoid answers."""
context = dspy.InputField(desc="may contain relevant facts")
question = dspy.InputField()
answer = dspy.OutputField(desc="often between 1 and 5 words")
Includiamo brevi descrizioni per i campi context e answer per definire linee guida più chiare su ciò che il modello riceverà e dovrebbe generare.
Creazione della pipeline
Ora definiamo la pipeline RAG.
class RAG(dspy.Module):
def __init__(self, rm):
super().__init__()
self.retrieve = rm
# This signature indicates the task imposed on the COT module.
self.generate_answer = dspy.ChainOfThought(GenerateAnswer)
def forward(self, question):
# Use milvus_rm to retrieve context for the question.
context = self.retrieve(question).passages
# COT module takes "context, query" and output "answer".
prediction = self.generate_answer(context=context, question=question)
return dspy.Prediction(context=[item.long_text for item in context], answer=prediction.answer)
Esecuzione della pipeline e ottenimento dei risultati
Ora abbiamo costruito questa pipeline RAG. Proviamola e otteniamo i risultati.
rag = RAG(retriever_model)
print(rag("who write At My Window").answer)
# The result:
# 'Townes Van Zandt'
Possiamo valutare i risultati quantitativi sul dataset.
from dspy.evaluate.evaluate import Evaluate
from dspy.datasets import HotPotQA
evaluate_on_hotpotqa = Evaluate(devset=devset, num_threads=1, display_progress=False, display_table=5)
metric = dspy.evaluate.answer_exact_match
score = evaluate_on_hotpotqa(rag, metric=metric)
print('rag:', score)
# The result:
# rag: 50.0
Ottimizzazione della pipeline
Dopo aver definito questo programma, il passo successivo è la compilazione. Questo processo aggiorna i parametri all’interno di ciascun modulo per migliorare le prestazioni. Il processo di compilazione dipende da tre fattori critici:
- Training Set: utilizzeremo i 20 esempi domanda-risposta del nostro dataset di training per questa dimostrazione.
- Metrica di validazione: stabiliremo una semplice metrica
validate_context_and_answer. Questa metrica verifica l’accuratezza della risposta prevista e garantisce che il contesto recuperato includa la risposta. - Ottimizzatore specifico (Teleprompter): il compilatore di DSPy incorpora diversi teleprompter progettati per ottimizzare efficacemente i tuoi programmi.
from dspy.teleprompt import BootstrapFewShot
# Validation logic: check that the predicted answer is correct.
# Also check that the retrieved context does contain that answer.
def validate_context_and_answer(example, pred, trace=None):
answer_EM = dspy.evaluate.answer_exact_match(example, pred)
answer_PM = dspy.evaluate.answer_passage_match(example, pred)
return answer_EM and answer_PM
# Set up a basic teleprompter, which will compile our RAG program.
teleprompter = BootstrapFewShot(metric=validate_context_and_answer)
# Compile!
compiled_rag = teleprompter.compile(rag, trainset=trainset)
# Now compiled_rag is optimized and ready to answer your new question!
Ora, valutiamo il programma RAG compilato.
score = evaluate_on_hotpotqa(compiled_rag, metric=metric)
print(score)
print('compile_rag:', score)
# The result:
# compile_rag: 52.0
Il punteggio di valutazione è aumentato dal valore precedente di 50.0 a 52.0, indicando un miglioramento nella qualità delle risposte.
Riepilogo
DSPy rappresenta un salto in avanti nelle interazioni con i modelli linguistici grazie alla sua interfaccia programmabile, che facilita l’ottimizzazione algoritmica e automatizzata dei prompt e dei pesi dei modelli. Sfruttando DSPy per l’implementazione di RAG, l’adattabilità a diversi modelli linguistici o dataset diventa estremamente semplice, riducendo drasticamente la necessità di noiosi interventi manuali.
Inoltre, DSPy introduce un modulo retriever MilvusRM nel suo workflow integrando il database vettoriale Milvus. Con questa nuova integrazione, gli sviluppatori possono automatizzare l’ottimizzazione dei prompt e le regolazioni dei parametri del modello all’interno delle applicazioni RAG per migliorare la qualità delle risposte. Se vuoi saperne di più, consulta la guida dettagliata a MilvusRM nella documentazione di DSPy!
Riferimenti
Pagina GitHub di DSPy: https://github.com/stanfordnlp/dspy
Paper di DSPy: https://arxiv.org/pdf/2310.03714
Documentazione di DSPy: https://dspy-docs.vercel.app/quick-start/installation/
Guida a MilvusRM: https://dspy-docs.vercel.app/docs/deep-dive/retrieval_models_clients/MilvusRM
Documentazione di Milvus: https://milvus.io/docs
Continua a leggere

Migrating Self-Managed Milvus to Zilliz Cloud for >99% Latency Reduction
Step-by-step guide to migrating 50M vectors from self-managed Milvus to Zilliz Cloud using milvus-backup. Achieve >99% query latency reduction with zero data loss.

AI Agents Are Quietly Transforming E-Commerce — Here’s How
Discover how AI agents transform e-commerce with autonomous decision-making, enhanced product discovery, and vector search capabilities for today's retailers.

Balancing Precision and Performance: How Zilliz Cloud's New Parameters Help You Optimize Vector Search
Optimize vector search with Zilliz Cloud’s level and recall features to tune accuracy, balance performance, and power AI applications.



