DSPy : Remplacer l'ingénierie de prompt fragile par des pipelines LLM compilés
Je me heurte sans cesse au même mur lorsque je réfléchis aux pipelines d'IA financière : on peut construire quelque chose qui fonctionne à merveille sur ses cas de test, puis le regarder s'effondrer silencieusement lorsqu'un fournisseur modifie le format de sa facture ou qu'un nouveau type de transaction apparaît. La fragilité réside presque toujours dans les prompts — des chaînes de caractères conçues manuellement que personne ne veut toucher. DSPy, introduit par Khattab et al. à Stanford et publié à l'ICLR 2024, propose une manière fondamentalement différente de construire des pipelines LLM qui mérite une attention particulière de la part de quiconque tente d'automatiser le travail comptable.
L'article
DSPy : Compiling Declarative Language Model Calls into Self-Improving Pipelines (Khattab, Singhvi, Maheshwari et al., ICLR 2024) recadre la construction de pipelines LLM comme un problème de programmation plutôt que comme un problème d'ingénierie de prompt. L'observation centrale est que les applications LLM modernes sont généralement construites comme des collections de chaînes de prompts codées en dur — ce que les auteurs appellent des « modèles de prompt » (prompt templates) — assemblées avec un flux de contrôle Python. Lorsque le modèle change, ou que la distribution des tâches évolue, quelqu'un doit réécrire ces chaînes manuellement.
DSPy remplace les modèles de prompt par deux abstractions : les signatures et les modules. Une signature est une spécification typée et déclarative de ce qu'un appel au modèle de langage (LM) doit faire, écrite de manière compacte comme question -> answer ou avec des descriptions de champs explicites dans une classe Python. Un module enveloppe une signature avec une stratégie de raisonnement — ChainOfThought, ReAct, ProgramOfThought, MultiChainComparison, et ainsi de suite. L'ajout critique est un compilateur (l'article l'appelle un teleprompter) qui prend un programme DSPy, un petit ensemble de données étiquetées et une métrique de validation, puis génère automatiquement des démonstrations par quelques exemples (few-shot), choisit parmi elles et produit des prompts optimisés pour cette métrique. Le compilateur n'a pas besoin d'étiquettes à chaque étape intermédiaire — il peut auto-générer des démonstrations en exécutant un programme « enseignant » sur des entrées non étiquetées et en filtrant les traces qui aboutissent à des résultats finaux corrects.
Idées clés
- Les signatures dissocient l'intention de l'implémentation. Écrire
question, context -> answersuffit à DSPy pour savoir comment construire, invoquer et optimiser l'appel LM sous-jacent. Le développeur n'écrit jamais de chaîne de prompt. - La compilation est un auto-amorçage piloté par des métriques. L'optimiseur
BootstrapFewShotexécute le programme sur les entrées d'entraînement, collecte les traces d'entrées-sorties où le pipeline réussit, et les utilise comme démonstrations — aucune annotation humaine des étapes de raisonnement intermédiaires n'est requise. - Le compilateur libère le potentiel des petits modèles. Sur GSM8K (problèmes mathématiques textuels), Llama2-13b standard obtient un score de 9,4 % avec un prompt zero-shot. Après une compilation DSPy avec des modules de réflexion et d'ensemble, il atteint 46,9 %. T5-Large (770 millions de paramètres), un modèle que la plupart des gens jugeaient inapte au raisonnement complexe, atteint 39,3 % de correspondance exacte des réponses sur HotPotQA en utilisant seulement 200 exemples étiquetés.
- Les prompts d'experts ne sont pas le plafond. Sur GSM8K, GPT-3.5 avec un prompt few-shot standard atteint 25,2 %. Une chaîne de pensée (chain-of-thought) conçue par un expert porte ce chiffre à environ 72–73 %. Le pipeline compilé de réflexion et d'ensemble de DSPy le pousse à 81,6 % — sans qu'aucun humain n'ait écrit de prompt.
- Les programmes sont composables. Un pipeline de questions-réponses par récupération multi-sauts dans DSPy fait environ 12 lignes de Python. L'équivalent dans LangChain, notent les auteurs, contient 50 chaînes dépassant les 1000 caractères de contenu de prompt conçu manuellement.
- Trois étapes de compilation. L'optimiseur fonctionne comme suit : génération de candidats (amorçage de traces), optimisation des paramètres (recherche aléatoire ou Optuna sur les hyperparamètres) et optimisation d'ordre supérieur (ensembles, flux de contrôle dynamique).
Ce qui tient la route — et ce qui ne tient pas
Les résultats empiriques sont réels et substantiels. Passer de 9,4 % à 46,9 % sur GSM8K avec Llama2-13b, en utilisant seulement une poignée d'exemples d'entraînement étiquetés, n'est pas incrémental — c'est le genre d'écart qui rend les petits modèles peu coûteux viables pour des tâches qui nécessitaient auparavant GPT-4. L'architecture est également véritablement élégante : les signatures sont faciles à lire, les modules sont composables et l'abstraction ne semble pas présenter de failles pour les tâches démontrées.
Les limites sont cependant réelles, bien que l'article ne les aborde pas dans une section dédiée. La plus importante : le compilateur n'est aussi performant que votre métrique. Si votre métrique de validation est imprécise ou mal alignée avec la qualité réelle de la tâche — ce qui est extrêmement courant en pratique — l'optimiseur trouvera des moyens astucieux de la maximiser tout en échouant sur ce qui vous importe réellement. Dans un domaine structuré comme la comptabilité, vous pourriez définir une métrique comme « l'écriture comptable est équilibrée », mais une écriture équilibrée peut toujours avoir des codes de compte complètement erronés. Les auteurs savent que ce problème existe, mais ils en laissent la responsabilité au développeur.
Une deuxième limite : la compilation nécessite toujours quelques données étiquetées. L'article affirme que l'on peut utiliser aussi peu que 10 exemples avec BootstrapFewShot, et que seules les valeurs d'entrée sont nécessaires (pas les étiquettes intermédiaires). C'est vrai dans le meilleur des cas, mais en pratique, la fiabilité de l'amorçage se dégrade lorsque le programme de départ ne peut résoudre aucun exemple d'entraînement. Si votre pipeline d'agent financier a une précision de base proche de zéro — comme c'est souvent le cas lors de la construction de quelque chose de nouveau — la compilation peut tourner à vide.
Troisièmement, et de manière plus subtile : DSPy optimise les prompts et les démonstrations, mais il n'optimise pas la structure du programme elle-même. Si vous avez relié les modules d'une manière fondamentalement erronée pour la tâche, le compilateur ne vous aidera pas. La conception du programme incombe toujours au développeur.
Pourquoi c'est important pour l'IA financière
Le problème de la fragilité des prompts est peut-être le plus grand obstacle pratique au déploiement d'agents d'IA financière en production. Un pipeline qui catégorise les transactions en faisant correspondre les descriptions aux codes de compte se dégradera chaque fois que les données du commerçant changeront de format, chaque fois qu'une nouvelle catégorie de dépenses apparaîtra ou chaque fois que le plan comptable sera mis à jour. Avec DSPy, vous définissez la tâche de manière abstraite (transaction_description, chart_of_accounts -> account_code, confidence) et laissez le compilateur trouver les démonstrations optimales chaque fois que la distribution change.
Pour Beancount spécifiquement, je peux imaginer un pipeline structuré en trois modules DSPy enchaînés : un qui extrait des données de transaction structurées à partir des exports bancaires bruts, un qui recherche le compte correspondant le mieux dans le plan comptable existant du grand livre, et un qui valide l'écriture comptable résultante par rapport aux contraintes de la comptabilité en partie double. Chaque module reçoit sa propre signature ; l'ensemble du programme est compilé par rapport à une métrique qui vérifie à la fois l'exactitude comptable et la conformité du format. Le problème de la qualité des métriques est ici le plus épineux — il faut une métrique qui détecte les mauvais codes de compte, pas seulement les écritures déséquilibrées — mais c'est un problème d'ingénierie soluble.
L'implication profonde : DSPy déplace le travail de « l'écriture de meilleurs prompts » vers « l'écriture de meilleures métriques et la collecte de petits ensembles de données étiquetées ». C'est une pratique d'ingénierie beaucoup plus durable pour un système financier en production qui doit évoluer au rythme des réglementations, des structures de plans comptables et des formats de transactions.
Ce qu'il faut lire ensuite
- OPRO: Large Language Models as Optimizers (Yang et al., arXiv:2309.03409) — l'approche de Google DeepMind pour l'optimisation des prompts via un raffinement itératif généré par le modèle ; un contrepoint utile à l'approche d'amorçage de DSPy.
- TextGrad: Automatic "Differentiation" via Text (Yuksekgonul et al., arXiv:2406.07496) — cadre l'optimisation comme une rétropropagation via des retours textuels plutôt que comme un amorçage piloté par des métriques ; montre des résultats solides sur les tâches de codage et scientifiques là où l'approche de DSPy est plus faible.
- DSPy Assertions: Computational Constraints for Self-Refining Language Model Pipelines (Singhvi et al., arXiv:2312.13382) — ajoute des contraintes strictes et souples aux programmes DSPy, permettant aux pipelines de s'autocorriger lorsque les sorties violent les règles du domaine ; directement pertinent pour faire respecter les invariants comptables.
