Показ агента почти всегда проходит хорошо. Программа получает задачу, ходит в три инструмента, возвращает разумный ответ. Через две недели после запуска начинаются жалобы, и обычно первым под подозрение попадает выбор модели.
Это почти всегда неверный адрес. Разница между показом и нагрузкой не количественная, а структурная, и её видно ещё до запуска — из арифметики.
Показ убирает четыре допущения сразу
В демонстрации агент живёт в условиях, которых в продакшене не бывает. Стоит разобрать их по одному, потому что каждое ломается по-своему.
Пользователь один. Значит, нет очереди, нет конкуренции за соединения к базе, нет лимитов у поставщика модели. Всё, что связано с параллелизмом, в показе просто отсутствует как класс проблем.
История короткая. В демонстрации диалог начинается с чистого листа и заканчивается через два-три хода. В работе история накапливается, и вместе с ней растёт контекст, который отправляется на каждом шаге. Запрос, занимавший две тысячи токенов на стенде, к десятому сообщению занимает пятнадцать — при том же коде.
Инструменты отвечают быстро. База тёплая, данных мало, индексы влезают в память. На боевых объёмах тот же запрос идёт на порядок дольше, и это не деградация, а нормальный режим.
Вопрос формулирует тот, кто знает, как формулировать. Автор демонстрации спрашивает так, чтобы получилось. Живой пользователь спрашивает так, как думает, и половина его формулировок ведёт агента по маршруту, который никто не проверял.
Продакшен снимает все четыре одновременно. Дальше начинается счёт.
Арифметика, которую можно сделать до запуска
Если один шаг в худшем случае занимает тридцать секунд, а шагов пять, верхняя граница — две с половиной минуты. Пользователь при этом рассчитывал на десять секунд, потому что столько занимал показ.
Обратите внимание: чтобы получить этот вывод, не нужны ни замеры, ни инциденты. Достаточно перемножить худшее время шага на их число. Упражнение занимает пять минут и делается на салфетке:
| Что перемножаем | Откуда берётся |
|---|---|
| Худшее время шага | Таймаут самого медленного инструмента, а не среднее по логам |
| Число шагов | Максимум, который разрешает оркестратор, а не типичный случай |
| Множитель повторов | Сколько раз шаг может быть повторён при неудаче |
Три числа, одно умножение. Команды, которые делают это до релиза, ловят проблему на бумаге, а не в поддержке.
Отдельно стоит посчитать не только верхнюю границу, но и то, что увидит пользователь в девяноста девяти случаях из ста. Среднее здесь бесполезно: агент по своей природе даёт распределение с длинным хвостом, потому что число шагов не фиксировано. Хвост и есть то, на что жалуются.
Первое, что пробуют, — и почему это не помогает
Обычная первая реакция — поднять таймаут. Логика понятная: запросы не укладываются, дадим им больше времени.
Это лечит симптом и усиливает причину. Таймаут на отдельный вызов ничего не говорит о суммарном времени запроса: пять шагов по сорок секунд — это уже больше трёх минут. Хуже того, увеличенный таймаут удлиняет и неудачные траектории. Если у оркестратора есть повторы, каждая неудача теперь стоит дороже, и вы платите за увеличенное время дважды: временем пользователя и деньгами.
Вторая типичная попытка — уменьшить число шагов, зашив маршрут жёстко. Она работает, но ценой самого агента: если порядок шагов известен заранее и не меняется, у вас обычный код, и он дешевле в обслуживании. Это не провал, а честный вывод — просто он означает, что агент здесь был не нужен.
Работает третье движение: бюджет задаётся один на весь запрос и убывает с каждым шагом. Когда он исчерпан, агент возвращает то, что успел, и честно говорит, чего не успел.
budget = Deadline(seconds=20, max_steps=5, max_retries=1)
for step in agent.run(task, budget=budget):
trace.emit(step)
if budget.exhausted():
return step.partial_result()
Это набросок, а не работающий фрагмент из проекта: он показывает, где стоит ограничитель, а не как устроен ваш оркестратор.
Существенно здесь одно — max_retries живёт на уровне цикла, а не внутри вызова инструмента. Разница кажется косметической, но она определяет порядок величины. Если положить ограничитель внутрь, пять шагов по два повтора дадут десять обращений вместо пяти. Причём в логе это будет выглядеть как обычная работа: никакой строчки «мы удвоили расходы» там не появится.
Частичный ответ как продуктовое решение
Возврат частичного результата обычно встречает сопротивление: кажется, что отдавать неполный ответ хуже, чем не отдавать ничего.
На практике наоборот. «Я нашёл три из пяти позиций, по остальным не успел» — это ответ, с которым человек может работать. Пустой экран через две минуты — нет. Плюс частичный ответ показывает, где именно агент застрял, что бесплатно даёт вам данные для разбора.
Сценарии, в которых частичный ответ действительно неприемлем, существуют: платёж не бывает частичным. Но их меньше, чем кажется на этапе проектирования, и стоит проверить каждый, прежде чем закладывать «всё или ничего».
Три места, где ломается раньше всего
Время ответа. Прямое следствие арифметики выше. Заметно сразу, чинится ограничением бюджета. Из трёх — самое простое.
Стоимость. Проявляется на первом счёте, то есть с задержкой в месяц. Механизм — повторы: агент, не справившийся с первого раза, пробует ещё, и это умножается на число пользователей. Именно поэтому расходы растут нелинейно относительно трафика, и линейная экстраполяция с пилота систематически занижает счёт.
Возможность понять, что произошло. Самое неприятное из трёх, потому что единственное, что нельзя добавить задним числом дёшево.
Когда агент делает пять шагов, обычный лог превращается в стену текста: видно, что ответ плохой, и не видно, на каком шаге всё свернуло не туда. Трассировка шагов — это не улучшение наблюдаемости, а условие возможности разбора. Минимум, который должен попасть в запись каждого шага:
- вход и выход шага целиком, а не их пересказ;
- какой инструмент вызывался и сколько он отвечал;
- сколько бюджета оставалось на момент шага;
- был ли это повтор и какой по счёту.
Последний пункт пропускают чаще всего, и именно он потом отвечает на вопрос «почему счёт вдвое больше расчётного».
Качество ответов модели в этом списке идёт последним, и это, пожалуй, главное, что стоит унести из текста.
Область ответственности расползается сама
Есть отказ, который не виден ни в метриках, ни в трассировке. Агента запускают на одном сценарии, он работает, и через две недели ему добавляют соседний — потому что «он же почти это умеет». Потом ещё один.
Каждое такое добавление по отдельности выглядит дешёвым. Вместе они превращают понятную задачу в неограниченную, а вместе с ней растёт и число шагов, и разброс времени, и счёт.
Лекарство скучное: написанный список того, чего агент не делает. Не список умений — список отказов, с явным «здесь передаём человеку». Он занимает полстраницы и экономит месяцы, потому что превращает вопрос «а может он ещё и это» из обсуждения в проверку по списку.
Наше мнение
Агент — распределённая система, а не текстовое поле. Всё скучное, что индустрия выучила про распределённые системы, применимо к нему без поправок: общий бюджет, ограничение повторов, идемпотентность, трассировка и предохранитель, который отключает автоматику, когда она начинает вредить.
Условие, при котором мы неправы: если у вас один шаг, он детерминирован, а цена ошибки нулевая, всё вышенаписанное — лишняя работа. Такие задачи существуют, и для них агент просто избыточен.
Есть и второе условие, более узкое. Если агент работает в фоне и никто не ждёт его ответа, требование к времени снимается — остаются только стоимость и наблюдаемость. Это заметно более простая инженерная задача, и переносить на неё выводы про интерактивные сценарии не стоит.
Что стоит проверить у себя
Бюджет задан на запрос или на вызов. Ограничитель повторов лежит в оркестраторе или в инструменте. Попадает ли номер повтора в трассировку. Есть ли сценарий, в котором частичный ответ лучше отсутствия ответа. И написан ли где-нибудь список того, чего агент не делает, — без него область ответственности расползается сама, и обычно быстрее, чем за две недели.
Редакции: к этому материалу нужны собственные замеры — распределение времени шага, доля повторов и разброс числа шагов на реальном трафике. До их появления в тексте нет ни одной цифры, выдающей себя за измерение.