Accueil / Articles / LangChain 1.x en pratique : chaînes, RAG, outils et agents localement

LangChain 1.x en pratique : chaînes, RAG, outils et agents localement

Apprenez à créer des chaînes, une génération améliorée par la récupération d’informations, des outils et un système RAG agentiel avec LangChain 1.x en utilisant une installation locale gratuite d’Ollama, sans besoin de clés API.

3586 mots

Ce troisième volet fait partie d’une série pratique qui poursuit les travaux précédents sur la création de RAG et d’agents à partir de zéro en Python pur. L’approche adoptée est délibérément différente : au lieu de monter vous-même chaque composant, vous verrez comment LangChain regroupe toute cette logique en quelques lignes de code seulement. Comme vous avez déjà construit les éléments de base manuellement, vous êtes en bonne position pour comprendre réellement ce que fait chaque abstraction, plutôt que de la considérer comme une magie. Cette compréhension est essentielle : elle fait la différence entre les développeurs qui utilisent LangChain efficacement et ceux qui doivent constamment lutter contre ses limites.

Tout dans ce tutoriel s’exécute localement et gratuitement, en utilisant un modèle local via Ollama ainsi que des embeddings locaux. Aucune clé API n’est nécessaire, et il n’y a pas de limites de fréquence à craindre.

Remarque sur les versions : ce tutoriel vise LangChain 1.x, testé avec langchain==1.3.11 et langchain-core==1.4.8. LangChain 1.0 a entraîné une réorganisation importante — l’API actuelle des agents se concentre sur create_agent, tandis que des composants plus anciens tels que AgentExecutor et initialize_agent ont été déplacés dans un package distinct langchain-classic. De nombreux tutoriels en ligne illustrent encore les schémas antérieurs à 1.0 ; les importations présentées ici sont à jour, et chacune a été vérifiée pour s’assurer qu’elle fonctionne correctement.

Comment suivre les instructions : ouvrez un fichier nommé lc.py, exécutez chaque bloc de code dans l’ordre, et terminez les exercices « C’est à vous » au fur et à mesure que vous les rencontrez. Chaque fois que vous verrez l’expression « vous avez construit ceci », elle fait référence à la mise en œuvre manuelle présentée dans les tutoriels précédents de cette série.

Étape 0 — Qu’est-ce que LangChain exactement

LangChain peut être compris comme une collection de composants standardisés et interchangeables permettant de créer des applications utilisant des modèles LLM — tels que des enveloppes de modèle, des modèles de prompts, des outils de récupération d’informations, des bases de données vectorielles, des outils et des agents. Tous ces éléments respectent une interface commune, ce qui vous permet de les relier entre eux et d’en remplacer un par un (changer de modèle, modifier la base de données vectorielle) sans avoir à réécrire la logique de votre application.

Le concept qui relie tout est le Runnable. Chaque composant expose la même méthode .invoke(), et n’importe quels deux composants peuvent être enchaînés entre eux à l’aide de l’opérateur de pipe |. Ce mécanisme de piping est connu sous le nom de LCEL, abréviation de LangChain Expression Language. Une fois que chaque partie de votre système utilise l’interface Runnable, tout un pipeline ou agent RAG peut être défini en seulement quelques lignes.

Un compromis important à mentionner dès le début : LangChain réduit le code répétitif et vous donne accès à un vaste catalogue d’intégrations prêtes à l’emploi. En contrepartie, il introduit des couches d’abstraction qui peuvent rendre le débogage plus difficile — il y aura des moments où vous préférerez examiner directement la boucle que vous avez écrite vous-même. Savoir quand cette abstraction en vaut la peine est là la véritable compétence, et nous reviendrons sur ce compromis à l’Étape 7.

Mise en place (l’environnement local gratuit)

pip install langchain langchain-core langchain-ollama langchain-huggingface langchain-text-splitters sentence-transformers

Vous aurez également besoin d’avoir Ollama installé (il est gratuit et fonctionne localement) ; après cela, vous devriez télécharger un modèle capable d’appeler des outils :

