Главная / Статьи

RAG без базы данных векторов: пайплайн в BigQuery SQL и моменты его сбоев

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

Издатель: REACTAPP.TOP11 мин чтения2592 слов

Источники: SQL Is All You Need: A Complete RAG Pipeline in SQL with BigQuery

База векторных данных, оркестратор, сервис генерации эмбеддингов, API-ключи, распределённые среди всех этих компонентов, и ещё три контейнера лишь для ответов на вопросы о собственных документах. Так выглядел процесс генерации с использованием поиска векторов в большинстве учебных материалов, но для многих категорий задач он может уже не понадобиться.

Функции искусственного интеллекта BigQuery позволяют большим языковым моделям вести себя как любые другие операторы SQL, подобно операторам JOIN или WHERE. Генерация эмбеддингов, поиск векторов и структурированная генерация происходят там, где уже находятся данные, в рамках систем управления доступом IAM, журналов аудита и механизмов контроля затрат, которые у вас уже есть. Это также не изолированная среда, ограниченная моделью одного поставщика — можно использовать Claude, Llama, Mistral, а также open-source-модели от Hugging Face.

В следующих разделах описывается создание одной и той же обработочной цепи двумя способами на публичном наборе данных, к которому может обратиться любой пользователь — корпусе новостей BBC по адресу bigquery-public-data.bbc_news.fulltext. Первый способ — это однократная версия обработки пакетами с использованием стандартных функций. Второй способ — это динамическая версия в пробной версии, при которой корпус постоянно обновляется по мере поступления новых записей. Далее приводится честный анализ тех случаев, когда этот подход не срабатывает, и когда Python остается лучшим решением.

Среда, которую предполагают большинство учебников

Стандартная настройка RAG обычно включает в себя:

  1. Инструмент для генерации эмбеддингов: хостинговый API или конечная точка модели, которую необходимо развернуть и обслуживать
  2. Векторную базу данных, что добавляет ещё одного поставщика, ещё один счёт и ещё одну цепочку синхронизации для поддержания соответствия источнику данных
  3. Оркестратор, такой как LangChain, LlamaIndex, или ручно написанный механизм связи между компонентами
  4. API для LLM с собственными ограничениями по скорости и квотами
  5. Хостинг всего этого, обычно несколько контейнеров, которые кто-то должен упаковать и постоянно обновлять

Посчитайте компоненты: пять систем и по меньшей мере три API-ключа. Затем есть стоимость, о которой никто не говорит, а именно то, что ваши данные покидают хранилище. Все правила управления, политики безопасности на уровне строк и требования к аудиту, встроенные в BigQuery, перестают действовать в момент экспорта данных во внешнее векторное хранилище.

Такая архитектура существовала потому, что альтернативы не было. Ситуация изменилась.

LLM как оператор SQL

Семейство функций AI.* в BigQuery теперь доступно для широкого использования. Для технологии RAG важны три функции:

  • AI.GENERATE_EMBEDDING создает эмбеддинги пакетно из любой таблицы.
  • VECTOR_SEARCH выполняет семантический поиск в качестве встроенной функции GoogleSQL. Вы можете добавить индексы IVF или TreeAH, причем TreeAH основан на алгоритме ScaNN от Google.
  • AI.GENERATE — это функция скалярного значения по строкам, которая может возвращать структурированный результат через output_schema; она может использоваться в любом месте, где применяются значения, включая операторы SELECT, WHERE и даже условия JOIN.

В вашем коде не появляются ключи. Аутентификация осуществляется через подключение к BigQuery, управляемый сервисный аккаунт, которому вы один раз предоставляете роль. Теперь существует стандартное подключение, что упрощает настройку.

Вы не привязаны к Gemini

Часто возникает опасение, что это привязывает вас к Gemini. На самом деле это не так, хотя детали часто неправильно понимаются. Функции бывают двух типов.

