«Дайте мне качественный датасет, и я переверну Землю!» — возможно, так перефразировал бы свою крылатую фразу Архимед, доведись ему тренировать современные LLM. Хороших наборов данных в открытом доступе не так много, а собрать свой — задача не из простых. О популярных способах сбора данных для датасетов, связанных с этим рисках и о решении, которое мы используем в YADRO, сегодня и поговорим.
Меня зовут Антон Шадрин, я работаю в DevOps-команде дивизиона искусственного интеллекта YADRO. В работе с моделями искусственного интеллекта, как и в CI/CD-пайплайне, есть похожий набор шагов.

Конкретные этапы могут немного отличаться в разных компаниях и для разных продуктов, но суть всегда остается прежней.
Начинается все с данных — это первый шаг. Нам важно понять, зачем нужные данные, какие именно и из каких источников. Это ценный ресурс, который собирать приходится отдельно и целенаправленно.
Как и зачем собирать данные
Модели становятся умнее и потребляют все больше ресурсов, а качество их работы напрямую зависит от качества и разнообразия данных.
Эволюция ChatGPT от OpenAI по количеству используемых параметров с 2018 по 2025 год:
Версия GPT |
Количество параметров |
GPT-1 |
<200 млн |
GPT-2 |
~1,5 млрд |
GPT-3 |
~175 млрд |
GPT-4 |
>400 млрд |
GPT-5 |
¯\_(ツ)_/¯ |
Недавно вышел GPT-5, который, по заявлению разработчиков, обладает знаниями примерно на уровне доктора наук практически в любой области. Количество параметров OpenAI не раскрывает. Для достижения такого уровня модель пришлось обучать на множестве самых разных данных — похоже, был проанализирован значительный объем информации в интернете, сгенерированы собственные выборки, а также привлечены специалисты по некоторым сферам знаний.
Нам тоже очень важно иметь надежный источник данных, чтобы формировать собственные датасеты — в первую очередь под конкретные задачи. Сбор данных можно организовать разными способами. Изначально мы рассматривали два варианта.
Первый — использовать открытые датасеты. У него есть очевидные минусы: мы не уверены в их качестве и соответствию поставленной задачи. Кроме того, обычно стандартные датасеты небольшие по объему.
Второй — написать веб-краулер. Это скрипт или программа, которая обходит интернет и пытается собрать нужные нам данные автоматически.
Допустим, нам нужно собрать фотографии. Мы задаем краулеру стартовую страницу — например, Википедию. Он начинает обходить все ссылки, парсит страницы и, как только находит изображение, скачивает его.
Способ кажется рабочим, пока мы не думаем о рисках, а их много.
Юридические — мы не всегда имеем право использовать найденные фото в корпоративных целях или для обучения моделей. Даже если источник общедоступен, лицензия может это прямо запрещать. Сюда же добавим авторские права: если переиспользовать такой контент, можно столкнуться с серьезными последствиями. Для крупной компании это очевидный риск.
Этические — краулер может собрать для датасета расистский или экстремистский контент, запрещенный законами РФ.
Качество данных — в интернете много изображений, но часть из них может быть в низком разрешении, с некорректной кодировкой или даже содержать вредоносный код.
В общем, при автоматическом сборе данных мы получаем много сырых, необязательно соответствующих задаче и потенциально опасных данных, которые придется тщательно проверять вручную — это сводит на нет выгоду от автоматизации.
Поэтому мы решили использовать третий способ — краудсорсинг. YADRO — большая компания, и даже нашего AI-дивизиона достаточно, чтобы собирать начальные датасеты для каких-то задач или инициатив. Так что модель краудсорсинга для сбора данных нам вполне подходит.
Краудсорсинг — это подход, при котором люди (сотрудники компании, внешние участники или фрилансеры) целенаправленно присылают данные для формирования датасета под конкретную задачу. Вместо автоматического сбора из интернета данные приходят от живых участников по заранее заданным правилам.
Где мы аккумулируем данные
Допустим, есть тысяча человек, готовых помочь собирать датасет. Как они будут передавать нам данные?
Можно пойти по простому пути: использовать облачное хранилище. Вариант технически рабочий, но у него есть серьезные ограничения. Во-первых, это непривычный для пользователя сценарий поведения. Вряд ли кто-то заходит в облачное хранилище, чтобы вручную загружать множество файлов для датасета. Это неудобно: нужно либо ставить отдельное приложение, которым вы, скорее всего, не пользуетесь, либо открывать веб-интерфейс и разбираться с ним. Во-вторых, если у нас несколько проектов, то данные нужно как-то разделять и писать инструкции о том, как называть файлы и где их размещать.
То есть мы предлагаем пользователям неудобный и нестандартный процесс, который требует усилий и не мотивирует к участию. Поэтому такой подход не работает.
Еще один вариант — использовать корпоративные мессенджеры, например Slack или Mattermost. Можно создать в нем чат или тред и собирать там данные для конкретного проекта, допустим, по сбору QR-кодов. Такой способ предполагает автоматизацию: бот будет следить за новыми сообщениями, забирать оттуда данные и отправлять их в хранилище, например S3.
В YADRO есть много AI-проектов: от улучшения наших продуктов, таких как планшет KVADRA_T, до тренировки LLM для использования внутри компании. Присоединяйся к команде:
Такое решение можно использовать, но и у него есть ограничения: далеко не все пользуются корпоративным мессенджером вне работы, а энтузиасты могут захотеть поделиться контентом в любое время: в отпуске, рано утром или в выходные.
Допустим, человек пошел в поход, увидел красивые горы или пейзаж, сделал фотографию и отправил ее друзьям.
Распространение контента в мессенджере — это то, что многие из нас делают каждый день. Мы фотографируем, записываем аудио, делаем кружочки и отсылаем друзьям — привычный пользовательский сценарий. Мы решили его не менять.
Человек делает все то же самое, что и обычно — просто вместо того, чтобы отправить фото другу, пересылает его нам. Без необходимости переключаться в другое приложение, следовать специальным инструкциям, выбирать формат файла и так далее.
Поэтому мы выбрали Telegram — привычный и удобный мессенджер, который активно используется как внутри компании, так и за ее пределами. А еще это один из самых популярных мессенджеров в России (если не самый популярный) — 100 млн уникальных пользователей во втором квартале 2025 года.
Telegram-бот: начало
Первая версия не отличалась богатым функционалом — на старте мы еще не знали, взлетит ли сама идея краудсорсинга для сбора датасетов в таком формате. Мы создали группу в Telegram, где участников ждал бот, который:
принимал фотографии от пользователей и сохранял их в хранилище S3,
удалял фото из группы.
Это простейший сценарий сбора данных:

