Разбор · ИИ-агенты · Эвалы
ИИ-агент в продакшне: браузер, тулы и эвалы
Собрать демо агента — 15 минут с любым фреймворком. Довести его до настоящего продакшна — год работы, и почти вся сложность лежит там, куда не заглядывают туториалы: где живёт стейт, как течёт стриминг, почему каждая модель ломает формат по-своему и как вообще тестировать систему, которая на один и тот же вопрос отвечает по-разному. Разбор одного честного пути — агент-аналитик поверх self-service BI, целиком в браузере.
С чего начинает фронтендер: агент в браузере
Первое решение, которое определяет всё остальное: агент живёт не на Python и не на тяжёлом сервере, а прямо в браузере. Если заходить в задачу с фронтенд-стороны, это не экзотика, а естественная стартовая точка — инструмент под рукой, стейт приложения уже в браузере, а данные (большие таблицы) тоже на клиенте. Загвоздка в том, что готовые фреймворки для агентов почти все предполагают Python и сценарий «чатик дёргает тулы, которые сходили в базу и вернули пару строк». Под фронтенд готового нет — приходится собирать самому. И специфика обратная: данных много, они на клиенте, и запихнуть таблицу целиком в модель нельзя — она захлёбывается. Значит, модель не должна видеть сырые данные; её задача — выбрать правильные инструменты.
Второе следствие — stateless. Можно было бы поднять стейт на сервере, но тогда на каждый из десятков инстансов Node пришлось бы шарить состояние через общий Redis: запрос приземляется на случайный инстанс, и где-то надо держать сессию по айдишнику. Это дорого. Держать весь контекст диалога в браузере конкретного пользователя — почти бесплатно. Поэтому браузер собирает контекст сам и ходит наружу только за следующим ответом — ровно как локальный кодинг-агент на твоей машине, только агент живёт во вкладке.
Водопровод: BFF, стриминг и имитация SSE
Напрямую из браузера в инференс ходить нельзя — спалишь ключи и системные промпты. Поэтому между ними стоит тонкий BFF на Node: он проксирует запрос, доклеивает ключи, промпты и мониторинг и улетает в модель. Сам по себе он ничего не помнит — весь стейт по-прежнему в браузере.
Дальше выясняется, что никто не готов смотреть на крутилку, пока модель думает, — нужен стриминг. Классический выбор — SSE, но у него неудобное свойство: это GET, и он предполагает stateful-бэкенд (сначала получаешь айдишник, потом вторым запросом тянешь стрим по нему). Для stateless-схемы это не годится. Поэтому SSE имитируют руками: POST-запрос, в ответ открывается стрим, и чанки из него вычитываются вручную. Работает — и, как выясняется, ровно так делают все.
Тулы, чарты и агентский цикл
Стриминга мало — нужны тулы. Модели описывают набор функций, и она знает, что их можно вызвать с параметрами. На этом строится агентский цикл: модель видит, что находится на дашборде, спрашивает тул «какие здесь есть чарты», получает список, говорит «дай данные вот из этого», следующий тул подкладывает данные — и в конце строится ответ.
Важная деталь про доверие. Модели не показывают сырые числа в чате — вместо этого она строит настоящий интерактивный чарт поверх настоящего датасета. Пользователь заходит в него, проверяет, что фильтры наложены правильно, двигает период. Доверие идёт не от того, что «модель написала циферку», а от того, что за ответом стоит реальный график, который можно перепроверить глазами. Плюс отдельный тул-судья в чистом контексте проверяет, что ответ соответствует вопросу и фильтры выставлены верно.
И ещё один шаг на входе — классификатор. Он отсекает вопросы не по теме: встроенному аналитику незачем отвечать, как приготовить свиные крылышки, — это просто сожжённые токены. Всё, что не про данные, лучше отрезать сразу. А то, что про данные, модель отвечает не из «памяти» (там галлюцинации, ничем не валидированные), а подтягивая куски документации через RAG.
Отдельные узкие агенты в разных частях продукта со временем срослись в одного — единый аналитик, который на входе понимает, к чему относится вопрос, и запускает нужную цепочку.
Зоопарк моделей: почему переносимость — это ад
Соблазн думать, что агент пишется один раз. На деле каждый инференс ведёт себя по-своему. Вышла новая версия популярной open-модели — и формат ответа поменялся: она вдруг начала заворачивать ответ во внешний объект, которого раньше не было. Чистка системного промпта помогает, но каждый такой переезд — расследование: почему лезет reasoning, почему не отключается, почему чанки летят иначе, у кого-то долетает usage, у кого-то нет.
Глубже — два слоя. Каждая модель по-своему понимает tool-calling, и вызовы приходится выкусывать разными регэкспами. Те, кто делает универсальный инференс, вынуждены знать особенности каждой модели и унифицировать их у себя — но если ходишь между несколькими источниками, каждый унифицировал по-своему, и договориться они не могут. А в on-prem ещё веселее: клиент приносит «любой OpenAI-совместимый API», и что там внутри — неизвестно.
Лечится это не файликом-под-каждую-модель, а общим кодом, который становится всё более устойчивым: он не знает про конкретную модель, он знает — пришло или не пришло это поле в структуре ответа; если не пришло — делаем так. Умные топовые модели прощают кривой промпт и переваривают всё; поэтому целиться надо не в них, а идти в обратную сторону — разгружать контекст и упрощать так, чтобы справилась и слабая модель. На on-prem вполне может стоять быстрый, но не самый умный 120B, и он тоже должен всё это переварить.
Мечта — иметь такой набор эвалов, чтобы можно было спокойно даунгрейдиться на модель попроще и дешевле, а не только гнаться за самой умной.
Эвалы: самая дорогая часть
Сначала агента просто прокликивали мышкой. В какой-то момент стало понятно, что так жить нельзя: сценариев десятки, каждый день добавляют новые, сделать регресс руками невозможно. Нужен эвал-харнес — и это оказалась огромная отдельная область, ради которой пришлось выбивать отдельные спринты. Эвалы делятся по стоимости:
- Дешёвые, кодом. Если ждёшь конкретное значение (среднее, сумму), никакая модель для проверки не нужна — тул вернул число, ты матчишь одно на другое. Сюда же смоуки: спросил столицу — проверил, что в ответе есть «Лондон».
- LLM-судья. Когда ответ богатый (аналитика), другая модель читает ответ первой и выносит вердикт. Тонкость: судью нельзя просить оценить всё сразу — если он завысил первый критерий, завысит и остальные (эффект ореола). Поэтому его дёргают отдельно по каждому критерию — полнота, скорость, расход токенов. Медленно и дорого, но надёжно.
- Железный пользователь. Третья модель играет живого — например, раздражённого — юзера и проходит весь цикл диалога. Своя опасность: две модели могут зациклиться в бесконечной взаимной вежливости.
И одного прогона мало. Систему недетерминированную гоняют по многу раз и смотрят не на один результат, а на устойчивость: остался ли агент за пять прогонов в тех же границах. Порог не обязан быть 100% — где-то допустимо флапать, считаешь «прошли на 80% — ок». Эвалы дороже и хрупче, чем классические E2E-тесты: каждый прогон стоит токенов, а любое изменение может их «сломать», хотя это далеко не всегда настоящая поломка — много false negative, которые надо разгребать. Отсюда стратегия — забрать максимум дешёвыми проверками кодом и звать судью только там, где без него никак.
Самое ценное топливо для эвалов — реальные диалоги пользователей (их стараются сохранять целиком, где позволяет чувствительность данных): такие кейсы сам не придумаешь. Люди умеют месяцами висеть в одном чате, пока агент не начинает сходить с ума, — и именно это разбирают, чтобы стать лучше.
Заменит ли это аналитиков
Короткий ответ — пока нет. Маленькому бизнесу биай-система, возможно, и не нужна: взял кодинг-агент, поднял ClickHouse, построил дашборды сам. Большому — нужна, и там же вопросы безопасности. Но данных, которые вытаскивает виртуальный аналитик, для качественной аналитики пока недостаточно. Настоящий аналитик на вопрос «какие были продажи» не отвечает одной цифрой — он добирает контекст, объясняет, почему так, что ещё может понадобиться. Это как «заменят ли тестировщиков разработчиками»: тесты написать можно, но вокруг куча теории, без которой продукт не построишь.
Разработка без рук: план → ревью → реализация → валидация
Отдельная тема — как всё это пишется. Код руками почти не пишется с зимы. Флоу устроен как цикл из нескольких моделей, проверяющих друг друга:
- План одной моделью.
- Ревью плана другой — модели буквально гоняют план друг через друга, пока он не устроит.
- Реализация — обычно самой сильной моделью.
- Ревью кода второй моделью, а поверх — внешний diff-вьюер с аннотациями: прямо в дифе пишешь «а вот здесь поправь, а это зачем сделал», закрываешь — и правки уходят в цикл.
Поверх — автоматизация входящего потока. Багрепорты из внутренней кнопки падают в очередь; агенту говоришь «посмотри очередь, найди проблемы в формулах» — он забирает тикеты, сам поднимает эвал-систему, что-то правит разово, а что-то добавляет в корзинку эвалов. Так закрылись тикеты, что висели по два месяца. Повторяющееся — сохраняется в скиллы, чтобы не настраивать руками. Тот же приём на дежурстве, когда команда неделю работает третьей линией поддержки: 50–70% тикетов закрываются моделью — она находит, кто последним трогал код, связывает всплеск ошибок с конкретным релизом, разгружает дежурного.
Почему работы стало больше, а не меньше
Ожидание, что ускорение освободит руки, не сбывается — буст просто стал новым бейзлайном. Во-первых, не все перешли на AI-разработку. Во-вторых, ИИ породил гору новых задач, которых раньше не было: тот же эвал-харнес не существовал, а теперь необходим, и на него нужны руки. Задач за год стало сильно больше — то же самое говорят и в командах, которые делают эти инструменты: нанимают ещё, потому что работы прибавляется.
Отдельный риск — бас-фактор. Артефактов почти не остаётся: планы часто не коммитят и не поддерживают, эвал-харнес крутится на личной машине автора, часть автоматики бежит на личных токенах и подписках. Уйдёт человек — систему придётся поднимать заново, разбираться, где токены, докупать новые. Мечта на горизонте — трекеры нового типа, которые хранят вокруг задачи контекст для агента, чтобы знания не запирались в голове одного человека или в локальном обсидиане без доступа.
Куда это движется
Скорость выросла в 3–5 раз: задача, которая раньше делалась неделю, закрывается за день, а что-то можно запустить на ночь и забрать результат к утру. Направление — меньше кода, больше продуктового понимания: обвешивать систему метриками, общаться с клиентами, потому что время освободилось и его логично вложить в качество. Контур будущего — не команда вокруг продукта, а один-два человека вокруг большой штуки, под которыми крутится куча моделей (совсем один — рискованно из-за того же бас-фактора).
И главный сдвиг за год: если раньше людям говорили «попробуйте, вдруг в мелочах пригодится», то теперь агенты закрывают почти все базовые разработческие задачи. Ускорение — космическое, и переосмыслять подход придётся ещё не раз: сейчас можно через месяц полностью поменять мнение, а потом ещё раз.