LangChain

Введение в LangChain: цепочки и модели | Курс по LangChain урок 2

Введение в LangChain: цепочки и модели | Курс по LangChain урок 2
Mikhail
Автор
Mikhail
Опубликовано 23.02.2026
0,0
Views 23

Цель урока

Вы поймёте, что такое LangChain и зачем он нужен, научитесь делать вызовы LLM, строить первые цепочки с помощью LCEL и стримить ответы модели в реальном времени.

Необходимые знания:

  • Python на среднем уровне
  • Базовое понимание того, что такое API и HTTP запросы
  • Установленный Python 3.10+

Ключевые концепции:

  • LangChain как фреймворк для работы с LLM
  • Runnable интерфейс
  • LCEL (LangChain Expression Language)
  • Chain - цепочка обработки данных
  • ChatModel vs LLM
  • Сообщения: HumanMessage, AIMessage, SystemMessage

Зачем нужен LangChain

Когда разработчик впервые начинает работать с языковыми моделями, первая мысль это использовать API напрямую. Это выглядит примерно так:

import openai

client = openai.OpenAI(api_key="...")
response = client.chat.completions.create(
    model="gpt-4o",
    messages=[{"role": "user", "content": "Объясни, что такое рекурсия"}]
)
print(response.choices[0].message.content)

Для одного запроса это работает. Проблемы начинаются, когда нужно:

  • передавать результат одного вызова в следующий
  • добавить шаблоны промптов с переменными
  • парсить структурированный вывод из текста
  • добавить память и контекст диалога
  • подключить внешние инструменты

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

Важный момент: LangChain - это не замена работы с API. Это слой абстракций, который решает конкретные инженерные задачи при построении приложений с LLM. Если ваша задача это один запрос к модели, LangChain избыточен.


Как устроен LangChain

LangChain построен вокруг одной центральной идеи, и здесь важно разграничить версии.

Раньше (v0.x) фреймворк строился вокруг Runnable как ключевой абстракции. Все основные компоненты модели, промпты, парсеры, реализовывали единый интерфейс с методом .invoke(), что позволяло соединять их через оператор |.

prompt | model | parser

Каждый компонент получает данные, обрабатывает их и передает следующему. Это и есть LCEL (LangChain Expression Language).

Сейчас (v1+) фокус сместился на агентов. Runnable остался в langchain-core как низкоуровневый примитив для компоновки, но центральной абстракцией стали агенты на базе LangGraph с поддержкой persistence, streaming и HITL.

# v1+: агенты
from langchain.agents import create_agent

agent = create_agent(
    model="gpt-4o-mini",
    tools=[search_web],
    system_prompt="Помощник с инструментами"
)
result = agent.invoke({"messages": [{"role": "user", "content": "Привет!"}]})

Таким образом, идея единого интерфейса Runnable не устарела, она просто опустилась на уровень ниже, став строительным блоком для более высокоуровневых агентских абстракций.

Далее в курсе мы будем затрагивать тему создание агентов с помощью create_agent. В примерах будет использоваться LCEL, это не влияет на суть и смысл использования инструментов. Вы это же сможете сделать с использованием агентов. Подробнее об агентах, буду рассказывать в новом курсе.


Первый вызов LLM с помощью LangChain

import os
from dotenv import load_dotenv
from langchain_openai import ChatOpenAI

load_dotenv()

# Модель берём из .env, меняете там, а не в коде
model = ChatOpenAI(model=os.getenv("MODEL_NAME", "gpt-4o"))

# Простой вызов
response = model.invoke("Объясни, что такое рекурсия")
print(response.content)

Метод .invoke() возвращает объект AIMessage, у которого есть атрибут .content со строкой ответа.

LangChain v1: У AIMessage появилось свойство .text (без скобок), синоним .content. Старый метод .text() со скобками устарел. В курсе используется .content — это корректно и является рекомендованным подходом.


Типы сообщений

LangChain работает с LLM через структурированные сообщения. Есть три основных типа:

  • SystemMessage - инструкция для модели, задает контекст и поведение
  • HumanMessage - сообщение от пользователя
  • AIMessage - ответ модели
import os
from dotenv import load_dotenv
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage, AIMessage, SystemMessage

