Python & AI Tutorials Logo
LangChain & LangGraph

2. Основы создания агентов

В Главе 1 вы сделали свой первый вызов LLM и увидели базовый поток «запрос–ответ». Теперь нам нужно понять, что именно мы строим: системы агентного ИИ (agentic AI). Эта глава закладывает базовые концепции, которые вы будете использовать на протяжении всей книги.

К концу этой главы вы поймёте:

  • Что делает ИИ-систему агентной (и почему это важно)
  • Зачем существуют фреймворки вроде LangChain и LangGraph
  • Как LLM на самом деле работают «под капотом» (и почему это влияет на дизайн агентов)
  • Экономику использования LLM (токены, стоимость и выбор модели)
  • Как писать эффективные промпты для агентных систем

Это концептуальная глава — к практическому кодингу мы вернёмся в Главе 3. Но эти концепции критически важны для понимания дизайн-решений, которые вы будете принимать при создании агентов.

2.1) Что такое агентный ИИ? (Чат-бот vs Агент)

Когда большинство людей думают об «ИИ-приложениях», они представляют чат-ботов: вы задаёте вопрос, ИИ отвечает, разговор заканчивается. Но агентный ИИ принципиально отличается. Агент не просто отвечает — он автономно действует для достижения целей.

Ключевое различие

Начнём с чёткого определения:

Чат-бот: система, которая отвечает на пользовательский ввод сгенерированным текстом. Взаимодействие чисто разговорное. У чат-бота нет возможности совершать действия в мире, кроме генерации следующего сообщения.

Агент: система, которая может:

  1. Воспринимать своё окружение (например, читать статус заказа, проверять складские остатки)
  2. Решать, какие действия предпринять (например, выбрать между: создать тикет, отправить email или эскалировать)
  3. Действовать на основе этих решений (например, вызвать API, обновить базу данных)
  4. Итерировать до достижения цели (например, проверить заказ → создать тикет → подтвердить → готово)

Ключевое отличие — автономность и действие. Чат-бот реагирует; агент проактивен.

Конкретный пример: поддержка клиентов

Посмотрим на это различие на практике.

Подход чат-бота:

Пользователь: "Я хочу вернуть деньги за заказ #12345"
 
Чат-бот: "Я понимаю, что вы хотите вернуть деньги. Чтобы обработать это, пожалуйста:
1. Перейдите на наш портал возвратов по адресу example.com/refunds
2. Введите номер заказа
3. Выберите причину
4. Отправьте форму
5. Подождите 24–48 часов для ручной проверки
 
Я могу помочь вам с чем-то ещё?"

Чат-бот предоставляет информацию, но не предпринимает действий. Пользователь должен сделать всю работу.

Подход агента:

Пользователь: "Я хочу вернуть деньги за заказ #12345"
 
Агент (внутренние рассуждения):
1. Пользователь хочет возврат по заказу #12345
2. Мне нужно проверить, что этот заказ существует
3. [Вызывает get_order_details(order_id="12345")]
4. Заказ найден, подходит для возврата
5. [Вызывает create_refund_ticket(order_id="12345", reason="customer_request")]
6. Тикет создан: TICKET-789
 
Агент: "Я создал тикет на возврат TICKET-789 для заказа #12345. 
Наша команда по возвратам обработает это в течение 3–5 рабочих дней. 
Вскоре вы получите подтверждение по электронной почте."

Агент выполнил действие: он проверил заказ, создал тикет и подтвердил результат. Проблема пользователя решена без ручных шагов.

Почему это важно для разработки

Понимание этого различия формирует то, как вы проектируете систему:

Разработка чат-бота:

  • Фокус на качестве ответов и сценариях диалога
  • Основная задача: генерировать полезный, точный текст
  • Простая архитектура: промпт → LLM → ответ
  • Нет необходимости во внешних интеграциях

Разработка агента:

  • Фокус на принятии решений и выполнении действий
  • Основные задачи: выбирать корректные действия, обрабатывать ошибки, поддерживать состояние
  • Сложная архитектура: восприятие → рассуждение → выбор действия → выполнение → проверка
  • Требуются интеграции инструментов, обработка ошибок, управление состоянием

Спектр автономности

Не все агенты одинаково автономны. Есть спектр:

Уровень 1: Ассистируемые действия

  • Агент предлагает действия, пользователь подтверждает каждое
  • Пример: «Я могу создать тикет на возврат. Продолжить?»
  • Самый безопасный подход для высокорисковых операций

Уровень 2: Ограниченная автономность

  • Агент действует в рамках заранее заданных ограничений
  • Пример: может создавать тикеты и отправлять email, но не может обрабатывать возвраты свыше $500 или напрямую получать доступ к платёжным системам
  • Самый распространённый вариант в продакшене (баланс эффективности и безопасности)

Уровень 3: Полная автономность

  • Агент самостоятельно действует для достижения целей
  • Пример: обрабатывает весь процесс возврата без участия человека
  • Требуются надёжные защитные ограничения (guardrails) и мониторинг

Ключевые характеристики агентов

Подытожим: система агентного ИИ обладает следующими основными свойствами:

  1. Использование инструментов (tool-using): может вызывать функции, API и внешние сервисы (без этого это просто чат-бот)
  2. Ориентация на цель (goal-directed): работает на достижение конкретных результатов, а не просто отвечает
  3. Многошаговость (multi-step): разбивает сложные задачи на последовательности действий
  4. Адаптивность (adaptive): корректирует поведение на основе промежуточных результатов
  5. С сохранением состояния (stateful): поддерживает контекст на протяжении нескольких взаимодействий

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

Теперь вы понимаете, что такое агенты и почему они мощны.

2.2) Зачем LangChain и LangGraph?

Возможно, вы думаете: «Зачем мне фреймворки? Разве я не могу просто вызывать OpenAI API напрямую?» Давайте разберёмся, зачем существуют фреймворки и какие проблемы они решают.

