Привет, Хабр! Меня зовут Роберт Арифулин. Я в Сбере разрабатываю ИИ-решения для банкоматов и других устройств самообслуживания. Сегодня я хочу рассказать, как мы сделали эволюционный агент, который автономно прокачивает промышленного агента по восстановлению работоспособности банкоматов.

Представьте: сложная нетипичная неисправность в банкомате, огромная инструкция с вариантами решения по устранению неисправностей на тысячи строк, и ИИ-агент на GigaChat, которому нужно выбрать правильную процедуру и сформировать решение для системы мониторинга.

Чтобы составить порядок работ по восстановлению банкомата, сотрудник мониторинга банкоматов обычно опирается не только на единую инструкцию (стандарт), но и на множество дополнительных, не всегда формализованных знаний: где в системе мониторинга найти нужную информацию, какие комбинации статусов компонентов банкомата помогут найти неявную проблему и какие внешние изменения могут повлиять на процессы восстановления работы. Эта информация часто разбросана по разным системам и напрямую агенту недоступна.

На первом этапе внедрения агента такие знания приходилось добавлять вручную: эксперты дописывали подсказки к шагам инструкции, чтобы помочь LLM выбрать правильный вариант ответа на вопрос. Это работало, но с ростом количества процедур и сценариев, в которых работает агент, такой подход становился всё более ресурсоёмким. Затраты на написание подсказок росли и снижали эффект от внедрения ИИ-решения. Мы понимали, что из-за перемен в окружении и процессах мы постоянно должны будем тратить на эту работу много сил. И чтобы избежать этого, разработали эволюционный агент для итеративного улучшения агента мониторинга банкоматов.

По сути, система начинает постепенно улучшать логику агента мониторинга банкоматов на основе накопленных случаев. Мы вдохновлялись лучшим решением из агентского соревнования ERC3, ключевой идеей которого было эволюционное усиление reasoning, а не бесконечное усложнение модели. Если говорить более научными терминами, то идею неплохо описывает статья «Reflexion: Language Agents with Verbal Reinforcement Learning».

Часть 1. Агент мониторинга банкоматов: production-контур

Реальный процесс выглядит так. Есть банкомат, при сбое в его работе или необходимости проведения плановых работ по его обслуживанию в целевой системе мониторинга регистрируют заявку (инцидент). Для устранения сбоя могут понадобиться дополнительные действия: замена расходных материалов, переинкассация денежных средств, ремонт и так далее. Чтобы всё это выполнять, в целевой системе мониторинга формируют заявки, которые далее направляют ответственным службам. Исполнение заявок и восстановление работы банкомата также контролируют в системе мониторинга.

Значительная часть потока уже обрабатывается встроенной автоматизацией: BPM-логика, построенная на системе хештегов и разметке NLP-моделей, закрывает типовые сценарии. Если стандартный автоматический контур не справляется, то случай попадает в следующий слой принятия решений — туда, где работает агент мониторинга банкоматов. Если и он не может принять решение, то задача уходит специалисту по мониторингу банкоматов.

Упрощённо цепочка такая:

Почему понадобился отдельный агент

Работа сотрудника линии мониторинга строится вокруг документов СОП (Стандарты операционных процедур). Это, фактически, деревья решений: нужно пройти по последовательности вопросов и прийти к корректной инструкции — закрыть заявку, вернуть её, создать инцидент, сменить рабочую группу, отправить команду на перезагрузку и так далее. Задача агента мониторинга банкоматов — заменить этот ручной этап.

Проблема в том, что у агента сейчас нет доступа к интерфейсу в том виде, как его видит человек: он не работает напрямую с экранами и карточками. Поэтому вся информация, которую сотрудник обычно собирает из нескольких внутренних сервисов, передаётся агенту сразу в виде одного большого JSON-контракта — слепка текущего состояния банкомата и связанных сущностей. Этот JSON содержит в среднем около 1200 строк и порядка 50 000 символов. Для человека это распределено по интерфейсам, а для агента —это один большой структурированный вход.

Что приходит агенту на вход

Наиболее значимые поля, с которыми агент работает чаще всего:

Поле

Значение для агента

Текущие бизнес‑статусы в банкомате

Позволяет ориентироваться в ключевых статусах банкомата.