Универсальные функции, такие как AI.GENERATE_TEXT, AI.GENERATE_TABLE и AI.GENERATE_EMBEDDING с аргументом MODEL, позволяют полностью контролировать запуск нужной модели. Поддерживаемые удаленные модели включают семейство Gemini, а также Anthropic Claude, Meta Llama и Mistral AI в качестве управляемых конечных точек, а также open-source модели с Hugging Face или Model Garden на платформе агентов Google (ранее Vertex AI). Для open-моделей функция CREATE MODEL может даже автоматически развернуть модель.

Управляемые функции (AI.IF, AI.CLASSIFY, AI.SCORE и скалярная функция AI.GENERATE при наличии аргумента endpoint) представляют собой упрощенные варианты, основанные на технологии Gemini. BigQuery сам выбирает модель и занимается формулировкой запросов к ней.

Утверждается, что для соблюдения требований необходимы эмбеддинги от самостоятельно развернутой мультиязычной модели E5 или Qwen, при этом ответы генерирует Claude. Всё начинается с одной команды CREATE MODEL. Что касается эмбеддингов, то здесь требуется полная замена, поскольку ничего дальше не меняется. При генерации ответов существует одно ограничение.

Путь первый: однократный пайплайн (общедоступен)

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

Шаг 0: однократная настройка

Создание подключения — это шаг, который чаще всего опускают учебные материалы, ориентированные на работы в хранилищах данных. Речь идет об учетной записи управляемой службы, через которую BigQuery получает доступ к моделям, и её создают лишь один раз.

Если у вас роль администратора BigQuery, самый простой способ — использовать стандартное подключение. Достаточно указать в запросе WITH CONNECTION DEFAULT, и BigQuery автоматически настроит его, предоставив необходимые права при первом использовании.

Если же вы хотите лично управлять подключением, для которого в пути два требуется указать us.my_connection, потребуются два команды: сначала создать подключение к облачному ресурсу, а затем предоставить его учетной записи сервиса роль Agent Platform User (roles/aiplatform.user, ранее Vertex AI User).

PROJECT_ID=my-poject-id

# 1. Create a Cloud resource connection (its location must match your dataset)
bq mk --connection --location=US --project_id=$PROJECT_ID \
  --connection_type=CLOUD_RESOURCE my_connection

# 2. Grant the connection's service account the Agent Platform User role
gcloud projects add-iam-policy-binding $PROJECT_ID \
  --member="serviceAccount:$(bq show --format=prettyjson \
    --connection $PROJECT_ID.US.my_connection | jq -r .cloudResource.serviceAccountId)" \
  --role=roles/aiplatform.user

Место, где присваивается роль, зависит от того, как ссылается на модель: в проекте, владеющем моделью, при передаче имени, или в проекте, указанном в URL-адресе конечной точки, при передаче полного URL. Аутентификация заканчивается на этом этапе — задаётся одна роль без ключей.

Затем используется модель эмбеддингов, при этом применяется стандартное подключение:

CREATE OR REPLACE MODEL `rag_demo_us.emb_model`
  REMOTE WITH CONNECTION DEFAULT
  OPTIONS (ENDPOINT = 'gemini-embedding-001');

Это утверждение представляет собой полную процедуру развертывания сервиса встраивания. Здесь нет контейнеров, нет точек входа для масштабирования и нет ключей для замены. Если вы развернули модель с открытым исходным кодом, замените её URL-адрес точки входа.

Шаг 1: встраивание корпуса

Одно утверждение используется для встраивания статей BBC:

CREATE OR REPLACE TABLE `rag_demo_us.news_emb` AS
SELECT *
FROM AI.GENERATE_EMBEDDING(
  MODEL `rag_demo_us.emb_model`,
  (
    SELECT title, body AS content
    FROM `bigquery-public-data.bbc_news.fulltext`
    WHERE category = 'tech'
  ),
  STRUCT('RETRIEVAL_DOCUMENT' AS task_type)
);