load_dotenv()
model = ChatOpenAI(model=os.getenv("MODEL_NAME", "gpt-4o"))

messages = [
    SystemMessage(content="Ты опытный Python-разработчик. Отвечай кратко и по делу."),
    HumanMessage(content="Что такое генератор в Python?"),
]

response = model.invoke(messages)
print(response.content)
# response - это AIMessage

Когда передаете простую строку в .invoke(), LangChain автоматически оборачивает её в HumanMessage. Явное использование типов сообщений дает больше контроля.


LCEL: строим первую цепочку

Теперь добавим шаблон промпта и соединим компоненты в цепочку:

import os
from dotenv import load_dotenv
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate

load_dotenv()

model = ChatOpenAI(model=os.getenv("MODEL_NAME", "gpt-4o"))

# Создаем шаблона промпта
prompt = ChatPromptTemplate.from_messages([
    ("system", "Ты опытный разработчик. Объясняй технические концепции четко и с примерами кода."),
    ("human", "Объясни концепцию: {concept}"),
])

# Собираем цепочку через оператор |
chain = prompt | model

# Вызываем цепочку, передаем словарь с переменными шаблона
response = chain.invoke({"concept": "замыкание в Python"})
print(response.content)

Что происходит при вызове chain.invoke({"concept": "замыкание в Python"}):

  1. prompt получает словарь, подставляет значения в шаблон, возвращает список сообщений
  2. model получает список сообщений, делает запрос к API, возвращает AIMessage

Оператор | - это не просто синтаксический сахар. Он создает объект RunnableSequence, который сам является Runnable. Это значит, что цепочку можно включить в другую цепочку.


Потоковый вывод (Stream)

Для длинных ответов удобно получать текст по мере генерации, а не ждать полного ответа:

import os
from dotenv import load_dotenv
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate


load_dotenv()

model = ChatOpenAI(model=os.getenv("MODEL_NAME", "gpt-4o"))
prompt = ChatPromptTemplate.from_messages([
    ("system", "Ты опытный разработчик. Объясняй четко и с примерами кода."),
    ("human", "Объясни концепцию: {concept}"),
])
chain = prompt | model

# .stream() возвращает итератор чанков
for chunk in chain.stream({"concept": "декоратор в Python"}):
    print(chunk.content, end="", flush=True)

Каждый chunk это AIMessageChunk с частью текста в .content. Метод .stream() работает у любого Runnable.


Batch

Если нужно обработать несколько запросов, .batch() делает это параллельно:

import os
from dotenv import load_dotenv
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate


load_dotenv()

model = ChatOpenAI(model=os.getenv("MODEL_NAME", "gpt-4o"))
prompt = ChatPromptTemplate.from_messages([
    ("system", "Ты опытный разработчик. Объясняй четко и с примерами кода."),
    ("human", "Объясни концепцию: {concept}"),
])
chain = prompt | model

concepts = [
    {"concept": "итератор"},
    {"concept": "контекстный менеджер"},
    {"concept": "метакласс"},
]

# Параллельные запросы к API
responses = chain.batch(concepts)

for concept, response in zip(concepts, responses):
    print(f"\n--- {concept['concept']} ---")
    print(response.content)

Параметры модели

temperature это ключевой параметр. При temperature=0 модель выбирает наиболее вероятный токен на каждом шаге. Для задач с однозначным ответом (код, структурированные данные) используйте ближе к 0. Для творческих задач 0.5 и выше.

max_tokens это ограничение длины ответа модели.

import os
from dotenv import load_dotenv
from langchain_openai import ChatOpenAI

load_dotenv()

model = ChatOpenAI(
    model=os.getenv("MODEL_NAME", "gpt-4o"),
    temperature=0,        # 0 = детерминированный вывод, 1 = более случайный
    max_tokens=500,       # ограничение длины ответа
    timeout=30,           # таймаут запроса в секундах
)

Распространенные ошибки

1. AuthenticationError

openai.AuthenticationError: Error code: 401 - Incorrect API key

Причина: API ключ не загружен или некорректный.

Решение: проверьте, что файл .env находится в той же директории, откуда запускаете скрипт, и что load_dotenv() вызывается до создания модели.