Описание заявки

Содержит первичную информацию о проблеме или текст ошибки.

Решение по заявке

Используется для анализа того, как проблема была устранена.

Рабочая группа

Указывает, какое подразделение обрабатывало заявку.

Тип работ

Определяет общую категорию вмешательства.

Вид работ

Конкретизирует действие (например, «замена чековой ленты» или «ремонт картридера»).

Активные случаи

Помогает избежать дублирования заявок по одной и той же проблеме.

Исторические случаи

Позволяет выявлять хронические неисправности и оценивать стабильность банкомата.

Состояние компонентов банкомата

Детальный статус датчиков и узлов: диспенсера, картридера, принтера и др.

То есть агенту нужно не просто «прочитать текст», он должен извлечь сигнал из большого неоднородного контракта, сопоставить несколько источников данных и принять строгое операционное решение.

Архитектура агента мониторинга банкоматов

Архитектурно агент состоит из трёх основных слоёв: выбор СОП, прохождение СОП с адресным извлечением фактов из JSON, и исполнение действия, находящегося в конечном узле СОП (заполнение и отправка другого JSON‑контракта с инструкцией для системы‑источника о дальнейших действиях по инциденту).

1. Выбор нужного СОП

Первый шаг — понять, по какому именно СОП нужно вести случай. Здесь используется двухуровневый механизм: сначала ML‑классификатор на CatBoost, затем LLM‑классификатор как fallback для редких СОП или случаев, когда ML‑модель недостаточно уверена.

В качестве признаков для ML‑модели используются:

  • эмбеддинги (EmbeddingsGigaR) по Описанию и Решению;

  • категориальные признаки по основным полям текущего случая.

На этом шаге задача не «решить инцидент», а правильно выбрать дерево, по которому агент будет двигаться дальше.

2. Прохождение СОП как дерева решений

После выбора СОП агент начинает идти по нему последовательно, вопрос за вопросом. Это не один запрос «дай финальный ответ», а именно прохождение дерева: на каждом узле нужно корректно ответить на вопрос, перейти в следующую ветку, затем в следующую — и так до финального шага, где содержится инструкция для системы. Во‑первых, это исторически сформированный процесс, которые позволяет людям не гадать «как восстановить работу устройства», а, отвечая на конкретные вопросы, приходить к результату. Во‑вторых, так на порядок проще определять, где агент ошибся, и воспроизводить результаты этой ошибки, чтобы улучшить процесс.

Масштаб:

  • используется больше 200 СОП;

  • один СОП может быть глубиной до 25 вопросов и содержать до 1000 вопросов суммарно во всех ветках.

При этом в размеченных данных, которые мы получаем от сотрудников Центра мониторинга банкоматов, есть не просто «правильный СОП», а правильный путь внутри него — в среднем это последовательность из 8 идущих подряд вопросов, по которой агент должен пройти без ошибки.

3. Инструменты: управляемый доступ к данным

Чтобы не заставлять LLM каждый раз перечитывать весь JSON как сплошной текст и не забивать контест, мы дали агенту набор инструментов, которые точечно извлекают нужные данные. Примеры инструментов, позволяющие достать из JSON релевантную вопросу в СОП информацию:

  • get_current_ticket_info — подробности текущей заявки;

  • get_current_incident_info — подробности текущего инцидента;

  • get_active_ticket_info — список активных заявок на банкомате;

  • get_component_info — статус компонент банкомата;

  • get_atm_info — общая информация о банкомате.

Все эти инструменты устроены однотипно: они читают конкретный фрагмент JSON и возвращают значение необходимого атрибута.

@tool
def get_atm_info(field: str):
   """
   Получение общих сведений о банкомате: IP-адрес, события,
   бизнес- и техстатусы, регистрационные сведения.

   За один вызов можно получить только одно поле.
   """
   if field in json_info:
       return str(json_info[field])
   return "None"

4. Принятие решения

Последним этапом идёт формирование ответа. Агент анализирует инструкцию на последнем шаге, извлекает из неё список действий, а затем для каждого действия заполняет поля по шаблону.

Команда

Контекст применения

Создание заявки

Регистрация нового обращения в системе сопровождения.

Возврат заявки

Возврат на предыдущий этап или уточнение данных, если информации недостаточно.

