LangChain est le framework IA le plus mentionné dans les tutos, les conférences, les offres d’emploi. J’ai passé plusieurs semaines à construire avec, puis j’ai tout réécrit directement avec les SDK. Voici ce que j’ai appris.
Ce que LangChain fait bien
Soyons honnêtes : LangChain résout de vrais problèmes.
- Démarrage rapide : chaîner un retriever + un LLM en 10 lignes
- Abstractions utiles : Document loaders, text splitters, vector store interfaces
- Écosystème : des centaines d’intégrations pré-faites
Pour un prototype ou un POC, c’est imbattable. Tu poses les fondations en une journée.
Pourquoi ça coince en production
1. Le debugging devient opaque.
Quand une chaîne LangChain produit une réponse inattendue, remonter jusqu’à la cause prend du temps. Les abstractions cachent exactement ce qui est envoyé au modèle. Avec un appel direct, le problème est immédiatement visible.
2. Les versions cassent.
LangChain a eu une v0.1, une v0.2, une v0.3 — chacune avec des breaking changes. J’ai eu des projets où mettre à jour une dépendance transitoire cassait silencieusement un pipeline en prod. Avec un wrapper maison sur le SDK officiel, tu contrôles tes dépendances.
3. L’overhead de performance.
Sur des pipelines batch, la couche d’abstraction LangChain ajoute de la latence mesurable. Pas dramatique, mais sur 100k appels/jour, ça compte.
4. Tu n’apprends pas ce que fait vraiment le modèle.
Si tu utilises LLMChain sans comprendre ce qu’il envoie comme prompt, tu ne peux pas optimiser. Les abstractions encouragent à ne pas regarder sous le capot.
Ce que j’utilise à la place
SDK officiel Anthropic (anthropic Python ou TypeScript). Stable, documenté, direct.
Pydantic pour la validation des sorties structurées. Mieux que le PydanticOutputParser de LangChain.
Propres abstractions minimalistes — 100-200 lignes de code que je comprends entièrement :
class LLMClient:
def __init__(self, model: str = "claude-sonnet-4-6"):
self.client = anthropic.Anthropic()
self.model = model
def complete(self, prompt: str, max_tokens: int = 1024) -> str:
response = self.client.messages.create(
model=self.model,
max_tokens=max_tokens,
messages=[{"role": "user", "content": prompt}]
)
return response.content[0].text
def complete_json(self, prompt: str, schema: type[T]) -> T:
text = self.complete(prompt + "\nRéponds uniquement en JSON valide.")
return schema.model_validate_json(text)
200 lignes que je maîtrise > 200 000 lignes que je subis.
Quand LangChain a du sens
- POC ou démo : oui, le time-to-demo est plus court
- Intégrations avec des systèmes exotiques (bases vectorielles obscures, formats propriétaires)
- Projets maintenus par une équipe qui connaît déjà bien LangChain
En dehors de ces cas, je préfère construire sur les SDK officiels. Le code est plus lisible, le debugging plus rapide, et le comportement en prod plus prévisible.
La règle que j’applique
Utilise un framework quand il te fait gagner du temps sur ta spécificité, pas sur la partie commune que tu pourrais écrire en une heure.
La partie commune (appel API, retry, parsing JSON) s’écrit en une heure. Investis ce temps.
Stéphanie Caumont
Product Owner IA · En savoir plus