Есть два момента, которые легко упустить. В запросе на ввод должна присутствовать колонка с именем content, при необходимости с псевдонимом, а добавление колонки title улучшает качество эмбеддингов для RETRIEVAL_DOCUMENT. Кроме того, важен параметр task_type: используйте RETRIEVAL_DOCUMENT для корпуса данных и RETRIEVAL_QUERY для вопроса пользователя, поскольку асимметричные эмбеддинги значительно способствуют улучшению качества поиска.

В полученной таблице содержатся ваши исходные столбцы, а также три дополнительных, сгенерированных столбца. В поле embedding хранится вектор, statistics — это значение в формате JSON с полями token_count и truncated, а status указывает на наличие ошибок. Обратите внимание, что более новая версия функции AI.GENERATE_EMBEDDING возвращает поле embedding, в отличие от ml_generate_embedding_result, используемого в более старых руководствах по ML.GENERATE_EMBEDDING. Смешивание этих двух форматов является типичной причиной ошибок при копировании-вставке.

Первая ошибка: тихая частичная неудача. Пакетная задача может считаться успешной в целом, хотя отдельные строки могут потерпеть неудачу из-за ограничений квот или сбоев в работе сервиса. BigQuery отображает информацию по каждой строке в поле status: пустая строка означает успех, поэтому всегда используйте фильтр WHERE status = ''. Если параллельные пакеты сталкиваются с ошибками исчерпания ресурсов, Google предоставляет скрипты для повторной обработки и пакет Dataform, которые продолжают работу до тех пор, пока не будут обработаны все строки.

Вторая ошибка: тихое обрезание. В полях statistics каждой строки указывается количество токенов и флаг обрезания. Статья новостей состоит примерно из нескольких сотен токенов, но документы, превышающие лимит входных данных модели, сохраняются в обрезанном виде без каких-либо ошибок. Для длинных документов необходимо отслеживать флаг обрезания и заранее разбивать их на части.

Шаг 2: по желанию добавьте индекс

Небольшие корпуса данных могут этого не делать, поскольку VECTOR_SEARCH переходит на точный поиск с использованием грубой силы. При больших объемах данных необходимо создать такой механизм:

CREATE OR REPLACE VECTOR INDEX news_idx
ON `rag_demo_us.news_emb`(embedding)
OPTIONS (index_type = 'IVF', distance_type = 'COSINE');

Некоторые особенности, на которые стоит обратить внимание: поддержание индекса выполняется автоматически на фоне, строки, добавленные позже, можно искать сразу (BigQuery сканирует незиндексированные данные методом грубой силы), таблицы объемом менее 10 МБ не получают индекса, а версия Standard не поддерживает индексы.

Шаг 3: получение и генерация данных в одном запросе

Включить вопрос, получить пять лучших статей, обработать запрос и сформировать текстовый ответ:

SELECT
  AI.GENERATE(
    (
      'Answer using only this context:\n',
      STRING_AGG(base.content, '\n---\n'),
      '\nQuestion: what were the major browser security stories?'
    ),
    endpoint      => 'gemini-2.5-flash',
    output_schema => 'answer STRING, confidence FLOAT64, sources ARRAY<STRING>'
  ).*
FROM VECTOR_SEARCH(
  TABLE `rag_demo_us.news_emb`, 'embedding',
  (
    SELECT embedding
    FROM AI.GENERATE_EMBEDDING(
      MODEL `rag_demo_us.emb_model`,
      (SELECT 'major browser security stories' AS content),
      STRUCT('RETRIEVAL_QUERY' AS task_type)
    )
  ),
  top_k => 5
);

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

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

Второй путь: корпус данных, самостоятельно обновляющийся (предварительная версия)

Решением является автономная генерация векторов. Необходимо указать эти векторы как генерируемые столбцы, после чего BigQuery берет на себя их постоянное обновление:

CREATE TABLE `rag_demo_us.news_live` (
  title STRING,
  body  STRING,
  body_embedding STRUCT<result ARRAY<FLOAT64>, status STRING>
    GENERATED ALWAYS AS (
      AI.EMBED(
        body,
        connection_id => 'us.my_connection',
        endpoint      => 'gemini-embedding-001')
    ) STORED OPTIONS (asynchronous = TRUE)
);

Затем вставьте данные из любого источника:

INSERT INTO `rag_demo_us.news_live` (title, body)
SELECT title, body
FROM `bigquery-public-data.bbc_news.fulltext`
WHERE category = 'tech';

Этот материал стоит прочитать дважды. Благодаря использованию GENERATED ALWAYS AS AI.EMBED(...) эмбеддинг превращается в метаданные таблицы, а не в код пайплайна. При вставке или обновлении исходной колонки BigQuery создает или обновляет вектор на фоне. При установке параметра asynchronous = TRUE вставки не ожидают вызовов модели, а обработка эмбеддингов выполняется в фоновых задачах, история которых содержит префикс gc_. Колонка имеет формат STRUCT<result, status>, поэтому сбои отображаются для каждой строки отдельно и не игнорируются.

Что касается выбора модели, в примере используется gemini-embedding-001, поскольку она была широко доступна и показывала хорошие результаты в мультиязычном рейтинге MTEB в то время. В стандартном примере из документации используется text-embedding-005, который работает не менее эффективно. Следует учитывать один недостаток: по умолчанию размер выходных данных составляет 3072 измерения, тогда как у моделей text-embedding — 768, что увеличивает затраты на хранение и построение индекса. Чтобы этого избежать, укажите меньший размер с помощью параметра model_params в функции AI.EMBED. Модель обучается по принципу «матрешки», поэтому размер данных можно легко сократить до 768 или 256.

Запросы также становятся проще. Функция AI.SEARCH определяет, с помощью какой модели была создана таблица, и сама встраивает строку поиска:

SELECT base.title, base.body, distance
FROM AI.SEARCH(
  TABLE `rag_demo_us.news_live`, 'body',
  'major browser security stories',
  top_k => 5
);

Поиск текста осуществляется в столбце с текстом, причем склад в фоновом режиме обрабатывает векторы. Здесь также поддерживаются векторные индексы: создайте его для исходного столбца, и обучение начнётся, как только 80% строк будут содержать эмбеддинги.

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

Однако применимо одно ограничение: данная часть находится в режиме превью и работает по условиям до официального выпуска, которые могут измениться. Также имейте в виду, что к каждой таблице добавляется одна автоматически сгенерированная колонка встраивания, что операции вставки должны выполняться пакетно, а не постепенно в виде частых запросов DML, поскольку одновременные операции DML могут замедлить процесс генерации, и что копии, клонирования и снимки хранят данные без настроек генерации. В примечаниях к выпуску указано, что его можно включить для новых или существующих таблиц с помощью команд CREATE TABLE или ALTER TABLE. Примечания к выпуску BigQuery являются авторитетным источником информации о статусе функций, поэтому обязательно ознакомьтесь с ними перед разработкой.

Что вы получаете без дополнительных усилий

Именно это является аспектом, который должен волновать команды по обработке данных в наибольшей степени, но при этом привлекает наименьше внимания.

  • Безопасность на уровне строк и столбцов сохраняется и в функции VECTOR_SEARCH. Строка, которую пользователю не разрешено видеть, никогда не попадает в запрос в качестве контекста RAG. Обеспечить такую же гарантию с помощью синхронизированной внешней векторной базы данных сложно.
  • IAM — единственная модель аутентификации. В переменных среды отсутствуют ключи, а для пайплайна нет инструкций по их обновлению.
  • Журналы аудита, присвоение затрат и квоты формируются на основе инфраструктуры, уже используемой командой платформы. Вы даже можете отслеживать расходы за отдельные задания в Cloud Billing с помощью метки bigquery_job_id_prefix.
  • Не требуется выполнять задачи синхронизации. Векторы хранятся рядом со строками, которые они описывают, поэтому при наличии только одного хранилища векторов оно не может отстать от основного хранилища данных. При использовании автономных эмбеддингов также не требуется ответственный за обновление данных.