Закрытие или подтверждение

Завершение жизненного цикла заявки при успешном решении.

Создание инцидента

Регистрация сбоя, требующего оперативного вмешательства (Service Desk).

Закрытие инцидента

Фиксация устранения неисправности.

Смена рабочей группы

Перевод инцидента на профильных специалистов (например, на инкассацию).

Смена бизнес‑статуса

Дистанционное изменение доступности сервиса на банкомате (ввод в работу или вывод из работы).

Команда на перезагрузку

Попытка программного восстановления работоспособности через перезапуск систем.

Не определено

Специальная категория для случаев, когда СОП не даёт однозначного алгоритма. Тогда агент создаёт действия на основе алгоритма «Неопределённого шага».

Как мы повышаем детерминированность ответов агенту: SGR и Structured Output

Если просто передать в LLM JSON и текст вопроса, то модель может начать слишком по‑разному трактовать одни и те же ситуации. На сложных узлах это быстро приводит к просадке качества. Поэтому в агенте мониторинга банкоматов мы используем SGR (Schema Guided Reasoning) — структурированный слой рассуждения, который заставляет модель идти по более жёсткой схеме:

  1. выделять полезную информацию из вопроса;

  2. определять, какие инструменты действительно нужны;

  3. проверять, собраны ли уже все данные;

  4. перечислять пробелы;

  5. собирать подтверждающие факты;

  6. строить план оставшихся шагов;

  7. и только потом формировать ответ.

Мы усиливаем это за счёт structured output на pydantic‑схемах. Так модель меньше галлюцинирует и строже соблюдает формат reasoning.

Ключевой элемент здесь insert_rules — поле, в которое подставляются дополнительные экспертные подсказки. Оно потом и становится точкой приложения для эволюционного агента.

class Conclusion(BaseModel):
   useful_information_from_question: List[str]
   insert_rules: str

   is_useful_information_in_get_current_ticket_info: bool
   is_useful_information_in_get_atm_info: bool
   is_useful_information_in_get_component_status_info: bool
   is_useful_information_in_get_active_ticket_info: bool
   is_useful_information_in_get_current_incident_info: bool

   is_data_collected: bool
   list_of_gaps: List[str]
   list_of_evidence: List[str]
   plan_remaining_steps: List[PlanStep]

   is_completed: bool
   answer: str

Что такое подсказки и где они применяются

Подсказки (hints) — это формализованные знания экспертов для конкретного узла дерева. Они привязываются к ключу вида СОП + вопрос и описывают, где искать нужные данные, как трактовать формулировку вопроса, какие поля обязательны для проверки и какие частные случаи нельзя пропускать. Хранятся подсказки в таблицах, потому что так их проще редактировать вместе с экспертами. Промпты при этом лежат в YAML, а схемы structured output — в pydantic‑моделях.

Количество подсказок для одного вопроса доходит до 19 в особенно сложных случаях.

Ниже пример одного из вопросов в СОП с подсказками. В таких местах без жёсткой подсказки модель начинает «обобщать», а нам нужно строгое соответствие логике принятия решения.

Вопрос: «В выполненных заявках по данной тематике, начиная с даты создания инцидента, проанализируйте информацию в поле „Решение/обходной путь“. Требовалось продолжение работ для устранения проблемы, указанной в инциденте?»

Варианты ответа:

  1. Да (переход в вопрос 302)

  2. Нет (переход в вопрос 303)

Пример подсказки для агента по ответу на вопрос (SGR‑схема): hint — вставляем в промпт после вопроса, из всех следующих подсказок формируется JSON c ожидаемыми типами данных и вставляется в insert_rules. Это позволяет направлять логику рассуждения LLM по необходимой нам схеме.

Название подсказки

Подсказка

Ожидаемый тип данных

hint

Проанализируй информацию в поле «Решение/обходной путь» в выполненных заявках. Сравни проблемы, описанные в решении заявок, с проблемой текущего инцидента.

string

is_incident_problem_checked

Верни True, когда получишь описание проблемы текущего инцидента для сравнения.

boolean

incident_problem

Верни описание проблемы текущего инцидента.

string

incident_creation_date

Верни дату создания текущего инцидента.

string

tickets_after_incident

Верни список выполненных заявок, созданных после даты создания инцидента.

