Привет, Хабр!

Как перепроверить отчёт, который прислали специалисты? Как перестать постоянно обращаться к аналитикам с ad hoc-запросами и часами ждать очередную таблицу? Может быть, стоит просто спросить нейросеть о показателях своей компании?

У этой концепции есть название — GenBI. Сама идея не нова: сегодня производители встраивают копилотов практически во все аналитические сервисы и обещают доступ к корпоративным данным через запросы на естественном языке.

Но на практике всё оказывается сложнее.

Когда говорят о GenBI, архитектуру часто описывают так: пользователь задаёт вопрос на естественном языке, большая языковая модель пишет SQL, база данных выполняет запрос, а модель объясняет результат.

Для демонстрации этого достаточно.

Для корпоративной аналитики — обычно нет.

Мы с командой занимаемся проектированием и разработкой корпоративных BI-систем на базе SQL Server, SSIS, SSAS Tabular и Power BI. В основном это on-premise-решения, где данные из учётных систем проходят через DWH и семантическую модель, прежде чем становятся доступны пользователям. В последнее время также занимаемся интеграцией таких моделей с LLM-интерфейсами.

Главная проблема здесь не в том, умеет ли LLM написать синтаксически корректный запрос. Современные модели умеют генерировать и SQL. Проблема в другом: откуда модель должна узнать, что именно компания называет выручкой, продажей, активным клиентом, себестоимостью или остатком?

Мой основной тезис прост:

GenBI становится надёжным только тогда, когда LLM работает не с физическими таблицами, а с уже подготовленной бизнес-моделью данных.

В наших проектах таким слоем выступает SSAS Tabular. LLM получает описание модели, формирует read-only DAX-запрос и вызывает отдельный API для его выполнения.

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

Архитектура решения

Упрощённая схема выглядит так:

Пользователь
    ↓
Custom GPT в ChatGPT
    ↓
Системный промт + knowledge-файл
    ↓
Формирование DAX
    ↓
GPT Action
    ↓
HTTP API выполнения запросов
    ↓
ADOMD.NET
    ↓
SSAS Tabular
    ↓
Результат запроса
    ↓
Интерпретация ответа в ChatGPT

В нашей реализации промежуточный сервис построен на FastAPI. Он принимает DAX, проверяет параметры запроса, обращается к SSAS через pythonnet и ADOMD.NET, а затем возвращает колонки и строки результата.

API не предоставляет LLM произвольный доступ к серверу. У него есть одна основная операция: выполнить read-only DAX в заранее разрешённой модели.

Сервис возвращает количество строк, время выполнения и идентификаторы запроса. Вызов защищён API-ключом, а обращения журналируются.

Задача API здесь не в том, чтобы «заниматься искусственным интеллектом». Он является узким интеграционным шлюзом между ChatGPT Actions и аналитическим сервером.

Какую роль в решении играет Custom GPT

Мы используем Custom GPT — это не отдельная языковая модель и не самостоятельно развёрнутый AI-сервис. Это настраиваемая версия ChatGPT от OpenAI, работающая внутри интерфейса ChatGPT.

В неё можно добавить:

  • инструкции;

  • knowledge-файлы;

  • доступные возможности;

  • внешние actions.

OpenAI описывает GPTs именно как версии ChatGPT, настроенные для конкретной задачи и объединяющие инструкции, знания и выбранные возможности.

В нашем случае Custom GPT играет роль аналитического оркестратора. Он должен:

  1. понять пользовательский вопрос;

  2. сопоставить его с объектами семантической модели;

  3. сформировать DAX;

  4. вызвать внешний action;

  5. получить табличный результат;

  6. объяснить его обычным бизнес-языком.

Action связывает ChatGPT с моим HTTP API. В терминах OpenAI GPT Actions позволяют описать схему внешнего API и дать ChatGPT возможность вызывать его на основании запроса пользователя.

То есть Custom GPT — это удобная готовая оболочка над LLM, knowledge и вызовами внешнего сервиса. Самих данных и вычислительного движка внутри него нет.

Зачем LLM нужны метаданные семантической модели

LLM не знает устройство модели конкретного клиента.