Сложность разработки агентов

Построить простого чат-бота на «сыром» API-вызове довольно просто:

python
import openai
 
response = openai.chat.completions.create(
    model="gpt-5-mini",
    messages=[{"role": "user", "content": "Привет!"}]
)
print(response.choices[0].message.content)

Для базовых сценариев это работает нормально. Но как только вы хотите построить агента, сложность резко растёт:

Проблема 1: Многошаговые рабочие процессы (workflows) Ваш агент должен:

  • Получать релевантные документы из базы знаний
  • Решать, какой инструмент вызвать, исходя из намерения пользователя
  • Выполнять инструмент и обрабатывать ошибки
  • Форматировать результаты и отвечать пользователю

Каждый шаг требует аккуратной оркестрации, обработки ошибок и управления состоянием.

Проблема 2: Абстракция провайдеров Что, если вы хотите:

  • Перейти с OpenAI на Anthropic или Google?
  • Использовать разные модели для разных задач?
  • Падать обратно на более дешёвую модель, если основная не сработала?

С «сырыми» API-вызовами вам пришлось бы переписать значительную часть кода под каждого провайдера.

Проблема 3: Память разговора Агентам нужно помнить контекст:

  • Предыдущие сообщения в диалоге
  • Извлечённые документы из предыдущих запросов
  • Промежуточные результаты вызовов инструментов

Ручное управление этим состоянием — источник ошибок и рутины.

Проблема 4: Интеграция инструментов Ваш агент должен:

  • Определить доступные инструменты со схемами
  • Позволить LLM выбрать, какой инструмент вызвать
  • Парсить аргументы инструмента из вывода LLM
  • Безопасно выполнять инструменты с валидацией
  • Обрабатывать ошибки инструментов и логику повторов

Это требует значительного количества шаблонного кода и вопросов безопасности на каждом шаге.

Проблема 5: Сложная маршрутизация Реальным агентам нужна условная логика:

  • «Если пользователь спрашивает о возвратах, извлечь документы политики»
  • «Если пользователь хочет возврат, создать тикет»
  • «Если вопрос не по теме, вежливо отказать»

Реализация этого через if-else быстро становится неподдерживаемой.

Что даёт LangChain

LangChain — это фреймворк для создания LLM-приложений. Он предоставляет:

1. Абстракция моделей

Единый интерфейс для разных провайдеров LLM (OpenAI, Anthropic, Google и т. д.).

python
from langchain_openai import ChatOpenAI
from langchain_anthropic import ChatAnthropic
 
# Одинаковый интерфейс, разные провайдеры
openai_llm = ChatOpenAI(model="gpt-5-mini")
anthropic_llm = ChatAnthropic(model="claude-4-5-sonnet")
 
# Оба используют .invoke() с одинаковым форматом сообщений
response = openai_llm.invoke([{"role": "user", "content": "Привет"}])

Меняйте провайдеров без переписывания логики приложения.

2. Компонуемые цепочки (LCEL)

LangChain Expression Language — синтаксис для соединения компонентов в пайплайны.

python
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
 
# Собираем пайплайн оператором | (как Unix pipes)
chain = prompt | llm | output_parser
 
# Выполняем весь пайплайн одним вызовом
result = chain.invoke({"input": "вопрос пользователя"})

Стройте сложные рабочие процессы (workflows) без ручной передачи данных между шагами. LCEL мы изучим в Главе 6.

3. Память разговора

Управление историей сообщений для диалогов с состоянием.

python
from langchain_core.chat_history import InMemoryChatMessageHistory
 
# Хелпер истории сообщений
history = InMemoryChatMessageHistory()
history.add_user_message("Привет")
history.add_ai_message("Здравствуйте!")
 
# Получаем сообщения при необходимости
messages = history.messages
response = llm.invoke(messages)

Абстрагируйте хранение сообщений с помощью вспомогательных классов вместо ручного управления списками. На этом этапе история всё ещё передаётся в модель явно. Мы интегрируем это в цепочки в Главе 8. LangGraph (Главы 15+) делает это ещё проще благодаря встроенному управлению состоянием.

4. Интеграция инструментов

Система на основе декораторов для экспонирования Python-функций в LLM.

python
from langchain_core.tools import tool
 
@tool
def create_ticket(order_id: str, reason: str) -> str:
    """Создаёт тикет в поддержку для заказа."""
    # Реализация здесь
    return f"Тикет создан для {order_id}"
 
# LangChain обрабатывает генерацию схемы и интеграцию с LLM

Превращайте любую Python-функцию в инструмент, который может быть обнаружен и вызван tool-enabled LLM или агентами — без ручного написания JSON-схем.

5. Загрузчики документов и векторные хранилища

Готовые компоненты для загрузки документов из разных источников и хранения их в виде поисковых эмбеддингов.

python
from langchain_community.document_loaders import TextLoader
from langchain_chroma import Chroma
from langchain_openai import OpenAIEmbeddings
 
# Загружаем документы
docs = TextLoader("support_docs.txt").load()
 
# Создаём поисковый индекс
embeddings = OpenAIEmbeddings()
vectorstore = Chroma.from_documents(docs, embeddings)
 
# Извлекаем релевантные документы
results = vectorstore.similarity_search("политика возвратов")

Стройте RAG-системы, используя готовые загрузчики и векторные хранилища, вместо реализации парсинга, эмбеддингов и извлечения (retrieval) с нуля.

Что даёт LangGraph

LangGraph расширяет LangChain для сложных агентных рабочих процессов (workflows). Он предоставляет:

1. Явное управление состоянием

Определяйте все данные агента в одной типизированной схеме вместо того, чтобы разбрасывать их по переменным.

python
from typing import TypedDict, Optional
from langchain_core.messages import BaseMessage
from langchain_core.documents import Document
 
