~/wiki

Cheatsheets — vue longue

retour à la liste

Toutes les pages concaténées sur un seul document, pour un Ctrl-F direct.

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.

See also

API en mouvement. Le cœur — StateGraph, nœuds, arêtes conditionnelles, checkpointer — est stable. Vérifier les détails sur docs.langchain.com.

Orchestration d'agents comme graphe d'états. Là où une chaîne LCEL est un pipeline linéaire, LangGraph autorise les cycles, les branchements et la reprise après interruption.

L'idée

Un état partagé, des nœuds qui le transforment, des arêtes qui décident du nœud suivant. Chaque nœud reçoit l'état et renvoie les clés qu'il modifie, pas l'état entier.

uv add langgraph langchain-anthropic

Graphe minimal

from typing import Annotated, TypedDict
from operator import add
from langgraph.graph import StateGraph, START, END

class State(TypedDict):
    site: str
    steps: Annotated[list[str], add]     # les mises à jour s'accumulent
    verdict: str | None

def navigate(state: State) -> dict:
    return {"steps": [f"ouverture de {state['site']}"]}

def judge(state: State) -> dict:
    return {"verdict": "findable" if len(state["steps"]) < 5 else "unreachable"}

builder = StateGraph(State)
builder.add_node("navigate", navigate)
builder.add_node("judge", judge)
builder.add_edge(START, "navigate")
builder.add_edge("navigate", "judge")
builder.add_edge("judge", END)

graph = builder.compile()
print(graph.invoke({"site": "versace.com", "steps": [], "verdict": None}))
{'site': 'versace.com', 'steps': ['ouverture de versace.com'], 'verdict': 'findable'}

Annotated[list, add] est le mécanisme central : sans le réducteur, chaque nœud écraserait steps. Avec, les listes se concatènent. Pour les messages, langgraph.graph.message.add_messages gère aussi la déduplication par id.

Branchement conditionnel

def should_retry(state: State) -> str:
    if state["verdict"] == "unreachable" and len(state["steps"]) < 20:
        return "navigate"
    return END

builder.add_conditional_edges("judge", should_retry, ["navigate", END])

La fonction renvoie le nom du nœud suivant. C'est ce qui crée les cycles, et donc les boucles d'agent.

Boucle agent avec outils

from langgraph.prebuilt import create_react_agent
from langchain_core.tools import tool

@tool
def click(selector: str) -> str:
    """Clique sur un élément et retourne le nouvel état de la page."""
    return f"cliqué sur {selector}"

agent = create_react_agent(llm, tools=[click])
out = agent.invoke({"messages": [("user", "Trouve Najim 100 ml sur versace.com")]})
print(out["messages"][-1].content)

create_react_agent monte la boucle standard : le modèle propose un appel, un nœud ToolNode l'exécute, le résultat repart au modèle, jusqu'à une réponse sans tool call. Pour tout contrôle fin — budget d'étapes, vérification, sous-agents — écrire le graphe à la main.

Persistance et reprise

from langgraph.checkpoint.memory import MemorySaver

graph = builder.compile(checkpointer=MemorySaver())
config = {"configurable": {"thread_id": "session-42"}}

graph.invoke({"site": "versace.com", "steps": [], "verdict": None}, config)
graph.invoke({"site": "dior.com"}, config)      # reprend le même fil

print(graph.get_state(config).values["steps"])

Le thread_id identifie une conversation. En production, remplacer MemorySaver par un checkpointer SQLite ou Postgres pour survivre au redémarrage.

Interruption humaine

graph = builder.compile(checkpointer=MemorySaver(), interrupt_before=["judge"])

graph.invoke(initial, config)              # s'arrête avant "judge"
state = graph.get_state(config)
graph.update_state(config, {"verdict": "override"})
graph.invoke(None, config)                 # None = reprendre où on s'est arrêté

C'est le mécanisme des portes d'approbation avant une action irréversible — indispensable dès qu'un agent écrit quelque part.

Suivre l'exécution

for event in graph.stream(initial, config, stream_mode="values"):
    print(event["steps"][-1] if event["steps"] else "…")

stream_mode : values (l'état complet à chaque étape), updates (seulement les deltas), messages (les tokens du LLM).

Budget d'étapes

graph.invoke(initial, {"recursion_limit": 25, **config})

Au-delà, GraphRecursionError. Un agent qui boucle sans progresser est le mode d'échec par défaut — le budget est une sécurité, pas une optimisation.

Visualiser

print(graph.get_graph().draw_mermaid())
graph TD;
	__start__ --> navigate;
	navigate --> judge;
	judge -.-> navigate;
	judge -.-> __end__;

Utile en démo client : le diagramme se colle tel quel dans un document.

LangChain ou LangGraph

Chaîne linéaire, un aller-retour, pas d'état : LCEL suffit. Cycles, outils, reprise après échec, validation humaine : LangGraph. Les deux se composent — un nœud de graphe peut être une chaîne LCEL.

See also