Лучшая инфраструктура — это та, которую вы никогда не используете.

Где возникают проблемы

  1. Оплата производится по токенам, причем Gemini 2.5 также берет плату за процесс мышления. Эти модели взимают плату за токены рассуждений помимо стоимости результата. Для задач классификации установка бюджета на мышление равным нулю в model_params снижает как время обработки, так и расходы. Если пропустить этот шаг, затраты на обработку группы данных могут расти незаметно. Сначала используйте AI.COUNT_TOKENS для оценки.
  2. Сложные запросы могут передавать модели другое количество строк, чем ожидается. Обычными причинами являются операции JOIN и использование ORDER BY с LIMIT. Google рекомендует сначала сохранить входные данные в таблицу, а затем выполнять обработку над этой таблицей.
  • Квоты действительно существуют. Пропускная способность при встраивании ограничивается квотами на количество токенов в минуту; по умолчанию это несколько миллионов. Этого достаточно для большинства рабочих нагрузок, но для встраивания всего данных озера за одну ночь требуется подготовка, либо использование функции Provisioned Throughput с параметром request_type => 'dedicated'.
  • VECTOR_SEARCH оплачивается в зависимости от количества просканированных байтов. При ценообразовании по запросу учитываются базовая таблица, индекс и запрос. Размещайте столбцы STORING в индексе, чтобы не приходилось снова обращаться к базовой таблице, и используйте условие WHERE для фильтрации уже на этапе обработки этих столбцов.
  • Ваши запросы не имеют версий. Запрос — это просто строка в SQL, поэтому не существует реестра, тестирования типа A/B или инструментов оценки. Команды, которые еженедельно перерабатывают запросы помощника в производстве, ощущают это на собственном опыте, и именно здесь полезна небольшая Python-слоя или использование dbt для хранения запросов в виде версионированной конфигурации.
  • Проверьте метку статуса

    Статус функций может меняться, поэтому убедитесь в этом. Как сообщалось ранее, AI.GENERATE и AI.GENERATE_TABLE стали доступны для широкого использования в конце января 2026 года, а модели-партнеры (Claude, Llama, Mistral) — в первой половине 2025 года. Функции предварительного просмотра, такие как автономные эмбеддинги, могли измениться с тех пор, и именно в записях об обновлениях можно это подтвердить.

    Действительно ли SQL — единственное, что вам нужно?

    Из этого следуют три вывода.

    Для пакетной и аналитической обработки RAG это заслуживает серьезного рассмотрения. Классификация документов, семантическое устранение дубликатов, пайплайны обогащения данных и внутренние системы вопросов и ответов на основе контролируемых данных — всё это способствует использованию подхода, оптимизированного под хранилище данных, с точки зрения управления, простоты и общей стоимости, при этом вы сохраняете возможность выбора модели.

    Ситуация с постоянно меняющимся корпусом данных иная, и автономные эмбеддинги меняют баланс факторов. Поиск никогда не был сложной частью реализации RAG в производстве; сложностью всегда было поддержание согласованности векторов с текущими данными, а генерируемые столбцы устраняют всю эту категорию задач.

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

    Для дальнейшего изучения в документации Google BigQuery представлен обзор технологий генеративного ИИ и матрица поддержки моделей, команда CREATE MODEL для партнерских и открытых моделей, функции AI.GENERATE, AI.GENERATE_EMBEDDING, AI.SEARCH, механизм автономной генерации эмбеддингов, функции поиска, управление векторными индексами, а также примечания к выпуску.