ollama pull llama3.2     # ~2 GB; needs ~8 GB RAM. qwen2.5 also works well.

Si vous préférez éviter Ollama, vous pouvez toujours exécuter les sections chain et RAG en utilisant un modèle local de Hugging Face via langchain-huggingface. Cependant, les sections agent dépendent d’un comportement fiable de appel d’outils, que les petits modèles gourmands en CPU ont tendance à gérer mal. L’utilisation d’Ollama est fortement recommandée pour les étapes 4 à 6.

Étape 1 — L’action principale : une chaîne avec le pipe |

Dans le tutoriel RAG précédent, vous avez assemblé manuellement une requête à l’aide d’une chaîne de caractères f, vous l’avez transmise au modèle et vous avez nettoyé la sortie avec .strip(). LangChain capture cette même séquence sous forme d’expression de pipe. Ajoutez ceci à lc.py :

from langchain_ollama import ChatOllama
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser

llm = ChatOllama(model="llama3.2", temperature=0)

prompt = ChatPromptTemplate.from_template(
    "Explain {topic} in exactly one sentence."
)

# The chain: prompt -> model -> plain-string parser
chain = prompt | llm | StrOutputParser()

print(chain.invoke({"topic": "retrieval-augmented generation"}))

Lecture du pipeline de gauche à droite : prompt convertit votre dictionnaire d’entrée en un message correctement formaté, llm transforme ce message en une réponse du modèle, et StrOutputParser() extrait le texte brut de l’objet de réponse.

Ce système a déjà été mis en place par vous. Ce pipeline est fonctionnellement identique à f"Explain {topic}..." suivi de generator(prompt) puis de [0]["generated_text"].strip() vu dans la démonstration RAG précédente — trois étapes manuelles, maintenant exprimées sous forme de trois Runnables connectés par des pipelines. La logique n’a pas changé ; seule l’interface a été standardisée.

Votre tour : chaque Runnable prend également en charge .batch() et .stream() directement. Essayez donc :

for piece in chain.stream({"topic": "vector embeddings"}):
    print(piece, end="", flush=True)   # tokens arrive as they're generated
print()
print(chain.batch([{"topic": "agents"}, {"topic": "chunking"}]))  # two at once

Remarquez que le streaming et le batchage ont été inclus gratuitement simplement parce que vous avez utilisé l’interface standard Runnable. Ce moment de « gratuité » représente, en miniature, toute la raison d’utiliser LangChain.

Étape 2 — RAG, à la manière de LangChain

C’est le moment de reconstruire votre pipeline RAG manuel à l’aide des éléments de base de LangChain. Chaque composant correspond directement à quelque chose que vous avez déjà écrit manuellement.

from langchain_huggingface import HuggingFaceEmbeddings
from langchain_core.vectorstores import InMemoryVectorStore
from langchain_text_splitters import RecursiveCharacterTextSplitter

# Same Nimbus knowledge base from the RAG tutorial
DOCUMENTS = [
    "Nimbus is a fictional note-taking app. The free plan, Nimbus Lite, allows up to 50 notes and 1 GB of storage.",
    "Nimbus Pro costs 8 dollars per month billed annually, or 10 dollars billed monthly. It includes 50 GB of storage and collaboration for up to 5 people.",
    "Nimbus stores notes encrypted at rest with AES-256. End-to-end encryption is Pro-only and must be enabled in Settings > Security.",
    "Nimbus offers a 30-day refund policy on all paid plans. Refunds reach the original payment method within 5 business days.",
    "Nimbus live chat support is staffed for Pro customers, Monday to Friday, 9am-6pm UTC. Free users get email support with a 48-hour response time.",
]

# 1. Split (↔ your chunk_text function)
splitter = RecursiveCharacterTextSplitter(chunk_size=200, chunk_overlap=40)
chunks = splitter.create_documents(DOCUMENTS)

# 2. Embed locally (↔ your sentence-transformers model)
embeddings = HuggingFaceEmbeddings(model_name="sentence-transformers/all-MiniLM-L6-v2")

