Explorer DSPy et son intégration avec Milvus pour concevoir des pipelines RAG hautement efficaces
Introduction
Les grands modèles de langage (LLMs) possèdent des capacités génératives transformatrices et sont augmentés par des outils tels que des bases de connaissances et des récupérateurs, alimentant des applications GenAI avancées comme les chatbots et les agents. Au cœur de l’interaction avec les LLMs se trouvent les prompts, qui sont des instructions guidant ces modèles pour effectuer des tâches spécifiques. Cependant, concevoir des prompts efficaces est un processus nuancé et complexe, nécessitant souvent des techniques sophistiquées telles que Chain-of-Thought et ReAct. Avec cet ajout, les prompts deviennent de plus en plus complexes. De plus, même des prompts identiques peuvent produire des résultats divergents selon les différents LLMs, tels que GPT-4 et Gemini, en raison de variations dans leurs méthodes de pré-entraînement et leurs jeux de données. Ce défi a suscité un regain d’intérêt pour le Prompt Engineering — une tâche exigeante en main-d’œuvre axée sur l’ajustement et l’optimisation des prompts afin d’obtenir des résultats meilleurs et personnalisés.
Si la conception manuelle de prompts fonctionne bien pour des applications LLM simples, elle devient frustrante et chronophage pour des pipelines complexes basés sur les LLM impliquant plusieurs composants. DSPy représente un changement de paradigme dans la manière dont les développeurs interagissent avec les modèles de langage en introduisant une interface programmable permettant l’optimisation algorithmique des prompts et des poids des modèles, conduisant à un développement plus efficace des modèles de langage. DSPy a intégré de manière transparente la base de données vectorielle Milvus, automatisant l’optimisation des applications de génération augmentée par récupération (RAG) grâce à une approche programmatique.
Dans les sections suivantes, nous explorerons l’essence de DSPy et ses mécanismes opérationnels, et fournirons un exemple pratique démontrant comment construire et optimiser une application RAG à l’aide de DSPy et de la base de données vectorielle Milvus.
Qu’est-ce que DSPy ?
DSPy, introduit par le Stanford NLP Group, est un framework programmatique conçu pour optimiser les prompts et les poids dans les modèles de langage, ce qui est particulièrement utile lorsque les LLMs sont intégrés à plusieurs étapes d’un pipeline. Il fournit divers modules composables et déclaratifs pour instruire les LLMs avec une syntaxe Pythonique.
Contrairement aux techniques traditionnelles de prompt engineering qui reposent sur la conception et l’ajustement manuels des prompts, DSPy apprend à partir d’exemples de questions-réponses et imite cet apprentissage pour générer des prompts optimisés en vue de résultats plus personnalisés. Cette approche permet le réassemblage dynamique de l’ensemble du pipeline, explicitement adapté aux nuances de votre tâche, éliminant ainsi la nécessité d’ajustements manuels continus des prompts.
Concepts clés et composants fondamentaux
Dans DSPy, trois éléments fondamentaux — Signatures, Modules et Optimizers (anciennement appelés Teleprompters) — constituent les blocs de construction principaux pour l’optimisation automatisée des prompts et l’affinage des modèles.
Signatures
Les Signatures sont des spécifications déclaratives définissant le comportement d’entrée/sortie d’un module DSPy. Elles indiquent au modèle de langage quelles tâches il doit exécuter plutôt que la manière dont nous devons formuler le prompt pour le modèle de langage.
- Une signature comprend trois éléments essentiels :
- Une description concise de la sous-tâche que le modèle de langage vise à résoudre.
- Une description d’un ou plusieurs champs d’entrée (par ex., des questions d’entrée) que nous fournissons au modèle de langage.
- Une description d’un ou plusieurs champs de sortie (par ex., des réponses aux questions) que nous attendons du modèle de langage.
Voici des exemples de signatures pour des tâches LLM populaires :
- Réponse aux questions :
"question -> answer" - Classification de sentiment :
"sentence -> sentiment" - Réponse aux questions augmentée par récupération :
"context, question -> answer" - Réponse aux questions à choix multiples avec raisonnement :
"question, choices -> reasoning, selection"
Ces signatures guident DSPy dans l’orchestration efficace des opérations LLM au sein de divers modules, favorisant une exécution des tâches rationalisée et précise.
Modules
Les modules DSPy abstraient les techniques de prompting conventionnelles au sein d’un pipeline LLM. Ils présentent trois caractéristiques clés :
- Chaque module intégré abstrait une technique de prompting spécifique (telle que Chain of Thoughts ou ReAct) et gère les signatures DSPy.
- Les modules DSPy disposent de paramètres apprenables, notamment des composants de prompt et des poids LLM, leur permettant de traiter les entrées et de générer des sorties.
- Les modules DSPy peuvent être combinés pour créer des modules plus grands et plus complexes.
- DSPy fournit sept modules intégrés à diverses fins, notamment
dspy.ReAct,dspy.ChainofThought,dspy.Predict,dspy.ProgramOfThought,dspy.ReAct,dspy.MultiChainComparisonetdspy.Retrieve.
Optimiseurs
Les optimiseurs DSPy, anciennement Teleprompters, sont des algorithmes conçus pour affiner les paramètres d’un programme DSPy — tels que les prompts et les poids LLM — afin de maximiser des métriques spécifiées comme l’exactitude. Un optimiseur DSPy typique nécessite trois entrées :
Votre programme DSPy : peut être un module unique (par ex.,
dspy.Predict) ou un programme complexe à plusieurs modules.La métrique choisie : une fonction qui évalue la sortie du programme et lui attribue un score (des scores plus élevés indiquent de meilleurs résultats).
Un petit ensemble d’entrées d’entraînement : souvent seulement 5 à 10 exemples.
Une fois que vous avez défini vos données d’entraînement, vos modules et vos métriques, l’optimiseur optimise les poids LLM, les instructions de prompt et les démonstrations few-shot afin d’améliorer l’efficacité du programme. Par exemple, l’optimiseur BootstrapFewShot génère des réponses qui s’alignent sur la métrique spécifiée, tandis que des modules comme COT (Chain of Thought) génèrent un raisonnement structuré pour parvenir à des réponses exactes. DSPy enregistre ces instances réussies et leurs justifications comme démonstrations few-shot pour traiter les futures requêtes de test.
En plus des blocs de construction de base mentionnés ci-dessus, DSPy intègre des données, des métriques et des assertions comme composants supplémentaires, enrichissant sa fonctionnalité et son adaptabilité. Consultez la documentation DSPy pour plus de détails.
Workflows DSPy : création de pipelines LLM efficaces
Comment DSPy fonctionne-t-il exactement dans la construction de pipelines LLM ? Par souci de clarté, nous pouvons décomposer le processus en plusieurs étapes clés.
- Premièrement, vous devez définir votre tâche et préparer quelques exemples d’entrées — souvent sans étiquettes (ou uniquement avec des étiquettes pour les sorties finales, si votre métrique les exige).
- Deuxièmement, construisez votre pipeline en choisissant parmi les modules intégrés, en attribuant à chaque module une signature (spécification d’entrée/sortie) et en incorporant harmonieusement ces modules dans votre code Python.
- Troisièmement, définissez la logique de validation de votre pipeline, notamment les métriques et les exemples d’entrées à utiliser pour évaluer la qualité des prompts et des résultats finaux.
- Quatrièmement, compilez votre code à l’aide d’un optimiseur DSPy, qui génère des instructions de haute qualité, des exemples few-shot automatiques ou des poids LLM mis à jour.
- Enfin, engagez-vous dans un processus itératif d’affinement de votre jeu de données, de votre programme ou de votre logique de validation afin d’atteindre le niveau de performance souhaité pour votre pipeline. Évaluez et améliorez continuellement afin de répondre aux exigences évolutives et d’optimiser les résultats.
DSPy vs. LlamaIndex/LangChain/AutoGPT
DSPy se distingue par son approche par rapport à de nombreux autres frameworks d’IA populaires comme LangChain, LlamaIndex et AutoGPT. Voici un aperçu plus détaillé de leurs différences et de leurs points communs :
LangChain est une boîte à outils pour créer des applications personnalisées. Elle exploite divers modèles de langage et packages utilitaires, permettant aux développeurs d’adapter les applications à des besoins spécifiques.
LlamaIndex est un framework d’orchestration conçu pour rationaliser l’intégration de diverses sources de données privées avec des modèles de langage. Il simplifie les tâches de gestion et de traitement des données.
AutoGPT est un agent d’IA avancé alimenté par GPT-4 et GPT3.5. Il est programmé pour prendre des décisions et effectuer des actions sur la base de règles et d’objectifs prédéfinis. Il met l’accent sur l’autonomie et les capacités de prise de décision.
Les caractéristiques distinctives de DSPy :
- DSPy optimise et automatise la construction de prompts pour améliorer l’interaction avec les modèles de langage.
- Contrairement à LangChain et LlamaIndex, qui s’adressent au développement d’applications de haut niveau avec des modules préconstruits, DSPy fournit des modules puissants et polyvalents capables d’apprendre à créer des prompts ou à affiner des LLM au sein de pipelines personnalisés. La force de DSPy réside dans sa capacité à adapter dynamiquement les prompts et à affiner les LLM en fonction de l’évolution des données, des ajustements du flux de contrôle du programme ou des variations de la langue cible. Ce processus d’optimisation automatisé peut aboutir à des sorties de qualité supérieure avec un effort minimal, en particulier lorsque les développeurs sont ouverts à l’idée de faire évoluer leurs programmes simples ou de prototypage vers des programmes plus sophistiqués à des fins de production.
- DSPy est idéal pour les cas d’utilisation nécessitant un modèle de programmation léger mais automatiquement optimisé, plutôt que de s’appuyer sur des prompts et des intégrations prédéfinis proposés par des bibliothèques comme LangChain et LlamaIndex.
Intégration de DSPy avec la base de données vectorielle Milvus
Milvus est une base de données vectorielle cloud-native, open-source, extrêmement flexible, fiable et ultra-rapide. Elle alimente la recherche de similarité vectorielle et s’avère particulièrement bénéfique pour créer diverses applications GenAI et de génération augmentée par récupération (RAG).
Milvus a été intégré au workflow DSPy en tant que module de récupération sous la forme du client MilvusRM, ce qui facilite la mise en œuvre d’un pipeline RAG rapide et efficace.
Construire une application RAG avec DSPy et Milvus
La génération augmentée par récupération (RAG) est une méthode qui permet aux LLM d’accéder à des référentiels de connaissances externes, de rechercher dans ces référentiels des informations contextuelles pertinentes pour les requêtes des utilisateurs, et de générer des réponses affinées.
Dans cette démonstration, nous allons créer une application RAG simple utilisant GPT-3.5 (gpt-3.5-turbo) pour la génération de réponses. Nous utilisons Milvus comme magasin vectoriel via MilvusRM et DSPy pour configurer et optimiser le pipeline RAG.
Prérequis
Avant de créer l’application RAG, installez le client MilvusRM et Milvus.
- Pour installer MilvusRM, exécutez le code suivant.
pip install dspy-ai[milvus]
- Installez Milvus. Consultez la documentation Milvus pour obtenir des instructions détaillées.
Chargement du jeu de données
Dans cet exemple, nous utilisons HotPotQA, une collection de paires question-réponse complexes, comme jeu de données d’entraînement. Nous pouvons les charger via 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]
Importation des données dans la base de données vectorielle Milvus
Ingérez les informations de contexte dans la collection Milvus pour la récupération vectorielle. Cette collection doit comporter un champ embedding et un champ text. Nous utilisons le modèle text-embedding-3-small d’OpenAI comme fonction d’embedding de requête par défaut dans ce cas.
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}])
Définir MilvusRM.
Vous devez maintenant définir 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)
Création des signatures
Maintenant que nous avons chargé les données, commençons à définir les signatures pour les sous-tâches de notre pipeline. Nous pouvons identifier notre entrée simple question et notre sortie answer, mais comme nous construisons un pipeline RAG, nous récupérerons des informations contextuelles depuis Milvus. Définissons donc notre signature comme 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")
Nous incluons de brèves descriptions pour les champs context et answer afin de définir des consignes plus claires sur ce que le modèle recevra et devra générer.
Création du pipeline
Maintenant, définissons le 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)
Exécution du pipeline et obtention des résultats
Maintenant, nous avons construit ce pipeline RAG. Essayons-le et obtenons des résultats.
rag = RAG(retriever_model)
print(rag("who write At My Window").answer)
# The result:
# 'Townes Van Zandt'
Nous pouvons évaluer les résultats quantitatifs sur le jeu de données.
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
Optimisation du pipeline
Après avoir défini ce programme, l’étape suivante est la compilation. Ce processus met à jour les paramètres au sein de chaque module afin d’améliorer les performances. Le processus de compilation dépend de trois facteurs essentiels :
- Ensemble d’entraînement : Nous utiliserons les 20 exemples de questions-réponses de notre jeu de données d’entraînement pour cette démonstration.
- Métrique de validation : Nous établirons une métrique simple
validate_context_and_answer. Cette métrique vérifie l’exactitude de la réponse prédite et s’assure que le contexte récupéré inclut la réponse. - Optimiseur spécifique (Teleprompter) : Le compilateur de DSPy intègre plusieurs teleprompters conçus pour optimiser efficacement vos programmes.
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!
Maintenant, évaluons le programme RAG compilé.
score = evaluate_on_hotpotqa(compiled_rag, metric=metric)
print(score)
print('compile_rag:', score)
# The result:
# compile_rag: 52.0
Le score d’évaluation est passé de sa valeur précédente de 50,0 à 52,0, indiquant une amélioration de la qualité des réponses.
Résumé
DSPy marque une avancée dans les interactions avec les modèles de langage grâce à son interface programmable, qui facilite l’optimisation algorithmique et automatisée des prompts et des poids des modèles. En tirant parti de DSPy pour l’implémentation de RAG, l’adaptabilité à différents modèles de langage ou jeux de données devient un jeu d’enfant, réduisant considérablement le besoin d’interventions manuelles fastidieuses.
De plus, DSPy introduit un module de récupération MilvusRM dans son flux de travail en intégrant la base de données vectorielle Milvus. Grâce à cette nouvelle intégration, les développeurs peuvent automatiser l’optimisation des prompts et les ajustements des paramètres du modèle dans les applications RAG afin d’améliorer la qualité des réponses. Si vous souhaitez en savoir plus, consultez le guide détaillé de MilvusRM dans la documentation DSPy !
Références
Page GitHub de DSPy : https://github.com/stanfordnlp/dspy
Article DSPy : https://arxiv.org/pdf/2310.03714
Documentation DSPy : https://dspy-docs.vercel.app/quick-start/installation/
Guide MilvusRM : https://dspy-docs.vercel.app/docs/deep-dive/retrieval_models_clients/MilvusRM
Documentation Milvus : https://milvus.io/docs
Continuer à lire

We spent 8 years making vector databases faster. Then we stopped.
Rarely queried embeddings still need to stay searchable. See how Vector Lakebase enables on-demand vector search without always-on compute costs.

Zilliz Cloud Now Available in AWS Europe (Ireland)
Zilliz Cloud launches in AWS eu-west-1 (Ireland) — bringing low-latency vector search, EU data residency, and full GDPR-ready infrastructure to European AI teams. Now live across 30 regions on five cloud providers.

Legal Document Analysis: Harnessing Zilliz Cloud's Semantic Search and RAG for Legal Insights
Enhance legal document analysis with Zilliz Cloud’s Semantic Search and RAG. Improve accuracy, efficiency, and scalability for contracts, case law, and compliance.



