A la fin de ce laboratoire, vous saurez : 1. Utiliser LangChain pour orchestrer des tâches d’analyse de documents 2. Extraire automatiquement des informations structurees d’un texte 3. Generer une proposition technique basee sur des données extraites 4. Enchaîner plusieurs opérations LLM dans un workflow coherent
Prerequis
Python 3.10+
Cle API OpenAI configuree (variable d’environnement ou fichier .env)
Connaissance de base de LangChain
Duree estimee : 45-60 minutes
Cas d’usage
Dans le monde de l’avant-vente, la réactivité et la pertinence sont des facteurs clés de succès. Répondre à un appel d’offre (RFP - Request for Proposal) est un processus chronophage qui demande de bien cerner les besoins du client pour proposer une solution adaptée.
Ce laboratoire démontre comment une IA agentique simple peut accélérer drastiquement ce processus. Nous allons utiliser LangChain, un framework puissant qui agit comme un “chef d’orchestre” pour les grands modèles de langage (LLMs), afin de :
Lire et comprendre un appel d’offre.
En extraire les informations stratégiques.
Générer une première ébauche de proposition technique.
Objet : Appel d'Offre - Solution de Prévision des Ventes. L'entreprise 'Global Retail' recherche un prestataire pour développer un système de prévision des ventes de ses produits phares. Les exigences techniques clés sont l'utilisation de modèles de Machine Learning (séries temporelles) et la livraison d'un dashboard interactif. Le projet doit être livré avant la fin du T4 2025. Notre objectif métier principal est de réduire les surplus de stocks de 15%.
Étape 2 : Créer une chaîne d’extraction d’informations
Nous allons définir un PromptTemplate qui guidera le LLM pour qu’il identifie précisément les points qui nous intéressons.
LCEL — LangChain Expression Language
La chaîne est assemblée avec LCEL (LangChain Expression Language), la syntaxe déclarative introduite par LangChain en 2023 pour composer des briques (prompt, modèle, parseur) via l’opérateur tube |. Chaque brique implémente l’interface Runnable, ce qui rend la chaîne homogène et lui donne de facto le support du batch, de l’asynchrone et du streaming. LCEL remplace l’ancienne classe LLMChain : on écrit désormais prompt | llm plutôt qu’un objet LLMChain(prompt, llm). C’est exactement ce pattern (extraction_prompt | llm) que nous utilisons ci-dessous. Pour le cadre général LangChain (chaînes, tools, mémoire), voir la référence bibliographique du Lab 8 (H. Chase, 2022).
from langchain_core.prompts import ChatPromptTemplatefrom langchain_openai import ChatOpenAI# Initialiser le modèle# Remplacez par votre modèle de prédilection si nécessaire (ex: via Ollama, Mistral, etc.)# Pensez à configurer votre clé API, par exemple avec : # import os# os.environ['OPENAI_API_KEY'] = 'VOTRE_CLE_ICI'llm = ChatOpenAI(temperature=0, model="gpt-5-mini")# Créer le template de prompt pour l'extraction avec ChatPromptTemplate (API moderne)extraction_template ="""Lis attentivement le texte de l'appel d'offre suivant et extrais les informations clés dans un format JSON valide.Texte de l'appel d'offre:--- {document_text}--- Extrais les informations suivantes:1. 'objectif_metier': L'objectif principal que le client cherche à atteindre.2. 'exigences_techniques': Les contraintes ou technologies spécifiques demandées.3. 'date_limite': La date ou période de livraison attendue.Ne retourne que le JSON, sans aucun autre commentaire ou texte d'introduction."""extraction_prompt = ChatPromptTemplate.from_template(extraction_template)# Utiliser LCEL (LangChain Expression Language) au lieu de LLMChainextraction_chain = extraction_prompt | llmprint("Chaîne d'extraction créée.")
Chaîne d'extraction créée.
Étape 3 : Exécuter la chaîne et extraire les points clés
import json# Utiliser invoke() au lieu de run() et extraire le contenuresponse = extraction_chain.invoke({"document_text": document[0].page_content})raw_result = response.contentextracted_data = json.loads(raw_result)print("Informations extraites de l'appel d'offre :\n")print(json.dumps(extracted_data, indent=2, ensure_ascii=False))
Informations extraites de l'appel d'offre :
{
"objectif_metier": "Réduire les surplus de stocks de 15%",
"exigences_techniques": [
"Utilisation de modèles de Machine Learning (séries temporelles)",
"Livraison d'un dashboard interactif"
],
"date_limite": "Fin du T4 2025"
}
Étape 4 : Créer une chaîne de génération de proposition
# Créer le template de prompt pour la génération avec ChatPromptTemplate (API moderne)generation_template ="""Agis en tant qu'architecte de solutions IA. En te basant sur les informations extraites de l'appel d'offre, rédige une ébauche de proposition technique en 3 points clairs et concis.Informations extraites:--- Objectif métier du client: {objectif_metier}Exigences techniques: {exigences_techniques}Date limite: {date_limite}--- Structure ta proposition comme suit:1. **Approche proposée:** Décris brièvement la solution envisagée pour atteindre l'objectif métier.2. **Technologies clés:** Liste les technologies qui seront utilisées, en accord avec les exigences.3. **Livrables:** Précise ce qui sera concrètement livré au client à la date limite."""generation_prompt = ChatPromptTemplate.from_template(generation_template)# Utiliser LCEL (LangChain Expression Language) au lieu de LLMChaingeneration_chain = generation_prompt | llmprint("Chaîne de génération créée.")
Chaîne de génération créée.
Étape 5 : Exécution de la generation
Maintenant que notre chaîne de generation est configuree, nous allons l’executer avec les données extraites precedemment. Le LLM va synthetiser une proposition structuree en trois volets (approche, technologies, livrables) adaptee aux besoins spécifiques du client.
# Exécuter la chaîne de génération avec invoke() et extraire le contenuresponse = generation_chain.invoke(extracted_data)proposition = response.contentprint("--- ÉBAUCHE DE PROPOSITION TECHNIQUE ---\n")print(proposition)
--- ÉBAUCHE DE PROPOSITION TECHNIQUE ---
1. **Approche proposée:**
- Construire une chaîne de valeur "données → prévision → optimisation → action" pour réduire les surplus de 15% : ingérer et nettoyer historiques ventes, stocks, promotions, prix et saisonnalité ; produire prévisions probabilistes de la demande (horizon court/moyen terme) ; convertir prévisions en recommandations d’inventaire (réapprovisionnement, safety stock, promotions ciblées).
- Méthodologie itérative : prototype (POC) sur un périmètre produit/rayon, validation métriques (MAPE, coverage, réduction de surplus simulée), puis industrialisation et déploiement global.
- Gouvernance et MLOps : pipelines reproductibles, tests, monitoring des dérives (data/model drift) et boucle de ré-entraînement automatique pour maintenir performance opérationnelle.
2. **Technologies clés:**
- Modèles séries temporelles : Temporal Fusion Transformer / Prophet / SARIMAX pour benchmarks ; LSTM/Seq2Seq ou modèles probabilistes (NGBoost, DeepAR) selon complexité.
- Orchestration et MLOps : Apache Airflow ou Prefect ; MLflow ou Kubeflow pour tracking et déploiement.
- Données & stockage : PostgreSQL / BigQuery / S3 (selon infra client).
- Serving & APIs : Docker + Kubernetes (ou services managés cloud) pour endpoints de scoring en temps réel/batch.
- Dashboard interactif : Tableau ou Power BI ou application web (Dash/Streamlit) selon intégration requise.
- Monitoring & observabilité : Prometheus + Grafana ou solutions cloud natives pour KPIs et alerting.
3. **Livrables (livraison finale : fin T4 2025) :**
- Modèles de prévision validés et déployés (code, artefacts MLflow/KF) avec documentation technique et jeux de données d’entraînement/test.
- API de scoring pour intégration aux systèmes existants + scripts d’ETL et pipelines d’orchestration en production.
- Dashboard interactif livré (tableaux, cartes de chaleur, scénarios what-if) présentant prévisions, recommandations de réapprovisionnement, indicateurs de surplus et progression vers l’objectif -15%.
- Plan opérationnel : stratégie de monitoring, règles d’alerte, calendrier de ré-entraînement automatique, et un guide d’exploitation + formation courte pour les équipes métier.
- Rapport d’impact attendu (simulation) et plan de déploiement pilote + roadmap post-livraison pour maintenir et améliorer les gains.
Conclusion
En quelques minutes, nous avons automatisé une partie significative du travail d’analyse d’un appel d’offre et de rédaction d’une proposition. Les gains sont multiples :
Gain de temps : L’analyse et la première ébauche sont quasi-instantanées.
Standardisation : La structure des réponses est homogène, ce qui facilite la relecture et la validation.
Fiabilité : L’IA est moins susceptible d’oublier une information clé lors de la lecture du document.
Dans un vrai projet, cet agent pourrait être enrichi avec des outils (tools) lui donnant accès à :
Une recherche web pour se renseigner sur l’entreprise cliente.
Une base de connaissances interne (via RAG) pour réutiliser des briques de projets précédents.
Un outil de pricing pour estimer le coût du projet.
Exercice 1 : Generer une estimation de budget
Dans cet exercice, vous allez créer une chaîne LangChain qui estime le budget d’un projet a partir des informations extraites de l’appel d’offre. L’objectif est de faire raisonner le LLM sur les couts probables (développement, infrastructure, maintenance).
Objectif : A partir des données extraites (exigences_techniques, date_limite), generer une estimation de budget structuree.
Indices : - Definissez un template qui demande au LLM d’estimer un budget en Jours-Homme et en euros - Utilisez ChatPromptTemplate.from_template() puis l’opérateur | pour créer la chaîne - Invoquez la chaîne avec les données de extracted_data
# Exercice 1 : Estimation de budget# Creez une chaine LangChain qui estime le budget du projet# Etape 1: Definissez le template de prompt# Indice: demandez au LLM d'estimer en JH et en euros, pour le developpement et l'infrastructurebudget_template =None# Remplacez None par votre template avec {exigences_techniques} et {date_limite}# Etape 2: Creez le prompt et la chaine avec LCEL# Indice: ChatPromptTemplate.from_template(...) puis | llmbudget_prompt =Nonebudget_chain =None# Etape 3: Invoquez la chaine avec les donnees extraites precedemment# Indice: budget_chain.invoke({"exigences_techniques": extracted_data["exigences_techniques"], ...})# response_budget = ...print("Exercice 1 a completer : estimation de budget avec LangChain")
Exercice 1 a completer : estimation de budget avec LangChain
Exercice 2 : Comparer deux appels d’offres
Dans un contexte reel, un avant-vente doit souvent comparer plusieurs opportunites. Cet exercice vous demande de créer une chaîne qui compare l’appel d’offre existant avec un second document fictif, et produit une synthese comparative.
Objectif : Créer une chaîne qui prend en entree deux textes d’appels d’offres et retourne un tableau comparatif en JSON.
Indices : - Definissez un template avec deux variables : {offre_1} et {offre_2} - Le format JSON de sortie pourrait contenir : critere, offre_1_valeur, offre_2_valeur, recommandation - Créez un second texte d’appel d’offre fictif dans une variable offre_2_texte
# Exercice 2 : Comparaison de deux appels d'offres# Etape 1: Definissez un second appel d'offre fictif# Indice: ecrivez un texte similaire a l'appel d'offre original avec des exigences differentesoffre_2_texte =None# Remplacez None par un texte d'appel d'offre fictif# Etape 2: Definissez le template de comparaison# Indice: le template doit contenir {offre_1} et {offre_2} comme variablescomparaison_template =None# Remplacez None par votre template# Etape 3: Creez le prompt et la chaine# Indice: meme pattern que les exercices precedentscomparaison_prompt =Nonecomparaison_chain =None# Etape 4: Testez avec les deux offres# Indice: comparaison_chain.invoke({"offre_1": document[0].page_content, "offre_2": offre_2_texte})print("Exercice 2 a completer : comparaison de deux appels d'offres")
Exercice 2 a completer : comparaison de deux appels d'offres
# Exercice : Creez votre propre chaine LangChain# Modifiez le template pour extraire differentes informations d'un document# TODO: Definissez un nouveau template d'extraction# Indice: inspirez-vous du template d'extraction utilise plus haut,# mais changez les champs a extraire (ex: 'client', 'budget', 'technologies')extraction_template_exo ="""Lis attentivement le texte suivant et extrais les informations dans un format JSON valide.Texte:--- {document_text}--- Extrais les informations suivantes:1. 'client': Le nom du client ou de l'organisation2. 'budget': Le budget mentionne (si disponible)3. 'technologies': Liste des technologies mentionneesNe retourne que le JSON, sans aucun autre commentaire."""# TODO: Creez le prompt et la chaine avec LCEL (prompt | llm)# Indice: utilisez ChatPromptTemplate.from_template() puis l'operateur |extraction_prompt_exo =None# Remplacez Noneextraction_chain_exo =None# Remplacez None# TODO: Testez avec le document de l'appel d'offre# Indice: utilisez .invoke({"document_text": document[0].page_content})# response_exo = ...# print("Resultat de l'exercice :")# print(response_exo.content)
Exercice 3 : Créer une chaîne d’extraction personnalisee
Créez votre propre chaîne LangChain en modifiant le template d’extraction pour extraire des informations différentes du document d’appel d’offre (client, budget, technologies).
References
LangChain, LangChain Expression Language (LCEL), 2023, python.langchain.com/docs/concepts/lcel. Syntaxe déclarative de composition de chaînes via l’opérateur tube | ; chaque composant implémente l’interface Runnable (batch, asynchrone, streaming). Primitive centrale de ce laboratoire (extraction_prompt | llm).
H. Chase, LangChain, octobre 2022, github.com/langchain-ai/langchain. Framework modulaire open-source pour applications LLM — chaînes composables, tools, mémoire. Référence bibliographique détaillée au Lab 8 (ADK Introduction).