class AgentState(TypedDict):
    messages: list[BaseMessage]
    retrieved_docs: list[Document]
    current_step: Optional[str]
 
# Все данные агента живут здесь — одно место для проверки при отладке

Больше не нужно искать, где живут данные и как они протекают между шагами. Изменения состояния явны: узлы читают state и возвращают обновления. IDE автодополняет имена полей, а проверки типов ловят ошибки ещё до выполнения.

2. Рабочие процессы на основе графа

Стройте рабочие процессы, объявляя шаги (узлы) и их связи (рёбра), а не пишите оркестрационный код.

python
graph = StateGraph(AgentState)
 
# Определяем узлы (шаги вашего workflow)
graph.add_node("retrieve", retrieve_docs)
graph.add_node("answer", generate_answer)
 
# Определяем рёбра (переходы между шагами)
graph.add_edge("retrieve", "answer")

Вы задаёте структуру — «сначала retrieve, потом answer» — а LangGraph управляет выполнением. Не нужно писать оркестрационный код для передачи состояния между шагами. Управляющий поток, такой как последовательность или ветвления, объявляется в самой структуре графа.

3. Условная маршрутизация

Принятие решения во время выполнения о том, какой путь выбрать в workflow.

python
from langchain_core.messages import BaseMessage
 
def route_request(state):
    last_message = state["messages"][-1].content
    if "refund" in last_message:
        return "create_ticket"
    else:
        return "answer_question"
 
graph.add_conditional_edges(
    "classify",
    route_request,
    {
        "create_ticket": "create_ticket",
        "answer_question": "answer_question",
    }
)

Ключевой инсайт: функция маршрутизации возвращает имя следующего узла ("create_ticket" или "answer_question").

Почему это важно: ваша логика принятия решений отделена от выполнения. Хотите изменить правила маршрутизации? Отредактируйте одну функцию. Хотите увидеть все возможные пути? Посмотрите определение графа. Хотите отладить, какой путь был выбран? Изучите трассировку выполнения — без копания в вложенных вызовах функций.

4. Чекпоинтинг и персистентность

Состояние автоматически чекпоинтится после каждого шага, что позволяет восстанавливаться после сбоев и делать workflow «пауза–продолжение».

python
from langgraph.checkpoint.sqlite import SqliteSaver
 
checkpointer = SqliteSaver("agent_state.db")
graph = graph.compile(checkpointer=checkpointer)
 
# Состояние автоматически сохраняется на каждом шаге
result = graph.invoke(
    input_state,
    config={"configurable": {"thread_id": "user-123"}}
)

После каждого шага LangGraph сохраняет текущее состояние в постоянное хранилище. Если процесс падает, выполнение может продолжиться с последнего чекпоинта, связанного с тем же thread ID.

Чекпоинтинг позволяет реализовывать workflow «пауза–продолжение», например ожидание одобрения человеком, в сочетании с условной маршрутизацией или прерываниями.

Когда использовать каждый фреймворк

Используйте LangChain, когда:

  • Строите простые цепочки (промпт → LLM → парсер)
  • Реализуете RAG-системы
  • Работаете с контекстом на основе сообщений (явно передаёте историю чата)
  • Абстрагируетесь от конкретных провайдеров LLM

Используйте LangGraph, когда:

  • Строите многошаговые agent workflow
  • Реализуете условную маршрутизацию
  • Управляете сложным состоянием между шагами
  • Требуются чекпоинтинг и восстановление после сбоев

2.3) Как работают LLM (для создателей агентов)

Чтобы создавать эффективных агентов, нужно понимать, как LLM на самом деле работают. Речь не о математике трансформеров — а о ментальной модели, которая влияет на то, как вы проектируете агентные системы.

Базовый механизм: предсказание токенов

Вот фундаментальный инсайт: LLM не «знают» факты так, как это делают базы данных. Они предсказывают следующий токен.

Разберём это на конкретном примере.

Ввод: "The capital of France is"

Что вам может казаться:

  1. LLM «ищет» столицу Франции в своей базе знаний
  2. LLM «извлекает» ответ: Paris
  3. LLM возвращает "Paris"

Что происходит на самом деле:

  1. LLM преобразует ввод в токены: ["The", "capital", "of", "France", "is"]
  2. LLM вычисляет распределение вероятностей по ВСЕМ возможным следующим токенам
  3. Самый вероятный следующий токен: "Paris" (наибольшая вероятность)
  4. LLM выбирает токен из распределения (обычно выбирая самый вероятный)
  5. LLM возвращает "Paris"

LLM не «знает», что Paris — столица. Она предсказывает, что "Paris" — самый вероятный следующий токен для данного шаблона ввода.

(Примечание: токенизация и вероятности здесь концептуальны и зависят от модели и токенизатора.)

Почему это важно для агентов

Модель предсказания токенов имеет глубокие последствия для дизайна агентов:

Следствие 1: LLM могут галлюцинировать

Поскольку LLM предсказывают токены (а не извлекают факты), они могут генерировать правдоподобную, но неверную информацию.

python
from langchain_openai import ChatOpenAI
 
llm = ChatOpenAI(model="gpt-4o-mini")
response = llm.invoke("Какова столица Атлантиды?")
print(response.content)
 
# Примечание: фактический вывод может отличаться. 
# Современные модели могут распознать Атлантиду как вымышленную и отказаться отвечать.
# Ключевой момент:
# без явной проверки фактов LLM могут генерировать правдоподобную, но неверную информацию.

Возможный (исторический или без ограничений) вывод:

Столица Атлантиды — Этерия. Согласно записям Платона, Этерия была портовым городом, расположенным на самом восточном краю острова, а её название происходило от убеждения, что город простирался до небес.

LLM генерирует разумно звучащий ответ, хотя Атлантида — вымышленная. Для агентов это означает:

  • Никогда не доверяйте выводу LLM вслепую
  • Проверяйте факты по авторитетным источникам
  • Встраивайте защитные ограничения (guardrails)

