Автоматизация
агентами — это
на 90% не промпт
Запускать агента только на промпте — это как спрашивать, почём в Одессе рубероид. Восемь живых историй про то, что вызов модели — самая маленькая часть работы.
Где запускать агента и как его изолировать
Когда говорят «я автоматизировал это агентом», обычно представляют так: сел, написал хороший промпт, агент работает. Промпт — сердце, остальное мелочи. Промпт пишется за час. Обвязка живёт месяцами и растёт по ошибкам — по каждой я расскажу отдельно, потому что эти ошибки и есть самое ценное.
Разберу восемь своих систем. Не по принципу «вот красивая архитектура», а честно: какую задачу решал, почему она вообще встала, как решил и где ошибался. Сгруппировал по типу инфраструктурной работы — именно типы проблем повторяются от проекта к проекту, а конкретный промпт у каждого свой.
Первый вопрос автономного агента — не «что он умеет», а «что он может сломать». Если агент имеет право писать в файлы и запускать команды, он рано или поздно напишет не туда и запустит не то. Вся работа здесь — построить ему песочницу, из которой он не дотянется до живого.
Автофикс ошибок из мониторинга: песочница важнее промпта
Задача была такая. На сервер приходят ошибки из системы мониторинга — она шлёт уведомление на каждый сбой в проде. Большинство из них — шум: чужие боты, сетевые таймауты, проблемы инфраструктуры, а не кода. Я устал заходить в мониторинг на каждый алерт, разбирать стектрейс и понимать, баг это вообще или ерунда.
Почему это надо было решать. Алертов много, реальных багов среди них мало. Каждый отнимает время на разбор, причём в 80% случаев разбор кончается «а, это не баг». Хотелось, чтобы кто-то отсеивал шум сам, а мне приносил уже не сырой стектрейс, а готовый разбор: вот это настоящий баг, вот фикс, смотри.
Как устроено. Приходит уведомление об ошибке — система поднимает в фоне агента. Агент получает промпт в три шага: сначала диагностируй, потом классифицируй ровно в один из четырёх вердиктов, и только если это настоящий баг — пиши минимальный фикс и открывай Pull Request на ревью. Готовый фикс прилетает мне в Telegram с объяснением простым языком, без кода.
Вот промпт — это и есть те самые «десять процентов». Жми на этапы схемы: видно, что вызов модели — один маленький шаг из многих, а вокруг него стоит вся остальная инженерия.
Первая: боевые репозитории на сервере и сам сервис-помощник работают под разными пользователями. Рабочая копия требует записи в общие служебные данные репозитория — а доступа на запись туда у сервиса нет.
Вторая, важнее: рабочая копия делит служебные данные с живым репозиторием, где прямо сейчас идёт работа — деплои, чужие коммиты. Полный клон отвязывает агента начисто: что бы он ни натворил у себя в песочнице, прод не пострадает.
Что в итоге. Промпт — три абзаца. Вокруг него: парсер уведомлений (мониторинг шлёт их в разных форматах в зависимости от версии интеграции), песочница-клон, маскировка секретов, безопасный дефолт, обработка гонки с PR, уведомления. Девяносто процентов кода — про то, чтобы автономный агент не наделал беды.
И честно про то, чего там нет: подпись уведомления не проверяется, нет таймаута на самого агента, нет дедупликации повторных уведомлений по одной ошибке. Это известные дыры — обвязку всегда есть куда достраивать.
Граф кода: чтобы агент не искал вслепую
Второй кейс этого блока — про другую грань изоляции. Не «куда агент не дотянется руками», а «откуда он берёт знание о коде, чтобы не выдумывать».
Проблема. Агент, разбирающий незнакомую ошибку, по умолчанию делает десятки операций поиска и чтений файлов: где объявлен символ, кто его вызывает, что сломается если поправлю. Это дорого по токенам и ненадёжно — агент может выдумать несуществующий файл и уверенно на него сослаться.
Решение. Один сервис заранее строит граф кода — карту того, что где лежит и что на что влияет — и отдаёт его всем агентам как набор инструментов «только для чтения». Один запрос к графу заменяет пяток операций поиска и сразу отдаёт компактную карту: вызов с фронта → обработчик → слой сервисов → таблицы базы → тесты.
Проблемы тут все про честность графа, а не про его ум. Точность важнее полноты: если связь между функциями не разрешается однозначно — граф её просто не покажет. Лучше пропустить настоящую связь, чем показать ложную: ложный «кто вызывает» отправит автономного агента чинить не то. Атомарная запись: пока граф пересобирается, запрос не должен увидеть полупустую карту — новая версия пишется во временное место и подменяется целиком. Здесь модель вообще не при чём: вся ценность — в инфраструктуре навигации. Промпта тут нет совсем.
Как будить агента и не терять события
Автономный агент должен на что-то реагировать. Либо его будит событие, либо он сам периодически смотрит, не появилось ли работы. Звучит просто — «по событию запусти перевод». На деле тут лежит самый коварный класс багов: событие, которое потерялось, потому что агент в этот момент лежал. И его никто не заметит, кроме пользователя, который не дождался результата.
Перевод по событию в базе: очередь важнее уведомления
Задача. Контент-редактор добавляет на платформу русскую запись (десятки записей, у каждой семь полей). Английская версия должна появиться сама, без отдельной кнопки и без ожидания ночного крона.
Почему так, а не по расписанию. Сначала перевод добирался по расписанию — раз в N минут скрипт смотрел, что не переведено. Медленно: редактор добавил запись и ждёт. В базе есть механизм: триггер на добавление записи шлёт уведомление, а слушающий процесс реагирует сразу. Поставил — заработало. А потом начались проблемы, ради которых весь этот рассказ.
Решение — не полагаться на уведомление как на доставку. Уведомление — это будильник, а не само задание. Само задание лежит в очереди в базе, которая никуда не девается:
Ещё одна засада: слушать уведомления из базы нельзя через общий пул соединений. Пул возвращает соединение в общую кучу после запроса — и подписка теряется. Нужен отдельный выделенный коннект, который живёт всё время и только слушает. Об этом не написано на видном месте — это узнаёшь, когда подписка молча перестаёт срабатывать.
Что в итоге. Сам перевод — это вызов модели с глоссарием, чтобы термины переводились канонически. Маленький кусочек. А вокруг: очередь в базе, разгребание при старте, добор пробелов, выделенный коннект, политика повторов, разумные тревоги. Вся надёжность — в обвязке, которая гарантирует, что ни одно событие не потеряется.
Бот-ответчик: опрос вместо событий и обязательный засев
Иногда событий нет вовсе — внешняя система не умеет их слать. Тогда агент сам периодически заглядывает: не появилось ли работы. И тут своя классическая ошибка.
Задача. Под уроками открытой платформы копятся комментарии и вопросы. Отвечать на них в реальном времени некому. Бот должен отвечать сам, по существу, с опорой на материал курса — круглосуточно.
Как. Раз в полторы минуты бот опрашивает движок комментариев, берёт последние, выбирает новые, по каждому поднимает ветку, ищет релевантный кусок курса в векторной базе и публикует ответ. Опрос, а не вебхуки — движок комментариев крутится тут же локально, поднимать вебхук-инфру дороже, чем раз в полторы минуты дёрнуть запрос.
Вывод блока. В обоих кейсах вызов модели — это «сгенерируй ответ» и «переведи текст», крошечная часть. Вся инженерия — в том, как агента надёжно разбудить и как при этом не потерять, не задублировать и не зациклить.
Память и состояние
Модель сама по себе беспамятна. Каждый вызов — с чистого листа. Если агенту нужно помнить — вчерашний разговор, открытую задачу, факт о пользователе — память придётся строить рядом. И вся сложность тут не в том, чтобы записать, а в том, чтобы не утонуть в дублях и не раздуть базу мусором.
Память бота: дедупликация — это и есть память
Задача. Бот-собеседник, которому бесполезно быть «рыбкой Дори» — забывать всё на каждом сообщении. Он должен помнить имя, прошлые темы, чего человек боится, какие подходы уже пробовал.
Как. После каждого обмена репликами бот в фоне прогоняет диалог через модель, та извлекает факты в структурированном виде, и бот кладёт их в векторную базу. В следующем разговоре достаёт релевантное по смыслу и подмешивает в контекст. И опять: извлечение фактов — это маленький промпт «выдели факты из диалога». Вся работа — вокруг.
Граф знаний из чата: вся ценность в линковке
Задача. Небольшая команда ведёт продукт в рабочем чате. Задачи, баги, решения тонут в потоке сообщений, трекер никто не ведёт. Хотелось «вечную память»: что открыто, что закрыто, что висит.
Как. Каждые несколько минут фоновый процесс берёт новые сообщения, вытаскивает из них моделью сущности и связи (задачи, баги, люди, вопросы) и складывает в граф. Ночью чистит дубли и связывает закрытые баги с коммитами в коде.
Связывание сделано не по одному признаку, а по нескольким сразу: совпадение имени, близость описаний, общий контекст. Дорогую модель-арбитра зовут только в «серой зоне» — когда по сумме признаков непонятно, одно это или разное. Уверенное совпадение сливается без модели, уверенное различие создаёт новую сущность. Модель — только на спорных случаях. Это держит число её вызовов в разумных рамках.
fix(...). Поиск по переписанному имени промахивается. Пришлось брать оригинальный текст коммита, а не переписанный, и понижать порог похожести — русское описание бага и английский коммит похожи слабо, пусть модель-арбитр разбирается, это копейки.Вывод блока. Память — это не «модель запоминает». Модель не запоминает ничего. Запоминает инфраструктура вокруг неё: векторная база, дедупликация, связывание, ночная чистка. Промпт извлечения в обоих кейсах — крошечный. Вся работа — не дать памяти захлебнуться в дублях.
Качество на выходе
Модель выдала результат. Можно ли его публиковать как есть? Почти никогда. Между «модель ответила» и «пользователь увидел» лежит слой контроля качества — и он часто сложнее самого вызова модели.
Дедупликация новостей: лучше пропустить дубль, чем потерять выпуск
Задача. Агент собирает свежие новости для ежедневного дайджеста. Источники каждый день выдают одни и те же события под разными заголовками. Без фильтрации дайджест повторял бы вчерашнее и публиковал бы по три формулировки одного анонса в одном выпуске.
Почему не сравнить заголовки. Текстовое сравнение не ловит перефразировки. «OpenAI выпустила модель» и «представлена новая модель от OpenAI» — текстуально разные, по смыслу одно. Нужна близость по смыслу: каждую новость превращаем в вектор и сравниваем векторы. Порог близости — рычаг строгости. Подвигай его и увидь, как меняется решение «оставить / выкинуть»:
И сравнивать надо против двух множеств. Первое — история («вчера уже было»). Второе неочевидно: своя же пачка — новости текущего прогона ещё не записаны в историю, и без внутрибатчевой проверки два пересказа одного анонса, пришедшие из разных источников, оба попадут в выпуск.
Голосовой ансамбль: один движок ошибается, трое голосуют
Последний кейс — самый наглядный про «модель готовая, ценность в обвязке». Тут вообще не одна модель, а несколько, и вся работа — в том, как их сложить.
Задача. Распознать голосовое сообщение, где встречаются редкие имена собственные и специальные термины. Одиночный движок на таких словах регулярно ошибается — у каждого свой профиль ошибок.
Идея. Не обучать свою модель, а скомбинировать несколько готовых движков. Работает потому, что ошибки движков не совпадают: где промахнулся один, другой обычно прав. Жми «свести» — в каждой позиции победит слово с большим весом голосов:
Здесь модель — это готовый чужой движок, их несколько, и ни строчки промпта. Сто процентов ценности — алгоритм выравнивания и голосования, плюс возня вокруг: ручная нормализация буквы «ё» (иначе «её» и «ее» голосуют как разные слова), восстановление заглавных в именах, выбор самой длинной гипотезы за основу. А сам ансамбль работает как подстраховка: основной разбор делает модель, голосование считается параллельно и подставляется, если модель не ответила.
Вывод блока. В обоих кейсах модель выдаёт сырьё, а качество финального результата делает слой контроля: дедупликация по смыслу с защитой от сбоя, голосование ансамбля с выравниванием. Между «модель ответила» и «можно показывать» — целый этаж инженерии.
Экономика: где утекают деньги
Эту тему я вынес отдельно, потому что она проходит сквозь все проекты. Автономный агент работает сам и круглосуточно — а значит, ошибка в экономике не стоит копейки разово, а капает каждый час, пока вы спите. И почти всегда деньги утекают не там, где «модель дорогая», а в обвязке вокруг вызова.
Размышление там, где оно не нужно
Самый дорогой урок. Модель-арбитр в графе знаний отвечает на бинарный вопрос: эта задача и та — одно и то же, да или нет? Если у модели включён режим размышления, она на простейший «да/нет» сжигает в десятки и сотни раз больше токенов — порядка двухсот вместо двух. Сам ответ — два токена. Остальное — «размышления», которые на бинарной классификации не добавляют точности.
Повторение одного и того же в каждом запросе
Второй урок — из перевода. Первый наивный прогон перевода интерфейса обошёлся примерно в двадцать долларов вместо ожидаемых трёх-пяти. Оказалось: в каждый из двух тысяч запросов копировался один и тот же глоссарий терминов. Девяносто восемь процентов денег ушло не на перевод, а на повторение глоссария две тысячи раз.
Дешёвую работу — дешёвой модели
В боте-собеседнике основной диалог идёт на сильной (дорогой) модели, а вспомогательную работу — извлечение фактов из диалога в фоне — делает дешёвая. Качество извлечения чуть ниже, но для фоновой задачи достаточно, а разница в цене на круглосуточном потоке существенная. Прикидка стоимости основной модели — порядка шестидесяти долларов в месяц (это оценка, токены там точно не считались, так что плюс-минус треть); гонять на ней ещё и фоновое извлечение было бы расточительством.
Что со всем этим делать
Если коротко — когда в следующий раз услышите «да это просто промпт», вспомните эти восемь историй. Промпт в каждой занял несколько абзацев и писался за час. А вокруг встало всё то, ради чего система реально работает в проде, а не в демо:
- Изоляция — песочница-клон, безопасный дефолт, маскировка секретов, чтобы автономный агент не снёс живое.
- Доставка событий — очередь как источник правды, разгребание при старте, засев первого запуска, чтобы ни одно событие не потерялось и не задвоилось.
- Память — векторная база, дедупликация, связывание, чтобы агент помнил, а не тонул в дублях.
- Контроль качества — дедуп по смыслу с защитой от сбоя, голосование ансамбля, чтобы наружу шло не сырьё.
- Экономика — выключенное размышление, кэш, дешёвая модель на дешёвое, чтобы круглосуточный процесс не разорил.
Промпт — это идея. Инфраструктура — это то, переживёт ли идея столкновение с реальностью: с рестартами, чужими коммитами, сбоями, потоком дублей и счётом за токены. Поэтому, когда планируете автоматизацию агентом, закладывайте время не на «придумать промпт» — на это уйдёт вечер. Закладывайте на те самые девяносто процентов.
// Обсуждение
Можно писать анонимно. Укажите email, чтобы получать уведомления об ответах.