Chaîner des prompts : guide pratique (LangChain et alternatives)
Chaîner des prompts avec LangChain (LCEL) : composer avec le pipe, passer la sortie d'un appel dans le suivant, et quand une fonction Python suffit.
Chaîner des prompts, c'est utiliser la sortie d'un appel à un modèle comme entrée du suivant, pour découper une tâche complexe en étapes fiables plutôt que de tout demander en un seul prompt géant. C'est la base de la plupart des workflows LLM sérieux, et LangChain en a fait sa primitive centrale avec LCEL (LangChain Expression Language). Ce guide montre comment enchaîner des prompts avec LangChain, comment faire passer la sortie d'un appel dans le suivant, et quand une simple fonction Python suffit largement.
Pourquoi chaîner plutôt que tout mettre dans un prompt
Un prompt unique qui doit « résumer, traduire, puis extraire les entités » demande au modèle de tout tenir en tête et de tout réussir d'un coup. Le découper en étapes donne trois avantages : chaque étape est plus simple donc plus fiable, vous pouvez inspecter la sortie intermédiaire quand quelque chose cloche, et vous pouvez insérer du code (validation, appel d'API, filtrage) entre deux appels. Le prix à payer : plus d'appels au modèle, donc plus de latence et de coût. On chaîne quand la fiabilité compte plus que la vitesse.
Chaîner avec LangChain (LCEL)
Dans le LangChain moderne, on assemble un prompt, un modèle et un parseur de sortie avec l'opérateur pipe |, puis on appelle .invoke() :
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
prompt = ChatPromptTemplate.from_template("Résume en une phrase : {texte}")
model = ChatOpenAI(model="gpt-4o-mini")
parser = StrOutputParser()
chaine = prompt | model | parser
resume = chaine.invoke({"texte": mon_texte})
Le | compose une séquence exécutable (RunnableSequence) : la sortie de chaque maillon devient l'entrée du suivant. StrOutputParser transforme le message du modèle en simple chaîne de caractères.
Faire passer la sortie d'un prompt dans le suivant
C'est le cœur du chaînage. Pour que le résultat d'un premier appel alimente une variable d'un second prompt, on injecte le premier maillon comme valeur d'entrée du second :
resume = prompt | model | StrOutputParser()
prompt_langue = ChatPromptTemplate.from_template("Traduis en anglais : {texte}")
chaine = {"texte": resume} | prompt_langue | model | StrOutputParser()
traduction = chaine.invoke({"texte": mon_texte})
Ici, {"texte": resume} exécute d'abord la chaîne de résumé, place son résultat dans la clé texte, puis la passe au second prompt. L'entrée initiale (mon_texte) traverse toute la séquence. Ce motif — un dictionnaire dont une valeur est une sous-chaîne — est la façon idiomatique de brancher une étape sur la suivante.
L'alternative : du Python simple
LangChain apporte de la structure, mais chaîner deux prompts ne l'exige pas. Avec le SDK d'un fournisseur, c'est deux appels et une variable :
resume = client.responses.create(
model="gpt-4o-mini",
input=f"Résume en une phrase : {mon_texte}",
).output_text
traduction = client.responses.create(
model="gpt-4o-mini",
input=f"Traduis en anglais : {resume}",
).output_text
Pour deux ou trois étapes linéaires, c'est plus lisible et sans dépendance. LangChain devient rentable quand la chaîne se ramifie, quand vous voulez du streaming, des exécutions parallèles, des reprises sur erreur, ou du traçage — bref quand l'orchestration elle-même devient le problème. En dessous, une fonction Python fait le travail.
Bonnes pratiques
- Validez entre les étapes. L'intérêt du chaînage, c'est de pouvoir vérifier la sortie intermédiaire avant de la passer plus loin. Ne branchez pas aveuglément.
- Gardez chaque prompt mono-tâche. Un maillon = une transformation. C'est plus facile à déboguer et à réutiliser.
- Attention au coût. Chaque maillon est un appel facturé. Trois étapes, c'est trois fois le coût — chaînez ce qui doit l'être, pas plus.
- Traitez les prompts comme du code. Versionnez-les, testez-les sur des cas connus. Un maillon qui dérive casse toute la chaîne en aval.
Ramifier : des étapes en parallèle
Toutes les chaînes ne sont pas linéaires. Quand deux étapes sont indépendantes — par exemple résumer et extraire les mots-clés d'un même texte — lancez-les ensemble avec un dictionnaire de runnables, que LangChain exécute en parallèle et renvoie sous forme de dictionnaire :
from langchain_core.runnables import RunnableParallel
fanout = RunnableParallel(
resume=prompt | model | StrOutputParser(),
mots_cles=prompt_mots_cles | model | StrOutputParser(),
)
resultat = fanout.invoke({"texte": mon_texte})
# resultat == {"resume": "...", "mots_cles": "..."}
Les deux branches reçoivent la même entrée et tournent en même temps, plus vite qu'en les enchaînant l'une après l'autre.
Streamer et inspecter
Toute chaîne LCEL expose la même interface : .invoke() pour une entrée, .batch() pour plusieurs, et .stream() pour recevoir les tokens au fil de leur génération. Quand une chaîne déraille, le plus rapide est d'appeler .invoke() sur chaque sous-chaîne isolément et de regarder la valeur intermédiaire — tout l'intérêt du chaînage, c'est que ces coutures sont inspectables.
Quand ne pas chaîner
Chaîner a un coût. Si un seul prompt bien écrit donne déjà des résultats fiables, le découper en trois appels n'ajoute que de la latence et de la dépense pour rien. Chaînez quand une étape dépend vraiment de la sortie nettoyée de la précédente, ou quand vous devez exécuter du code — une requête en base, une validation — entre les deux. Sinon, gardez un seul appel et passez à la suite.
Pour aller plus loin
Le chaînage de prompts est un pattern, pas un outil : on peut l'implémenter avec LangChain, avec un SDK nu, ou avec un skill qui encode le workflow pour votre assistant. Pour des skills orientés IA et données, voyez la catégorie data & IA ou le catalogue de skills. D'autres guides pratiques sont sur le blog.