Следствие 2: Контекст — это всё

LLM видит только те токены, которые вы ей предоставляете. У неё нет памяти о предыдущих разговорах, если вы явно не включили этот контекст.

python
# Первый вызов
response1 = llm.invoke("Меня зовут Алиса")
print(response1.content)  # "Рада познакомиться, Алиса!"
 
# Второй вызов (отдельный вызов)
response2 = llm.invoke("Как меня зовут?")
print(response2.content)  # "У меня нет доступа к вашему имени..."

Во втором вызове нет контекста из первого. Для агентов это означает:

  • Вы должны управлять историей разговора
  • Важны ограничения окна контекста
  • Управление состоянием критично

Следствие 3: Промпты — это инструкции, а не запросы

Поскольку LLM предсказывают токены, формулировка промпта драматически влияет на качество вывода.

python
# Слабый промпт (в стиле запроса)
response = llm.invoke("политика возвратов")
# Вывод: "Что вы хотите узнать о политике возвратов?"
 
# Сильный промпт (в стиле инструкции)
response = llm.invoke(
    "Вы — агент службы поддержки. Объясните нашу политику возвратов ясно и кратко."
)
# Вывод: "Наша политика возвратов позволяет вернуть товар в течение 30 дней..."

Для агентов это означает:

  • Промпты — ваш основной механизм управления
  • Промпт-инжиниринг — ключевой навык
  • System messages задают поведение агента

Следствие 4: Структурированный вывод требует подсказок

LLM естественным образом генерируют свободный текст. Чтобы получать структурированный вывод (JSON, конкретные форматы), нужны явные инструкции.

python
# Без указания структуры
response = llm.invoke("Извлеките order ID из: 'I want a refund for order #12345'")
print(response.content)
# Вывод: "order ID — 12345" (простой текст, непоследовательный формат)
 
# С указанием структуры
response = llm.invoke(
    'Извлеките order ID и верните ТОЛЬКО JSON-объект в формате: {"order_id": "..."}\n\n'
    "Text: 'I want a refund for order #12345'"
)
print(response.content)
# Вывод: {"order_id": "12345"} (структурировано, парсится)

Для агентов это означает:

  • Используйте схемы, чтобы ограничивать формат вывода
  • Явно задавайте форматы вывода
  • Валидируйте и парсите ответы LLM

Свободный текст оптимизирован для людей. Агентам нужно явное форматирование, чтобы обеспечить машинную читаемость.

Детерминизм, стохастичность и современные LLM

LLM по своей природе — вероятностные системы. Они генерируют текст, предсказывая наиболее вероятные следующие токены, а не выполняя детерминированные правила. В результате один и тот же ввод не всегда гарантирует один и тот же вывод.

В ранних моделях разработчики явно управляли этой случайностью с помощью параметров вроде temperature. Низкие значения давали более предсказуемые ответы, а высокие стимулировали вариативность и креативность.

Многие современные reasoning-oriented модели больше не выставляют параметры вроде temperature или top_p. Вместо этого они управляют стратегиями декодирования и семплирования внутри, чтобы приоритизировать стабильные, структурированные рассуждения. Однако это не означает, что такие модели полностью детерминированы.

Даже эти модели не гарантируют идентичные ответы. Вывод может различаться из-за:

  • Внутреннего семплирования: модель может идти разными путями рассуждений, выдавая ответы, которые отличаются структурой, деталями или формулировкой.
  • Обновлений модели: провайдеры постоянно обновляют модели без уведомлений, поэтому один и тот же промпт может давать разные ответы со временем.
  • Фильтров безопасности: модерация контента может привести к тому, что в одном случае модель ответит прямо, а в другом — будет уклоняться, откажется или переформулирует.
  • Политик инструментов: в агентных системах модель может вызвать разные инструменты — или не вызвать ни одного — для одного и того же ввода, изменяя пути выполнения.

Что это означает для разработчиков агентов:

Ключевая проблема — не настройка параметров, а предсказуемость. Агентные системы следует проектировать, исходя из того, что вывод LLM может различаться по формулировкам, структуре или даже выводам, если он явно не ограничен.

Это приводит к нескольким конкретным принципам проектирования агентных систем:

  • Никогда не полагайтесь на точные формулировки для логических решений — управление потоком должно опираться на структурированные сигналы (схемы, enums, флаги), а не на сопоставление конкретных фраз в выводе модели.
  • Навязывайте структуру на границах — когда вывод LLM потребляется кодом, ограничивайте его схемами, валидаторами или строгими форматами, чтобы программе не приходилось «интерпретировать» свободный текст.
  • Проверяйте всё, что важно — факты, влияющие на деньги, права доступа или необратимые действия, должны проверяться инструментами или внешними системами, а не приниматься на веру из модели.
  • Считайте вывод LLM предложениями, а не решениями — модель предлагает, что делать, но система решает, нужно ли, когда и как действовать.

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

Ключевой вывод: создавайте надёжных агентов через схемы, валидацию и интеграцию инструментов — а не надеясь на стабильные ответы LLM.

В следующем разделе мы рассмотрим экономические последствия токен-ориентированной обработки.

2.4) Токены: фундаментальный ресурс

Токены — базовая единица, которую обрабатывают LLM. Понимание токенов важно, потому что они определяют и что возможно (ограничения), и что дорого (стоимость) в агентных системах.

Что такое токен?

Токен — это минимальная единица текста, над которой LLM рассуждает и которую генерирует.