Пользователи Telegram-бота исправно снабжали нас данными — все отлично работало. И мы решили дальше развивать бот.
В нашей компании есть много проектов, связанных с AI. Всем им нужны ресурсы для работы с моделями на GPU. Поэтому мы решили создать свою MLOps-платформу. О том, как она устроена и для чего используется — читайте в статье.
Добавим геймификацию — если хранить статистику, то мы можем формировать ежедневные рейтинги, показывать прогресс и награждать самых активных участников за предоставленный контент, поддерживая соревновательный дух. Для этого в базу данных достаточно добавить пару микросервисов:

Также реализуем валидацию данных — Telegram не различает, прислали ли фото или видео в бот. Для мессенджера это просто «документ», которым может быть что угодно — даже zip-архив. Поэтому мы добавили этап валидации, который, конечно, нагружает изначально легковесную архитектуру бота, не предназначенную для таких задач.

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

Диалог нужен не только для снимков или QR-кодов — он пригодится для других данных и, к примеру, AB-тестирования. При этом данные могут быть смешаны в рамках одного диалога: сначала бот просит фото, а затем — видео.
В первой версии из-за ее ограничений мы реализовать такие сценарии не могли.
Вторая версия с расширенным функционалом
Мы создали ее на базе Kubernetes, что логично для DevOps-команды, которая хорошо знает этот инструмент. В нем есть манифесты, которые нам пригодятся по двум причинам. Во-первых, они декларативны, то есть мы не расписываем шаги, а описываем целевое состояние системы, к которому хотим прийти. Во-вторых, мы работаем с двумя почти не связанными блоками:
metadata — все, что связано с описанием проекта, фактически его UI, а также данные для него;
spec — описывает фактическую логику: то, что должно реально происходить, как должен вести себя объект в кластере.
Мы решили перенять этот подход и применить его для второй версии бота. Идея заключается в том, чтобы описывать проекты по сбору данных так же, как в Kubernetes описываются ресурсы — декларативно, в виде манифестов. Мы используем JSON — его проще парсить и валидировать в нашем пайплайне.
Суть в том, что любой участник проекта — разработчик, DevOps или специалист из команды по сбору данных — может сам создать манифест, так как знать код при этом не требуется. Манифест — это просто JSON-файл, в котором указывается, что именно собирается, какие данные нужны и как все должно работать. Дальше из этого манифеста автоматически генерируется код, настраивается бот, появляются нужные кнопки, и проект становится доступным для сборки. Пример манифеста:
"project_name": "QRCodes",
"project_meta": {
"description": {
"message": "Очень притягивающее описание проекта",
"attachments": [
"some/attachment/to/message.png"
]
},
"target_data_count": 10000,
"target_data_types": [
"document",
"photo"
],
"admins": [
"antonsh118",
"somebody"
],
"max_file_size": "20MB"
}
Содержимое манифеста довольно простое и логичное. Начинается все с имени проекта и базовых метаданных.Это описание помогает пользователю понять, о чем проект: что именно он собирает, зачем это нужно и сколько данных желательно собрать.
Далее в манифесте указываются типы данных, которые мы хотим принимать. Это важно для валидации — чтобы в проект не начали массово загружать лишнее и не засоряли хранилище ненужным контентом.
Также задаются администраторы проекта — те, кто может добавлять бота в чаты, запускать и останавливать сбор, публиковать сообщения и управлять процессом.
Дополнительно прописывается максимальный размер загружаемого файла.
Если в будущем потребуется что-то расширить — добавить новые параметры, настройки или триггеры — это не проблема: структура манифеста легко позволяет дополнить функционал. Так можно описать проект целиком, без ручной настройки.
Логика работы проекта описывается в блоке spec и определяет поведение бота при взаимодействии с пользователем:
"project_spec": {
"allow_groups": true,
"steps": [
{
"description": "Ask for image",
"action": "ask_files",
"file_type": "photo",
"message": "Отправь мне QR-код"
},
{
"description": "Ask for video",
"action": "ask_files",
"file_type": "document",
"message": "Отправь мне видео с QR-кодом"
}
]
}
Первый элемент — это флаг allow_groups. Я уже упоминал, что изначально бот работал в групповом чате, и нам хотелось сохранить эту возможность.Это удобно: люди просто кидают данные в общий тред. К тому же за время первых запусков уже накопилась небольшая база участников, и терять ее не хотелось.
allow_groups означает, что бот может сидеть в группе и принимать все подряд — без диалога, просто как «пылесос» входящих данных. Если для проекта это допустимо, включаем флаг — и данные начинают собираться в таком режиме.
Второй элемент — это steps (шаги), базовая единица диалога. Например, если нужно запросить две разные единицы данных, делается два шага: один — «запросить изображение», второй — «запросить видео». Эти шаги задаются декларативно и исполняются ботом в нужном порядке.
Но как описать бесконечное количество возможных сценариев сбора данных в одном коротком JSON-файле? Фактически любой проект по сбору данных сводится к двум базовым действиям:
Запросить файл у пользователя с помощью action: ask_files — изображение, видео или аудио.
Отправить сообщение пользователю через send_message — инструкцию, благодарность или напоминание.
Мы можем посылать только запрос, только сообщение или совершить оба этих действия. Если добавить параметр loop, то действия можно закольцевать. Параметр times сообщит боту, сколько единиц контента нужно запросить — например, две фотографии.
Также с помощью стоп-слова можно организовать бесконечный сбор данных, пока пользователь сам не решит закончить с помощью команды /stop.
Как превратить JSON-файл в работающий проект
Манифест — это просто текст в файле, даже не код. Что с ним делать? На помощь приходит GitOps, который преобразует файл в полноценный рабочий проект. Он разворачивается в кластере и становится доступен Telegram-боту:

Как это работает:
Любой человек — сборщик данных, разработчик или просто инициатор проекта — отправляет JSON-манифест в систему контроля версий.
В системе манифест проходит валидацию и ревью. После того, как изменения были приняты в репозиторий, система контроля версий отправляет уведомление в CI — у нас это Jenkins.
-
Jenkins выполняет несколько задач:
генерирует исходный код на основе манифеста,
собирает Docker-образ проекта,
создает Argo CD Application — описание того, как развернуть проект в кластере,
фиксирует полученные артефакты в системе контроля версий.
В результате у нас есть полностью подготовленный проект — он лежит в системе контроля версий и готов к развертыванию. Для этого мы используем Argo CD. Один из его ключевых компонентов — это ресурс ApplicationSet. Он следит за репозиторием и автоматически реагирует на появление новых файлов Argo CD Application.
Если Argo CD видит, что в репозитории появился новый проект, он разворачивает его в кластере и начинает мониторинг: следит, чтобы проект был в рабочем состоянии, и при сбоях автоматически его перезапускает.
Когда проект развернут в Kubernetes, он уже знает, как подключиться к компоненте Bot Core (о ней — чуть позже). То есть, как только проект появляется в кластере, он сразу становится доступен пользователю в Telegram. Его можно будет увидеть, нажав на кнопку в интерфейсе бота.
Если говорить об архитектуре, то на верхнем уровне она выглядит вот так:

На схеме выделены три основных блока:
статистика — нужна для отслеживания активности, формирования рейтингов и награждения участников;
фронтенд — это Telegram и все, что с ним связано, в том числе бот как сущность внутри мессенджера;
проекты — это те самые «рабочие лошадки», которые принимают и сохраняют пользовательские данные в хранилище.
Рассмотрим каждый из блоков по отдельности. Начнем с условного фронтенда:

Почему «условного»? Потому что фактически интерфейсом пользователя является сам Telegram. Мы не отрисовываем UI — это делает Telegram, достаточно предоставить ему нужные сервисы и логику взаимодействия.
Ключевой компонент здесь — микросервис Bot Core, который:
обеспечивает аутентификацию и авторизацию пользователей,
знает, какие проекты сейчас подключены и доступны для взаимодействия,
сам по себе не обрабатывает данные, не хранит файлы и не реализует бизнес-логику — он скорее координирует действия между Telegram и проектами.
Задача Bot Core — принять данные от пользователя в Telegram и передать их в нужный проект, который сохраняет данные в хранилище S3.
«Большой» Redis рядом с Bot Core на общей схеме нужен для двух задач:
кеширование информации для сервиса авторизации/аутентификации пользователей, чтобы не загружать Bot Core постоянными запросами;
синхронизации между репликами Bot Core, если таких несколько — это нужно для масштабирования.
Пользователи взаимодействуют с нашей системой через Telegram, но все, что касается обработки и сохранения данных, происходит внутри отдельных проектов. Bot Core просто передает данные в нужный проект по обычному HTTP. Они передаются потоково — это важно, чтобы не перегружать сеть, особенно если речь идет о больших файлах.

Рядом с каждым проектом развернут свой «маленький» Redis. Это небольшой экземпляр, обычно один под (pod) в Kubernetes с ограниченными ресурсами. Он используется только для кеширования и синхронизации реплик проекта. Мы специально не делаем один общий Redis для всех проектов: в случае сбоя он стал бы точкой отказа. А вот потеря «маленького» Redis у конкретного проекта не критична.
Такой подход делает систему более отказоустойчивой. Redis развернут рядом с самим приложением, что минимизирует сетевые задержки. Он не требует больших ресурсов и дает достаточный уровень отказоустойчивости.
В хранилище S3 есть выделенный бакет, в котором сохраняются все данные, поступающие от пользователей. Пространства в нем достаточно, и структура организована так, чтобы все было понятно и прозрачно. Данные в бакете разделены по проектам — каждый проект сохраняет свои файлы отдельно. Кроме того, структура дополнительно разбита по пользователям. Это позволяет точно определить, кто именно и что отправил.
Такое разделение важно в том числе и для контроля качества. Интеграция с блоком статистики позволяет отследить, какой пользователь загрузил тот или иной файл. Это особенно полезно, когда нужно бороться с недобросовестной активностью — например, если пользователь пытается отправлять нерелевантные или дублирующие данные.
Теперь давайте посмотрим, что происходит, когда из системы нужно извлечь информацию и отдать ее пользователю.