array

completed_tickets_solutions

Верни список решений/обходных путей из выполненных заявок, созданных с даты создания текущего инцидента из списка tickets_after_incident.

array

matched_tickets_solutions

Верни список решений/обходных путей из выполненных заявок, созданных с даты создания текущего инцидента из списка tickets_after_incident и имеющих совподающие проблемы с инцидентом.

array

work_continuation_required

Логика определения:

Верни True, если problem_matching=True, и при этом в решении явно сказано, что требуются дальнейшие работы.

Учти, что если есть две заявки с matched_tickets_solutions с проблемой в инциденте, но в одной говорится, что проблема УЖЕ устранена, то возвращай False, потому что она является продолжением первой.

boolean

answer_logic

Действуй строго по инструкции:

1. Сначала проверь is_incident_problem_checked:

— Если False, то запланируй шаг получения описания проблемы и даты создания текущего инцидента.

2. После получения данных (is_incident_problem_checked = True):

— Найди все выполненные заявки, созданные с даты создания инцидента (tickets_after_incident).

— Проанализируй их решения (completed_tickets_solutions).

— Сравни каждое решение с incident_problem.

— Определи matched_tickets_solutions: из списка (completed_tickets_solutions оставь только те которые совпадают по проблеме с incident_problem).

3. Финальный ответ:

— Если `work_continuation_required = True И в найденных заявках есть указание, что требуются дальнейшие работы в «Да, требовалось продолжение работ».

— Если work_continuation_required = False ИЛИ проблематика совпадает, но дальнейшие работы не требуются ХОТЯ БЫ в одной выполненной заявке из «Нет, продолжение работ не требовалось».

string

Что агент мониторинга банкоматов возвращает в систему

Так как целевая система мониторинга обращается к агенту как к API, то на выходе агент тоже возвращает JSON.

result_json["body"] = {
        "deviceId": info_json["body"]["deviceId"],
        "requestId": info_json["body"]["requestId"],
        "result": [{
			"final_instruction": current_question,
			"used_sop": used_sops_text,
			"summary": summary
			}],
        "reasoning": reasoning,
        "status": status,
        "commandsToExecute": additionInformationMap,
 	}

Часть 2. Эволюционный агент: контур, который улучшает логику агента мониторинга банкоматов

Если агент мониторинга системно проседает на сложных узлах, то первое естественное решение, воспроизводимо улучшающее качество агента, — вручную добавить или переписать подсказку. Но проблема в том, что в production‑системе такой подход быстро перестаёт масштабироваться:

  1. СОП много, и каждый из них длинный.

  2. Сами СОП меняются.

  3. В Описании и Решении много сокращений, внутренних обозначений и тематического жаргона.

  4. Подсказки начинают разрастаться и дублировать друг друга.

То есть ручной промпт‑инжиниринг работает как точечная мера, но плохо работает как долгосрочный механизм сопровождения. Именно здесь и появляется второй контур — эволюционный агент.

Что делает эволюционный агент

Эволюционный агент — это отдельный агент, который не отвечает системе мониторинга и не обрабатывает заявки напрямую. Его задача — анализ работы агента мониторинга и улучшение его reasoning‑слоя с помощью новых подсказок.

На вход он получает:

  • входной JSON;

  • вопрос из СОП;

  • ответ и журналы размышлений агента мониторинга;

  • текущие промпты агента мониторинга, включая SGR‑схему и подсказки;

  • правильный ответ

После этого он:

  1. анализирует, почему production‑агент пришёл не к тому выводу;

  2. формулирует гипотезу, чего именно не хватило в логике;

  3. предлагает изменение — обычно новую или обновлённую подсказку;

  4. отправляет эту «мутацию» на проверку.

Как выглядит цикл эволюции

Цикл эволюции представляет собой замкнутый итеративный процесс, который начинается с прогона батча сложных случаев через RUN‑агент (агент мониторинга банкоматов), где сопоставляются контекст инцидента, текущая SGR‑схема и эталонный ответ разметчика. Полученные журналы рассуждений передаются Analizer‑агенту, который локализует корневые причины ошибок и формулирует вектор необходимых исправлений. На основе этого анализа Evolver‑агент модифицирует или создаёт новые подсказки в схеме рассуждений, после чего обновлённая логика проходит стадию ReRun — повторного тестирования на проблемных сценариях. Если «плохие» случаи переходят в разряд успешных, а «хорошие» не испортились, то изменения отправляются на Regress‑тест, чтобы гарантировать сохранность корректной работы на исторических данных. Весь цикл завершается принятием новой схемы только при наличии статистически значимого улучшения, в то время как неудачные итерации либо обогащают контекст для новой попытки исправления, либо приводят к прекращению работы над случаем, если исчерпан лимит попыток.

Дедупликация: чтобы правила и схемы не раздулись

На ранних этапах стало видно, что если просто добавлять новую подсказку для каждого сложного случая, то набор правил быстро становится шумным: похожие подсказки дублируют друг друга, частично конфликтуют и усложняют сопровождение. Поэтому мы добавили отдельный этап дедупликации: LLM анализирует набор подсказок, ищет семантически близкие и предлагает их объединить.

Это дало важный практический эффект: с каждым новым случаем правила перестали расти линейно, благодаря чему снизился риск внутренних противоречий. Кроме того, команде стало значительно проще сопровождать и поддерживать подсказки.

Как мы проверяем, что новая мутация действительно полезна

Главный риск автоматического улучшения — «починить» один случай и одновременно сломать уже работающие. Поэтому каждая новая мутация проходит регрессионную проверку. Мы используем два базовых сценария:

Сценарий 1. Батчевая эволюция

На вход подаются: 6 плохих случаев и 4 хороших, на их основе строится новая подсказка. При этом мутация считается удачной, если исправляет хотя бы два плохих случая и не ломает ни одного хорошего.

Сценарий 2. Точечная эволюция

Берётся один плохой случай и на его основе строится новая подсказка. Затем она проверяется на трёх случайных хороших случаях. Здесь ключевая метрика — accuracy, потому что задача дискретная: нужно правильно ответить на конкретный вопрос, чтобы попасть в корректную ветку дерева.

Сравнение качества эволюционного агента и эксперта‑разметчика

Чтобы сравниться с экспертом, мы взяли вопрос, на который уже были в ручную разработаны подсказки, и мы знаем качество работы агента как без них (в предыдущем релизе), так и с ними (в текущем релизе). Вытаскиваем размеченные случаи из текущего релиза по этому вопросу, и запускаем агент мониторинга отвечать на вопрос с подсказками, сформированными эволюционным агентом на данных предыдущего релиза. Получаем честный А/Б на новых данных.

При использовании эволюционного агента с SGR‑схемой:

  • качество вырастает на 5%;

  • потребление токенов вырастает на 8%;

  • среднее время работы вырастает на 18%.

Качество заметно выросло, несмотря на лишь незначительное увеличение токенов и времени — это удачный результат.

Подход

Accuracy

Среднее кол‑во токенов

Среднее время, sec

Затраченное время на написание, ч.

Кол‑во правил

Evolution

0,97

14 423

65

6

10

Human

0,92

13 345

55

2

5

Заключение: агент, который учит агента

Мы создали систему, в которой ИИ перестал быть просто исполнителем и стал собственным методистом. Разделение на production‑контур и контур эволюции позволило нам внедрять изменения безопасно: основной агент мониторинга остаётся стабильным и предсказуемым, пока эволюционный агент в фоновом режиме «штурмует» сложные случаи, в которых раньше пасовали даже опытные эксперты.

Практика показала, что такой подход работает на реальных данных. Мы увидели, как автоматическая мутация выводит точность на уровень 0,97, обходя ручной промпт‑инжиниринг. Эволюционный агент доказал свою эффективность в самых «шумных» и нетипичных инцидентах, где человеческий фактор и тематический жаргон раньше создавали барьеры для автоматизации.

Сегодня через этот агентный контур проходит уже 61% трафика заявок, и это только начало. Наш путь от жёстких правил к самообучающимся SGR‑схемам — это наглядный пример того, как концепции вроде Reflexion превращаются из академических статей в надёжный индустриальный фундамент для крупнейшей сети банкоматов страны.

Хочу поблагодарить коллег, без которых этот проект не увидел бы свет: Сергей Заикин, Анна Лаптева и отдельное спасибо CDS калана устройств самообслуживания Василию Потеряеву.