В зависимости от языка и контекста токен может представлять:

  • Слово (agent)
  • Часть слова (calculat, ion)
  • Число или символ (#, 123)
  • Пунктуацию или пробелы

Токены — это не символы и не слова; это модель-специфичные единицы, создаваемые токенизатором.

Любая информация, отправляемая модели или генерируемая ею, измеряется в токенах:

  • System instructions
  • Сообщения пользователя
  • Извлечённые документы
  • Описания инструментов
  • Вывод модели

Токены — фундаментальная валюта взаимодействия с LLM.

Токены как системное ограничение

Токены — это не только стоимость, но и жёсткий лимит того, что ваш агент может сделать в одном запросе.

У каждой LLM есть окно контекста: фиксированный максимум токенов, которые она может обработать за раз.

Пример сценария: Вашему агенту поддержки клиентов нужно:

  • System instructions: 200 токенов
  • Последние 10 сообщений: ~2 000 токенов
  • 3 извлечённые статьи помощи: ~1 500 токенов
  • Сгенерированный ответ: ~200 токенов
  • Итого: 3 900 токенов

Если окно контекста вашей модели — 4 000 токенов, вы используете 97,5% ёмкости. Ещё одно длинное сообщение — и система перестаёт работать.

(Современные модели обычно имеют окна контекста 128K+ токенов, но принцип остаётся тем же: контекст конечен, и вы должны проектировать с учётом этого лимита.)

Что происходит при превышении лимита:

  • Старые сообщения отбрасываются → агент забывает ранний контекст, ломая непрерывность диалога
  • Извлечённые документы обрезаются → критическая информация теряется, приводя к неверным ответам
  • Запрос полностью падает → система вообще не может ответить

Нельзя просто заплатить за большее пространство. Окно контекста — жёсткий лимит, как попытка уместить 2 литра в бутылку объёмом 1 литр.

Вот почему долгоживущие агенты должны активно управлять тем, что остаётся в контексте, а что — нет. Управление токенами — ключевая архитектурная задача, а не деталь оптимизации.

На что токены влияют напрямую

Помимо немедленного ограничения окна контекста, токены формируют два критически важных дизайн-решения:

1. Стратегия памяти: полная история vs суммаризация

Хранение полной истории разговора сохраняет детали, но приводит к непрерывному росту расхода токенов.

Частая альтернатива — суммаризация памяти:

  • Заменить старые сообщения компактным резюме
  • Сохранить намерение при уменьшении стоимости по токенам

Этот компромисс влияет на:

  • Стоимость
  • Точность
  • Долгосрочную согласованность поведения агента

Таким образом, дизайн памяти — это задача управления токенами.

2. Размер чанка в RAG и стратегия извлечения

В Retrieval-Augmented Generation (RAG) документы перед извлечением делятся на чанки.

  • Большие чанки

    • Меньше вызовов извлечения
    • Выше стоимость по токенам на запрос
    • Больше нерелевантного контекста
  • Маленькие чанки

    • Ниже стоимость по токенам
    • Выше точность
    • Риск пропустить ключевую информацию

Размер чанка — критическое дизайн-решение: он напрямую влияет и на стоимость, и на качество ответов.

Экономика токенов

Понимание стоимости токенов помогает строить экономически эффективные системы.

Типичные цены (2026):

МодельВвод (за 1M токенов)Вывод (за 1M токенов)
GPT-5$1.25$10.00
GPT-5-mini$0.25$2.00
Claude 4.5 Sonnet$3.00$15.00
Gemini 3 Pro$2.00$12.00
Gemini 3 Flash$0.50$3.00

Ключевой инсайт: выходные токены стоят в 4–8 раз дороже входных, а значит неконтролируемая генерация часто является главным драйвером стоимости в продакшн-системах.

Быстрая оценка стоимости:

Для типичного запроса с 500 входными токенами и 50 выходными токенами при использовании GPT-5-mini:

  • Ввод: (500 / 1,000,000) × $0.25 = $0.000125
  • Вывод: (50 / 1,000,000) × $2.00 = $0.0001
  • Итого: ~$0.000225 за запрос

При 10 000 запросов/день: ~$67.5/месяц

Оптимизация стоимости на практике

Стратегия 1: Подбирайте модель под сложность задачи

Используйте меньшие и более дешёвые модели для простых задач:

python
from langchain_openai import ChatOpenAI
 
# Дорогая модель для сложных рассуждений
complex_llm = ChatOpenAI(model="gpt-5")
 
# Дешёвая модель для простых задач
simple_llm = ChatOpenAI(model="gpt-5-nano")
 
def get_llm_for_task(task_type):
    if task_type == "complex_reasoning":
        return complex_llm
    else:
        return simple_llm

Стратегия 2: Балансируйте размер контекста и качество

Включайте только необходимый контекст:

python
# Эффективно: включаем только релевантные чанки
relevant_chunks = retrieve_top_k(user_question, k=3)  # ~500 tokens
prompt = f"Context: {relevant_chunks}\n\nQuestion: {user_question}"

Важно: чрезмерно агрессивное сокращение контекста может ухудшить точность ответов. Балансируйте экономию с качеством.

Стратегия 3: Контролируйте длину вывода

Ограничивайте, сколько модель генерирует:

python
# Контроль стоимости
llm = ChatOpenAI(model="gpt-5-mini", max_tokens=100)
response = llm.invoke(messages)
# Максимум 100 выходных токенов

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

В следующем разделе мы рассмотрим ландшафт моделей и научимся выбирать подходящую модель для каждой задачи.

2.5) Понимание ландшафта моделей

Выбор правильной LLM для вашего агента требует баланса между:

  • Окно контекста: сколько текста модель может обработать?
  • Стоимость: сколько стоит каждый запрос?
  • Задержка (latency): как быстро модель отвечает?
  • Возможности: насколько хорошо модель умеет рассуждать?

Давайте рассмотрим ландшафт моделей 2026 года.

Основные семейства моделей

Модели OpenAI GPT

