Головна / Статті

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 та моделі з відкритим кодом від 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 у вигляді керованих кінцевих точок, а ще моделі з відкритим кодом від Hugging Face чи Model Garden на платформі агентів Google (раніше Vertex AI). Для моделей з відкритим кодом функція 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 сканує нейндексовані дані методом brute force), таблиці об’ємом менше 10 MB не отримують індексу, а версія 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 виміри, на відміну від 768 у моделях text-embedding, що збільшує витрати на зберігання та індексування. Щоб уникнути цього, вкажіть менший розмір через параметр 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% рядків міститимуть ембеддинги.

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

Однак існує одне обмеження: ця частина знаходиться у прев’ю, за умовами до GA, які можуть змінитися. Також слід пам’ятати, що кожна таблиця має один автоматично створений стовпець імбеддингу, що вставки мають виконуватися пакетно, а не постійно через операції 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, можливості автономної генерації ембеддингів, функції пошуку, керування векторними індексами та примітки до версій.