# 3. Store + index (↔ your numpy array of vectors). No server needed.
vectorstore = InMemoryVectorStore.from_documents(chunks, embeddings)

# 4. Retriever (↔ your retrieve() with cosine top-k)
retriever = vectorstore.as_retriever(search_kwargs={"k": 3})

for doc in retriever.invoke("How much does Pro cost?"):
    print("-", doc.page_content[:70], "...")

↔ c’est vous qui avez construit tout cela. RecursiveCharacterTextSplitter remplit la fonction de découpage du texte, mais il le fait avec plus de prudence : il divise le texte en fonction des limites entre paragraphes et phrases plutôt que de se baser uniquement sur le comptage des mots. HuggingFaceEmbeddings n’est qu’un enveloppe autour du même modèle all-MiniLM-L6-v2 que vous avez utilisé précédemment. InMemoryVectorStore remplace votre tableau numpy de vecteurs, et sa méthode .as_retriever() effectue la même recherche top-k par similarité cosinus que vous aviez codée manuellement. Quatre lignes suffisent ici pour couvrir tout ce que vous avez développé aux étapes 2 à 4 précédemment.

Ensuite, connectez la récupération des données à la génération en utilisant LCEL :

from langchain_core.runnables import RunnablePassthrough
from langchain_core.output_parsers import StrOutputParser

rag_prompt = ChatPromptTemplate.from_template(
    "Answer using only the context. If it's not there, say you don't know.\n\n"
    "Context:\n{context}\n\nQuestion: {question}\nAnswer:"
)

def format_docs(docs):
    return "\n\n".join(d.page_content for d in docs)

rag_chain = (
    {"context": retriever | format_docs, "question": RunnablePassthrough()}
    | rag_prompt
    | llm
    | StrOutputParser()
)

print(rag_chain.invoke("How much does Nimbus Pro cost per month?"))

Le dictionnaire situé au début de la chaîne fait fonctionner deux branches en parallèle : question transmet simplement l’entrée telle quelle, tandis que context dirige cette même entrée vers le récupérateur et formate les résultats. Les deux sorties se retrouvent ensuite dans le prompt. ↔ vous avez construit ceci correspond essentiellement à votre ancienne fonction rag_answer() — récupérer, insérer dans un prompt, générer — condensée en une seule expression.

Votre tour : Essayez d’appeler rag_chain.invoke("Les utilisateurs libres peuvent-ils utiliser le chat en direct ?"), puis suivez cela d’une question non liée comme rag_chain.invoke("Quelle est la capitale de la France ?"). Faites attention à la réponse « Je ne sais pas » — il s’agit de la même vérification que dans le tutoriel RAG précédent, qui souligne à nouveau le point suivant : la qualité de la récupération détermine la qualité de la réponse. Ensuite, appelez retriever.invoke(...) séparément pour voir exactement ce qui a été extrait lorsque la réponse semble incorrecte. Cette séparation — vérifier la récupération indépendamment de la génération — est une habitude de débogage que vous avez déjà utilisée, et LangChain la conserve en gardant ces deux étapes comme des Runnables distincts.

Étape 3 — Outils

Dans le tutoriel sur les agents, vous avez défini les outils sous forme de dictionnaire TOOLS et écrit vous-même un analyseur basé sur des expressions régulières pour extraire le nom de l’outil et ses paramètres à partir du texte brut généré par le modèle. LangChain élimine complètement la nécessité d’un tel analyseur grâce à l’appel natif d’outils : vous décrivez ce que fait un outil, le modèle répond par une requête structurée, et LangChain s’occupe de l’affectation. La définition d’un outil se fait ainsi, en utilisant le décorateur @tool :

from langchain_core.tools import tool
import ast, operator, datetime

_OPS = {ast.Add: operator.add, ast.Sub: operator.sub, ast.Mult: operator.mul,
        ast.Div: operator.truediv, ast.Pow: operator.pow, ast.USub: operator.neg}