Даже если она прекрасно знает синтаксис DAX, ей неизвестно:

  • какие таблицы существуют;

  • как называются поля;

  • какие связи активны;

  • какие меры уже созданы;

  • от чего эти меры зависят;

  • какой календарь используется для конкретного процесса.

Эта информация передается через knowledge-файл, сформированный из BIM-описания Tabular-модели.

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

Например, модель видит не только меру [Валовая прибыль], но и то, что она использует уже существующие показатели реализации и себестоимости. А мера остатков может зависеть от календарного контекста и накопления движений до выбранной даты.

Это помогает LLM не изобретать расчёты заново.

Knowledge-файл при этом не содержит фактических продаж, остатков или денежных движений. Он отвечает на вопрос «как устроена модель», но не отвечает на вопрос «какие значения находятся в ней сейчас».

За фактическими данными LLM обращается к SSAS.

Именно поэтому knowledge и action решают разные задачи:

Knowledge:
что существует и как этим пользоваться

Action:
выполнить запрос и получить фактический результат

Почему DAX поверх SSAS оказался удобным вариантом

Я не считаю, что DAX как язык принципиально проще SQL.

Сложные DAX-выражения бывают гораздо менее очевидными, чем SQL. Преимущество появляется не из-за синтаксиса, а из-за того, что запрос выполняется поверх готовой семантической модели, меру написал BI-разработчик, согласовал с бизнесом, и мы точно знаем, что она считается правильно по всем срезам.

В SSAS Tabular мера — это исполняемое определение бизнес-показателя. Она рассчитывается в контексте выбранных фильтров и аналитических разрезов. Microsoft определяет меры Tabular-модели как DAX-расчёты, результат которых зависит от полей и фильтров, выбранных потребителем отчёта.

Если пользователь спрашивает:

Покажи валовую прибыль по месяцам за 2026 год.

LLM не обязана выяснять, из каких физических колонок складывается прибыль. Ей достаточно выбрать готовую меру и календарное измерение:

EVALUATE
SUMMARIZECOLUMNS(
    'Календарь'[Год Месяц],
    FILTER(
        ALL('Календарь'),
        'Календарь'[Год] = 2026
    ),
    "Валовая прибыль", [Валовая прибыль]
)

DAX-запросы возвращают таблицы и требуют как минимум конструкции EVALUATE с табличным выражением.

Получается, что генератор в основном оперирует тремя типами объектов:

  • готовыми мерами;

  • измерениями;

  • фильтрами.

Это значительно проще, чем задача самостоятельного построения SQL со всеми соединениями и расчётами.

Самое важное: DAX-запрос использует ту же меру, что уже применяется в существующих отчётах. Пользователь не получает отдельную «версию валовой прибыли по мнению LLM».

Но разве нельзя сделать то же самое с DWH?

Можно.

Это главное возражение, и оно вполне справедливо.

Для DWH также можно сформировать knowledge-файл, включив в него:

  • таблицы и поля;

  • связи;

  • гранулярность фактов;

  • описания витрин;

  • допустимые пути соединений;

  • определения показателей;

  • примеры запросов;

  • словарь бизнес-терминов.

При хорошем денормализованном хранилище или аккуратной звёздной схеме text-to-SQL может работать достойно.

Более того, SSAS — не единственный способ создать семантический слой. Существуют отдельные metrics layer и semantic layer решения поверх DWH. Например, dbt Semantic Layer позволяет централизованно описывать метрики и автоматически строить необходимые SQL-запросы и соединения.

Есть и другие продукты, которые предоставляют управляемый семантический слой над хранилищем и предлагают LLM или BI-инструментам обращаться к нему вместо генерации произвольного SQL.

Поэтому корректная формулировка звучит не так:

DAX работает, а SQL не работает.

Она звучит так:

Запросы к семантическому слою значительно надёжнее запросов непосредственно к физическому слою данных.

В нашей архитектуре SSAS удобен потому, что семантический слой уже существует и является исполняемым.

Это принципиальное отличие от обычного текстового описания DWH.

Можно ли заменить семантический слой текстовым описанием?

Представим два варианта.

В первом случае в файле метаданных модели содержится текстовое описание:

Выручка рассчитывается как сумма продаж без НДС
за вычетом возвратов.

