Я открыла биллинг и увидела ноль. Не «мало осталось», а ровно ноль. Виноват был не сложный сценарий и не наплыв пользователей: один агент на расписании отработал сам с собой около 14 часов и сделал 129 вызовов языковой модели. Задача, которую он при этом решал, была детерминированной - ее можно было закрыть скриптом с обычным условием.
Это статья о том, почему я перестала лечить ненадежность агентной системы добавлением новых агентов. Дальше - что именно сломалось, какой слой оказался мертвым грузом, и правила, которые заменили количество: бюджет вызовов, стоп-критерии, ретро после каждой задачи и память под системой контроля версий. И почему результат надежнее проверять одним скептиком, чем толпой судей.
Пару терминов сразу.
- Агент - программа, которая в цикле сама решает, какой шаг сделать следующим, и спрашивает об этом языковую модель.
- Вызов - одно обращение к модели, за которое вы платите.
Соблазн роя: почему хочется добавить еще одного агента
Логика добавления агента всегда выглядит убедительно. Результат вышел кривой - добавим аналитика, который разберет задачу до начала работы. Аналитик ошибся - добавим скептика, который его проверит. Скептик спорит с исполнителем - добавим оркестратора, который их рассудит. Каждый шаг кажется дешевой страховкой: еще одна пара глаз, еще один слой защиты.
Проблема в том, что пара глаз здесь недетерминированная. Новый агент - это не проверка, а еще один источник решений, который может ошибиться, зациклиться или уйти не туда. Слои защиты складываются в надежность только тогда, когда каждый слой надежнее того, что он проверяет. Если вы ставите вероятностного контролера над вероятностным исполнителем, вы получаете не гарантию, а вторую вероятность и вдвое больший счет.
Вторая ловушка - ощущение работы. Рой производит много видимой активности: обсуждения, планы, промежуточные документы. Со стороны кажется, что система думает. На деле она может потратить весь бюджет на разговор о задаче, к которой еще не приступала.
Инцидент, который все расставил по местам
Агент с языковой моделью висел на расписании и просыпался каждые 30 минут. За примерно 14 часов он сделал 129 вызовов и обнулил баланс.
Здесь важна арифметика. Пробуждений за это время было около тридцати, а вызовов - 129. То есть одно пробуждение - это не один вызов: внутри каждого запуска агент еще и рассуждал, что ему делать. Ставя агента на расписание, вы умножаете не число запусков, а число запусков на среднюю длину внутреннего цикла. Вторую величину вы обычно не контролируете и в голове не держите - именно она и превращает «раз в полчаса, это же копейки» в пустой счет к утру.
Вывод я записала себе большими буквами: детерминированная проверка - это скрипт, а не агент. Если условие выражается как «если A, то B», языковая модель в этом месте не нужна. Она не добавляет ума, она добавляет расход и разброс. Модель нужна там, где вход неструктурирован и заранее неизвестно, какие шаги понадобятся. Проверка состояния по расписанию - не тот случай.
И отдельный урок про предел. У агента на расписании нет естественного конца: человек закрывает ноутбук, а цикл - нет. Все, что работает без человека в контуре, должно иметь границу, заданную снаружи, а не надежду на то, что оно само остановится.
flowchart LR
subgraph BEFORE["Было - агент на расписании"]
A1["Пробуждение каждые 30 минут"] --> A2["Внутренний цикл рассуждений"]
A2 --> A3["129 вызовов за ~14 часов"]
A3 --> A4["Баланс обнулен"]
end
subgraph AFTER["Стало - скрипт плюс бюджет"]
B1["Проверка условия скриптом"] --> B2["0 вызовов модели"]
B2 --> B3["Модель только там, где вход неструктурирован"]
B3 --> B4["Не более 5 вызовов на задачу"]
endМертвый слой: 63 файла, которые никто не открыл
Второй симптом той же болезни я нашла не в счетах, а в репозитории. В системе жил доменный слой из 63 файлов-анкет - структурированных описаний предметной области, которые агенты должны были заполнять и потом переиспользовать. Их делали «на будущее»: придет похожая задача - не начнем с нуля.
За полтора месяца - ноль повторных использований. Ни один файл не понадобился второй раз. Слой я заархивировала.
Это тот же рой, только застывший в файлах. Логика идентичная: раз мы не знаем, что понадобится, сделаем побольше, вдруг пригодится. Разница в том, что лишние агенты жгут бюджет сразу, а мертвые артефакты жгут внимание постепенно. Они видны в дереве файлов, попадают в контекст, их приходится учитывать при рефакторинге и объяснять новому человеку.
Проверка простая и неприятная: у любого артефакта, созданного «на будущее», должны быть дата появления и счетчик обращений. Если через месяц счетчик на нуле - это не задел, это балласт.
flowchart TD
T["Задача"] --> Q["Триаж: 5 вопросов"]
Q --> S{"Стоп-критерий сформулирован?"}
S -- "нет" --> X["Не начинаем"]
S -- "да" --> B["Бюджет: не более 5 вызовов"]
B --> E["Исполнитель - один, без роя"]
E --> K["Скептик - один проход, ищет отказ"]
K -- "не годится" --> E
K -- "годится" --> R["Обязательное ретро"]
R --> M["Память под контролем версий"]
M -.-> QПравила, которые заменили количество
Вводила я их не в лаборатории, а на живых задачах - там, где система работает на отельную сеть и на северного туроператора, и где неверный результат стоит не абстрактной репутации, а чьего-то рабочего дня.
1) Бюджет вызовов
Не больше 5 вызовов модели на задачу. Один исполнитель вместо роя.
Бюджет полезен не тем, что экономит деньги - хотя экономит, - а тем, что заставляет систему выбирать. Когда вызовов пять, нельзя позволить себе два на «обдумать подход» и один на «уточнить формулировку». Приходится сразу идти в дело. Ограничение работает как проектное решение, а не как бухгалтерия.
2) Триаж из пяти вопросов
Перед запуском задача проходит через пять вопросов:
- что это за задача
- стоит ли ее вообще делать
- какая стратегия
- какие ресурсы нужны
- когда остановиться
Второй вопрос отсекает половину работы, которую никто не просил. Пятый - главный.
Стоп-критерий формулируется до начала, а не тогда, когда стало ясно, что не получается. Если сформулировать его не удается, это сигнал: задача поставлена плохо, и агент здесь ни при чем.
3) Ретро после каждой задачи
Обязательное, а не по настроению. Что было задачей, что пошло не так, какое правило из этого следует. Именно так система учится: не тем, что в ней стало больше ролей, а тем, что после каждой задачи в наборе правил что-то меняется.
4) Память под системой контроля версий
Организационная память системы - накопленные правила и выводы - лежит там же, где код, под системой контроля версий. Значит, у каждого правила есть автор, дата и причина; видно, когда оно появилось; его можно откатить.
Память, которая живет в базе без истории, за пару месяцев превращается в свалку утверждений, про которые непонятно, кто их туда положил и верны ли они еще.
Проверка: один скептик вместо толпы судей
Популярная схема верификации выглядит так: несколько агентов дают варианты ответа, отдельный агент-судья выбирает лучший. В исследовательских задачах, где вы честно не знаете, какая стратегия сработает, у нее есть смысл. В прикладной работе я от нее отказалась.
Причин три:
- Цена: N кандидатов плюс судья - это N+1 вызовов там, где на всю задачу отведено пять.
- Судья не эксперт: он выбирает из того, что ему принесли. Если все кандидаты ошиблись одинаково (а они к этому склонны), судья уверенно выберет ошибку.
- Размытая ответственность: когда ошибку пропустили пятеро, чинить нечего - механизма «виноватого» нет.
Вместо этого - один проход скептика. Скептик получает готовый результат и одну инструкцию: найти место, где это неверно. Не «оцени качество», не «выбери лучший вариант», а покажи конкретное место, где сломается.
Применить у себя
Что проверить на практике
- Посчитайте вызовы модели за сутки и разделите на число решенных задач. Если среднее вас удивило - вы не знаете, куда уходит бюджет, и это первая проблема, а не деньги.
- Найдите всех агентов, которые запускаются по расписанию. По каждому спросите: если бы решение принимал скрипт с обычным условием, изменился бы результат? Там, где ответ «нет», перепишите скриптом.
- Поставьте потолок вызовов на задачу. Начните с 5 и смотрите, что упирается в границу: часто это не сложность задачи, а болтовня на подступах.
- Выпишите пять вопросов триажа и прогоните через них три последние задачи задним числом. Особое внимание пятому: был ли записан стоп-критерий или система остановилась потому, что вы ее выключили.
- Пройдите по артефактам, созданным «на будущее», и посчитайте обращения к каждому. Ноль за месяц - в архив, не в бэклог.
- Замените схему «несколько кандидатов плюс судья» на один проход скептика с инструкцией искать конкретный отказ. Прогоните на десятке задач и сравните, какая схема ловит больше настоящих ошибок.
- Заведите ретро на три строки после каждой задачи и положите его под систему контроля версий рядом с кодом.
Как понять, что получилось
Главный критерий один: расход перестал быть сюрпризом. Вы можете назвать верхнюю границу вызовов до запуска, а не узнать ее из биллинга.
Остальные признаки:
- у каждой задачи есть записанный стоп-критерий
- со временем растет число правил, а не число агентов
- разбор инцидента заканчивается изменением правила, а не появлением нового агента
- в репозитории нет артефактов, к которым за месяц никто не обратился
- скептик хотя бы иногда говорит «не годится» - и это подтверждается на практике.
Надежность агентной системы дает не количество исполнителей, а дисциплина: стоп-критерии, бюджет вызовов, обучение через ретро и память под версионированием. Рой выглядит как сила, а ведет себя как расход. Пять правил обходятся дешевле пяти агентов - и, в отличие от них, работают одинаково в любое время суток.