Что это решает
Есть повторяющаяся операция: каждый день кто-то открывает три системы, сверяет данные, пишет письмо и заводит задачу. Скриптом её не закрыть — вход каждый раз разный, и решение зависит от того, что нашлось по дороге. Вопрос «как создать ИИ агента» появляется именно здесь: нужна не демонстрация в чате, а штука, которая делает операцию целиком и не портит данные при первой неожиданности. Ниже — маршрут от описания операции до версии, которую можно оставить работать без присмотра.
Как устроено
Сборка раскладывается на шесть частей, и каждая живёт отдельным файлом, а не в голове автора.
Контракт задачи. Вход, выход, критерий готовности, запрещённые действия, поведение при нехватке данных. Пока критерий готовности нельзя проверить механически, агента собирать рано.
Инструменты. Функции, которыми агент дотягивается до систем. Каждая с описанием для модели и схемой аргументов для кода.
Промпт. Роль, порядок действий, правила выбора инструментов, формат ответа, запреты.
Петля с бюджетом. Лимит итераций, таймаут на инструмент, потолок токенов. Бюджет исчерпан — остановка с внятным сообщением, а не молчаливое зацикливание.
Журнал прогонов. Полная трасса каждого запуска: шаг, инструмент, аргументы, ответ, токены.
Набор проверок. Файл с задачами и ожидаемыми результатами, который прогоняется после каждой правки промпта.
Структура проекта, которой хватает надолго:
agent/
contract.md вход, выход, критерий готовности
prompt.md системный промпт
tools/ по файлу на инструмент
loop.py петля, лимиты, проверка прав
evals/cases.jsonl задачи с известным ответом
runs/ трассы прогонов
Порядок сборки идёт снизу вверх: сначала инструменты и права, потом промпт, потом автономия. Промпт, написанный раньше инструментов, описывает желаемое, а не доступное.
Как подключить
Шаг 1. Записать операцию руками. Пройти её самому и зафиксировать каждое действие: какой экран, какое поле, откуда берётся значение, какое решение принимается и по какому признаку. Пункты вида «тут смотрю, нормально ли» разворачивать до проверяемого условия — иначе это условие придётся выдумывать модели.
Шаг 2. Написать контракт. Одна страница: что подаётся на вход, что считается результатом, как проверить готовность механически, что запрещено трогать, что делать при нехватке данных. Страница не пишется — задача не готова к агенту, и никакая модель этого не исправит.
Шаг 3. Разрезать операцию на инструменты. Каждое действие из первого шага — кандидат. Один инструмент делает одно действие и возвращает данные в читаемом виде. Функция «сделай всё сразу» отлаживается хуже, чем три отдельные, и по трассе не видно, где она сломалась.
Описание для модели пишется как инструкция коллеге:
name: find_orders_without_payment
описание: возвращает заказы за период, у которых нет платежа.
Период задаётся датами включительно. Удалённые заказы не возвращает.
При пустом результате возвращает пустой список, а не ошибку.
аргументы: date_from (YYYY-MM-DD), date_to (YYYY-MM-DD), limit (int)
Шаг 4. Выдать права отдельно. Технический пользователь под агента, роль в базе только на чтение нужных таблиц, свой токен на каждый внешний сервис, свой ящик для отправки. На первой сборке право записи не выдаётся вообще: сначала пусть научится читать и отвечать.
Шаг 5. Написать промпт. Блоки: роль и зона ответственности, порядок действий, правила выбора инструментов, формат итогового ответа, запреты, поведение при нехватке данных. Общие призывы вроде «будь внимателен» не меняют ничего. Меняют конкретные фразы: «дат нет — спроси период», «строк больше лимита — сообщи и не додумывай остаток», «инструмент вернул ошибку дважды — остановись и опиши, что пробовал».
Шаг 6. Собрать петлю с предохранителями.
messages = [system, task]
for step in range(MAX_STEPS):
reply = model.create(messages=messages, tools=TOOLS)
log(step, reply)
if not reply.tool_calls:
return reply.text
for call in reply.tool_calls:
if not allowed(call.name, call.arguments):
messages.append(tool_result(call.id, "отказано: нет доступа"))
continue
try:
out = run_with_timeout(TOOLS[call.name], call.arguments)
except Exception as e:
out = f"ошибка инструмента: {e}"
messages.append(tool_result(call.id, out))
Ошибка возвращается модели текстом — по нему агент чинит вызов сам. Процесс останавливает только исчерпанный бюджет.
Шаг 7. Собрать набор проверок. Файл evals/cases.jsonl, по строке на задачу:
{"task": "заказы без оплаты за прошлую неделю", "expect_tools": ["find_orders_without_payment"]}
{"task": "выручка за позапрошлый век", "expect": "отказ: данных за период нет"}
Половина задач — обычные, половина — граничные: пустой результат, закрытый доступ, вопрос вне компетенции, противоречивый вход. Граничные важнее обычных: на них видно, врёт агент или отказывается.
Шаг 8. Запустить в режиме подсказчика. Агент готовит письмо, тикет или изменение — отправляет человек. Каждая ручная правка уходит в одно из трёх мест: промпт, описание инструмента, набор проверок. Через несколько дней правки перестают приносить новое — это и есть сигнал к следующему шагу.
Шаг 9. Отдать автономию по одному действию. Сначала обратимые: черновик, комментарий, метка, запись в служебную таблицу. Необратимые — письмо клиенту, платёж, удаление — держатся за подтверждением дольше остальных, а часть остаётся там навсегда.
Правило: право записи выдаётся по одному действию и только после того, как правки человека в режиме подсказчика перестали приносить новое.
Полезные сценарии
- Ежедневная сводка по нескольким системам с явным указанием расхождений между ними.
- Первичный разбор обращения: классификация, сбор контекста клиента, черновик ответа.
- Подготовка документов: сбор реквизитов и позиций, финальная кнопка за человеком.
- Дежурство по ошибкам: сортировка новых записей трекера, поиск похожих, короткая выжимка в чат.
- Наполнение карточек товара по описанию поставщика с проверкой обязательных полей.
- Проверка данных перед выгрузкой: пустые поля, дубли, суммы, которые не сходятся.
Ограничения
Операция без цифрового следа не собирается. Звонок, бумажный акт, договорённость в курилке — сначала цифровой вход, потом агент.
Ожидание людей агент не ускоряет. Если операция стоит три дня в согласовании, автоматизация трёх минут работы ничего не даст.
Промпт не заменяет данные. Когда нужного поля нет в системе, никакая формулировка не заставит агента его достать. Он заполнит поле догадкой, и догадка будет выглядеть уверенно.
Инструменты ломаются вслед за чужими API. Смена схемы у поставщика роняет агента так же, как любую интеграцию. Обслуживание набора инструментов — постоянная работа, а не разовая.
Первая версия ломается не там, где ожидали. Обычно причина не в модели, а в описании инструмента или в формате, который он возвращает. Без журнала это не находится.
Редкая операция не окупается. Разовая задача раз в квартал дешевле делается руками.
Как проверить результат
Прогонять весь набор проверок после каждой правки промпта. Промпт ведёт себя как код: правка одной строки меняет поведение на соседних задачах. Прогон делается до выката, а не после жалобы.
Считать долю вмешательств. Сколько результатов ушло без правок, сколько потребовало доработки, сколько переделано целиком. Растёт доля правок — искать причину в изменившихся данных, а не в модели.
Читать трассы подряд за день, а не выборочно. Выборка показывает удачные случаи; сплошной просмотр за короткое окно показывает те, где агент уверенно пошёл не туда.
Отдельно проверять отказы. Забрать доступ к инструменту и посмотреть на ответ: сообщение об отказе — норма, цифры из воздуха — повод остановить внедрение.
Сравнить время и стоимость. Токены и минуты из журнала против времени человека на ту же операцию, на одинаковом объёме.
Держать выключатель. Одна переменная окружения или флаг, который останавливает агента без выката. Проверить, что он действительно останавливает — на живом прогоне.