Данные находятся в таблице fact_movements.
Сумма операции хранится в поле amount_without_vat.
Продажам соответствует movement_type = 1,
возвратам — movement_type = 2.

LLM должна самостоятельно превратить это описание в SQL. Ей необходимо выбрать таблицы и поля, правильно применить условия, построить соединения и воспроизвести бизнес-логику показателя.

Во втором варианте в семантической модели уже существует мера:

[Выручка]

Knowledge-файл сообщает модели, что такая мера существует, а SSAS знает, как её вычислить.

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

Во втором — указывает на уже исполняемую бизнес-логику.

Именно поэтому один knowledge-файл поверх произвольного DWH не всегда даёт тот же результат, что BIM-описание поверх Tabular-модели.

Чтобы добиться сопоставимой надёжности с DWH, недостаточно перечислить таблицы. Необходимо либо:

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

  • предоставить библиотеку проверенных SQL-шаблонов;

  • либо реализовать собственный слой, который будет строить SQL по формализованным правилам.

После этого подход действительно может оказаться не хуже SSAS.

Но тогда его преимущество возникает не потому, что LLM научилась лучше писать SQL. Просто между LLM и DWH появился тот самый семантический слой который мы обсудили ранее.

А что если подключать LLM напрямую к учётной базе?

Сразу скажу это очень плохая идея. База учётной системы обычно отражает внутреннюю механику приложения, а не аналитическую картину бизнеса как DWH.

Один показатель может зависеть от нескольких документов, регистров, статусов и видов движений. Названия технических объектов часто понятны только разработчикам конкретной системы.

Особенно опасны не синтаксические ошибки, а правдоподобные неверные результаты.

Модель может:

  • задвоить сумму после JOIN;

  • использовать дату документа вместо даты движения;

  • смешать план и факт;

  • не учесть возвраты;

  • взять сумму с НДС;

  • посчитать строки вместо документов.

Запрос выполнится. Пользователь получит таблицу. Числа будут выглядеть реалистично.

Именно поэтому прямое подключение к OLTP-системе я считаю плохой основой для GenBI.

А что насчёт datalake?

С datalake проблема ещё заметнее.

Озеро может содержать сырые файлы, очищенные наборы, исторические снимки и несколько версий одной сущности. Оно решает задачу хранения и обработки данных, но само по себе не определяет бизнес-метрики.

В системном промте мы можем описать содержимое озера, но описание не превратит сырые данные в согласованную аналитическую модель.

Работающий GenBI поверх datalake возможен только если между озером и LLM развернут слой подготовленных данных и бизнес-определений.

Название технологии вторично. Важен порядок слоёв:

Источники
    ↓
Подготовленные данные
    ↓
Семантическая модель
    ↓
LLM-интерфейс

Попытка убрать средние уровни обычно просто переносит их сложность в промпт.

Вывод

Я бы сформулировал итог так:

LLM не должна строить бизнес-модель заново при каждом пользовательском вопросе.

Её задача — перевести естественный язык в запрос к уже существующей и согласованной бизнес-модели.

На практике многие компании пытаются перейти к GenBI слишком рано. Данные из 1С и других учётных систем переносятся в отдельные таблицы и витрины, поверх них создаются десятки дашбордов, но единой архитектуры данных и согласованных определений показателей так и не появляется.

В результате каждый отчёт может по-своему рассчитывать выручку, себестоимость, остатки и прочие показатели. Подключение LLM к такому контуру не устраняет проблему уже существующего хаоса.

Поэтому настоящий GenBI, на мой взгляд, начинается не с выбора LLM.

Он начинается с построения зрелой аналитической платформы: корпоративного хранилища, подготовленных данных и исполняемого семантического слоя, в котором однозначно определён бизнес-смысл показателей.

Только после этого естественный язык становится надёжным интерфейсом к корпоративной аналитике.

P. S. Чтобы не верить мне на слово, я развернул тестовую версию решения на демонстрационной модели Contoso.

По ссылке ниже можно открыть Custom GPT и задать ему свои вопросы о продажах, товарах, магазинах и других данных модели:

Открыть Sensu GenBI в ChatGPT

Данные в модели синтетические, а сама сборка предназначена только для демонстрации подхода. Но проверить, как LLM работает с готовой семантической моделью и формирует DAX-запросы, вполне можно.