Всем привет, меня зовут Максим Шаланкин, Machine Learning Team Lead в команде финтеха Big Data MWS. Внутренняя аналитика в финтехе живет в режиме постоянной гонки: новые клиенты приходят, ведут себя по‑разному, а бизнесу нужно быстро понимать, чем группы с разными сценариями отличаются друг от друга. При этом значительная часть работы аналитика — это однотипное сравнение когорт и поиски закономерностей в табличках и графиках. Фоновый LLM‑агент, который умеет сам поднимать данные, запускать статистические проверки и возвращать готовые инсайты, заметно сокращает путь от данных до решения.
Ниже — разбор, как устроен такой background‑агент для анализа активности новых клиентов, на чем он основан, как мы минимизируем галлюцинации и почему выбрали архитектуру, похожую на Claude Skills, а не идем в сторону MCP‑подхода.
СодержаниеЧто мы называем background LLM‑агентом
Задача: автоматический анализ активности клиентов
Skills и tools: из функций в навыки
Как агент выбирает skill и что происходит под капотом
Grounding: как мы минимизируем галлюцинации
Observability: что мониторить у LLM‑агента
Почему мы не пошли в MCP и выбрали skills‑подход
Как это помогает бизнесу
Примеры исполнения
Куда развивать дальше
Под background LLM‑агентом мы понимаем автономную, асинхронную и прерываемую программную систему, которая автоматически запускается в фоне и использует свободные вычислительные ресурсы для выполнения дополнительной, но ресурсоемкой и потенциально прерываемой работы. Основная цель таких агентов — повысить общую утилизацию оборудования, например GPU, и минимизировать финансовые потери от простоев, характерных для LLM-инференса. Они могут запускаться системным менеджером, когда основные задачи переходят в фазы с низкой нагрузкой (например, фазу Decode из-за ограничений Memory Bandwidth), эффективно заполняя «окна простоя» и обеспечивая адаптивную загрузку вычислительной инфраструктуры.
В классической инженерной терминологии background — это фоновые задачи: cron‑job, Celery worker, nightly batch. Формально наш агент сейчас именно так и запущен: по расписанию, в часы минимальной нагрузки на инфраструктуру, он поднимает нужные данные и прогоняет аналитические сценарии.
Важно честно признать: на текущем этапе это не автоматический runtime, который подстраивается под каждое свободное вычислительное окно на GPU, а контролируемый фоновый запуск в заранее выбранные периоды (такой же минимальной нагрузки на вычислительные ресурсы).
При этом целевая модель немного шире привычного cron: мы смотрим в сторону прерываемого фонового агента, который понимает состояние вычислительных ресурсов, умеет останавливаться и продолжать анализ в «окнах простоя». Это уже ближе к тому, как работают длинные LLM‑инференсы с чередованием фаз Prefill/Decode и «дырами» в утилизации: агент не конкурирует с основными онлайн‑нагрузками, а аккуратно встраивается между ними. Но в этом материале речь пойдет именно о прикладном сценарии в продуктовой аналитике, а не о низкоуровневой оптимизации инференса.
Исходная боль проста и знакома любой продуктовой команде. Есть новые клиенты, которые быстро становятся активными и приносят выручку, есть те, кто оседает в «спящих» или отваливается. Аналитикам регулярно приходится отвечать на одни и те же вопросы: чем отличаются эти группы, какие признаки поведения или профиля объясняют разницу, что общего у «успешных» новичков.
Раньше это выглядело так: формулируем гипотезы, собираем SQL‑запросы, делаем выгрузки, вручную считаем описательные статистики, строим графики, спорим о том, какие эффекты более значимы. Каждый такой проход занимает часы, легко растягивается на дни и почти всегда покрывает лишь небольшой подмножество возможных разрезов. Со временем становится очевидно, что часть работы просится в автоматизацию — особенно систематический перебор признаков и проверку «банальных» гипотез.
Наш background‑агент берет на себя именно этот пласт — типовые сравнения когорт и поиск статистически устойчивых различий. Ему достаточно постановки задачи на естественном языке вроде «Сравни новых клиентов, ставших активными, и новых клиентов, оставшихся пассивными, найди ключевые отличия по поведению в первые N дней». Дальше включается агентная архитектура, и через некоторое время команда получает готовый отчет с перечислением найденных паттернов и ссылкой на визуализации.
Чтобы LLM‑агент мог не просто «рассуждать», а проводить осмысленный анализ, нужен слой инструментов. В нашем случае он реализован в виде набора python‑классов (tools), специализирующихся на конкретных статистических и аналитических задачах.
Одна из ключевых архитектурных идей: tools — это python‑классы с единым интерфейсом. Мы выбрали такой подход, потому что хотели общий контракт вызова: у каждого tool есть метод run(), а результат валидируем и оформляем через ToolOutput (Pydantic). Такой подход упрощает тестирование и отладку — tool можно запускать как обычный модуль с фикстурами данных. Все метаданные и условия применения инкапсулированы непосредственно в коде и промптах, поэтому контроль логики и валидации мы осуществляем через Python, а не через внешние конфиги.
Анализ корреляций: расчет связи между бинарными/категориальными признаками и целевой метрикой c помощью Point-Biserial-корреляции.
Сравнение описательных статистик между группами: проверка различий по средним, медианам, дисперсиям.
Анализ категориальных признаков: классические критерии вроде хи‑квадрат для частотных таблиц: выявление зависимых и независимых фичей с p‑values, association и chi2.
Важность признаков: использование моделей Random Forest для оценки вклада фичей в предсказание целевой переменной.
Обнаружение выбросов: выявление аномалий в группах с помощью IQR- или Z-score-методов.
Анализ взаимодействий признаков: поиск синергий или конфликтов, влияющих на целевую метрику.
Визуализация распределений: генерация boxplot, гистограмм и других диаграмм для быстрого визуального ревью.
Чтобы инструменты действительно работали в составе агента, между ними нужен четкий контракт: все tools должны возвращать результат в согласованной структуре, которую легко проверять и использовать дальше по цепочке
# Tool делает статистический анализ категориальных признаков tool = CategoricalFeatureAnalysisTool(df, target="target") result = tool.run(segment_group="user_segments") ToolOutput( tool_name="CategoricalFeatureAnalysisTool", status=0, # SUCCESS summary="Found 5 dependent features, 12 inDependent features", details={ "dependant_categorical_features": [ ("user_segment_a", {"pval": 0.01, "association": 0.23, "chi2": 15.68}), ("user_segment_b", {"pval": 0.03, "association": 0.15, "chi2": 8.42}), ], "independant_categorical_features": [ ("user_segment_c", {"pval": 0.12, "association": 0.05, "chi2": 2.31}) ], "feature_names": ["user_segment_a", "user_segment_b", "user_segment_c"] }, plot_settings={ "type": "CategoricalFeatureAnalysisTool", "data": [...], "title": [...] } )
Эта структура одновременно решает две задачи:
дает агенту почву (ground truth) для интерпретации;
упрощает визуализацию: plot_settings — декларативное описание графиков, дальше их можно рендерить отдельно, например в HTML.
Сами по себе эти tools — всего лишь библиотека функций. Чтобы агента можно было назвать действительно «агентом», они объединены в более крупные сущности — skills. Skill — это осмысленный аналитический сценарий: «сравнение двух выборок», «оценка результата эксперимента», «поиск аномальных сегментов». Каждый skill описан в YAML: имя, краткое описание, теги (например, cohort_comparison, continuous_metric, categorical_features), условия применения и ограничения.
Поверх этого описания лежит инструкция: какие tools использовать, в каком порядке, с какими параметрами, какие артефакты ожидать на выходе. В результате LLM‑агенту не нужно «изобретать» пайплайн каждый раз — он выбирает готовый skill и наполняет его конкретными параметрами задачи (какие когорты сравниваем, какие метрики считаем, какие фильтры по данным применяем).
Агент работает преимущественно в background‑режиме: ему необязательно ждать прямого запроса от пользователя — он способен сам выбирать подходящий сценарий исходя из заданных бизнес‑правил и расписания. При этом, если поступает явное текстовое задание (от аналитика или продуктовой команды), на первом шаге включается роутер: этот компонент анализирует формулировку задачи, сопоставляет ее с метаданными skill и выбирает один или несколько наиболее релевантных сценариев. Например, если задача звучит как «сравни поведение двух групп новых клиентов», роутер запускает skill для сравнительного анализа выборок.
Дальше запускается выполнение. Skill забирает нужные выборки из хранилища, подготавливает данные (фильтрация, нормализация), последовательно вызывает соответствующие tools и собирает результаты: статистики, p‑values, рейтинги важности признаков, ссылки на графики. На каждом шаге результат проходит валидацию: соответствие схеме, отсутствие NaN, разумный диапазон значений. Если какой‑то tool не может дать устойчивый ответ (слишком мало наблюдений, шумные данные), сценарий фиксирует это и корректно сообщает в итоговом отчете.
Финальный шаг — формирование ответа. Здесь LLM получает строго структурированный вход — набор проверенных агрегатов и артефактов. Ему нужно перевести структуру результата в человеческий текст: «Для этой когорты значимо выше retention на 7‑й день», «Основное отличие — больший процент пользователей, совершивших транзакцию X» и так далее. Там, где модель пытается выйти за пределы данных, срабатывают ограничители: доступ к «фантазии» есть только в части причины и нарратива, но не в числах.
В финтех-сценариях критично минимизировать любые искажения результатов, поэтому мы внедрили многоуровневую систему защиты.
LLM не имеет права:
добавлять признаки, которых нет в результатах tool‑вызова;
менять p‑value/association/chi2;
придумывать «рост 10%», если tool вернул «5%».
Задача LLM на этом этапе — только интерпретация и редактирование, не генерация фактов. Мы дополнительно на стороне пайплайна проверяем, что в ответах нет «придуманных» данных: сравниваем значения с исходным выводом tools, вплоть до проверки по регулярным выражениям и строгому сопоставлению чисел и имен признаков.
Пример правила grounding (фрагмент system‑промпта):
You MUST only use features present in the tool results. Never introduce new feature names. Never change numeric values (pval, association, chi2). If evidence is insufficient, say "insufficient evidence" and list what is missing.
Чтобы LLM не допускал импровизации, его ответы жестко структурированы: для каждого блока задаем четкие требования к формату, значениям и именам полей. Например, используем строгие схемы с параметрами strict: true и additionalProperties: false — это практически не оставляет модели простора для вольностей и обеспечивает повторяемость результатов.
Пример схемы для observation‑блока:
{ "type": "object", "additionalProperties": false, "properties": { "observations": { "type": "array", "items": { "type": "object", "additionalProperties": false, "properties": { "feature": {"type": "string"}, "effect": {"type": "string"}, "evidence": { "type": "object", "additionalProperties": false, "properties": { "pval": {"type": "number"}, "association": {"type": "number"} }, "required": ["pval", "association"] } }, "required": ["feature", "effect", "evidence"] } } }, "required": ["observations"] }
Далее этот JSON валидируется и только после этого попадает в отчет.
Прагматичное решение, которое оказалось неожиданно эффективным:
Шаг 1: агент делает анализ на английском, потому что качество reasoning у многих моделей там стабильнее.
Шаг 2: отдельным коротким промптом переводим вывод на русский, фиксируя терминологию.
Побочный бонус: часто это еще и экономит токены — вместо одного большого русскоязычного промпта с длинными инструкциями получается компактный EN‑анализ + короткий перевод (а переводить структурированный текст проще, чем «свободный»).
У агента есть несколько классов failure modes: нет данных, плохая выборка, tool упал, LLM недоступен, summarizer сломался.
Поэтому в цепочке заложены правила деградации:
если tool вернул пусто — этап пропускается, отчет продолжается, но с предупреждением;
если LLM не ответил или не прошел schema‑валидацию — fallback на следующую модель;
если summarizer недоступен — возвращаем частичный отчет (графики + таблицы + сырой список наблюдений);
если данных недостаточно для выводов — возвращаем «сырой» статистический блок без интерпретации.
У фонового агента важно следить не только за запусками и падениями, но и качеством и стоимостью.
Мы оцениваем качество работы агента через процент успешных валидаций структурированных выводов и количество отклоненных ответов из-за несоответствия схеме. Для инструментов (tools) фиксируем уровень успешности их выполнения, среднее время ответа и количество обработанных признаков. Также отдельно считаем стоимость: сколько токенов тратится на анализ и перевод, как распределяются запросы по моделям и как часто происходит fallback. Для прозрачности сохраняем сквозной hash_id, чтобы всегда можно было восстановить весь путь данных от входа до финального отчета.
Model Context Protocol (MCP) сегодня активно обсуждается как способ подключать модели к внешним данным и инструментам через единый протокол. Для многих сценариев это разумный путь: общая экосистема коннекторов, понятный формат capabilities, интероперабельность между системами.
В нашем случае требования оказались другими. Нужен был полностью управляемый, self‑hosted слой над внутренней аналитикой, с минимальной «лишней» сложностью и предсказуемым поведением. Мы сознательно выбрали подход, близкий к Claude Skills: навыки описаны в виде файлов с инструкциями и метаданными, композиция поверх уже существующих python‑tools, прогрессивное раскрытие инструкций только тогда, когда skill действительно используется. Такой формат проще ревьюить, версионировать и развивать внутри продуктовой команды, чем полноценную MCP‑интеграцию.
При этом мы разделяем ключевые идеи: модульность, явное описание инструментов, отделение декларации возможностей от их реализации. Если в будущем появится потребность в более открытой и межсистемной архитектуре, переход к MCP будет проще именно потому, что логика уже разложена по skill, а не «зашита» в монолитные промпты.
Для бизнеса ценность background‑агента в аналитике клиентов можно развести на несколько плоскостей.
Во‑первых, время: путь от постановки вопроса «Чем отличаются эти две группы?» до первого консистентного ответа сокращается с часов и дней до минут. Это особенно заметно в сценариях, где важно вовремя подправить онбординг, изменить коммуникации или скорректировать риск‑модели для новых клиентов.
Во‑вторых, экономия: фоновый запуск аналитических сценариев в периоды низкой нагрузки позволяет эффективнее использовать уже купленную инфраструктуру (GPU-ресурсы). Не нужно держать отдельные мощные кластеры только ради периодического анализа когорт — агент занимает те «окна», которые все равно оставались бы пустыми. Точных цифр экономии мы намеренно не приводим, но даже частичное «уплотнение» простойных ресурсов дает заметный эффект.
В‑третьих, масштаб и надежность: одна и та же методология анализа применяется ко множеству когорт и продуктов, а не зависит от личного стиля конкретного аналитика. Это снижает риск пропустить важный эффект только потому, что у кого‑то не дошли руки до нужной комбинации фильтров и разрезов.
Чтобы было понятнее, как работает пайплайн агента на практике, приведем конкретный пример с типовым запросом из аналитики. Ниже схема: какой input получает система и что начинает происходить внутри.
Input: «Сравни cohort_jan и cohort_feb (активные vs пассивные), найди ключевые различия».
Логика выполнения (упрощенно):
Собрать фичи из источников → merge по ключу клиента.
Балансировка классов → корректность сравнений.
Вызов tools по плану агента:
описательные статистики / доли сегментов;
категориальные тесты (chi2, p‑values);
корреляции;
выбросы;
генерация plot_settings.
LLM‑интерпретация (EN) строго по tool results → structured output.
Перевод (RU) отдельным промптом.
Генерация HTML‑отчета по Jinja2: hypothesis_list, correlation_analysis, outlier_analysis, summary.
В экосистеме LLM‑агентов сегодня достаточно фреймворков — от LangGraph и AutoGen до специализированных библиотек для multi‑agent-сценариев. Для нашего кейса мы сознательно выбрали путь без тяжелых надстроек: достаточно аккуратно спроектированного слоя skills/tools, собственного роутера и стандартных средств оркестрации фоновых задач. Это снижает входной порог для команды, упрощает эксплуатацию и позволяет тонко контролировать, где заканчивается «магия» модели и начинаются классические инженерные гарантии.
Развивать такого агента можно по трем направлениям:
расширение библиотеки skills за пределы анализа новых клиентов (маркетинг, риск‑аналитика, поддержка A/B‑экспериментов);
более глубокие проверки качества выводов (например, сравнение с историческими результатами);
постепенный переход от cron‑подхода к действительно адаптивному background‑режиму, учитывающему состояние вычислительных ресурсов.
Это эволюционный путь, в котором LLM остается инструментом, а не самоцелью — и именно такой подход кажется наиболее здоровым для применения генеративных моделей в прикладном финтех‑продукте.
Агентная архитектура с прозрачными инструментами, понятной оркестрацией и фокусом на инженерных практиках позволяет автоматизировать рутинную аналитику, сделать ее быстрым, надежным и масштабируемым бизнес‑процессом без избыточной сложности. Такой подход снижает барьер внедрения LLM‑технологий и дает реальную ценность компаниям, где важны скорость и качество решений.
Источник


Финансы
Поделиться
Поделиться этой статьей
Скопировать ссылкуX (Twitter)LinkedInFacebookEmail
Торговля ИИ не умерла: Взгляд изнутри на Wal