def _ev(n):
    if isinstance(n, ast.Constant): return n.value
    if isinstance(n, ast.BinOp):   return _OPS[type(n.op)](_ev(n.left), _ev(n.right))
    if isinstance(n, ast.UnaryOp): return _OPS[type(n.op)](_ev(n.operand))
    raise ValueError("unsupported")

@tool
def calculator(expression: str) -> str:
    """Evaluate a basic arithmetic expression like '8 * 12'."""
    return str(_ev(ast.parse(expression, mode="eval").body))

@tool
def get_today(_: str = "") -> str:
    """Return today's date in ISO format."""
    return datetime.date.today().isoformat()

Voici les détails qui méritent une attention particulière. Vous pouvez examiner précisément ce que LangChain a généré à partir de votre fonction :

print(calculator.name)         # 'calculator'
print(calculator.description)  # the docstring
print(calculator.args)         # {'expression': {'title': 'Expression', 'type': 'string'}}

Ce dernier ligne est bien une sortie réelle et vérifiée. LangChain a examiné votre indication de type (expression: str) ainsi que la documentation, afin d’en construire un schéma — c’est précisément ce schéma que le modèle lit pour déterminer s’il doit invoquer l’outil et comment le faire. ↔ c’est vous qui avez créé ce schéma ; auparavant, vous écriviez manuellement des descriptions d’outil à l’intérieur de votre SYSTEM_PROMPT et analysiez vous-même la sortie du modèle. Désormais, c’est la documentation elle-même qui sert de description, et l’analyse se fait automatiquement. Cela explique pourquoi les documents de description et les indications de type ont une réelle importance — ce ne sont pas simplement de la documentation, ils définissent la manière dont le modèle comprend et utilise l’outil. Une documentation négligée entraîne un modèle qui invoque l’outil de manière incorrecte.

Votre tour : Remplacez la docstring du calculateur par quelque chose d’inutile, comme """effectue des calculs mathématiques""", puis vérifiez à nouveau .description. À l’étape 4, vous verrez de vos propres yeux comment une docstring insuffisante entraîne de pires décisions de sélection d’outil de la part du modèle. La description que vous écrivez agit comme le volant qui guide le comportement du modèle.

Étape 4 — Agents en une seule appel

C’est ici que les travaux précédents portent leurs fruits. Votre agent personnalisé nécessitait une boucle, un espace de travail temporaire, un analyseur, un mécanisme de gestion des erreurs, une limite par étape, ainsi qu’un prompt système pour enseigner au modèle le format ReAct. Dans LangChain 1.x, tout cela se résume en une seule appel de fonction : create_agent.

from langchain.agents import create_agent

agent = create_agent(
    model=llm,                                   # your ChatOllama from Step 1
    tools=[calculator, get_today],               # the @tool functions from Step 3
    system_prompt="You are a helpful assistant. Use tools for math and dates.",
)

result = agent.invoke(
    {"messages": [{"role": "user", "content": "What is 8 times 12, and what is today's date?"}]}
)
print(result["messages"][-1].content)

C’est l’agent dans son intégralité, du début à la fin. ↔ c’est vous qui l’avez construit — en totalité. La fonction create_agent exécute internement le cycle raisonner-agir-observer, envoie les tâches à l’outil approprié, renvoie les observations au modèle, vérifie la condition d’arrêt et respecte la limite de pas — chaque élément que vous avez assemblé manuellement à l’intérieur de run_agent. En sous-main, elle s’appuie sur LangGraph, ce qui explique pourquoi le comportement itératif est si fiable.

Si vous souhaitez voir le processus de raisonnement se dérouler — le même effet que celui obtenu avec verbose=True — diffusez les étapes intermédiaires au lieu d’attendre simplement une réponse finale :

inputs = {"messages": [{"role": "user", "content": "How much is a year of Nimbus Pro?"}]}
for chunk in agent.stream(inputs, stream_mode="updates"):
    print(chunk)

