LangChain
API en mouvement rapide. Ce qui suit couvre le cœur stable — LCEL, modèles de chat, prompts, parsers, retrievers. Vérifier contre docs.langchain.com avant de s'appuyer sur un détail.
Installation
uv add langchain langchain-openai langchain-anthropic langchain-community
Les intégrations sont dans des paquets séparés depuis la 0.1 : le cœur ne dépend d'aucun fournisseur.
Appeler un modèle
from langchain_anthropic import ChatAnthropic
from langchain_core.messages import HumanMessage, SystemMessage
llm = ChatAnthropic(model="claude-sonnet-4-5", temperature=0, max_tokens=1024)
resp = llm.invoke([
SystemMessage("Tu réponds en une phrase."),
HumanMessage("Qu'est-ce qu'un agent computer use ?"),
])
print(resp.content)
Un agent computer use est un système qui perçoit l'écran et agit via souris et clavier
pour accomplir des tâches à la place d'un utilisateur.
Quatre méthodes sur tout composant : invoke, batch, stream, et leurs variantes
asynchrones ainvoke, abatch, astream.
LCEL, l'opérateur |
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
prompt = ChatPromptTemplate.from_messages([
("system", "Tu es un analyste e-commerce concis."),
("human", "Le produit {produit} est-il trouvable sur {site} ?"),
])
chain = prompt | llm | StrOutputParser()
print(chain.invoke({"produit": "Najim 100 ml", "site": "versace.com"}))
Chaque maillon reçoit la sortie du précédent. StrOutputParser extrait .content du
message, sinon on manipule un objet AIMessage.
Sortie structurée
from pydantic import BaseModel, Field
class Findability(BaseModel):
findable: bool = Field(description="Produit atteignable par la navigation")
clicks: int = Field(description="Nombre de clics jusqu'à la fiche produit")
path: list[str] = Field(default_factory=list)
structured = llm.with_structured_output(Findability)
result = structured.invoke("Sur versace.com, combien de clics pour Najim 100 ml ?")
print(result.clicks, result.findable)
3 True
with_structured_output s'appuie sur le function calling natif du fournisseur. C'est plus
fiable qu'un JsonOutputParser derrière un prompt qui supplie de rendre du JSON.
Tools et agents
from langchain_core.tools import tool
@tool
def get_price(sku: str) -> float:
"""Retourne le prix TTC d'un SKU."""
return 129.0
llm_with_tools = llm.bind_tools([get_price])
msg = llm_with_tools.invoke("Quel est le prix du SKU AB-12 ?")
print(msg.tool_calls)
[{'name': 'get_price', 'args': {'sku': 'AB-12'}, 'id': 'toolu_01X…', 'type': 'tool_call'}]
Le décorateur @tool dérive le schéma des annotations de type et la description de la
docstring — donc la docstring est un élément fonctionnel, pas un commentaire.
bind_tools ne fait que proposer l'appel : c'est à la boucle d'exécuter l'outil et de
renvoyer un ToolMessage. Pour la boucle complète, passer à langgraph.
RAG minimal
from langchain_community.document_loaders import PyPDFLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_openai import OpenAIEmbeddings
from langchain_community.vectorstores import FAISS
docs = PyPDFLoader("manuel.pdf").load()
splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=150)
chunks = splitter.split_documents(docs)
store = FAISS.from_documents(chunks, OpenAIEmbeddings(model="text-embedding-3-small"))
retriever = store.as_retriever(search_kwargs={"k": 4})
RecursiveCharacterTextSplitter coupe d'abord sur les paragraphes, puis les phrases, puis
les mots. C'est le défaut raisonnable ; chunk_overlap évite de trancher une idée en deux.
from langchain_core.runnables import RunnablePassthrough
template = ChatPromptTemplate.from_template(
"Réponds uniquement à partir du contexte.\n\nContexte:\n{context}\n\nQuestion: {question}"
)
def format_docs(docs):
return "\n\n".join(d.page_content for d in docs)
rag = (
{"context": retriever | format_docs, "question": RunnablePassthrough()}
| template
| llm
| StrOutputParser()
)
print(rag.invoke("Quelle est la garantie ?"))
RunnablePassthrough laisse passer l'entrée telle quelle pendant que l'autre branche va
chercher les documents. Les deux branches du dict s'exécutent en parallèle.
Streaming
for chunk in chain.stream({"produit": "Najim", "site": "versace.com"}):
print(chunk, end="", flush=True)
Observabilité
export LANGCHAIN_TRACING_V2=true
export LANGCHAIN_API_KEY=ls__...
export LANGCHAIN_PROJECT=mon-projet
Toutes les exécutions apparaissent alors dans LangSmith, avec les prompts réels, les latences et les tokens. C'est le principal argument pour rester dans l'écosystème.
Quand ne pas l'utiliser
Pour un simple appel à un modèle avec un prompt, le SDK du fournisseur suffit et se debugge mieux. LangChain se justifie quand on veut changer de fournisseur sans réécrire, brancher des retrievers existants, ou tracer dans LangSmith.