2. RateLimitError / Payment Required

openai.RateLimitError: Error code: 429
# или при использовании Rus-GPT:
# Error code: 402 - Payment Required

Причина: превышен лимит запросов или исчерпан баланс.

Решение: проверьте баланс в OpenAI dashboard или на rus-gpt.com.

3. ValidationError при вызове цепочки

pydantic.ValidationError: ... missing required field

Причина: в шаблоне промпта есть переменная {variable}, которую не передали в .invoke().

Решение: проверьте, что все переменные шаблона присутствуют в словаре.

4. Передача строки вместо словаря

chain.invoke("замыкание")  # ошибка, если в промпте есть переменные
chain.invoke({"concept": "замыкание"})  # правильно

Полный пример кода урока

import os
from dotenv import load_dotenv
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.messages import SystemMessage, HumanMessage

load_dotenv()

# --- Базовый вызов модели ---

model = ChatOpenAI(model=os.getenv("MODEL_NAME", "gpt-4o"))

response = model.invoke("Объясни, что такое рекурсия")
print(response.content)


# --- Типы сообщений ---

messages = [
    SystemMessage(content="Ты опытный Python-разработчик. Отвечай кратко и по делу."),
    HumanMessage(content="Что такое генератор в Python?"),
]

response = model.invoke(messages)
print(response.content)

# --- Цепочка с шаблоном промпта ---

prompt = ChatPromptTemplate.from_messages([
    ("system", "Ты опытный разработчик. Объясняй технические концепции четко и с примерами кода."),
    ("human", "Объясни концепцию: {concept}"),
])

chain = prompt | model

response = chain.invoke({"concept": "замыкание в Python"})
print(response.content)
print()

# --- Потоковый вывод ---

print("Потоковый вывод:")
prompt = ChatPromptTemplate.from_messages([
    ("system", "Ты опытный разработчик. Объясняй четко и с примерами кода."),
    ("human", "Объясни концепцию: {concept}"),
])
chain = prompt | model

for chunk in chain.stream({"concept": "декоратор в Python"}):
    print(chunk.content, end="", flush=True)
print()

# --- Batch ---

print("\nBatch:")
prompt = ChatPromptTemplate.from_messages([
    ("system", "Ты опытный разработчик. Объясняй четко и с примерами кода."),
    ("human", "Объясни концепцию: {concept}"),
])
chain = prompt | model

concepts = [
    {"concept": "итератор"},
    {"concept": "контекстный менеджер"},
    {"concept": "метакласс"},
]

responses = chain.batch(concepts)

for concept, response in zip(concepts, responses):
    print(f"\n--- {concept['concept']} ---")
    print(response.content)

Практическое задание

Создайте инструмент для генерации документации функций.

Требования:

  1. Промпт принимает два параметра: code (код функции) и style (стиль документации: "Google", "NumPy" или "reStructuredText")
  2. Модель должна возвращать только строку docstring, без лишних объяснений
  3. Реализуйте потоковый вывод результата
  4. Обработайте случай, когда переданный код не является функцией и добавьте соответствующую инструкцию в системный промпт

Пример входных данных:

code = """
def calculate_discount(price: float, discount_percent: float) -> float:
    if discount_percent < 0 or discount_percent > 100:
        raise ValueError("Discount must be between 0 and 100")
    return price * (1 - discount_percent / 100)
"""
style = "Google"

Ожидаемый результат: готовый docstring в указанном стиле.


Итоги урока

В этом уроке промпт был простым, строки с переменными. В следующем уроке разберем PromptTemplate более детально few-shot примеры, парциальные шаблоны и главное, это output parsers. Это позволит получать от модели не просто текст, а структурированные данные: словари, списки, модели Pydantic. Это важно для построения надежных пайплайнов.

<< Урок 1

Урок 3 >>


Подписывайтесь на мой Telegram канал

Если вам нужен ментор и вы хотите научиться разрабатывать AI агентов, пишите, обсудим условия

Авторизуйтесь, чтобы оставить комментарий.

Комментариев: 0

Нет комментариев.

Тут может быть ваша реклама

Пишите info@aisferaic.ru

Похожие туториалы