DSPy : programmer ses prompts au lieu de les écrire
Tous ceux qui construisent des pipelines LLM finissent par buter sur le même mur : les prompts. On les écrit à la main, on les copie-colle, on les bricole jusqu’à ce que « ça marche à peu près », puis le modèle change, ou la tâche dérive et tout est à refaire. DSPy, projet open source issu de l’université de Stanford (Apache 2.0), propose un changement de paradigme : traiter les prompts comme du code, avec des signatures typées, des modules composables, et un compilateur qui optimise automatiquement les prompts et les exemples few-shot pour atteindre une métrique cible.
Dans une stack IA souveraine (Ollama, vLLM, LiteLLM, LangGraph, pgvector…), DSPy vient combler une brique absente du site jusqu’ici : la discipline d’ingénierie des prompts, sur le même modèle que scikit-learn a discipliné le machine learning classique.
Le principe : séparer la logique et le prompt
Au lieu d’écrire quelque chose comme « Tu es un assistant expert qui… réponds de manière concise… au format JSON… voici un exemple… », on déclare une signature :
import dspy
class ClassifierUnRapport(dspy.Signature):
"""Classe un rapport technique dans une des catégories métier."""
texte: str = dspy.InputField(desc="Contenu brut du rapport")
categorie: str = dspy.OutputField(desc="Catégorie parmi : infrastructure, data, IA, gouvernance")
justification: str = dspy.OutputField(desc="Une phrase expliquant le choix")
classifier = dspy.ChainOfThought(ClassifierUnRapport)
resultat = classifier(texte=mon_rapport)
DSPy génère lui-même le prompt en fonction du modèle utilisé, insère les champs, force le format, et intègre automatiquement une chaîne de raisonnement (ChainOfThought) ou d’autres stratégies (ReAct, ProgramOfThought, MultiChainComparison…). On décrit quoi faire, DSPy s’occupe du comment.
L’optimiseur : le vrai saut qualitatif
Là où DSPy devient intéressant, c’est son compilateur. À partir de quelques dizaines d’exemples d’entraînement et d’une fonction de score (précision, F1, BLEU, ou une métrique maison, même un autre LLM qui juge), DSPy optimise :
- les instructions du prompt (MIPROv2, COPRO) ;
- les exemples few-shot sélectionnés dans le jeu d’entraînement (BootstrapFewShot) ;
- et même, sur un petit modèle, les poids par fine-tuning (BootstrapFinetune).
from dspy.teleprompt import MIPROv2
def score(exemple, prediction, trace=None):
return exemple.categorie == prediction.categorie
optimiseur = MIPROv2(metric=score, auto="medium")
classifier_optimise = optimiseur.compile(
classifier,
trainset=exemples_train,
valset=exemples_val,
)
classifier_optimise.save("classifier_v1.json")
Résultat concret : sur des tâches réelles (classification, QA, extraction), l’optimisation automatique gagne typiquement 5 à 20 points de métrique par rapport à un prompt écrit à la main, et surtout rend la démarche reproductible et versionnable.
Intégration dans une stack open source
DSPy est agnostique du modèle. Via dspy.LM, il s’appuie sur LiteLLM pour cibler indifféremment un Ollama local, un vLLM auto-hébergé, ou un fournisseur cloud :
# Ollama local
dspy.configure(lm=dspy.LM("ollama_chat/qwen2.5:14b", api_base="http://localhost:11434"))
# vLLM via LiteLLM
dspy.configure(lm=dspy.LM("hosted_vllm/Qwen2.5-32B-Instruct", api_base="http://vllm:8000/v1"))
Le couplage naturel avec les briques déjà couvertes sur askem.eu :
- LangGraph pour l’orchestration d’agents : DSPy fournit les étapes « intelligentes » compilées, LangGraph gère la machine d’état.
- Outlines pour le forçage de format : DSPy structure les sorties via ses signatures, Outlines garantit la validité JSON au niveau du décodage.
- pgvector / Qdrant pour le retrieval : DSPy a un
Retrievede série qui se branche sur n’importe quel backend. - Langfuse pour l’observabilité : la trace complète des appels DSPy est compatible OpenTelemetry.
- RAGAS ou un juge LLM comme métrique dans l’optimiseur : boucler évaluation et compilation.
Quand DSPy apporte et quand il est de trop
DSPy brille dans des situations précises :
- pipelines LLM récurrents et critiques, qui doivent rester stables quand le modèle change ;
- tâches où l’on dispose d’une métrique automatisable (classification, extraction, QA avec vérité terrain, tri de tickets…) ;
- portage d’un prototype bricolé vers quelque chose de production-grade ;
- comparaison objective de plusieurs modèles open source sur la même tâche.
À l’inverse, DSPy apporte peu pour des prompts ponctuels, conversationnels ou créatifs sans métrique claire. Il n’est pas un remplacement d’un framework d’agent comme LangGraph : il complète, il n’orchestre pas.
Points de vigilance
- Coût d’optimisation : la compilation consomme des appels LLM. Prévoir un budget ou compiler contre un modèle local avant d’aller en production.
- Taille du jeu de validation : en dessous de 50 exemples fiables, l’optimiseur sur-apprend. Investir dans un jeu de tests propre est un prérequis, pas un luxe.
- Versionner l’optimisé :
compiled.save("v1.json")produit un artefact reproductible à commiter comme un modèle ML classique. - Observabilité : activer le tracing Langfuse dès le début : un pipeline DSPy en production reste une boîte à prompts qu’on a intérêt à inspecter.
Pour commencer
pip install dspy
Les tutorials officiels couvrent un pipeline RAG complet, la classification et l’extraction structurée — tous reproductibles en local avec Ollama en moins d’une heure.
Ressources
- dspy.ai — site officiel, documentation et tutoriels.
- stanfordnlp/dspy — dépôt GitHub (Apache 2.0).
- Publication fondatrice : DSPy: Compiling Declarative Language Model Calls into Self-Improving Pipelines, Khattab et al., ICLR 2024.
À retenir : DSPy ne remplace pas un bon prompt, il remplace la culture artisanale du prompt par une culture d’ingénierie. Dans une stack IA souveraine, c’est la brique qui transforme la question « est-ce que ça marche ? » en « est-ce que c’est au-dessus du seuil ? ».
