LangChain на собеседовании Data Scientist

Проверь себя · 1/3разбор после ответа
В таблице сопряжённости есть ячейка с наблюдаемой частотой 0 (например, в одном сегменте никто не купил). Что важнее всего проверить перед выводами по chi-square?

Что такое LangChain

LangChain — фреймворк для приложений поверх LLM. Его главная идея — provider-agnostic абстракции: код пишется против единого интерфейса, а провайдера модели (OpenAI, Anthropic, локальная модель через Ollama) можно подменить, не переписывая логику. Из кирпичиков — модели, промпт-шаблоны, цепочки, агенты, память, ретриверы — собираются пайплайны от простого запроса до RAG и агентов.

from langchain_openai import ChatOpenAI

llm = ChatOpenAI(model="gpt-4o-mini")
response = llm.invoke("Привет")  # современный вызов — через .invoke()

На собесе DS LangChain спрашивают в блоке про LLM и generative AI. Интервьюер проверяет, понимаете ли вы базовые абстракции (чем chain отличается от agent, зачем нужна память), и — что важнее — когда фреймворк оправдан, а когда достаточно прямого вызова API. Умение сказать «здесь LangChain не нужен» ценится не меньше, чем знание его компонентов.

Chains

Chain — это конвейер вызовов со структурированными входом и выходом: промпт-шаблон подставляет переменные, модель отвечает, парсер приводит ответ к нужному формату. В современном LangChain цепочки собирают через LCEL (LangChain Expression Language) — композицию компонентов оператором |:

from langchain_core.prompts import PromptTemplate

prompt = PromptTemplate.from_template("Переведи на {lang}: {text}")
chain = prompt | llm  # LCEL: промпт → модель
result = chain.invoke({"lang": "русский", "text": "Hello"})

Оператор | читается как «выход слева подаётся на вход справа», поэтому цепочки легко наращивать: prompt | llm | output_parser. Ключевое свойство chain — детерминированный маршрут: вы сами задаёте порядок шагов, и модель не решает, что делать дальше. Старый API LLMChain делал то же самое, но объявлялся классом; в свежих версиях он считается устаревшим в пользу LCEL.

Agents

Агент — это когда модель сама решает, какой инструмент вызвать и в каком порядке, чтобы выполнить задачу. Классическая схема — ReAct (reasoning + acting): модель рассуждает, выбирает инструмент, смотрит результат и повторяет цикл, пока не придёт к ответу.

from langchain.agents import initialize_agent, Tool

tools = [
    Tool(name="search", func=search_web, description="поиск в интернете"),
    Tool(name="calc", func=calculate, description="калькулятор"),
]
agent = initialize_agent(tools, llm, agent="react")
agent.run("Какая погода в Москве, умножь температуру на 2")

Здесь маршрут недетерминированный: в отличие от chain, порядок вызовов определяет сама модель. Это гибко, но платно и рискованно — агент тратит много токенов на рассуждения, может зациклиться или выбрать не тот инструмент. initialize_agent — классический API; в актуальных версиях LangChain агентную часть перенесли в LangGraph, о котором ниже.

Memory

По умолчанию LLM не помнит предыдущие сообщения — каждый вызов независим. Память (memory) хранит состояние между вызовами: историю диалога, её краткую сводку или векторное хранилище фактов, — и подмешивает это в промпт.

from langchain.memory import ConversationBufferMemory

memory = ConversationBufferMemory()
chain = ConversationChain(llm=llm, memory=memory)

ConversationBufferMemory дословно складывает всю историю и на каждом шаге вставляет её в промпт. Просто, но опасно: длинный диалог быстро упирается в лимит контекстного окна и раздувает стоимость. Поэтому на длинных сессиях берут ConversationSummaryMemory (модель суммирует историю) или ретривер поверх векторной базы, который подтягивает только релевантные куски.

Готовься к собесу аналитика как в Duolingo
10 минут в день — SQL, Python, A/B, метрики. 1700+ вопросов в Telegram
Открыть Карьерник в Telegram

LangGraph

LangGraph — развитие LangChain для сложных агентных сценариев. Вместо линейной цепочки процесс описывается графом: узлы — это шаги, рёбра — переходы, причём переходы могут быть условными и образовывать циклы.

from langgraph.graph import StateGraph

builder = StateGraph(AgentState)
builder.add_node("classify", classifier)
builder.add_node("route_a", handler_a)
builder.add_node("route_b", handler_b)
builder.add_conditional_edges("classify", route)  # ветвление по результату
graph = builder.compile()