Это важная часть системы, поскольку мы планировали поощрять участников, которые проявляют наибольшую активность. Потеря информации о том, что конкретный пользователь отправил файл, в нашем случае даже критичнее, чем потеря самого файла. Поэтому события о загрузках проходят через Kafka — это добавляет отказоустойчивость и гарантирует, что ни одно событие не пропадет.
Далее события попадают в микросервис StatExporter, который отвечает за запись статистики в базу данных. Он сохраняет информацию в нужном формате в Postgres.
Оттуда данные читает другой микросервис — StatViewer. Он решает как минимум две задачи. Первая — это поддержка самого Bot Core: бот каждый день, каждую неделю и каждый месяц публикует обновленную статистику, и брать ее нам откуда-то нужно. Как раз StatViewer отдает эти данные боту. Вторая задача — создание отчетов для команд Data Collection и Data Annotation. Они формируются в двух вариантах:
просто дамп базы, который можно загрузить и обработать,
отчет в формате XLSX, если нужно быстро посмотреть данные в привычном виде, провести анализ, посчитать темпы роста, построить графики и так далее.
Немного о сложностях
В ходе проекта пришлось решить ряд дополнительных задач, среди которых — авторизация, безопасность и оптимизация процесса разработки.
Авторизация
Нужно было понять: откуда брать информацию о пользователях и как удостовериться, что человек — наш сотрудник? Очевидное решение — написать собственный auth-сервис, завести базу, заставить пользователей логиниться через Telegram и сверять их с нашими данными.
Но это далеко не лучший вариант. Во-первых, речь идет о персональных данных: придется доказывать, что они хранятся надежно, соблюдать требования ИБ и проходить согласования. Во-вторых, это лишняя точка отказа, которую нам совсем не хотелось создавать.
Но у нас в YADRO уже есть работающая система Spaces, где зарегистрированы большинство сотрудников, которые используют Telegram. Мы просто обратились в наш департамент цифровых сервисов и получили доступ к API.
Теперь мы можем по Telegram ID пользователя определить, работает ли он в YADRO. Более того, можем получить дополнительную информацию — например, email, чтобы отправить письмо победителю рейтинга за неделю.
Генерация исходного кода
Задача несложная, но трудоемкая — особенно если делать это вручную. Генерация кода через синтаксическое дерево (AST) требует аккуратности: легко допустить совсем небольшую ошибку, из-за которой все перестанет работать.
Хотелось избежать полностью ручной реализации. Мы воспользовались внутренним сервисом Code Assistant в YADRO. Он отлично справился: сгенерировал объемный код, который потребовал минимальной доработки. Я писал бы его несколько часов или даже дней.
Кроме ассистента для написания кода в YADRO есть ряд собственных AI-инструментов: от сервиса транскрибации встреч до умного поиска по документации. Как устроен инференс-кластер YADRO и какие модели в нем работают — читайте в статье.

Безопасность и локальное хранение данных
Можно было бы использовать Яндекс.Диск или другие открытые/корпоративные хранилища. Но здесь есть ряд вопросов: где именно хранятся данные, как они обрабатываются и какая у этой инфраструктуры политика. Мы выбрали решение, которое позволяет нам полностью контролировать все эти аспекты — Local Bot API.
Фактически это локальный сервер Telegram API, который развернут в контуре YADRO. Он изолирован от внешнего интернета, и все сетевое взаимодействие ограничено. Данные хранятся у нас, и мы полностью управляем их жизненным циклом: сколько они хранятся, когда и как удаляются, какие политики применяются.
Благодаря этому серверу мы решили несколько проблем:
ликвидировали зависимость от внешней инфраструктуры,
сняли ограничение на размер файла в Telegram,
получили поддержку webhooks вместо длинных опросов (long polling), что упрощает архитектуру и делает систему надежнее.
Советую всем, кто разрабатывает боты для Telegram, обратить внимание на Local Bot API — развернуть его просто, а проблем он решает много.
Заключение
За короткое время нам удалось собрать более 15 тысяч единиц данных силами всего одного дивизиона, и при этом мы не занимались его продвижением внутри компании. Это позволило закрыть потребности нескольких проектов для улучшения функционала вычислительной фотографии в планшете KVADRA_T для бизнес-заказчиков.
В ближайшем будущем планируем распространить бот среди сотрудников всех подразделений YADRO. Кроме того, рассматриваем возможность предоставлять его как сервис для реализации любых задач по сбору данных.