Как создать ИИ-агента: от одной функции до рабочего сценария

ИИ-агентом часто называют любой чат с моделью. Для разработки этого определения мало. Рабочий агент не только формулирует ответ, но и двигает задачу вперед: получает цель, выбирает допустимое действие, вызывает инструмент, читает результат и решает, что делать дальше.

Начинать при этом лучше не с «универсального помощника», а с одного узкого сценария. Например, агент получает обращение клиента, находит заказ и готовит проект ответа оператору. В первой версии он ничего не отправляет сам. Такой объем можно проверить, а ошибку можно остановить до того, как она затронет клиента.

Ниже разберем, как создать ИИ-агента поэтапно: от одной функции до ограниченного рабочего процесса.

Сначала опишите результат, а не личность агента

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

Для примера зафиксируем его так:

По номеру заказа получить его статус, сопоставить статус с правилами поддержки и подготовить черновик ответа. Если номера нет, запросить его. Если заказ не найден или пользователь просит возврат, передать обращение человеку.

Уже из этого описания видны границы:

• какие данные нужны на входе;

• какой внешний источник понадобится;

• что агент может сделать сам;

• в каких случаях он обязан остановиться;

• кто принимает окончательное решение.

Полезно записать и отрицательный объем. Агент не меняет заказ, не оформляет возврат, не обещает срок, которого нет в системе, и не отправляет сообщение без подтверждения оператора. Эти запреты важнее красивого системного промпта.

Выберите одну функцию

Первая функция должна быть безопасной и легко проверяемой. В нашем сценарии это get_order_status. Она принимает номер заказа и возвращает структурированный результат, например:

Например, агент может получить номер заказа A-1042, статус shipped и ожидаемую дату доставки, а затем составить ответ пользователю.

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

У функции должен быть узкий контракт:

• название описывает одно действие;

• аргументы имеют типы и обязательные поля;

• ответ не содержит лишних персональных данных;

• ошибка отличается от пустого результата;

• вызов можно записать в журнал без сохранения секретов.

На первом шаге выбирайте чтение, а не запись. Получить статус безопаснее, чем изменить адрес доставки. Когда чтение работает нестабильно, добавление действий только увеличивает цену ошибки.

Соберите минимальный цикл агента

Обычная последовательность выглядит так:

1. Пользователь отправляет сообщение.

2. Приложение передает модели инструкции, сообщение и описание доступных инструментов.

3. Модель либо отвечает, либо запрашивает вызов функции.

4. Код проверяет аргументы и выполняет функцию.

5. Результат возвращается модели.

6. Модель готовит итог или запрашивает следующий допустимый шаг.

Сам вызов инструмента выполняет не модель. Его выполняет ваш сервер. Это принципиальная граница: именно сервер решает, можно ли обращаться к данным, какие поля вернуть и сколько раз повторять запрос.

Задайте пределы цикла заранее. Например, не больше трех вызовов инструментов на одно обращение и не больше одного повторного запроса при временной ошибке. Если лимит исчерпан, агент создает понятную передачу оператору вместо бесконечной попытки «разобраться».

Отделите инструкции, данные и состояние

У агента есть как минимум три разных слоя контекста.

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

Данные приходят из источников: статус заказа, справочник доставки, политика возврата. Их нельзя заменять памятью модели. Если срок находится в API, агент должен получить его оттуда.

Состояние хранит ход конкретной задачи: пользователь уже назвал номер заказа, функция вернула статус, оператор еще не подтвердил ответ. Не стоит бесконечно отправлять модели всю историю. Храните нужные поля явно и передавайте только релевантные сообщения.

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

Добавьте правила принятия решений

Модель лучше справляется, когда у нее есть короткая карта решений.

Для нашего примера:

• нет номера заказа: попросить номер;

• заказ найден: объяснить статус по данным API;

• заказ не найден: не придумывать причину, передать оператору;

• запрос о возврате: подготовить сводку и передать человеку;

• API недоступен: сообщить, что статус сейчас нельзя проверить, и предложить повтор или связь с оператором;

• пользователь просит изменить заказ: не вызывать инструмент чтения как будто изменение выполнено.

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

Подключайте действия только после черновиков

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

1. Агент предлагает действие и показывает параметры.

2. Человек подтверждает их.

3. Сервер повторно проверяет права и актуальность данных.

4. Функция выполняет запись с ключом идемпотентности.

5. Результат сохраняется в журнале и показывается оператору.

Ключ идемпотентности защищает от случайного повторения одной операции при сетевом сбое. Серверная проверка нужна потому, что текстовое «подтверждаю» само по себе не заменяет контроль доступа.

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

Проверьте агента на фиксированном наборе задач

Тест «я поговорил с ним пять минут, вроде отвечает» плохо воспроизводится. Соберите таблицу примеров с ожидаемым исходом.

В нее стоит включить:

• обычный заказ с известным статусом;

• сообщение без номера;

• несуществующий номер;

• два номера в одном сообщении;

• запрос на возврат;

• попытку получить чужие данные;

• ошибку и задержку API;

• инструкцию пользователя игнорировать системные правила;

• повтор одного сообщения;

• смену темы в середине диалога.

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

Сохраняйте этот набор и прогоняйте его после изменений модели, промпта, инструмента и схемы данных. Ошибки из реальных обращений добавляйте как регрессионные тесты.

Запускайте ограниченно

Первый запуск лучше провести на небольшой доле обращений и только в режиме черновика. Назначьте владельца, который каждый день смотрит:

• долю корректно завершенных сценариев;

• причины передачи человеку;

• ошибки инструментов;

• лишние или повторные вызовы;

• случаи, когда оператор переписал черновик;

• время и стоимость одной задачи.

Не соединяйте все источники сразу. Сначала один канал, один тип задачи и один владелец данных. Если агент ошибается, вы сможете понять, какой слой сломался: инструкция, модель, функция, внешний API или бизнес-правило.

Продумайте ошибки инструмента

Даже простая функция чтения возвращает не только успешный объект. Зафиксируйте разные исходы: неверный формат номера, запись не найдена, нет прав, сервис временно недоступен и ответ не уложился в таймаут. Не сводите их к одной пустой строке. Иначе модель будет угадывать, отсутствует ли заказ или сломалась интеграция.

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

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

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

Определите владельцев системы

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

Версия промпта, схемы инструмента и набора тестов должна быть связана с релизом. Если после обновления качество изменилось, вы сможете сравнить конкретные версии, а не спорить о впечатлении от «нового агента».

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

Оцените экономику одной задачи

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

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

Заранее выберите срок эксперимента и минимальное число случаев для разбора. Не расширяйте сценарий посреди проверки: сначала завершите сравнение одной версии. Иначе изменение модели, промпта и бизнес-правила одновременно не позволит понять, что дало результат.

Решение о расширении принимайте по этим данным.

На курсе «Вайбкодинг на максималках» этот маршрут продолжается через работу с Git, базой данных, API, Docker, безопасностью и деплоем. Это полезно, если агент должен стать частью реального приложения, а не остаться примером в ноутбуке.

Что должно получиться в первой версии

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

Расширять такого агента можно по одному риску за раз: сначала добавить второй источник чтения, затем подтверждаемое действие, потом новый канал. Так вы сохраняете понимание системы и не маскируете архитектурные ошибки общим словом «агент».