Когда последний раз видел чужой LangChain-проект, в котором можно было разобраться без экскурсовода? Я не могу вспомнить. Каждый раз одно и то же: цепочки, обёрнутые в цепочки, агенты, которые зовут инструменты, которые зовут ещё агентов, и где-то в этом слоёном пироге живёт логика, которую автор уже сам забыл.
Я сам через это прошёл. Начинал с туториала, который выглядел красиво. Потом потратил два вечера, пытаясь понять, почему мой ретривер ведёт себя не так, как написано в доке — а дока уже устарела, потому что вышла новая версия и всё переименовали. Потом ещё полчаса искал, в каком именно слое абстракции теряется мой промпт. В итоге переписал всё руками — и стало проще.
Что такое LangChain на самом деле
LangChain продаёт себя как фреймворк для LLM-приложений. На практике — набор обёрток, которые скрывают вызовы к API за слоями классов и протоколов. LCEL — это DSL для описания цепочек. Агенты — цикл, который решает, какой инструмент вызвать следующим. Memory — буфер с историей сообщений.
Всё это звучит удобно, пока не сталкиваешься с реальной задачей.
Возьмём ретривал. В LangChain есть VectorStoreRetriever, MultiQueryRetriever, ContextualCompressionRetriever и ещё добрый десяток вариантов. У каждого свои параметры, свои зависимости, своя логика работы. Когда результаты не те — непонятно, где копать. Абстракция съела контекст. А если написать то же самое в лоб — сто строк Python с чистым вызовом Chroma или Pinecone — сразу видно, что происходит в каждый момент.
Проблема не в идее, а в реализации
Я не против абстракций вообще. Они хороши, когда реально прячут сложность, а не добавляют новую. LangChain добавляет.
Вот пример из жизни. Был у меня простой агент, который должен был отвечать на вопросы по базе знаний и при необходимости вызывать внешний API. Классический use case, для которого LangChain якобы и создан. Взял AgentExecutor, подключил инструменты, запустил. Агент периодически зависал, иногда вызывал инструменты по кругу, иногда игнорировал один из них без объяснений.
Дебаггинг был мучением. verbose=True выдавал стену логов, в которой нужно было искать момент, где что-то пошло не так. Промежуточные состояния — внутри классов, которые я не писал. В итоге я написал агентный цикл с нуля: while not done, парсинг ответа модели, вызов нужной функции, подстановка результата обратно в контекст. Меньше 80 строк. Зависаний больше не было.
Версионный ад и документация, которая врёт
Отдельная история — темп изменений в библиотеке. За последние полтора года LangChain прошёл через несколько крупных рефакторингов. langchain разделился на langchain-core, langchain-community, langchain-openai и так далее. Старые цепочки на LLMChain объявили устаревшими, переписали на LCEL. Документация обновлялась не всегда синхронно с кодом.
Несколько раз находил на Stack Overflow ответ, который работал в версии 0.0.x — и не работал в 0.1.x, потому что класс переехал или переименовался. Это не редкость, это норма для LangChain. Начинаешь новый проект, копируешь код из туториала годичной давности — и готовься к сюрпризам.
Для продакшена это серьёзная проблема. Компания вложила время в разработку на LangChain, всё работает — и вдруг обновление ломает половину логики, потому что внутренние интерфейсы поменялись. Я видел такое несколько раз у коллег. Заморозить версию можно, но тогда теряешь исправления багов и совместимость с новыми моделями.
Когда LangChain всё-таки имеет смысл
Честно: для прототипов и учебных проектов он нормально работает. Нужно за час показать демо с RAG-пайплайном — готовые кубики есть. Хочешь разобраться в чужой идее — LCEL читается прилично.
Но это и есть потолок. Как только проект переходит в серьёзную разработку — начинается борьба с фреймворком вместо решения задачи. Нужно кастомизировать поведение — лезешь в кишки классов, которые не рассчитаны на это. Нужно протестировать логику — а она размазана по десяти слоям. Нужно оптимизировать запросы — а ты даже не до конца понимаешь, что именно отправляется в API.
Альтернатива простая. Для большинства задач хватает прямых вызовов к SDK модели плюс несколько утилитарных функций, написанных руками. Semantic Kernel — если нужна структура. LlamaIndex — если фокус именно на данных и ретривале. Или вообще без фреймворка — код получается короче и понятнее.
Настоящая проблема
LangChain стал популярным в момент, когда никто толком не понимал, как строить LLM-приложения. Он заполнил пустоту и дал людям ощущение прогресса. Но пустота заполнилась — паттерны устоялись, опыт накопился, стало понятно, что агентный цикл не такой уж магический.
И теперь LangChain — как jQuery в 2024 году. Когда-то решал реальные проблемы. Сейчас те же задачи решаются чище без него, а фреймворк добавляет больше, чем убирает.
Я не говорю, что его надо избегать религиозно. Но когда в следующий раз потянешься за pip install langchain — сначала спроси себя: а что именно он даст, чего нельзя написать за полчаса самому?
