Transcription
Так, ну а что, мы с вами переходим к нашему следующему докладу и последнему на сегодняшний день. А доклад про агентный LLM сервис для доступа к аналитической информации. Надо сказать, что докладчик вообще не айтишник, поэтому закод строго не ругайте. Вот. Ну я к тому, что вообще нейросети, они дают возможность вообще всем людям, кому это интересно сейчас, да, делать какие-то сервисы, продукты, которые помогают и автоматизируют какие-то процессы. Артём, тебе слово.
Да, спасибо. А, всем привет. Меня зовут Муркин Артём. Я, спасибо. Я старший аналитик данных а здесь, в Имане и работаю здесь уже почти 6 лет. 13 июля. Господи, так скоро будет уже 6 лет, как прилетело. И сегодня вам расскажу, расскажу, как, как говорил, а ранее Денис, про лопату для бизнеса, которая будет позволя, которая, а позволяет, а, точнее, я надеюсь, будет позволять бизнесу более эффективно и быстрее копать, принимать решения, основанные на аналитике данных.
С чего всё началось? А некоторое время тому назад у нас в компании был внутренний Иахакатон, и, э, команда от моего департамента, а, реализовала такой концепт достаточно, наверное, популярное, но сложное решение. Это текст SQL. А по сути человек задаёт вопрос на русском языке и получает ответ с необходимыми ему данными. Некоторым, многим, наверное, даже коллегам в компании эта идея понравилась, и из этого родился этот проект.
А проблема, которую мы пытаемся решить, это достаточно типовая ситуация. Аналитики заняты, много задач, тикетов. А вопросы пользователей, а заказчиков, продуктов и так далее сразу в работу зачастую взять нельзя. А бэклог растёт, очередь копится, а ответ продукту, я не знаю, бизнес-оунеру нужно получить именно сегодня. Сейчас, к сожалению, дашборды далеко не всегда могут ответить на поставленные вопросы, не могут дать нужную, допустим, детализацию.
А что у нас в итоге? Как я уже говорил ранее, пользователь задаёт вопрос, а система подбирает необходимые базы данных, необходимые таблицы, поля, инструкцию, пишет запрос, а, обрабатывает результаты. и отдаёт пользователю ответ. В ответе сейчас у нас текст, таблица, Excel-файл, то есть там таблицу Excel файла, и это в том случае, если необходимо, то есть, допустим, больше одного числа, то есть одно число можно просто вывести текстом.
А как сегодня выглядит сервис? У нас реализовано, э, то есть вот на скриншоте можно видеть, а, вместе с примером вопросы. А, в данный момент мы поддерживаем историю вопросов, диалоги, то есть можно с системой общаться как почти точно так же, как мы привыкли общаться с LLM-ками. То есть не просто спрашивать, аэ, сколько было, допустим, открытых кошельков или какой был оборот, но и продолжать уточнять. То есть, допустим, в данном случае мы говорим, сколько было открытых кошельков в мае 2025 года. Можно написать, а сколько было в декабре? Аэ, и там сгруппируй по неделям, и оно вернёт нормальный человек-считаемый ответ.
Аа как у нас сейчас всё это реализовано? А мы используем достаточно популярный фреймворк - это LangGraph. Почему именно LangGraph, а не ранее уже даже сейчас описаны агентные системы? LangGraph в текущих реалиях позволяет нам достичь определённой детерминированности работы пайплайна с меньшими, наверное, даже усилиями с нашей стороны. А у нас есть некоторые несколько различных графов. Графы включают себя узлы. А что характерно для LangGraph, через все узлы проходит единое состояние. То есть как это работает? Вы задаете вопрос, как уже ранее приводили пример, сколько было открытых кошельков, этот вопрос записывается. Потом нода "дополняет" получает в себя этот вопрос. Она получает в себя в момент, э, обращения номер диалога, там первый - это вопрос или второй, или третий, историю и так далее, и так далее. И на выходе вы получаете в конце огромный достаточно э здоровый такой массив данных в JSON, а с которым успела поработать каждая нода.
А также LangGraph позволяет удобно, а реализовать условную условную маршрутизацию по предварительно заданным правилам. Аа что это позволяет делать? Позволяет реализовать устранение ошибок именно там, где это нужно, с чёткими, понятными заданными правилами. Позволяет, а, работать с уточняющими вопросами как от системы пользователю, так и от пользователя к системе и экономить токены, экономить компьютер наших, э, GPU в тех случаях, когда нам это понятно, куда нам надо дальше двигаться, и мы можем пропустить ту или иную ноду.
А, ну и, соответственно, после каждого, после каждой итерации, э, у нас реализована система памяти, которая сохраняет в себе, а, суть вопроса, а, тему, последний SQL запрос и так далее. То есть такое краткое самари, а, которое постоянно в процессе диалога пересчитывается, а, и используется в последующих, а, итерациях.
А весь процесс можно разбить на две части. Это подготовка данных для ответа, то есть то, что дальше пойдёт в промт, то есть контекст, и непосредственно сама SQL генерация. К сожалению, я не могу рассказать об очень многом. Просто потому, что это не поместится в доклад. На самом деле каждый этап отдельно может потянуть на отдельный доклад. Поэтому мы пройдём по с вами по ключевым нодам, а отправимся в такое маленькое путешествие с вопросом а по графу. А на каждом слайде я буду показывать, что в ноду заходит в этот узел и что она отдаёт. Максимально упрощённо, просто чтобы было понятно, а как это у нас работает.
Первая нода - это Context Resolver. А это первый узел в графе. Его задача э достаточно и простая, и в то же время достаточно сложная. А если кто-то взаимодействовал с непосредственно сам писал код с по взаимодействию с LLM, то есть передача истории и диалога зачастую э может ломать ответ. А то есть LLM-ка начинает элементарно путаться. А как мы что мы сделаем для того, чтобы этого не происходило? В начале каждой итерации эта нода получает вопрос пользователя и в том случае, и если необходимо, она его преобразует. Если вопрос новый, а система просто по сути его пропускает, читает, сколько кошельков было открыто в мае 2025 года, а и отдаёт дальше, сколько кошельков было открыто в мае 2025 года. Ничего не поменялось, всё прекрасно. Но если, допустим, пользователь задаёт уточняющий вопрос, например, как здесь: "А в декабре изгруппируй по неделям". Система берёт предыдущий вопрос или историю диалога с пользователем и преобразует этот вопрос, а, и преобразованный вопрос, дальше уже участвует в тех нодах, а, в тех этапах, где это необходимо. И мы не забиваем контекст истории диалога, и нам не приходится бороться с тем, что LLM начинает глючить, неправильно интерпретировать то, о чём шёл диалог. А плюс это позволяет нам контролировать, как система, э, взаимодействует с пользователем, насколько она корректно, а, интерпретирует его вопрос.
А как это работает? Вот, например, на на скриншоте можно видеть, да? То есть вот, э, мы сначала спросили, сколько было в мае, просим уточнить про по декабрь. Система поняла, она вернула а ответ про а декабрь с группировкой по неделям, вывела таблицу, дала ссылку на скачивание Excel, потому что здесь более чем одно значение.
Следующий этап, а после определения вопроса пользователя, это определение темы. А это этап, который определяет, к какой теме и домену относится тот или иной поставленный вопрос. А у нас, как и, наверное, у многих из вас, есть большое количество различных тем. То есть у нас в данном случае, допустим, могут быть карты, кошельки, платежи, а-а, там B2C, B2B, переводы, э, и так далее. И, и в каждой, соответственно, опять же, большой теме есть подтемы. Они могут пересекаться друг с другом в том или ином виде. То есть, например, тема про кошельки. В кошельках могут быть вопросы про карты, в картах могут быть вопросы про кошельки. А, и необходимо корректно определить, к какой конкретно теме относится поставленный вопрос для того, чтобы далее давать корректный ответ пользователю.
Как мы это делаем? У нас есть два шага. Первый шаг - это набор правил. По сути, просто регулярки, у регулярных выражений. У тех или иных тем есть ключевые слова, у них есть свои веса. В том случае, если, э, мы набираем а нужный нужное количество, а, нужную уверенность, то мы остаёмся на этой теме. Если мы не добираем, то дальше вступает в работу LLM-классификатор. У него есть чёткие инструкции, там достаточно такой суровый промт, и он должен определить и отдать уровень уверенности. А после этого, э, у нас, э, вступает в работу достаточно сложная логика, э, слияния полученных результатов от регулярных выражений и этой LLM. Но по итогам эта нода должна дать, к какой теме относится. В данном случае это топик "wallets". И уровень уверенности в данном случае это 0,94. То есть дальше мы работаем только с таблицами, которые относятся к теме наших кошельков.
Следующий этап - даты. А если опять же, те, кто пробовали с LLM-ками взаимодействовать, тем более в особенности за рамками современных уже, а, более навороченных агентных систем, а, мы здесь работаем с достаточно небольшими локальными моделями, заставить LLM-ку всегда консистентно, корректно определять даты достаточно проблематично. Во-первых, это точка сечения в данных, отсутствие источников, а, знаний о том, какая дата у нас сегодня. А, и, соответственно, здесь мы опять же используем, э, LLM, мы определяем дату. А у нас опять здесь два шага. А мы извлекаем временной диапазон из вопроса. В данном случае у нас май 2025 года. LLM ищет это и трансформирует это в понятный, а, в корректный, правильный формат, который мы можем использовать дальше. А также мы используем в том случае, если LLM-ка где-то накосячила, у нас есть процесс нормализации дат, то есть это всегда должен быть набор в JSON. А данная нода читает, сколько кошельков было открыто в мае 2025 года и возвращает, а, простой JSON.
Аа дальше мы переходим к подбору необходимых таблиц. Ранее мы определили, к какой к какой теме, к какому домену у нас относится поставленный вопрос. И LLM в данном случае выбирает нужные таблицы. И опять два шага. А выбирает нужные таблицы по тексту запроса, выбранному домену. Аа, но LLM может пропустить необходимые таблицы. То есть она, э, выбирает таблицу. Вот, ээ, супер. Эта таблица отвечает на должны отвечать на поставленные вопросы. Но для того, чтобы работать с этой таблицей, нам, допустим, нужны справочники. LLM их может запросто пропустить. Поэтому мы опять же детерминированно добавляем обязательные связанные таблицы, а, которые необходимы для того, чтобы элементарно данные отфильтровать, а, расшифровать, разметить и так далее.
Аа как мы добиваемся того, чтобы LLM-ка могла выбирать таблицы? А для каждой таблицы у нас есть краткая самари, а, ключевых моментов, которые необходимы LLM для того, чтобы понять, о чём конкретно аа вещает нам каждая таблица, то есть описание тех данных, которые там есть, зачем она может применяться и так далее. А данная нода читает, как я же говорил, сколько было кошельков, тему определённую, топик и отдаёт релевантные таблицы, их список, а, и связи связи между ними и ключи, по которым это всё дело можно джойнить и объединять.
Последний этап подготовки данных для ответа, аэ, мы собираем федеральный финальный контекст. Во-первых, мы объединяем здесь, а, господи, опять два шага. Мы объединяем всё здесь, то, что мы подготовили ранее. На самом деле здесь гораздо, в реальном пайплайне всё здесь гораздо больше. Э на экран, честно говоря, не влезет. Мы, э, после того, как мы всё это собрали, мы это дело всё обогащаем, а, примерами и инструкциями. Для каждого домена есть своя конкретная инструкция. То есть, допустим, про кошельки мы можем говорить, что вот кошельки, смотри, вот эти. А если пользователь говорит, ээ, не знаю, там что-нибудь про кошельки, то есть там можно посмотреть сюда. Если мы говорим, допустим, про карты, у нас есть, не знаю, там карты какие-нибудь, то есть есть определённые организмы, которые могут использоваться, а, и инструкция позволяет системе корректно интерпретировать, что конкретно хочет получить пользователь, а, какие непосредственно нужно написать селекты, какие нужно применить фильтры. А, и а примеры позволяют LLM лучше корректно написать, потому что мы скармливаем уже подготовленные примеры, которые, в принципе, должны либо отвечать на поставленные вопросы схожи, либо, а, скажем так, хотя бы помогают LLM понимать структуру. А здесь мы читаем, сколько было вот этих кошельков. А, топик, выбранные таблицы. Здесь ещё даты, на самом деле, в том числе есть. А, и отдаём большой огромный набор данных, описание таблицы, инструкцию, примеры и так далее на генерацию. Это, собственно говоря, упрощённо этап подготовки данных. Э, к несчастью, многое пришлось ставить за границ этого выступления.
А, но могу добавить ещё, что в том случае, если система, а, вообще понимает, даже так не понимает, неправильно интерпретирует, что в том или ином этапе есть определённая неоднозначность, она прерывает выполнение графа, генерирует возможные интерпретации, а, этой неоднозначности и задаёт пользователю уточняющие вопросы. То есть вместо того, чтобы пытаться некорректно или хоть как-то ответить, проще переспросить. А простой пример. Если опять же мы говорим про те же самые кошельки, пользователь может спросить: "А, допустим, сколько карт выпустили пользователи кошелька?" А вопрос, в принципе, по своей сути корректный, потому что для того, чтобы выпустить карту, нужно будет пользователем кошелька. Но система увидит здесь, что есть карты, есть кошелёк и задаст вопрос: "О какой теме вы конкретно хотите поговорить? Это карты или кошельки?" Потому что это разные таблицы, разные предметные области. И пользователь может выбрать один, два или написать ответ просто: "Я хочу там кошельки, я хочу карты". А и система дальше пойдёт по нужному пути.
Следующий этап и финальный на данный момент - это SQL и подготовка ответа. А первая нода в этом процессе - удивительно SQL-генератор. Что он делает? Генерирует SQL. А, но в том числе он, а, по результатам валидации или ошибок выполнения на базе данных может, а, повторно пытаться сгенерировать корректный с учётом полученной ошибки. То есть, например, допустим, то есть, например, мы, а, допустим, система сгенерировала неправильные, не знаю, там, временные рамки. Мы неправильно, допустим, интерпретировали до этого даты, и возвращается ошибка, что, мол, там, ну, там с датами косяк, а, и система должна это исправить. А что у нас здесь происходит? LLM получает в себя весь тот объём данных, генерирует SQL, и она должна вернуть, а, SQL-код и объяснение, почему конкретно а этот SQL-код, почему он отвечает на поставленный вопрос пользователя.
А дальше у нас идёт валидация SQL запроса. Здесь у нас четыре шага. Первый этап - это форматирование. Мы приводим к единому формату, потому что LLM-ка может отдать, а, там, не знаю, там какие-то лишние кавычки, скобки, а, пропустить какой-нибудь пробел, всё, что ду что душой угодно, сколько мы здесь ошибок поймали, сколько мы здесь поймали шишек, это словами не передать. А после этого, после того, как мы разобрали, искали запрос, всё супер, у нас есть готовый SQL запрос, мы готовы выполнять, но мы его не выполняем. Почему? Потому что нужно проверить его на опасные команды. Будет очень грустно и обидно в том случае, если где-то там, где не надо, LLM-ка исполнит DROP, DELETE, TRUNCATE, случайно удалит или почистит ту или иную базу. Очень сильно этого не хочется. Или случайно что-нибудь запишет, куда не надо.
После этого мы выполняем парсинг запроса без реального выполнения. Это на этом в том случае, если у нас есть ошибка, а в 99%, наверное, случаев, если практически не в 100, на этом этапе мы эту ошибку видим ещё до выполнения. А, то есть, по сути, это эмуляция выполнения SQL запроса на базе данных, а, но без самого непосредственного выполнения. То есть мы строим здесь уже план выполнения, проверяем доступность всех необходимых полей, таблиц и так далее. И а ещё один момент, а, когда LLM-ка имеет свойство иногда галлюцинировать, и она может использовать, например, не ту таблицу, которую необходима. Но бывают такие узкие такие корнеркейсы, когда таблицу она использует не ту, но попадает в нужное наименование и, э, которые есть в системе. А, и мы сталкиваемся в данном случае с возможными ошибками. Поэтому на данном этапе, э, нода валидации сверяет, что SQL-запрос обращается только к тем таблицам, которые были определены в момент подбора.
И финальный этап подготовки - это result, то есть это подготовка результата. А мы выполнили SQL-код, мы получили датасет, мы его выгрузили. А после этого мы формируем финальный ответ пользователю. Как это происходит? Аа у нас есть три шага основных. Это подготовка данных. То есть мы смотрим, что мы, э, мы приводим к единому знаменателю. А в данном случае мы просто-напросто а перекладываем всё это дело в читаемый вид, а, то есть корректно оформляем, а, оставляем нужное количество точек после запятой, подобные приятные мелочи, чтобы это было нормально, человека-читаемое. Там добавляем пробелы, где нужно. А, и но здесь ещё важный важный этап. Мы это дело всё переводим в JSON. А потому что JSON наряду с Markdown очень удобен LLM для а восприятия тех значений, которые в неё в LLM-ку передаются. То есть, если мы передаём просто набор чисел, LLM весело начинает замечательно путаться. Но в том случае, если мы передаём таблицу в Markdown формате или из личного опыта, а, таблицу, наверное, лучше всё-таки JSON. А LLM понимает гораздо лучше, а, как эти данные интерпретировать, гораздо реже ошибается.
После этого на основе отформатированных подготовленных данных мы генерируем Excel файл, как ранее видели на скриншоте, который пользователь может себе скачать. А, и LLM интерпретирует результат, даёт ответ на естественном языке, опять же, как ранее видели на скриншоте. А, и, в принципе, это финальная часть э такого достаточно свещённого пайплайна, но это просто ответ на один вопрос.
А важная часть наблюдаемости и тестирования. Как до этого говорил мой коллега Александр, который рассказывал про а системы для сравнения качества различных LLM-ок. Langfuse - прекрасная вещь. Для наблюдаемости всем этим процессом мы используем Langfuse. А стандартные логи, а, мягко выражаясь, крайне неудобно. Если кто-то из здесь присутствующих или тех, кто слушает онлайн, а, пробовали дебажить достаточно объёмный LLM пайплайн, а, с помощью стандартных логов, это абсолютно бесконечный поток текста, а, который совершенно невозможно нормально читать, как-то воспринимать. Langfuse же позволяет, э, заворачивать каждый этап и логировать его и удобно его, мм, читать, интерпретировать, а, что где пошло не так, что где, сколько времени заняло, где было то или иное количество ретраев. Например, на скриншоте, э, что можно увидеть? То есть это сгенерированный SQL-код, это описание, почему конкретно такой SQL-код был сгенерирован. Это уровень уверенности, что что этот SQL-код отвечает на поставленный вопрос пользователя. Количество ретраев, а, количество ошибок валидации и финальный, например, ответ пользователю.
А тестирование, тестирование SQL, э, такого текст SQL решения, это очень большая проблема, это большая сложная боль. Почему? Потому что для каждой темы нужно подготовить достаточно объёмный датасет эталонных вопросов с эталонными SQL запросами. То есть такие пары: вопрос-SQL, вопрос-SQL, вопрос-SQL. А, то есть на каждую тему, по-хорошему говоря, минимум нужно подготовить 50 эталонных вопросов для того, чтобы хоть что-то релевантное, а, на и похожее на реальную работу, э, оно покрывало собой. А есть две основные метрики при оценке, а, качества SQL-генерации. А это Exact Match. Мы её не используем. Почему? Потому что Exact Match, как из названия следует - это точное совпадение. Добиться от LLM идеально точного совпадения генерации аа практически невозможно и в целом абсолютно бессмысленно. Поэтому мы используем используем основную метрику - это Execution Accuracy. Как это работает? А в том случае, если SQL запрос отличается, но отдаёт один и тот же мм набор данных в результате своего выполнения, то всё хорошо, всё классно, мы молодцы, LLM молодец, всё супер. Аа и мы проверяем это всё дело, э, опять же в несколько этапов. Пункт первый. Мы сравниваем, а, мм, достаточно по жёстким эвристикам. А, и в том случае, если у нас где-то ошибка, ну, то есть расхождение в, э, нашем детерминированном коде, мы говорим, что здесь у нас косяк. Аа, и дальше у нас работает LLM-судья. LLM-судья в данном случае может более гибко интерпретировать э результаты. То есть простой пример, а, колонка "FEB_count" не найдена в результате, а, мы ожидаем, что такая колонка должна быть, потому что эталонный датасет отдавал и отдаёт такую-то колонку, ля-ля-ля, пятая десятая. Но LLM решила её переобозвать и написала "total_FEB_2025". Цифры одни и те же. А, но в том случае, если мы жёстко сравниваем, а, всё будет криво и косо. А что мы, какие ещё возможны сценарии? Мы, допустим, просим LLM, э, задаём вопрос, сколько было, не знаю, там, какой оборот был, итого, а потом типа там разбей по методам. И она разбивает по методам, но в том числе добавляет, например, колонку не только в абсолютных величинах, но в процентах. В том случае, если мы сравниваем жёстко, ответ неверный, а, позволяет ответить, что всё хорошо. А в данном случае итоговый результат - true. В том случае, если говорит, что нет, всё плохо, там криво, косо, иногда ошибается. А мы проверяем всё это дело руками. К несчастью, полностью автоматизировать весь этот процесс не получается, как бы не хотелось. И по личному опыту могу сказать, что этот процесс, наверное, один из самых максимально трудоёмких.
Какие у нас есть планы на будущее? Краткосрочные - мы хотим добавить там генерацию графиков, а, и расширить уже ныне существующие предметные области. А это, наверное, второй по трудоёмкости процесс, потому что нужно описывать все данные, которые они были описаны. Нужно описывать таблицы, инструкции, а, думать, как с этим работать, собирать конкретные релевантные примеры, как для, которые как те примеры, которые мы поставляем при подготовке контекста, так и для тестирования.
Среднедолгосрочное - это добавить элементы автономности в определённые участки пайплайна. Я не уверен, что мы на данном этапе готовы полностью это перетаскивать на агентную, а, скажем так, на какой-то либо агентный фреймворк, будь то OpenCodе или что-нибудь другое, потому что, э, ну, по моим личным ощущениям и моём личном понимании, а, агенты, э, усложняют процесс получения именно детерминированного результата. И если с кодом, с генерацией кода здесь возможна достаточно большая гибкость, то корректная интерпретация того, что хочет пользователь с точки зрения именно данных, а, выбрать правильное поле в правильной таблице. Тут всё несколько даже не то, что сложнее, я бы сказал, наверное, иначе. Но для определённых этапов автономный агент может стать аэ абсолютно прекрасным инструментом. Например, это, а, какая-либо более динамическая работа с данными, а, изучение аа различных, то есть, допустим, в процессе ответа, в том случае, если не были не найдены необходимые данные, агент в теории может обращаться к этим самым базам данных, а, искать, что ещё может, допустим, подойти пользователю, а, какие куски данных, например, были пропущены. Не куски данных, извиняюсь, куски, э, таблиц были пропущены в описаниях. Возможно, где-то в описаниях есть косяки или что-то в в таблицах поменялось, а описание этих данных это дело не отображает. А, ну или, например, мм, пользователь получил табличку с данными, но он хочет получить что-то, а, более сложное. А, допустим, автономный агент может выступать как что-то вроде, а, маленького дата-аналитика, который на Python что-то делает, например, там, не знаю, там, посчитай там среднее отклонение, а, построй там какие-то графики, а, построй прогноз на этих данных. И LLM-ка должна, в принципе, достаточно без больших для себя сложностей подготовить код, который, например, может, построить прогноз определённый временной ряд, например, там, на следующий месяц, и отдать этот ответ пользователю. Вот здесь автономный агент вселяет лично в меня, да и в моих коллег большие надежды.
Также, а, сейчас мы для каждой темы готовим а описание инструкции всё вручную, а, но мы хотим прикрутить добавить возможность использования нашего дата-каталога. Спасибо большое коллегам, которые его делали и продолжают делать. А попробовать прикрутить историю наших тикетов в Jira, потому что там есть огромное количество бизнесовых знаний, а, которые зачастую, э, с которыми рядом идут обычные SQL-запросы. И это вполне возможно позволит нам отвечать на более сложные вопросы, даже те, которые мы в своих описаниях, инструкциях не ещё не успели описать, или они достаточно редко встречаются. Мы, э, там редко с ними можем как-то, ну, в принципе сталкиваться. А в идеальной картине мира пользователи задают вопрос. А-а, система смотрит, допустим, в нашу историю Jira тикетов, находит, допустим, похожий. Вот у нас был такой-то тикет, хотите ли я его повторю, меняет даты, необходимые, допустим, фильтры и отдаёт. В данном этапе, правда, наверное, всё-таки не сразу пользователь, а, допустим, нам, как департаменту аналитики данных, а, полученный результат на ревью. Это хотелось бы достичь, что-то получить, что-то вроде автономного дата-агента. А, и, соответственно, как я уже говорил, поддержка более сложных вопросов. То есть это те вопросы, которые требуют нескольких этапов, а, большого количества запросов, последующие работы с этими данными, а, которые, допустим, мы не можем напрямую сджойнить друг с другом. А, и, соответственно, поддержка более глубокой аналитики. Это, а, например, тот же самый Python, более сложные какие-либо вопросы. Возможно, какой-нибудь простенький ML.
А, в принципе, на этом, наверное, сегодня всё. А, всем спасибо.