Введение в AI-агентов и продакшн-подход

Что такое AI-агент и чем он отличается от чат-бота

Содержание курса

ReAct: как выглядит один прогон агента

У цикла, который мы собрали, есть имя — ReAct, от reasoning and acting: модель чередует рассуждение и действие. Она смотрит на то, что уже произошло, решает, что делать дальше, делает один шаг и снова смотрит на результат. Никакого плана целиком наперёд у неё нет: решение принимается заново на каждой итерации, с учётом всего, что накопилось.

Разберём это на одном прогоне целиком. Дадим агенту два инструмента и задачу, которую в один шаг не решить.

import sqlite3
import smtplib
from contextlib import closing
from email.message import EmailMessage

from langchain.tools import tool
from langchain.agents import create_agent

@tool
def run_sql(query: str) -> str:
    """Выполнить SQL-запрос к базе клиентов и вернуть результат или текст ошибки."""
    try:
        with closing(sqlite3.connect("clients.db")) as conn:
            return str(conn.execute(query).fetchall())
    except sqlite3.Error as exc:
        return f"SQLError: {exc}"

@tool
def send_email(to: str, subject: str, body: str) -> str:
    """Отправить письмо на указанный адрес."""
    message = EmailMessage()
    message["To"], message["Subject"] = to, subject
    message.set_content(body)
    with smtplib.SMTP("localhost") as server:
        server.send_message(message)
    return "sent"

agent = create_agent(
    model=MODEL,
    tools=[run_sql, send_email],
    system_prompt="Ты аналитик. Структуру базы выясняй запросами, не угадывай.",
)

task = "Посчитай клиентов с балансом выше 1000 и отправь число на ivan@example.com"

result = agent.invoke(
    {"messages": [{"role": "user", "content": task}]},
    {"recursion_limit": 12},
)

for message in result["messages"]:
    message.pretty_print()

Обратите внимание на run_sql: ошибка базы не поднимается наверх, а возвращается строкой. Это сделано намеренно — сейчас станет видно, зачем.

Трасса

Вот что напечатал последний цикл — полный список сообщений, накопленный за прогон:

Трасса прогона в консоли: запрос пользователя, ошибка SQL на угаданном имени колонки, запрос структуры таблицы, правильный подсчёт, отправка письма и финальный ответ

Разберём по шагам, что здесь произошло.

Первая итерация. Модель видит только задачу и описания двух инструментов. Названия колонок она не знает, поэтому берёт самое правдоподобное — balance. База отвечает ошибкой, и эта ошибка возвращается модели как обычный результат инструмента.

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

Третья итерация. Имя колонки известно, запрос переписан, получено число.

Четвёртая итерация. Данные есть, остаётся вторая половина задачи — письмо. Модель сама решает, что теперь очередь send_email, и подставляет в аргументы результат прошлого шага.

Пятая итерация. Модель возвращает обычное сообщение без заявок на вызов — цикл завершается сам. Лимит в 12 шагов не понадобился.

Что уходит в модель на каждой итерации

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

Рост списка сообщений от итерации к итерации: на каждом шаге в модель уходит весь накопленный диалог

Из этого следуют два практических вывода.

Первый: агент помнит свои ошибки. Модель не повторила неудачный запрос, потому что видела его результат. Если инструмент проглатывает ошибку и возвращает пустоту вместо описания проблемы, эта способность пропадает — модель будет тыкаться вслепую. Поэтому текст ошибки для агента полезнее аккуратного None.

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

Где смотреть трассу

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

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

Начать заново?

Помощник начнёт новый диалог по этой задаче. Текущая переписка останется в истории до конца срока хранения.

О переписке

Можно приложить до трёх фото PNG, JPEG или WebP по 5 МБ.

Переписка и фото хранятся 90 дней после последнего принятого сообщения. Преподаватель получит доступ к диалогу, когда вы попросите его помочь.

Проверка решения остаётся отдельным действием.