МодельОкно контекстаСтоимость вводаСтоимость выводаLatencyЛучше всего подходит для
GPT-5400K tokens$1.25 / 1M$10.00 / 1M~2–4sСложные рассуждения, продвинутый код
GPT-5-mini400K tokens$0.25 / 1M$2.00 / 1M~1.5–3sОбщие задачи, чат, суммаризация
GPT-5-nano400K tokens$0.05 / 1M$0.40 / 1M~1–2sКлассификация, извлечение

Модели Anthropic Claude

МодельОкно контекстаСтоимость вводаСтоимость выводаLatencyЛучше всего подходит для
Claude Opus 4.5200K tokens$5.00 / 1M$25.00 / 1M~2–4sГлубокие рассуждения, анализ
Claude Sonnet 4.5200K tokens$3.00 / 1M$15.00 / 1M~1.5–3sСбалансированная производительность, кодинг

Модели Google Gemini

МодельОкно контекстаСтоимость вводаСтоимость выводаLatencyЛучше всего подходит для
Gemini 3.0 Pro1M tokens$2.00 / 1M$12.00 / 1M~3–5sОгромный контекст, исследования
Gemini 3.0 Flash1M tokens$0.50 / 1M$3.00 / 1M~1–2sВысоконагруженные приложения

Особенности, зависящие от провайдера

OpenAI:

  • Лучше всего подходит для: универсальных агентов, широких многодоменных workflows, function calling
  • Сильные стороны: универсальные рассуждения, сильный инструментарий для разработчиков и экосистема, частые обновления моделей

Anthropic:

  • Лучше всего подходит для: сценариев, чувствительных к безопасности, структурированных и длительных рассуждений
  • Сильные стороны: глубокий анализ, методичные ответы, расширенное мышление с сильной alignment

Google:

  • Лучше всего подходит для: загрузки огромного контекста и мультимодальных задач
  • Сильные стороны: анализ больших массивов документов, мультимодальное понимание, высокопроизводительная обработка

Сопоставление моделей и задач

Выбирайте исходя из сложности задачи, размера контекста и требований к latency:

По сложности задач

Простые задачи → модели с оптимизацией стоимости (GPT-5-nano, GPT-5-mini):

  • Классификация намерений, анализ тональности, извлечение ключевых слов, простое форматирование
  • Выбирайте, когда: стоимость — главный приоритет

Умеренные задачи → сбалансированная модель (GPT-5-mini):

  • Ответы на вопросы, суммаризация, выбор инструмента
  • Выбирайте, когда: нужно балансировать стоимость и качество

Сложные задачи → модели с упором на возможности (GPT-5, Claude Sonnet, Gemini Pro):

  • Многошаговые рассуждения, генерация кода, подробный анализ
  • Выбирайте, когда: важны возможности рассуждения, стоимость приемлема

Максимальная сложность → премиальные модели (Claude Opus):

  • Очень сложные рассуждения, mission-critical решения
  • Выбирайте, когда: точность важнее всего, стоимость вторична

По требованиям к latency

В реальном времени (ощущаемая задержка ~1s или меньше) → быстрые модели (GPT-5-nano, Gemini Flash):

  • Пользовательский чат
  • Интерактивные приложения

Почти в реальном времени (1–3s) → большинство моделей:

  • Стандартные агентные задачи

Batch (>3s) → модели с высокими возможностями:

  • Фоновый анализ

Соображения по окну контекста

Стандартные задачи: все основные модели поддерживают 200K+ токенов, чего достаточно для большинства agent workflow.

Особые случаи:

  • Нужно 400K токенов: семейство GPT-5 (анализ целых документов, большие кодовые базы)
  • Нужно 1M токенов: модели Gemini (целые книги, огромные наборы документов)

Практический совет: даже при больших окнах контекста селективный retrieval (RAG) обычно даёт лучшие результаты.

Ключевые выводы

  1. Не существует одной «лучшей» модели → разные модели сильны в разных задачах
  2. Подбирайте модель под сложность задачи → не переплачивайте за простые задачи
  3. Окно контекста ≠ лучше → используйте селективный retrieval
  4. Latency влияет на пользовательский опыт → учитывайте время ответа для интерактивных задач

На практике: большинство агентов используют несколько моделей — дешёвые модели для простых задач и более мощные модели для сложных рассуждений. Мы реализуем это в следующих главах.

В следующем разделе мы научимся управлять поведением модели через эффективный промптинг.

2.6) Основы промптинга для агентных систем

Промпты — ваш основной интерфейс управления поведением LLM. Для агентов эффективный промптинг критичен — он определяет, принимает ли ваш агент правильные решения, вызывает ли нужные инструменты и выдаёт ли надёжные результаты.

Анатомия промпта

Полный промпт состоит из трёх компонентов:

1. System Message (Роль и ограничения) Определяет роль агента, возможности и границы.

2. Контекст (релевантная информация) Даёт информацию, необходимую для выполнения задачи.

3. Инструкция (конкретная задача) Говорит агенту ровно, что нужно сделать.

Посмотрим это на практике:

python
from langchain_openai import ChatOpenAI
 
llm = ChatOpenAI(model="gpt-5-mini")
 
response = llm.invoke([
    # System message: Определяем роль и ограничения
    {
        "role": "system",
        "content": """Вы — агент службы поддержки TechCorp.
        
Ваши возможности:
- Отвечать на вопросы о политиках возврата
- Создавать тикеты в поддержку
- Давать рекомендации по устранению неполадок
 
Ваши ограничения:
- Отвечайте только на вопросы о продуктах TechCorp
- Никогда не обещайте сроки возврата
- Всегда будьте вежливы и профессиональны"""
    },
    
    # User message: Контекст + Инструкция
    {
        "role": "user",
        "content": """Контекст: Клиент купил ноутбук модели X500 2026-01-15.
Сегодня 2026-02-20. Наша политика возврата допускает возврат в течение 30 дней.
 
Инструкция: Клиент хочет возврат. Что мне ему сказать?"""
    }
])
 
print(response.content)

Вывод:

Я понимаю, что вы хотите вернуть деньги за ноутбук модели X500. К сожалению, 
поскольку покупка была 15 января, а сегодня 20 февраля, мы вышли 
за пределы 30-дневного периода возврата. Однако я могу создать тикет в поддержку, 
чтобы рассмотреть другие варианты, например гарантийное обслуживание или обмен. 
Хотите, чтобы я продолжил?

System messages: задаём поведение агента

System message — это место, где вы определяете личность и возможности вашего агента. Это самая важная часть промптинга для агентов.

Слабый system message:

python
system_message = "Вы — полезный помощник."

Сильный system message:

python
system_message = """Вы — агент службы поддержки TechCorp.
 
ROLE:
Вы помогаете клиентам с запросами на возврат, вопросами о продуктах и техническими проблемами.
 
CAPABILITIES:
- Отвечать на вопросы, используя предоставленную документацию
- Создавать тикеты в поддержку при необходимости
- Давать пошаговые инструкции по устранению неполадок
 
CONSTRAINTS:
- Отвечайте только на вопросы о продуктах TechCorp
- Если вы чего-то не знаете, скажите об этом — никогда не угадывайте
- Всегда указывайте источники при использовании документации
- Никогда не обещайте конкретные сроки или результаты
 
TONE:
Профессиональный, эмпатичный и ориентированный на решение.
"""

Почему сильная версия работает лучше:

  1. Явные возможности → агент понимает, что он может делать
  2. Чёткие ограничения → агент знает, чего избегать
  3. Заданный тон → согласованная «личность»
  4. Конкретные инструкции → меньше неоднозначности

Ясность инструкций: будьте конкретны

LLM следуют инструкциям буквально. Размытые инструкции дают ненадёжные результаты.

Размытая инструкция:

python
instruction = "Помогите клиенту с возвратом."

Конкретная инструкция:

python
instruction = """Проанализируйте запрос клиента и определите:
1. Подходит ли заказ для возврата? (Сравните дату покупки с политикой возврата)
2. Если подходит: объясните процесс возврата
3. Если не подходит: объясните почему и предложите альтернативы
 
Оформите ответ так:
- Eligibility: [YES/NO]
- Reason: [Краткое объяснение]
- Next Steps: [Что клиенту делать дальше]
"""

Управление контекстом: давайте только нужное

Агентам нужен контекст, чтобы принимать решения, но слишком много контекста тратит токены и путает модель.

Избыточный контекст (расточительно):

python
context = f"""
История компании: TechCorp была основана в 1995 году...
Каталог продуктов: Мы продаём 500+ продуктов, включая...
Политика возврата: {refund_policy_text}
Политика доставки: {shipping_policy_text}
Политика гарантии: {warranty_policy_text}
История клиента: {full_customer_history}
"""
# 5000+ токенов, большинство нерелевантно

Селективный контекст (эффективно):

python
context = f"""
Релевантная политика: {refund_policy_text}
Детали заказа: {order_details}
"""
# 200 токенов, всё релевантно

Форматирование вывода: структурируйте ответы

Для агентов часто нужен структурированный вывод (JSON, конкретные форматы), а не свободный текст.

Неструктурированный вывод (сложно парсить):

python
response = llm.invoke([
    {"role": "system", "content": "Вы — агент поддержки."},
    {"role": "user", "content": "Нужно ли нам создать тикет по этому запросу на возврат?"}
])
print(response.content)
# Вывод: "Да, думаю, стоит создать тикет, потому что..."
# Проблема: сложно парсить, непоследовательный формат

Структурированный вывод (легко парсить):

python
response = llm.invoke([
    {"role": "system", "content": """Вы — агент поддержки.
    
Всегда отвечайте в этом JSON-формате:
{
  "action": "CREATE_TICKET" or "ANSWER_QUESTION" or "ESCALATE",
  "reason": "Краткое объяснение",
  "response_text": "Что сказать клиенту"
}"""},
    {"role": "user", "content": "Клиент хочет возврат по заказу #12345, покупка была 40 дней назад"}
])
print(response.content)

Вывод:

json
{
  "action": "CREATE_TICKET",
  "reason": "Заказ вне 30-дневного окна возврата, нужна ручная проверка",
  "response_text": "Я создал тикет в поддержку для рассмотрения вашего запроса на возврат. Наша команда свяжется с вами в течение 24 часов."
}

Для надёжного структурированного вывода мы будем использовать схемы Pydantic в Главе 7.

Few-shot примеры: показывайте, а не только говорите

Для сложных задач примеры эффективнее длинных инструкций.

Zero-shot (только инструкции):

python
prompt = """Извлеките order ID, название продукта и проблему из сообщений клиента.
 
Customer message: "My laptop X500 order #12345 won't turn on"
"""
# Модель может испытывать трудности с форматом

Few-shot (с примерами):

python
prompt = """Извлеките order ID, название продукта и проблему из сообщений клиента.
 
Example 1:
Input: "My laptop X500 order #12345 won't turn on"
Output: {"order_id": "12345", "product": "laptop X500", "issue": "won't turn on"}
 
Example 2:
Input: "Order 67890 - phone not charging"
Output: {"order_id": "67890", "product": "phone", "issue": "not charging"}
 
Now extract from this message:
Input: "My tablet order #11111 has a cracked screen"
Output:
"""

Модель учится паттерну по примерам и применяет его последовательно.

Паттерны промпт-инжиниринга для агентов

Паттерн 1: Chain of Thought (Рассуждение)

Для сложных решений попросите модель «думать пошагово»:

python
prompt = """Вам нужно решить, создавать ли тикет в поддержку.
 
Подумайте об этом шаг за шагом:
1. О чём просит клиент?
2. Можно ли ответить на это, используя существующую документацию?
3. Требуется ли ручное вмешательство?
4. Какое действие мы должны предпринять?
 
Customer message: "I want a refund for order #12345 but I lost the receipt"
 
Reasoning:
"""

