- BrainTools - https://www.braintools.ru -

10 марта Google выкатил Gemini Embedding 2 – embedding-модель, которая умеет превращать в векторы не только текст, но и картинки, видео, аудио и PDF. Причем всё это ложится в одно векторное пространство.
Почему это круто? До сих пор если вы хотели искать по видеобиблиотеке через RAG, приходилось городить огород: сначала транскрибировать аудиодорожку, потом описывать кадры через Vision LLM, склеивать это в текст, и только потом эмбеддить. Каждый шаг – потеря информации. Тон голоса, визуальный контекст, динамика в кадре – все это терялось при “переводе” в текст.
Теперь же, как заявляют Гугл, можно скормить модели MP4-файл напрямую, и она вернёт вектор. Тот же вектор, что живет в том же пространстве, что и векторы текстовых документов. Текстовый запрос “как настроить авторизацию” найдет и статью из вашей базы знаний, и фрагмент видеоинструкции – через один и тот же старый добрый трюк – косинусное расстояние.
В этой статье разберем что нового в модели, а потом построим полноценный мультимодальный RAG с нуля, используя Python, Supabase, Gemini API.
Для тех кто подзабыл или не сталкивался – очень коротко.
RAG (Retrieval-Augmented Generation) – это когда LLM отвечает не из галлюцинаций головы, а сначала ищет релевантную информацию в вашей базе данных. Схема такая:
Берем вопрос пользователя, прогоняем через embedding-модель – получаем вектор
Ищем в векторной БД ближайшие по смыслу документы (косинусное расстояние).
Найденные документы подставляем в промпт как контекст
LLM генерирует ответ, опираясь на этот контекст
Зачем это нужно? LLM не знает ваших внутренних документов, не читала ваши видеоинструкции и понятия не имеет что написано в корпоративной вики. RAG решает эту проблему – модель получает актуальный контекст прямо в момент запроса. А еще это экономит лимиты, так как не надо загружать всю информацию в контекст.
Векторная БД тут ключевой компонент. Она хранит эмбеддинги и умеет быстро находить похожие. PostgreSQL с расширением pgvector, Pinecone, Weaviate, Qdrant – вариантов много. Мы будем использовать Supabase, потому что это по сути hosted Postgres с pgvector из коробки – для новичков и просто попробовать самое то.
Все, база есть. Едем дальше.
Раньше embedding-модели от Google (да и от большинтсва других вендоров) работали только с текстом. Хотите эмбеддить картинку? Опишите её текстом, а потом эмбеддьте описание. Хотите видео? Транскрибируйте, потом эмбеддьте транскрипцию. Это как искать музыку по словесному описанию мелодии (ну там вот так: “тра-та-там-бам-бам”) – сработать может конечно, но так себе.
Gemini Embedding 2 позволяет нативно (as is) принимать разные типы контента:
Текст – до 8192 входных токенов (в 4 раза больше чем предыдущая модель)
Картинки – до 6 штук за запрос, PNG и JPEG
Видео – до 120 секунд, MP4 и MOV
Аудио – нативно, без промежуточной транскрипции.
PDF – до 6 страниц
Все это проецируется в единое пространство размерностью 3072. То есть вектор от текстового запроса и вектор от видеоклипа – это точки в одном и том же координатном пространстве, и их можно сравнивать напрямую.
Кроме обработки одной модальности за раз, модель умеет принимать смешанный вход. Можно отправить в одном запросе картинку и текстовую подпись к ней – и получить один эмбеддинг, который учитывает связь между ними. Раньше пришлось бы эмбеддить отдельно и потом как-то агрегировать.
Если вам говорят о чем-то эти значения, то Google показывает цифры где Embedding 2 опережает Amazon Nova 2 и Voyage Multimodal 3.5, особенно сильно по видео-задачам (68.8 vs 60.3 vs 55.2). Но это их собственные бенчмарки, независимых пока толком пока нет. Впрочем, отрыв заявлен не маленький, так что есть шансы что в независимых тестах картина будет похожей.
Окей, теория – это хорошо, но старая добрая практика позволяет пощупать все тонкости самому. Давайте построим игрушечную рабочую систему.
На практике нельзя просто взять Gemini Embedding 2, заэмбеддить видео и радоваться. Вот почему: когда пользователь задает вопрос, система находит релевантный видеофрагмент по вектору – отлично. Но дальше этот фрагмент часто нужно передать LLM чтобы она сформировала ответ. И тут проблема: LLM не может «прочитать» MP4. Она работает с текстом. В итоге система находит видео, но отвечает в духе «вот вам двухминутный клип, ответ где-то там». Выглядит не как самая полезная вещь – есть пространство что улучшить.
Решение – два параллельных вызова при загрузке каждого медиафайла:
Gemini Embedding 2 создает нативный (то есть самого видео/аудио) вектор из сырого медиа – это для поиска
Gemini Flash (или любая обычная генеративная модель на ваш вкус [1]) смотрит/слушает медиа и пишет текстовое описание – это для столбца content в БД, см. ниже.
Вектор находит совпадение. Текстовое описание дает LLM материал для ответа. Без описания совпадения будут так себе – найдены поиском, но бесполезны для генерации ответа и анализа.
Как итог получаем лучшее из двух миров: улучшенный поиск (ведь теперь находится идеально подходящее видео) и готовый контекст для LLM для будущего анализа.
Python 3.10+
google-genai – SDK для Gemini API
Supabase – hosted PostgreSQL с pgvector
ffmpeg – для нарезки видео
opencv-python – для извлечения кадров
Создаём директорию и ставим зависимости:
mkdir multimodal-rag && cd multimodal-rag
pip install google-genai supabase python-dotenv opencv-python
Проверяем что ffmpeg установлен:
ffmpeg -version
Если нет – на macOS brew install ffmpeg, на Ubuntu sudo apt install ffmpeg, на Windows проще всего через choco install ffmpeg.
Структура проекта будет такой:
multimodal-rag/
├── .env
├── config.py
├── migration.sql
├── video_chunker.py
├── ingest.py
├── query.py
└── assets/
├── docs/ # текстовые документы (.md, .txt)
├── images/ # картинки (.png, .jpg)
└── video/ # видеофайлы и чанки
Нужно три вещи:
Gemini API Key – берем на aistudio.google.com/apikey [2] (есть бесплатный тир)
Supabase Project URL – создаем проект на supabase.com [3], идём в Settings → API.
Supabase Anon Key – там же.
Создаём .env:
GEMINI_API_KEY=ваш_ключ
SUPABASE_URL=ваш_url
SUPABASE_KEY=ваш_key
И config.py:
import os
from dotenv import load_dotenv
load_dotenv()
GEMINI_API_KEY = os.getenv("GEMINI_API_KEY")
SUPABASE_URL = os.getenv("SUPABASE_URL")
SUPABASE_KEY = os.getenv("SUPABASE_KEY")
EMBEDDING_MODEL = "gemini-embedding-2-preview"
EMBEDDING_DIM = 1536
LLM_MODEL = "gemini-2.0-flash"
Пара замечаний по моделям. gemini-embedding-2-preview – это та самая мультимодальная модель. Для размерности берем 1536, а не полные 3072, потому что pgvector имеет свои ограничения (HNSW-индекс в 2000 измерений). Не переживаем – потеря качества при обрезке до 1536 минимальна.
gemini-2.0-flash – быстрая и дешёвая генеративная модель, которую мы используем для двух задач: генерация описаний медиа при загрузке и генерация финальных ответов. Можете заменить на другую модель если хотите – на суть архитектуры это не влияет.
ID моделей могут меняться. Перед тем как запускать, сверьтесь с актуальной документацией Google AI Studio.
Supabase – это по сути PostgreSQL в облаке, но пустой. Когда ты создаёшь проект, там нет ни таблиц, ни расширений -чистый лист. Нужно сказать базе: “включи расширение pgvector, создай таблицу documents с такими-то столбцами, создай индекс для быстрого поиска по векторам, создай функцию match_documents которую мы будем вызывать из Python”.
Для этого создаем migration.sql. Миграция тут – это просто SQL-скрипт, который создает структуру базы данных в Supabase. Тут одна таблица и одна функция для поиска:
-- Включаем pgvector
CREATE EXTENSION IF NOT EXISTS vector WITH SCHEMA public;
-- Основная таблица
CREATE TABLE documents (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
content TEXT, -- текст или описание медиа
embedding VECTOR(1536), -- вектор от Embedding 2
source_type TEXT NOT NULL, -- 'text', 'image', 'video'
source_file TEXT, -- имя файла
chunk_index INT, -- номер чанка (для видео)
metadata JSONB DEFAULT '{}',
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- HNSW-индекс для быстрого поиска
CREATE INDEX idx_documents_embedding
ON documents USING hnsw (embedding vector_cosine_ops);
-- Функция поиска похожих документов
CREATE OR REPLACE FUNCTION match_documents(
query_embedding VECTOR(1536),
match_count INT DEFAULT 5,
filter_source_type TEXT DEFAULT NULL
) RETURNS TABLE(
id UUID,
content TEXT,
source_type TEXT,
source_file TEXT,
chunk_index INT,
metadata JSONB,
similarity FLOAT
) AS $$
BEGIN
RETURN QUERY
SELECT
d.id, d.content, d.source_type, d.source_file,
d.chunk_index, d.metadata,
1 - (d.embedding <=> query_embedding) AS similarity
FROM documents d
WHERE (filter_source_type IS NULL OR d.source_type = filter_source_type)
ORDER BY d.embedding <=> query_embedding
LIMIT match_count;
END;
$$ LANGUAGE plpgsql;
Обратите внимание [4] на два столбца: embedding хранит нативный вектор (для поиска), content хранит текст или сгенерированое описание (для LLM). Это те самые два параллельных канала, о которых я говорил выше.
Как применить миграцию – есть два варианта.
Вариант А – через Supabase CLI:
# Установка CLI (если еще нет)
npm install -g supabase
# Инициализация и линковка
supabase init
supabase login
supabase link --project-ref ваш_project_ref
# Копируем миграцию
mkdir -p supabase/migrations
cp migration.sql supabase/migrations/20250101000000_init.sql
# Применяем
supabase db push
Project ref – это поддомен из вашего Supabase URL. Если URL https://abcxyz.supabase.co [5], то ref – это abcxyz.
Вариант Б – просто копируете SQL в SQL Editor в дашборде Supabase и выполняете. Для туториала это проще.
Gemini Embedding 2 принимает максимум 120 секунд видео за запрос – грустно конечно. И поэтому ксли ваше видео длиннее – его нужно нарезать. Но нарезать тупо по 120 секунд – плохая идея – контекст на границе чанка потеряется.
Поэтому режем на ~97-секундные сегменты с 15-секундным нахлестом. 97 секунд даёт запас до лимита, а 15-секундный overlap гарантирует что контекст на стыках не пропадет. Значения подобраны наугад, можете экспериментировать тут. Это, конечно примитивный подход. В идеале чанкинг видео нужно делать по сменам сцен или по семантическим границам, но для рабочего прототипа хватит.
video_chunker.py:
"""Нарезка видео на сегменты для Gemini Embedding 2 (макс 120 сек на чанк)."""
import os
import subprocess
import cv2
def get_duration(video_path: str) -> float:
"""Получаем длительность видео через ffprobe."""
result = subprocess.run(
["ffprobe", "-v", "quiet", "-show_entries", "format=duration",
"-of", "csv=p=0", video_path],
capture_output=True, text=True
)
return float(result.stdout.strip())
def chunk_video(
input_path: str,
output_dir: str = "assets/video",
segment_duration: int = 97,
overlap: int = 15,
) -> list[str]:
"""Разбиваем видео на перекрывающиеся сегменты."""
os.makedirs(output_dir, exist_ok=True)
duration = get_duration(input_path)
chunks = []
start = 0
index = 0
while start < duration:
chunk_path = os.path.join(output_dir, f"chunk_{index:03d}.mp4")
cmd = [
"ffmpeg", "-y", "-ss", str(start), "-t", str(segment_duration),
"-i", input_path, "-c", "copy", chunk_path,
]
subprocess.run(cmd, capture_output=True)
chunks.append(chunk_path)
print(f"Создан {chunk_path} (начало={start}с)")
start += segment_duration - overlap
index += 1
return chunks
def extract_thumbnail(video_path: str, output_path: str | None = None) -> str:
"""Извлекаем кадр из середины видео как превью."""
cap = cv2.VideoCapture(video_path)
total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
cap.set(cv2.CAP_PROP_POS_FRAMES, total_frames // 2)
ret, frame = cap.read()
cap.release()
if not ret:
raise RuntimeError(f"Не удалось прочитать кадр из {video_path}")
if output_path is None:
base = os.path.splitext(video_path)[0]
output_path = f"{base}_thumb.jpg"
cv2.imwrite(output_path, frame)
return output_path
if __name__ == "__main__":
import sys
if len(sys.argv) < 2:
print("Использование: python video_chunker.py <видеофайл>")
sys.exit(1)
paths = chunk_video(sys.argv[1])
print(f"nСоздано {len(paths)} чанков.")
Это ядро системы. Здесь реализуются те самые два параллельных вызова для медиа.
ingest.py:
"""Загрузка текста, картинок и видео в Supabase через Gemini Embedding 2."""
import os
import glob
from google import genai
from google.genai import types
from supabase import create_client
from config import (
GEMINI_API_KEY, SUPABASE_URL, SUPABASE_KEY,
EMBEDDING_MODEL, EMBEDDING_DIM, LLM_MODEL,
)
# Инициализация клиентов
gemini = genai.Client(api_key=GEMINI_API_KEY)
supabase = create_client(SUPABASE_URL, SUPABASE_KEY)
# ===== Функции эмбеддинга =====
def embed_text(text: str) -> list[float]:
"""Эмбеддинг текста."""
result = gemini.models.embed_content(
model=EMBEDDING_MODEL,
contents=[text],
config=types.EmbedContentConfig(
task_type="RETRIEVAL_DOCUMENT",
output_dimensionality=EMBEDDING_DIM,
),
)
return result.embeddings[0].values
def embed_image(image_path: str) -> list[float]:
"""Нативный эмбеддинг картинки - без конвертации в текст."""
with open(image_path, "rb") as f:
image_bytes = f.read()
mime = "image/png" if image_path.endswith(".png") else "image/jpeg"
result = gemini.models.embed_content(
model=EMBEDDING_MODEL,
contents=types.Content(parts=[
types.Part.from_bytes(data=image_bytes, mime_type=mime),
]),
config=types.EmbedContentConfig(output_dimensionality=EMBEDDING_DIM),
)
return result.embeddings[0].values
def embed_video(video_path: str) -> list[float]:
"""Нативный эмбеддинг видео - без транскрипции."""
with open(video_path, "rb") as f:
video_bytes = f.read()
result = gemini.models.embed_content(
model=EMBEDDING_MODEL,
contents=types.Content(parts=[
types.Part.from_bytes(data=video_bytes, mime_type="video/mp4"),
]),
config=types.EmbedContentConfig(output_dimensionality=EMBEDDING_DIM),
)
return result.embeddings[0].values
# ===== Генерация описаний (для столбца content) =====
def describe_content(file_path: str, mime_type: str) -> str:
"""
Просим Gemini посмотреть/послушать медиафайл
и написать текстовое описание.
Это описание пойдёт в столбец content,
чтобы LLM могла сослаться на него при генерации ответа.
"""
with open(file_path, "rb") as f:
data = f.read()
response = gemini.models.generate_content(
model=LLM_MODEL,
contents=types.Content(parts=[
types.Part.from_bytes(data=data, mime_type=mime_type),
types.Part.from_text(
text="Подробно опиши содержимое этого файла для базы знаний. "
"Включи все ключевые концепции, процессы и связи."
),
]),
)
return response.text
# ===== Вставка в Supabase =====
def insert_document(
content: str,
embedding: list[float],
source_type: str,
source_file: str,
chunk_index: int | None = None,
metadata: dict | None = None,
):
"""Сохраняем документ в базу."""
supabase.table("documents").insert({
"content": content,
"embedding": embedding,
"source_type": source_type,
"source_file": source_file,
"chunk_index": chunk_index,
"metadata": metadata or {},
}).execute()
# ===== Пайплайны загрузки =====
def ingest_text_docs(docs_dir: str = "assets/docs"):
"""
Загрузка текстовых документов.
Тут всё просто - текст идёт и в content, и в эмбеддинг.
"""
files = glob.glob(os.path.join(docs_dir, "*.md")) +
glob.glob(os.path.join(docs_dir, "*.txt"))
for path in files:
filename = os.path.basename(path)
print(f"Загружаем текст: {filename}")
with open(path, "r", encoding="utf-8") as f:
text = f.read()
vector = embed_text(text)
insert_document(
content=text,
embedding=vector,
source_type="text",
source_file=filename,
)
print(f" Готово ({len(vector)} измерений)")
def ingest_images(images_dir: str = "assets/images"):
"""
Загрузка картинок - два вызова:
1. embed_image() - нативный вектор для поиска
2. describe_content() - текстовое описание для LLM
"""
files = glob.glob(os.path.join(images_dir, "*.png")) +
glob.glob(os.path.join(images_dir, "*.jpg"))
for path in files:
filename = os.path.basename(path)
print(f"Загружаем картинку: {filename}")
mime = "image/png" if path.endswith(".png") else "image/jpeg"
# Вызов 1: описание для content
print(" Генерируем описание...")
description = describe_content(path, mime)
# Вызов 2: нативный эмбеддинг для поиска
print(" Создаём эмбеддинг...")
vector = embed_image(path)
insert_document(
content=description,
embedding=vector,
source_type="image",
source_file=filename,
metadata={"description": description},
)
print(f" Готово ({len(vector)} измерений)")
def ingest_video_chunks(video_dir: str = "assets/video"):
"""
Загрузка видеочанков - тоже два вызова на каждый чанк.
Предполагается что видео уже нарезано через video_chunker.py
"""
chunk_files = sorted(glob.glob(os.path.join(video_dir, "chunk_*.mp4")))
for i, path in enumerate(chunk_files):
filename = os.path.basename(path)
print(f"Загружаем видеочанк: {filename}")
print(" Генерируем описание...")
description = describe_content(path, "video/mp4")
print(" Создаём эмбеддинг...")
vector = embed_video(path)
insert_document(
content=description,
embedding=vector,
source_type="video",
source_file=filename,
chunk_index=i,
metadata={"description": description, "chunk_index": i},
)
print(f" Чанк {i+1} загружен ({len(vector)} измерений)")
if __name__ == "__main__":
print("=== Загрузка текстовых документов ===")
ingest_text_docs()
print("n=== Загрузка картинок ===")
ingest_images()
print("n=== Загрузка видеочанков ===")
ingest_video_chunks()
print("nЗагрузка завершена.")
Давайте ещё раз проговорим что тут происходит, потому что это самое важное место.
Для текста все тривиально: сам текст идёт в content, его же эмбеддинг идёт в embedding– тут один вызов.
Для картинок и видео делаем два отдельных вызова к разным моделям:
embed_image() / embed_video() → embedding (нативный вектор от сырого медиа, через Embedding 2)
describe_content() → content (текстовое описание, через Gemini Flash)
Еще раз: вектор и описание обслуживают совершенно разные цели. Вектор – это про поиск, а описание это про генерацию ответа. Если убрать описание, LLM не сможет ничего сказать о найденном видео. Если убрать нативный эмбеддинг и вместо него эмбеддить описание (как делают многие фреймворки) – потеряется качество поиска, потому что описание это lossy-представление оригинала.
Теперь поисковый движок. Логика [6] такая: эмбеддим вопрос → ищем через RPC-функцию в Supabase → собираем контекст из найденных документов → генерируем ответ. Летс го.
query.py:
"""Поисковый движок: эмбеддинг вопроса → поиск → генерация ответа."""
from google import genai
from google.genai import types
from supabase import create_client
from config import (
GEMINI_API_KEY, SUPABASE_URL, SUPABASE_KEY,
EMBEDDING_MODEL, EMBEDDING_DIM, LLM_MODEL,
)
gemini = genai.Client(api_key=GEMINI_API_KEY)
supabase = create_client(SUPABASE_URL, SUPABASE_KEY)
def query_rag(
question: str,
top_k: int = 5,
source_type: str | None = None,
) -> tuple[str, list[dict]]:
"""
RAG-запрос: эмбеддим вопрос → ищем в Supabase → генерируем ответ.
Возвращает (ответ, список_совпадений).
"""
# 1. Эмбеддим вопрос
# Обратите внимание: task_type="RETRIEVAL_QUERY" (не DOCUMENT)
result = gemini.models.embed_content(
model=EMBEDDING_MODEL,
contents=[question],
config=types.EmbedContentConfig(
task_type="RETRIEVAL_QUERY",
output_dimensionality=EMBEDDING_DIM,
),
)
query_vector = result.embeddings[0].values
# 2. Поиск через RPC-функцию
rpc_params = {
"query_embedding": query_vector,
"match_count": top_k,
}
if source_type:
rpc_params["filter_source_type"] = source_type
matches = supabase.rpc("match_documents", rpc_params).execute()
if not matches.data:
return "Ничего не найдено.", []
# 3. Собираем контекст из найденных документов
context_parts = []
for m in matches.data:
label = f"[{m['source_type'].upper()}] {m['source_file']}"
if m.get("chunk_index") is not None:
label += f" (чанк {m['chunk_index']})"
label += f" - similarity: {m['similarity']:.3f}"
context_parts.append(f"{label}n{m['content']}")
context = "nn---nn".join(context_parts)
# 4. Генерируем ответ
prompt = (
"Ты - помощник, отвечающий на вопросы на основе мультимодальной "
"базы знаний с текстовыми документами, картинками и видео.nn"
f"Контекст:n{context}nn"
f"Вопрос: {question}nn"
"Дай чёткий, подробный ответ на основе контекста выше. "
"Ссылайся на конкретные источники где возможно."
)
response = gemini.models.generate_content(model=LLM_MODEL, contents=prompt)
return response.text, matches.data
if __name__ == "__main__":
import sys
question = " ".join(sys.argv[1:]) if len(sys.argv) > 1 else
"Как настроить авторизацию?"
print(f"Вопрос: {question}n")
answer, sources = query_rag(question)
print(f"Ответ:n{answer}n")
print(f"Источники ({len(sources)}):")
for s in sources:
print(f" [{s['source_type']}] {s['source_file']} - {s['similarity']:.3f}")
Обратите внимание на task_type. При загрузке документов мы использовали RETRIEVAL_DOCUMENT, а при поиске – RETRIEVAL_QUERY. Это подсказывает модели оптимизировать вектор под соответствующую задачу. Мелочь, но на качество поиска влияет.
Полный пайплайн от начала до конца:
# 1. Подготовка - положите файлы в assets/
# assets/docs/ - ваши .md и .txt файлы
# assets/images/ - картинки .png и .jpg
# 2. Если есть длинное видео - нарезаем на чанки
python video_chunker.py assets/video/my_video.mp4
# 3. Загружаем все в базу
python ingest.py
# 4. Задаём вопросы
python query.py "Как работает система авторизации?"
python query.py "Что показано на архитектурной диаграмме?"
Результаты должны приходить из разных модальностей – и текстовых документов, и описаний картинок, и видеофрагментов. Все через один вектроный поиск – ну кайф же.
Перейдем от сладких речей и чуть-чуть спустимся на землю – вот что нужно иметь в виду.
Preview-статус. Модель сейчас в public preview. Это значит что API может меняться, гарантий стабильности нет и для боевого продакшена нужно дважды подумать.
Лимит 120 секунд на видео. Для коротких роликов нормально, но для часовых записей чанкинг превращается в отдельную инженерную задачу. Наш подход с фиксированными сегментами – это бейзлайн. В реальном проекте стоит смотреть в сторону semantic chunking.
Несовместимость с предыдущими эмбеддингами. Если у вас есть существующая база на gemini-embedding-001 или text-embedding-005 – придётся переиндексировать все с нуля. Векторные пространства разных моделей несовместимы, смешивать их в одном индексе нельзя.
Стоимость аудио вдвое выше. Текст, картинки и видео – $0.25 за миллион токенов. Аудио – $0.50. Поэтому аккуратней с аудио.
Реальный фидбек. Команда Ragie (платформа для RAG) провела тесты и обнаружила интересную вещь: для видео описания сгенерированные через Vision LLM иногда работают не хуже нативных мультимодальных эмбеддингов при поиске, при этом обходятся дешевле и быстрее. Это не значит что нативные эмбеддинги бесполезны – они ловят то, что текстовое описание может упустить (визуальные детали, динамику). Ключевые слова там – “иногда” и “не хуже”. Но это не лишнее напоминание, что в каждом конкретном случае стоит тестировать оба подхода.
Чанкинг – нерешенная задача. Для текста есть много подходов к семантическому разбиению. Для видео все гораздо сложнее. Простая нарезка по времени может разрезать объяснение пополам, а нахлест только частчно решает проблему.
Gemini Embedding 2 – это серьезный шаг вперед для мультимодального поиска. Возможность эмбеддить видео, картинки и аудио нативно, в одно пространство с текстом – это то, чего не хватало для полноценных мультимодальных RAG-систем.
Но, как мы показали в туториале сама по себе модель не решает проблему. Ключ – в правильной архитектуре данных: нативные эмбеддинги дают поиск, а текстовые описания дают LLM материал для ответов – их симбиоз позволит создавать по-настоящему мощные RAG системы.
Если у вас есть база знаний с видеоинструкциями, скриншотами, записями созвонов – теперь все это можно сделать поисковым и не через костыли с транскрипцией, а нативно.
Полезные ссылки:
Надеюсь тебе понравилось. Лучшая благодарность – это твоя подписка на мой Telegram-канал [11] 😊
Автор: ab429
Источник [12]
Сайт-источник BrainTools: https://www.braintools.ru
Путь до страницы источника: https://www.braintools.ru/article/27175
URLs in this post:
[1] вкус: http://www.braintools.ru/article/6291
[2] aistudio.google.com/apikey: https://aistudio.google.com/apikey
[3] supabase.com: http://supabase.com
[4] внимание: http://www.braintools.ru/article/7595
[5] https://abcxyz.supabase.co: https://abcxyz.supabase.co
[6] Логика: http://www.braintools.ru/article/7640
[7] Видео вдохновение этого разбора: https://www.youtube.com/watch?v=gmbW_lXXIkc
[8] Официальный блог Google о Gemini Embedding 2: https://blog.google/technology/developers/gemini-embedding-model-2/
[9] Документация Gemini API – Embeddings: https://ai.google.dev/gemini-api/docs/embeddings
[10] Vertex AI документация: https://cloud.google.com/vertex-ai/generative-ai/docs/models/gemini/embedding-2
[11] Telegram-канал: https://t.me/idei_iz_doliny
[12] Источник: https://habr.com/ru/articles/1010030/?utm_campaign=1010030&utm_source=habrahabr&utm_medium=rss
Нажмите здесь для печати.