Ключевое отличие от «магии» агентов — явное состояние (AgentState), которое проходит через узлы, и явные переходы, которые вы контролируете. Это даёт то, чего не хватает production-системам: предсказуемость, обработку ошибок, чекпоинты (можно возобновить прогон с места сбоя) и human-in-the-loop (пауза на подтверждение человеком). Поэтому для прода агентов сегодня чаще собирают на LangGraph, а не на старом initialize_agent.

Альтернативы

LangChain — не единственный вариант, и на собесе полезно знать, чем он отличается от соседей:

  • LlamaIndex — заточен под RAG и индексацию документов. Если задача — «отвечать по корпусу документов», часто удобнее его.
  • Haystack — зрелый фреймворк для поисковых и QA-пайплайнов, сильная сторона — production-ready retrieval.
  • Pydantic AI — делает ставку на типобезопасность и валидацию структурированного вывода через Pydantic.
  • Vercel AI SDK — TypeScript-first, для LLM-фич во фронтенде и Node.js-бэкенде.
  • OpenAI Assistants API — нативный путь без стороннего фреймворка, но с привязкой к одному провайдеру (vendor lock-in).

У LangChain самая большая экосистема и интеграции, но его же и чаще всего критикуют за избыточные абстракции — для простых задач слои фреймворка добавляют сложности больше, чем пользы.

Частые ошибки

Тащить LangChain туда, где хватит одного вызова API. Для одиночного запроса к модели фреймворк — лишний слой. Хороший ответ на собесе включает «а нужен ли здесь вообще LangChain».

Путать chain и agent. Chain — детерминированный конвейер, маршрут задаёте вы. Agent — модель сама решает, что вызвать. Смешивать их в голове — типичный провал.

Недооценивать стоимость и непредсказуемость агентов. Каждый шаг рассуждения — это токены и латентность, а цикл может не сойтись. Для прода нужны лимиты на число шагов и явное состояние (LangGraph).

Не следить за контекстом в памяти. ConversationBufferMemory растёт неограниченно и однажды превышает окно модели. На длинных диалогах нужна суммаризация или ретрив по релевантности.

Связанные темы

FAQ

Чем chain отличается от agent?

Chain — это фиксированный конвейер: вы заранее задаёте последовательность шагов, и она всегда одинаковая. Agent даёт модели свободу самой выбирать инструменты и порядок вызовов под конкретный запрос. Chain предсказуем и дёшев, agent гибок, но дороже и рискованнее. На собесе за этот вопрос цепляются часто, так что держите разницу наготове.

Когда LangChain использовать не стоит?

Когда задача решается одним-двумя вызовами модели без сложной оркестрации. Прямой запрос к API провайдера будет проще, прозрачнее и легче в отладке, чем цепочка абстракций LangChain. Фреймворк оправдан, когда появляются несколько шагов, инструменты, память или RAG — то есть когда оркестрация действительно нужна.

В чём разница между LangChain и LangGraph?

LangChain хорош для линейных цепочек и быстрых прототипов. LangGraph описывает процесс как граф с явным состоянием, условными переходами и циклами, добавляет чекпоинты и human-in-the-loop. Для сложных агентов в проде берут LangGraph — там важны предсказуемость и восстановление после сбоев, чего линейным цепочкам не хватает.

LangChain или LlamaIndex для RAG?

LlamaIndex изначально заточен под RAG: индексация документов, ретрив, работа с разными источниками — из коробки и с меньшим количеством ручной сборки. LangChain тоже умеет RAG, но как одну из многих возможностей. Если продукт целиком про «вопросы-ответы по корпусу документов», LlamaIndex обычно даёт результат быстрее; если RAG — лишь часть большого пайплайна с агентами, удобнее оставаться в LangChain/LangGraph.

Как бороться с переполнением контекстного окна в памяти?

Не хранить весь диалог дословно. Варианты: суммаризировать историю (ConversationSummaryMemory), держать только последние N сообщений (скользящее окно) или вынести факты в векторную базу и подтягивать ретривером лишь релевантные фрагменты. Иначе ConversationBufferMemory растёт, упирается в лимит модели и раздувает стоимость каждого запроса.

Это официальная информация?

Нет. Статья основана на документации LangChain и LangGraph и типовых практиках. Конкретные требования зависят от компании, продукта и уровня позиции.


Тренируйте Data Science — откройте тренажёр с 1500+ вопросами для собесов.