Lorsque l’exécution se déroule, vous verrez chaque décision prise par le modèle ainsi que chaque résultat retourné par l’outil, affichés un après l’autre. Il s’agit du même schéma Pensée/Action/Observation que dans le suivi manuel, mais présenté sous forme d’objets de mise à jour structurés plutôt que de texte brut que vous deviez parser vous-même.

Votre tour : Essayez une question qui oblige le modèle à utiliser deux outils en chaîne — par exemple, demandez-lui de déterminer la date de fin d’une fenêtre de remboursement de 30 jours sachant qu’un essai a commencé aujourd’hui. Vérifiez s’il appelle correctement d’abord get_today, puis calculator dans l’ordre. Ensuite, revenez à l’étape 7 du matériel sur les agents — tous les modes de défaillance que vous y avez documentés (format d’ sortie erroné, noms d’outils inventés, boucles infinies) peuvent encore se manifester ici. Le framework ne renforce pas le raisonnement d’un modèle faible ; il cache simplement la logique sous-jacente. Comprendre cette différence est précisément ce qui vous permettra de déboguer ces agents plus rapidement que quelqu’un qui se lance directement dans le framework sans en construire un d’abord.

Étape 5 — Assembler le tout : un agent qui récupère des informations (agentic RAG)

C’est ici que convergent les trois leçons précédentes. Prenez votre chien de recherche et utilisez-le comme un outil, puis donnez cet outil à l’agent. Désormais, c’est l’agent lui-même qui décide quand une recherche de document est nécessaire — et il peut effectuer des recherches multiples ou combiner la récupération d’informations avec des calculs selon ses besoins.

@tool
def search_nimbus_docs(query: str) -> str:
    """Search the Nimbus product documentation for facts about plans, pricing, refunds, security, and support."""
    docs = retriever.invoke(query)
    return "\n\n".join(d.page_content for d in docs)

smart_agent = create_agent(
    model=llm,
    tools=[search_nimbus_docs, calculator, get_today],
    system_prompt=(
        "You answer questions about the Nimbus app. "
        "Use search_nimbus_docs for any product facts, and calculator for arithmetic. "
        "Base answers only on retrieved facts."
    ),
)

q = "How much would Nimbus Pro cost a team of 4 for a full year?"
result = smart_agent.invoke({"messages": [{"role": "user", "content": q}]})
print(result["messages"][-1].content)

Pour répondre correctement à une telle question, l’agent doit d’abord rechercher le prix de l’abonnement mensuel, puis seulement calculer 8 * 12 * 4. Il s’agit donc de la récupération d’informations (provenant du premier tutoriel) présentée sous forme d’outil (du troisième), mise en œuvre par un agent (du deuxième) — trois concepts distincts fonctionnant ensemble au sein d’un même système. Permettre à l’agent de choisir le moment de la récupération d’informations offre une grande flexibilité par rapport au rag_chain à chemin fixe de l’Étape 2, et c’est un schéma fréquemment utilisé dans les systèmes de production réels.

Votre tour : Diffusez également l’exécution de cet agent en utilisant smart_agent.stream(..., stream_mode="updates"), et confirmez l’ordre des opérations — la recherche a lieu avant le calcul. Si votre modèle local tente de résoudre les opérations arithmétiques par lui-même au lieu d’utiliser l’outil de calculateur (tendance courante chez les modèles plus petits), renforcez l’instruction système avec une phrase du type "Vous DEVEZ utiliser le calculateur pour chaque étape arithmétique."> C’est le même remède qui a fonctionné dans le tutoriel des agents.

Étape 6 — Un bref aperçu de ce qu’il y a d’autre