Модель явно покажет рассуждения, делая решения более прозрачными и надёжными.

Паттерн 2: Ограниченная генерация (Безопасность)

Ограничьте возможные ответы модели:

python
prompt = """Классифицируйте намерение клиента. Ответьте РОВНО ОДНИМ из следующих вариантов:
- REFUND_REQUEST
- PRODUCT_QUESTION
- TECHNICAL_ISSUE
- OFF_TOPIC
 
Customer message: "How do I reset my password?"
 
Classification:
"""

Это не позволяет модели генерировать неожиданные ответы. Явно ограничивая варианты:

  • Предотвращаете ошибки парсинга (всегда один из четырёх вариантов)
  • Блокируете нежелательные действия (не позволяет выполнить неопределённые действия)
  • Упрощаете отладку (ограниченное пространство вывода проще трассировать)

Паттерн 3: Самокритика (Качество)

Попросите модель проверить и улучшить собственный вывод:

python
prompt = """Сгенерируйте ответ клиенту, затем раскритикуйте его.
 
Customer message: "I want a refund"
 
Шаг 1 — Сгенерируйте ответ:
[Your response here]
 
Шаг 2 — Критика:
- Is this response accurate?
- Is it helpful?
- Does it follow company policy?
- What could be improved?
 
Шаг 3 — Итоговый ответ (с учётом критики):
[Improved response here]
"""

Этот многошаговый подход часто даёт более качественные ответы, потому что:

  • Раннее выявление ошибок: модель пересматривает свои рассуждения перед финализацией
  • Улучшение тона и ясности: саморефлексия помогает выявить неясный или неподходящий язык
  • Соответствие политике: шаг критики проверяет соблюдение правил

Частые ошибки в промптинге

Ошибка 1: Предположение, что модель «знает» вещи

LLM не имеют доступа к информации в реальном времени или неявному контексту. Всегда явно предоставляйте все необходимые данные.

python
# Плохо: предполагает, что модель знает текущую дату
prompt = "Подходит ли этот заказ для возврата? Заказ #12345"
 
# Хорошо: предоставляем всю необходимую информацию
prompt = f"""Подходит ли этот заказ для возврата?
Order #12345
purchased {purchase_date}
Today: {current_date}
Policy: 30-day returns
"""

Ошибка 2: Двусмысленные инструкции

Размытые глаголы вроде «обработать», «разобраться» или «заняться» оставляют слишком много пространства для интерпретации. Явно задавайте точное действие.

python
# Плохо: что значит "разобраться"?
prompt = "Разберись с этим запросом на возврат"
 
# Хорошо: явное действие
prompt = "Определи, подходит ли этот запрос на возврат. Если да — создай тикет. Если нет — объясни почему."

Ошибка 3: Перегрузка контекстом

Включение нерелевантной информации тратит токены, увеличивает задержку и может запутать модель. Извлекайте только то, что нужно для конкретной задачи.

python
# Плохо: 10 000 токенов контекста, большинство нерелевантно
prompt = f"""
{entire_knowledge_base}
 
Question: What's the refund policy?
"""
 
# Хорошо: извлекаем только релевантный раздел
prompt = f"""
{refund_policy_section}
 
Question: What's the refund policy?
"""

Ошибка 4: Непоследовательное форматирование

Когда формат вывода меняется, downstream-код ломается. Всегда указывайте точный формат, который вы ожидаете, особенно для структурированных данных.

python
# Плохо: иногда JSON, иногда простой текст
prompt = "Ответь своим решением"
 
# Хорошо: всегда задавайте формат
prompt = 'Ответь в формате JSON: {"decision": "...", "reason": "..."}'

Ключевые выводы

Эффективный промптинг для агентов требует:

  1. Чётких system messages → определить роль, возможности, ограничения
  2. Конкретных инструкций → сказать модели ровно, что делать
  3. Селективного контекста → давать только релевантную информацию
  4. Структурированного вывода → явно задавать формат
  5. Few-shot примеров → показать нужный паттерн
  6. Промптов на рассуждение → попросить пошаговое мышление

Промптинг — навык, который улучшается с практикой. В течение книги вы увидите, как эти паттерны применяются в реальных агентных системах.


Итоги главы

Теперь вы понимаете базовые концепции создания систем агентного ИИ:

Агентный ИИ vs Чат-боты:

  • Агенты действуют автономно для достижения целей
  • Агенты используют инструменты и принимают многошаговые решения
  • Агенты поддерживают состояние и адаптируются на основе результатов

Почему важны фреймворки:

  • LangChain даёт абстракцию моделей, цепочки, память, инструменты и RAG
  • LangGraph добавляет управление состоянием, маршрутизацию и чекпоинтинг
  • Фреймворки уменьшают количество шаблонного кода и позволяют строить сложные workflows

Механика LLM:

  • LLM предсказывают токены, а не извлекают факты
  • Контекст задаётся явно, а не неявно
  • Промпты — это инструкции, а не запросы
  • Структурированный вывод требует подсказок
  • Окна контекста конечны

Экономика токенов:

  • Выходные токены стоят в 4–8 раз дороже входных
  • Выбор модели даёт влияние на стоимость в 10–100 раз
  • Селективный контекст резко снижает расходы
  • Мониторинг и бюджеты предотвращают неконтролируемые затраты

Выбор модели:

  • Подбирайте модель под сложность задачи
  • Учитывайте требования к контексту и ограничения latency
  • Используйте multi-model архитектуры для оптимизации стоимости
  • Начинайте с дешёвых моделей, повышайте только при необходимости

Основы промптинга:

  • System messages задают поведение агента
  • Конкретные инструкции дают надёжные результаты
  • Селективный контекст повышает качество и снижает стоимость
  • Структурированный вывод позволяет парсинг и валидацию
  • Few-shot примеры эффективно обучают паттернам