Что такое 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: ошибка базы не поднимается наверх, а возвращается строкой. Это сделано намеренно — сейчас станет видно, зачем.
Трасса
Вот что напечатал последний цикл — полный список сообщений, накопленный за прогон:
Разберём по шагам, что здесь произошло.
Первая итерация. Модель видит только задачу и описания двух инструментов. Названия колонок она не знает, поэтому берёт самое правдоподобное — balance. База отвечает ошибкой, и эта ошибка возвращается модели как обычный результат инструмента.
Вторая итерация. Теперь в диалоге лежит текст ошибки. Модель читает его и меняет решение: вместо повторной попытки угадать запрашивает структуру таблицы. Вот ради чего нужен цикл — не ради красоты, а ради возможности исправиться, увидев результат.
Третья итерация. Имя колонки известно, запрос переписан, получено число.
Четвёртая итерация. Данные есть, остаётся вторая половина задачи — письмо. Модель сама решает, что теперь очередь send_email, и подставляет в аргументы результат прошлого шага.
Пятая итерация. Модель возвращает обычное сообщение без заявок на вызов — цикл завершается сам. Лимит в 12 шагов не понадобился.
Что уходит в модель на каждой итерации
Ответ короткий: всё, что вы видите выше и было накоплено к этому моменту. На пятой итерации модель получает и исходную задачу, и текст ошибки с первого шага, и все результаты инструментов.
Из этого следуют два практических вывода.
Первый: агент помнит свои ошибки. Модель не повторила неудачный запрос, потому что видела его результат. Если инструмент проглатывает ошибку и возвращает пустоту вместо описания проблемы, эта способность пропадает — модель будет тыкаться вслепую. Поэтому текст ошибки для агента полезнее аккуратного None.
Второй: каждая итерация дороже предыдущей. Запрос растёт, и на длинных прогонах основная часть счёта приходится на повторную отправку одного и того же контекста. Прогон из пяти шагов — это пять вызовов модели, а не один.
Где смотреть трассу
Печать сообщений в консоль годится, чтобы разобраться на своей машине. В работающем сервисе так отладить агента нельзя: прогоны идут параллельно, шагов десятки, а вопрос обычно один — на каком шаге всё свернуло не туда. Для этого прогон пишут не строками в лог, а деревом вложенных отрезков: вызов модели, вызов инструмента, шаг агента, с длительностью и потраченными токенами у каждого. Такой разбор ждёт нас в части курса про эксплуатацию.
Что стоит унести отсюда прямо сейчас: разбор поведения агента всегда начинается с трассы. Пока вы не видите последовательность «что модель решила — что вернул инструмент», любые рассуждения о том, почему агент ответил ерунду, остаются гаданием.
