Мы — команда, которая обеспечивает D‑People (data‑аналитиков, исследователей данных (data scientist) и data‑инженеров) Сбера удобными и функциональными инструментами для работы с данными. Наш департамент развивает внутреннюю корпоративную аналитическую платформу (КАП). В ней есть множество удобных инструментов, и в статье мы расскажем об одном из них — позволяющем работать с данными на естественном языке.
Какую проблему мы решаем?
Идея использования data‑driven подхода не нова, ещё в конце прошлого века появилась концепция, согласно которой ключевыми факторами для принятия решений являются результаты анализа данных и проверка гипотез на основании имеющейся статистики. Однако несмотря на то, что этот подход оказался эффективнее highest paid person's opinion и best practices, далеко не все люди, принимающие решения, могут и умеют работать с данными.
Так, может, не всем нужно с ними работать? Есть люди, которые специализируются именно на анализе данных, умеют имеют к ним доступ и могут поделиться готовыми результатами. И в этом есть доля правды. Для того, чтобы использовать data‑driven подход, не обязательно делать аналитику самостоятельно. Достаточно поставить задачу data‑аналитику.
Но тут возникают следующие нюансы:
У data‑аналитика может быть сформирован список задач, в который уже никак не помещается ещё одна новая срочная задача, а результат нужен как можно скорее.
Data‑aналитик может быть не погружен в ваш конкретный случай, ему требуются уточнения по задаче и детальное описание, что именно нужно проверить. Всё это займёт ещё какое‑то время.
У аналитика может не быть доступа к нужной вам информации, потребуется его предоставить.
В таком случае решение задачи заметно усложняется. Не стоит также забывать о ситуациях, когда привлечение data‑аналитика в целом и не нужно.
Например, руководителю необходимо посмотреть, сколько компаний было зарегистрировано в Москве в 2024 году. У него есть соответствующая выгрузка в Excel, но там столько всяких полей… При этом привлекать data‑аналитика для такого, вроде бы, несложного запроса кажется излишним. Было бы славно просто спросить, сколько таких компаний, и получить готовый ответ.
Проведя анализ, мы выявили несколько категорий сотрудников, которым необходимо использовать аналитику при принятии решений: руководство, менеджеры, product owner'ы, бизнес‑аналитики и т. д. Далеко не все из них знают SQL и могут работать с инструментами для анализа данных, следовательно, им приходится прибегать к помощи data‑аналитиков. Поэтому мы решили предложить им инструмент, позволяющий обрабатывать данные на естественном языке. Он должен был обладать понятным для пользователя интерфейсом, быстро обрабатывать запросы, поддерживать популярные форматы данных и предоставлять осмысленную аналитику. Так появился AI DataChat.
В чём суть AI DataChat и при чём здесь технология text2sql
Определив проблему и выделив основные категории пользователей внутри нашей компании, мы решили сделать прототип инструмента в тестовом контуре и дать пользователям доступ к нему для проведения Proof of concept (PoC). Важно понять, насколько это решение может закрыть потребности нашей потенциальной аудитории и как оно должно выглядеть на этапе опромышливания. Размещение прототипа в тестовом контуре позволило достигнуть сразу несколько важных целей:
Проверить гипотезу о пользе инструмента до проведения полноценной разработки.
Быстро дать доступ к прототипу разным бизнес‑командам для тестирования.
Оперативно обработать обратную связь и добавить новую функциональность.
Заранее оценить стоимость внедрения инструмента и определить объём работ.
Но вернёмся в самое начало. Первый вопрос, который у нас возник: «Где может быть точка контакта с нашими пользователями?» Разработка собственного UI для проверки концепции казалась слишком дорогой и долгой, поэтому мы выбрали в качестве места для обращения к сервису внутренний мессенджер компании. Он отлично подошёл нам сразу по нескольким критериям: во‑первых, он имеет стенды в dev‑среде, что облегчает интеграцию; во‑вторых, у него есть готовый интерфейс для взаимодействия с пользователями, причём этот интерфейс уже хорошо знаком сотрудникам. Так у нас появилось понимание, что это может быть некий бот внутри мессенджера.
Итак, с точкой контакта мы определились. Теперь — наполнение бота. Конечно, нам бы хотелось всего и сразу, но на этапе проверки гипотезы было важно сформулировать, что точно войдёт в прототип.
Вопросов было больше, чем ответов:
Как получать данные, с которыми желает работать пользователь?
Если это загруженные в бот файлы, то с какими форматами мы сможем обеспечить работу? Какой максимальный объём файлов?
Если пользователь захочет уточнить свой запрос, нужно ли будет повторять его заново и добавлять уточнение? Как сделать удобно, но «недорого»?
А если пользователь захочет работать с несколькими таблицами одновременно?
Стоит ли выводить готовый SQL или лучше возвращать только результат?
В каком формате отправлять результаты исполненных SQL‑запросов — в теле сообщения от бота или отдельным файлом?
И мы начали сбор требований. В итоге создали бота, который имеет следующие возможности:
Принимает и обрабатывает запросы на естественном языке, переводя их в SQL.
Исполняет SQL‑запросы на данных пользователя, которые он подгружает в бот через кнопку «Прикрепить файл» (доступные форматы: Excel, CSV, Parquet).
Поддерживает контекст: распознаёт, задаёт ли пользователь новый вопрос или уточняет предыдущий.
Предоставляет результаты запроса в нескольких форматах: ответ в теле сообщения и детализация в Excel‑файле (например, если пользователь просит выборку, отфильтрованную по определённым признакам).
Работает с готовым SQL (если пользователь решил написать его самостоятельно).
Проверяет полученные SQL‑запросы на корректность.
Позволяет визуализировать полученные результаты: пока только строит гистограммы, но мы думаем над расширением возможностей.
Функциональность мы развивали постепенно, получая запросы от пользователей в ходе тестирования инструмента. Но помимо приятных дополнений, например, визуализации и контекста, пользователям важна точность полученных результатов. За результат в нашем случае отвечает используемая в нашем решении технология text2sql.
Так-так, а теперь подробнее
Text2sql — это технология, которая позволяет получать запросы на естественном языке, распознавать, какой запрос и к каким полям хотел сделать пользователь, и возвращать в результате корректный SQL‑запрос, который затем можно выполнить и получить ожидаемый ответ.
Эту технологию можно разбить на четыре этапа:
Получение и обработка запроса пользователя на естественном языке.
Сбор статистики (данных, наиболее релевантных запросу), формирование правильного промпта для LLM на основании запроса на естественном языке.
Исполнение промпта и формирование корректного SQL‑запроса.
Проверка полученного SQL‑запроса на корректность, как синтаксическую, так и логическую.
Каждый из этих четырёх этапов помогает максимально приблизить финальный результат к правильному ответу на вопрос пользователя. Для измерения этой «правильности» мы используем метрику accuracy. Для её повышения можно использовать несколько подходов.
Первый подход — дообучение LLM под нужные нам сценарии. На заре проекта мы исследовали саму технологию и пытались определить, какие модели могут выдать нам наиболее релевантные результаты. Оценивали целый ряд LLM. Даже пробовали дообучать модели самостоятельно: собирали данные, составляли автоматизированные бенчмарки на различных наборах данных, дообучали и экспериментировали с количеством эпох. Итог получился неутешительный: при самостоятельном обучении нам удалось добавить результатов ~80% на отдельных базах данных с помощью адаптеров. Однако каждый адаптер увеличивал точность на одной определённой базе данных, поэтому под каждую из них нам нужно было бы сформировать свой адаптер. Но это сделало бы тиражирование медленным и дорогим, так как при подключении новой базы нам нужно заложить время и ресурсы на разработку.
Тогда мы обратились к команде GigaChat с просьбой включить в обучающую выборку для нейросетевой модели сценарии с генерацией SQL по запросу на естественном языке. Точность SQL, которую мы получили по итогам обучения, оказалась удовлетворительной для бизнеса, поэтому мы отказались от идеи дообучать нейросеть самостоятельно и остановились на использовании централизованного экземпляра этой модели.
Второй подход — это prompt engineering, то есть формирование наиболее точного и правильного промпта для модели с использованием различных параметров и подсказок, облегчающих ей работу. Здесь всё просто и сложно одновременно. Ниже расскажем об этом, а также о получении запроса, поиске релевантных данных и проверке SQL‑запроса на корректность.
AI DataChat
Технология text2sql, как идея, не является уникальной. Во фреймворках вроде langchain даже есть готовые функции для генерации SQL по запросу на естественном языке. Но наш бот — это не только преобразование текста в код, это интерфейс, логика, различные сценарии взаимодействия и множество уникальных решений, чтобы превратить text2sql в полноценного ассистента. Расскажем про все возможности бота как со стороны пользователя, так и со стороны сервиса. В качестве «мозга» в этом проекте мы выбрали самую мощную модель от Сбера — GigaChat‑Pro (30b-8k), а в качестве удобного фреймворка для взаимодействия с ним — библиотеку GigaChain.
Загрузка данных
Важный этап, на котором строится дальнейшее взаимодействие пользователя с ассистентом — это загрузка данных.
Бот умеет обрабатывать несколько форматов файлов. Для примера разберём самый популярный для различных отчётов — Excel‑файлы.
После загрузки файла начинается обработка и сохранение данных. Файл разбираем на листы, и каждый лист учитываем как отдельная таблица.
Однако этого недостаточно для дальнейшей работы. Поскольку Excel не является промышленным инструментом для работы с табличными данными, частенько на листах творится полная неразбериха. Чтобы избежать дальнейших проблем из‑за беспорядочных названий колонок и таблиц, все метаданные мы приводим к универсальному формату. В первую очередь все названия на русском языке переводим на английский, чтобы LLM было проще генерировать запросы и она не ошибалась в синтаксисе. Потребуется следующий промпт:
После этого все нежелательные символы (тире, переносы строк и т. д.) заменяем на нижние подчёркивания, а весь шрифт приводим к нижнему регистру.
Только после этих манипуляций загружаем данные в DuckDB — БД, находящуюся в оперативной памяти и позволяющую очень быстро исполнять запросы, чтобы не заставлять пользователя ждать ответа по несколько минут.
Далее идёт этап создания векторной базы данных. Зачем она нужна — расскажем позже. Под каждую колонку создаём отдельную коллекцию, которая хранит все уникальные значения из этой колонки в виде эмбеддингов — текстовых значений, преобразованных в векторы огромной размерности. Эмбеддинги в нашем решении мы получаем с помощью хостинга GigaChat API, где развёрнута специальная модель для их создания. Векторная база размещается в оперативной памяти. А на случай, если пользователь в дальнейшем захочет загрузить файл, с которым уже работал, чтобы несколько раз не создавать одни и те же эмбеддинги, мы дублируем базу на диск для подгрузки в случае повторного обращения к уже обработанным данным.
После этого идёт этап создания синтетических описаний. Чтобы модели было проще генерировать SQL‑запросы, для каждой колонки мы создаём описание на основе метаданных с помощью LLM. Промпт выглядит следующим образом:
Сгенерированные описания также сохраняем в оперативную память для дальнейшей работы.
После всех вышеописанных этапов бот готов отвечать на вопросы пользователя и отправляет сообщение следующего вида:
Предпросмотр в этом случае — это первые 5 строк из каждой получившейся таблицы, чтобы пользователь мог видеть пример данных, с которыми далее будет работать. Каждая кнопка выводит в сообщение данные с одноимённого листа:
Итак, мы передали боту данные, посмотрели на то, как они выглядят в преобразованном виде, и готовы задавать вопросы.
Запрос на естественном языке
Для начала зададим боту простой вопрос по выборке:
Бот ставит реакцию, сообщая, что он про нас не забыл, и на некоторое время уходит думать.
Внутри происходит следующее. Поскольку вопрос первый, бот не анализирует предыдущий (про этот шаг расскажем далее) и сразу инициирует генерирование SQL‑запроса в LLM. Блок для создания SQL‑запросов имеет рабочее название LiveQuery и отвечает за формирование правильного промпта, отправку его в LLM и парсинга SQL из её ответа. Промпт состоит из следующих блоков:
-
Общие инструкции по созданию SQL и метаданные, чтобы модель понимала, с какой выборкой работает.
-
Пользовательское описание, в котором он может указать важные по его мнению особенности, которые необходимо учитывать при работе с выборкой.
Это описание можно указать при загрузке файла.
-
Примеры удачных SQL‑запросов по той же выборке, чтобы модель могла на них ориентироваться.
-
И наоборот, невыполненные запросы вместе с ошибками, чтобы LLM учитывала их и не допускала при генерировании новых запросов.
Промпт передаём в модель, после чего форматируем ответ:
Извлекаем тело SQL‑запроса (все современные модели обучены отдавать SQL в формате markdown).
При необходимости заменяем кавычки в блоке WHERE.
Второй пункт необходим во избежание простых синтаксических ошибок, поэтому обратились к механической проверке.
После получения SQL‑запроса следует ряд проверок. Первая — на выполнение. Бот пытается выполнить SQL‑запрос на ранее сформированной БД и отлавливает ошибки, если они появляются. С полученной ошибкой он заново обращается в LiveQuery, чтобы её исправить. Делается это с помощью специального промпта, который нужен только для исправления синтаксиса запроса и не содержит лишней для этой задачи информации:
В случае, если запрос успешно выполнился, но не вернул данных, идёт этап логической проверки. Для этого запрос вновь разбиваем на блоки. Из блока WHERE достаём все пары (колонка, значение), каждую из которых передаём в функцию проверки. Тут нам и пригождается ранее созданная векторная база. Бот достаёт ближайшие (по косинусному расстоянию) 5 значений для конкретной колонки и обращается в LLM с следующим запросом:
Если модель считает, что в БД нет информации, которую ищет пользователь, ему приходит сообщение:
И этап обработки запроса заканчивается.
Если же модель считает, что изначально в фильтре были указаны неверные значения, она возвращает то, которое, по её мнению, должно быть указано в фильтре. Далее это значение подставляем в SQL, и цикл проверок повторяется.
После того, как бот получил данные, он отправляет пользователю сообщение:
Здесь мы видим SQL‑запрос, с помощью которого была получена выборка. Это удобно для визуального контроля ошибки, если бот использовал неверные фильтры, и позволяет самостоятельно доработать запрос.
Также сообщение содержит ответ на естественном языке. Он был получен с помощью следующего промпта:
Если по истечении количества итераций боту не удалось получить никаких данных, то пользователь увидит следующее сообщение:
В случае успешного ответа, помимо текста, пользователь увидит две кнопки. Кнопка «Выгрузить данные» отправит в чат два сообщения:
Первое — оформленная в Excel в виде «умной таблицы» выборка:
Второе — кнопка, которая позволяет загрузить ту же выборку обратно в бот как новый файл:
Кнопка «Визуализировать ответ» запустит механизм создания графика. Алгоритм заранее готовит данные в удобном формате, чтобы LLM могла генерировать более простой код, а далее обращается к ней со следующим промптом:
Если модель написала качественный код, который на выходе даёт график в виде картинки, то пользователь увидит следующее сообщение:
Работа с несколькими выборками
Бот может держать в памяти до трёх выборок одновременно для каждого пользователя. Между выборками можно переключаться с помощью кнопок:
Все выборки, кроме трёх последних, автоматически удаляются.
Режим диалога
Чтобы бот был ботом, а не просто системой «вопрос ‑— ответ», мы добавили поддержку диалога. То есть пользователь может уточнять свои вопросы и общаться как с обычным человеком. При этом бот будет учитывать предыдущий контекст:
Бот обрабатывает первый запрос, далее пользователь уточняет.
На входе в цикл обработки запроса у бота есть функция, которая анализирует, является ли новый запрос продолжением диалога. Классификация выполняется с помощью следующего промпта:
Если бот решает, что запрос является продолжением, то суммаризирует:
То есть контекст из текущего и предыдущего сообщения сливается в одно целое. И далее этот запрос обрабатывается, будто он независимый.
Обработка SQL
Если пользователь передаёт не текст, а SQL‑запрос, то бот просто выполняет его в БД. Если пользователь ошибся при написании, то бот вернёт ему ошибку:
Если запрос правильный, то бот покажет часть полученных данных, а также отправит выборку целиком:
Общая схема работы выглядит следующим образом:
А что в итоге?
Мы создали уникальный по своим возможностям чат‑бот, который упрощает работу с данными для множества сотрудников. Мы научили его вести диалоги, думать и разбираться в данных, что качественно отличает его от обычного text2sql, ведь бот не просто создаёт и выполняет SQL, а является полноценным инструментом для работы с данными.
В дальнейшем мы планируем расширить его функциональность и добавить возможность подключения инструмента к базам данных.
Авторы:
Алина Грибанова, участник профессионального сообщества Сбера DWH/BigData. Профессиональное сообщество отвечает за развитие компетенций в таких направлениях как экосистема Hadoop, PostgreSQL, GreenPlum, а также BI‑инструментах Qlik, Apache SuperSet и др.
Андрей Литвин, участник профессионального сообщества Сбера DWH/BigData. Профессиональное сообщество отвечает за развитие компетенций в таких направлениях как экосистема Hadoop, PostgreSQL, GreenPlum, а также BI‑инструментах Qlik, Apache SuperSet и др.