À ce stade, vous avez déjà le squelette essentiel en place. Quelques éléments supplémentaires de LangChain méritent d’être connus, chacun correspondant à quelque chose que vous avez déjà construit manuellement :

  • Chargeurs de documents (langchain-community) — permettent d’ingérer des PDF, des pages web, des pages Notion et d’autres sources similaires directement dans les mêmes objets Document que votre outil de segmentation de texte utilise déjà. Cela remplace le collage manuel de textes dans une liste par un chargement réel et structuré.
  • Stockages vectoriels de niveau production — permettent de remplacer le stockage en mémoire par Chroma ou FAISS (importés via from langchain_chroma import Chroma) afin de conserver les embeddings sur disque et d’augmenter la capacité au-delà des limites de la mémoire. Comme ces outils exposent tous deux l’interface .as_retriever() identique, rien dans la chaîne de traitement ne doit être modifié — c’est précisément cette cohérence qui justifie ce changement.
  • Histoire de la mémoire et des messages — enroulez une chaîne afin qu’elle conserve le contexte des tours précédents, transformant ainsi une chaîne à un seul échange en une conversation continue.
  • Analystes de sortie au-delà des simples chaînes de caractères — forcez la réponse d’un modèle en JSON ou un objet Pydantic validé, plutôt que de compter sur le fait que le texte brut soit déjà bien formaté.
  • LangGraph — lorsque la logique de boucle intégrée dans create_agent ne suffit plus (chemins bifurqués, étapes d’approbation avec intervention humaine, plusieurs agents collaborant), vous passez à LangGraph, le moteur de graphes de niveau inférieur sur lequel create_agent lui-même est construit.
  • Étape 7 — Quand recourir à LangChain, et quand ne pas le faire (un avis honnête)

    À ce stade, vous avez construit le même type de système à deux reprises — une fois de zéro, une fois avec un framework — ce qui vous place dans une bonne position pour effectuer cette appel vous-même. C’était l’objectif de passer en revue les deux versions.

    LangChain s’avère utile lorsque vous assemblez de nombreuses intégrations existantes — quelques chargeurs de documents, plusieurs bases de données vectorielles, plus d’un fournisseur de modèles — et que vous ne souhaitez pas développer manuellement la logique de streaming, de batchage, de tentative répétée et de suivi pour chacune d’elles. Il est également avantageux lorsque vous prévoyez de changer fréquemment les composants et que vous souhaitez une interface stable pour les remplacer, ou encore lorsque vous développez un agent et que vous préférez ne pas gérer vous-même la boucle de raisonnement.

    Écrire à la main est souvent la meilleure solution lorsque l’application est suffisamment petite pour que l’apprentissage des abstractions de LangChain prenne plus de temps que d’écrire simplement les cinquante lignes environ que vous savez déjà rédiger. C’est également la meilleure option lorsque l’on a besoin d’une visibilité totale sur ce qui s’exécute — parcourir les couches du framework pour déboguer un problème constitue une véritable source de friction, et cette plainte est justifiée — ou encore lorsque l’ajout d’une couche d’indirection cacherait une logique qui est en réalité plus claire en Python pur. Le pipeline RAG ainsi que l’agent que vous avez construit manuellement dans les tutoriels précédents sont tout à fait adaptés à un usage en production ; l’utilisation d’un framework ne rend en rien le code écrit manuellement inférieur.

    Il n’y a pas de réponse unique et correcte ici. La raison pour laquelle vous avez d’abord appris la version manuelle est que choisir d’adopter ce framework constitue une décision réfléchie prise en connaissant parfaitement ce qu’il remplace, et non un choix par défaut auquel on recourt parce que ses mécanismes internes restent obscurs.

    Étape 8 — Où aller ensuite

    • Si votre configuration locale semble trop lente, des versions gratuites hébergées sont disponibles chez Groq et Google Gemini. Le changement consiste en une seule modification : remplacer ChatOllama par ChatGroq, ou utiliser init_chat_model(“gemini-...”, model_provider=“google_genai”) — car tout le reste fonctionne via la même interface standard. Vous aurez besoin d’une clé API pour l’un ou l’autre, mais leurs versions gratuites ne coûtent rien.
  • LangSmith est l’outil de suivi et de débogage de LangChain. Lorsqu’une chaîne ou un agent se comporte de manière anormale, il vous permet d’examiner chaque étape, chaque entrée et chaque sortie. Il dispose d’une version gratuite et constitue la réponse directe aux plaintes du type « Je ne peux pas voir l’intérieur du framework ».
  • LangGraph mérite d’être exploré lorsque vous avez besoin de workflows à état, de plusieurs agents coopérant entre eux, ou d’points de contrôle avec intervention humaine qui dépassent le simple cycle d’appel d’outils.
  • La documentation officielle se trouve sur docs.langchain.com. Vérifiez bien que le contenu que vous lisez concerne la version 1.x — tout ce qui a été écrit avant 1.0 fait référence à AgentExecutor, initialize_agent ou LLMChain, des éléments qui ont depuis été déplacés ou rendus obsolètes.
  • Le modèle mental à retenir

    LangChain est essentiellement un ensemble de composants que vous construisez vous-même, standardisés derrière une seule interface — Runnable — et reliés par |. Rien à l’intérieur n’est véritablement une idée nouvelle une fois que vous avez vous-même conçu ces éléments :

    • Une chaîne correspond au flux prompt-modèle-parseur que vous avez déjà écrit, simplement mis en série.
    • Un retriever représente votre logique d’incorporation de données et de recherche cosinus, encapsulée dans une interface commune.
    • Un outil est une fonction que vous avez écrite accompagnée d’un schéma généré automatiquement, permettant au modèle de l’appeler directement sans que vous ayez à parser son résultat texte.
    • Un agent, via create_agent, correspond à tout votre cycle raisonner-agir-observer, regroupé en une seule appel.

    Lorsqu’un problème survient, vous le déboguez de la même manière que d’habitude : vous isolez le composant défaillant. Vous testez le récupérateur séparément, vous affichez les valeurs de .args d’une fonction ou vous suivez pas à pas les étapes intermédiaires de l’agent. Le framework ne change que la quantité de code à taper — il n’affecte pas ce qui se passe réellement, et vous comprenez déjà ce qui se produit.

    Résolution de problèmes

    • Si vous rencontrez une erreur ImportError avec create_agent ou langchain_ollama, c’est probablement dû à une installation antérieure à la version 1.0 ou à l’absence d’un paquet nécessaire. Exécutez pip install -U langchain langchain-ollama et vérifiez que langchain.__version__ commence par 1..
  • Si un tutoriel que vous lisez utilise AgentExecutor ou initialize_agent, il s’agit de l’API ancienne. Dans la version 1.x, ces fonctions ont été déplacées dans langchain-classic ; le nouveau code doit utiliser create_agent à la place.
  • Une erreur « connection refused » provenant d’Ollama signifie que le serveur n’est pas en cours d’exécution — lancez-le avec ollama serve ou ouvrez l’application, et assurez-vous que votre modèle apparaît dans ollama list.
  • Si l’agent ignore un outil ou tente de réaliser des calculs par lui-même, c’est probablement parce que le modèle ne lance pas les outils de manière fiable, ce qui est une limitation courante des modèles plus petits, ou bien la documentation de l’outil est trop vague. Affinez le prompt système ainsi que la documentation de l’outil, ou essayez un modèle plus puissant comme qwen2.5.
  • HuggingFaceEmbeddings semble lent la première fois que vous l’utilisez, car il télécharge le modèle d’incorporation (environ 80 MB) et le met en cache localement. La récupération des données est ensuite rapide.
  • La première appel à un modèle via Ollama est lent, car le modèle est chargé en RAM ; les appels suivants sont rapides.
  • Vous avez maintenant construit des systèmes RAG, des agents, ainsi que le framework qui les intégre — d’abord manuellement, puis avec LangChain. Vous comprenez la couche que la plupart des gens n’accèdent qu’indirectement. Amusez-vous à l’utiliser.

    Lectures complémentaires

  • Gérer LLM en tant que juge comme système de production vivant — Découvrez comment le cycle de vie en quatre étapes de Netflix — données fiables, entraînement ajusté selon des critères précis, déploiement sécurisé et surveillance continue — permet à un juge LLM de rester précis à grande échelle.