Как построить RAG-базу знаний на text-embedding-3-large: chunking, embeddings и rerank
Многие демо RAG выглядят просто: загрузил PDF, задал вопрос — AI ответил.
Но когда доходит до реального продакшна, сразу появляются проблемы:
- Документ слишком длинный — не помещается в контекст
- Поиск по ключевым словам не находит похожие по смыслу фрагменты
- Пользователь спрашивает разговорно, а документация написана официально
- Найденные куски не по теме — модель начинает “галлюцинировать”
- Чем больше данных, тем выше задержки и стоимость поиска
text-embedding-3-large решает одну из ключевых задач: преобразует вопросы и документы в сравнимые семантические векторы.
В этой статье — никакой воды, только практический разбор рабочего RAG-процесса с инженерной точки зрения.
Базовая архитектура RAG-системы
Типовая база знаний на RAG делится на два основных контура.
Первый — оффлайн-индексация:
- Сбор документов
- Очистка текста
- Разделение на чанки (chunking)
- Генерация векторов через embedding-модель
- Сохранение в векторную базу данных
Второй — онлайн-ответы на вопросы:
- Пользователь задаёт вопрос
- Преобразование вопроса в embedding
- Поиск похожих чанков в векторной базе
- (Опционально) rerank — уточняющая сортировка
- Сборка контекста
- Генерация ответа через чат-модель
text-embedding-3-large используется на шаге 4 (оффлайн) и шаге 2 (онлайн).
Он не отвечает на вопросы напрямую, но определяет, получит ли модель нужную информацию.
Шаг 1: Подготовка документов и разбиение на чанки
Качество RAG во многом зависит от того, как вы режете текст на чанки.
Слишком крупные чанки — много “шума”, модель видит лишнее.
Слишком мелкие — не хватает контекста, информации мало.
Рекомендации для старта:
| Тип документа | Рекомендуемый размер чанка | overlap |
|---|---|---|
| FAQ / Справка | 200-500 токенов | 30-80 |
| Техническая дока | 400-800 токенов | 80-120 |
| Длинные отчёты | 600-1000 токенов | 100-150 |
| Документация кода | По функции/классу/заголовку | По ситуации |
Пример кода для разбиения:
def chunk_text(text, chunk_size=600, overlap=100):
words = text.split()
chunks = []
start = 0
while start < len(words):
end = start + chunk_size
chunk = " ".join(words[start:end])
chunks.append(chunk)
start = end - overlap
return chunks
В продакшне не режьте только по пробелам. Лучше разбивать по заголовкам, абзацам, спискам, границам кода.
Шаг 2: Генерация векторов через text-embedding-3-large
Пример на OpenAI-совместимом API. Можно использовать тот же SDK для вызова /v1/embeddings.
from openai import OpenAI
client = OpenAI(
api_key="your-api-key",
base_url="https://crazyrouter.com/v1"
)
def get_embedding(text: str):
text = text.replace("\n", " ")
response = client.embeddings.create(
model="text-embedding-3-large",
input=text,
encoding_format="float"
)
return response.data[0].embedding
Если нужно контролировать размерность вектора, добавьте параметр dimensions:
response = client.embeddings.create(
model="text-embedding-3-large",
input=text,
dimensions=1536,
encoding_format="float"
)
Чем меньше размерность, тем дешевле хранение и поиск, но качество может упасть. Тестируйте на реальных вопросах.
Шаг 3: Сохранение в векторную базу данных
Для прототипа можно использовать файлы или SQLite, но для продакшна лучше выбрать специализированную векторную БД.
Популярные варианты:
| Инструмент | Когда использовать |
|---|---|
| pgvector | Уже используете PostgreSQL, не хотите новых сервисов |
| Qdrant | Отдельная векторная БД, простая установка, мощная фильтрация |
| Milvus | Масштабируемый поиск по большим данным |
| Pinecone | Облачный сервис, не требует поддержки |
| Weaviate | Есть схема и гибридный поиск |
В любом случае, для каждого чанка сохраняйте минимум такие поля:
{
"id": "doc_001_chunk_003",
"text": "chunk content here",
"embedding": [0.0123, -0.0456],
"metadata": {
"source": "billing-guide.md",
"title": "Billing Guide",
"section": "Token pricing",
"updated_at": "2026-05-18"
}
}
Метаданные важны — они позволяют фильтровать по продукту, языку, времени, правам доступа.
Шаг 4: Семантический поиск при запросе
Когда пользователь задаёт вопрос, сначала превращаем его в вектор, затем ищем похожие чанки в базе.
def retrieve(query: str, vector_db, top_k=5):
query_vector = get_embedding(query)
results = vector_db.search(
vector=query_vector,
top_k=top_k,
filter={"language": "ru"}
)
return results
Если нет векторной базы, можно показать cosine similarity на numpy:
import numpy as np
def cosine_similarity(a, b):
a = np.array(a)
b = np.array(b)
return float(np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b)))
Но если данных больше нескольких тысяч, переходите на специализированную векторную БД.
Шаг 5: Добавляем rerank — меньше “мимо кассы”
Embedding-поиск — это “грубый” отбор: быстро, но не всегда точно.
Rerank — “точная” сортировка: оценивает релевантность вопроса и каждого кандидата.
Рекомендованный пайплайн:
- Embedding-поиск — top 20 кандидатов
- Rerank — сортировка по релевантности
- В чат-модель идут top 5
Это надёжнее, чем просто брать top 5 по embedding, особенно если:
- Много технической документации
- База знаний для поддержки
- Юридические/финансовые документы
- Мультиязычные базы
- Много похожих по названию документов
Crazyrouter поддерживает endpoint /v1/rerank — его можно встроить в RAG-процесс для точной сортировки после поиска.
Шаг 6: Передаём найденные чанки в чат-модель
После поиска собираем prompt:
def build_prompt(question, chunks):
context = "\n\n".join(
f"Source: {c['metadata']['source']}\n{c['text']}"
for c in chunks
)
return f"""
Вы — строгий помощник базы знаний.
Отвечайте только на основе приведённых материалов.
Если ответа нет — скажите “В предоставленных данных ответа нет”.
Материалы:
{context}
Вопрос: {question}
"""
Далее вызываем чат-модель:
answer = client.chat.completions.create(
model="gpt-5-mini",
messages=[{"role": "user", "content": build_prompt(question, chunks)}]
)
print(answer.choices[0].message.content)
Embedding-модель и чат-модель работают вместе:
-
text-embedding-3-large: ищет релевантные материалы -
gpt-5-mini/ Claude / Gemini: формирует ответ на основе найденного
Преимущества text-embedding-3-large для мультиязычного RAG
Во многих компаниях документация на разных языках:
- Английская API-дока
- Русский справочный центр
- Японские мануалы
- Корейские статьи сообщества
- Вьетнамские туториалы
Проблема мультиязычного RAG: пользователь спрашивает на одном языке, а ответ — в документации на другом.
text-embedding-3-large официально позиционируется как embedding-модель высокого качества для английского и других языков. Для кросс-языкового поиска стоит протестировать её в первую очередь.
Но не полагайтесь только на официальные метрики — соберите свой набор тестовых запросов:
| Вопрос | Верный документ | Язык | Найден? |
|---|---|---|---|
| Почему быстро списываются средства? | billing-token-cost.md | ru | да/нет |
| how to set API base URL | quickstart.md | en | да/нет |
| Как настроить Claude Code? | integrations/claude-code.md | ru | да/нет |
Оценивайте recall в top 3 / top 5.
Рекомендации для продакшна
1. Индексируйте только изменённые чанки
При обновлении документа пересчитывайте embedding только для новых/изменённых чанков.
Сохраняйте hash каждого чанка:
import hashlib
def content_hash(text):
return hashlib.sha256(text.encode("utf-8")).hexdigest()
Если hash не изменился — пропускайте.
2. Используйте batch-embedding
Не вызывайте API по одному чанку. Обычно Embeddings API поддерживает пакетную обработку.
response = client.embeddings.create(
model="text-embedding-3-large",
input=[chunk1, chunk2, chunk3],
encoding_format="float"
)
Так быстрее и проще контролировать лимиты.
3. Гибридный поиск
Чисто векторный поиск может пропустить точные совпадения по ключевым словам: коды ошибок, номера заказов, имена функций.
Надёжнее комбинировать:
- BM25 / поиск по ключевым словам
- Векторный поиск
- Объединение результатов
- Rerank
4. Добавляйте ссылки на источник в ответах
Не ограничивайтесь только текстом ответа. Лучше указывать заголовок и ссылку на документ.
Это повышает доверие пользователя и облегчает отладку.
5. Фильтрация по правам доступа
В корпоративных базах знаний обязательно фильтруйте по правам.
Не фильтруйте после поиска — добавляйте условия доступа прямо в запрос к векторной базе.
Частые проблемы и их решения
| Проблема | Причина | Как исправить |
|---|---|---|
| Не находится нужный документ | Чанк слишком большой/маленький, короткий запрос | Подберите размер чанка, перепишите запрос |
| Ответ ссылается не на тот документ | Мало кандидатов, нет rerank | Брать top 20 + rerank |
| Высокая задержка | Каждый раз пересчитывается embedding документа | Индексируйте документы оффлайн, запросы embedding — онлайн |
| Растёт стоимость | Дублирующая индексация, слишком большая размерность | Удаляйте дубли по hash, тестируйте меньшую размерность |
| Плохо ищет по разным языкам | Модель не подходит для мультиязыка | Тестируйте large, соберите мультиязычный тестовый набор |
Когда НЕ стоит использовать text-embedding-3-large?
Не всегда нужен embedding максимального качества.
Можно обойтись без него, если:
- Данных мало, хватает поиска по ключевым словам
- Это внутренний инструмент, требования к качеству невысокие
- Очень ограниченный бюджет, а база — одноязычный FAQ
- Проект на стадии MVP, нет реальных пользовательских запросов
Лучше сначала протестировать small и large на реальных вопросах и только потом решать, стоит ли переходить на более дорогую модель.
Вывод: успех RAG наполовину зависит от поиска
Какой бы ни был мощный чат-модель, если в контексте не те данные — ответ будет ошибочным.
text-embedding-3-large позволяет находить информацию по смыслу, а не только по ключевым словам.
Если строите продакшн RAG, рекомендую такой порядок:
- Соберите реальные документы
- Разбейте их на адекватные чанки
- Постройте векторный индекс на text-embedding-3-large
- Оцените recall top 5 на реальных вопросах
- Добавьте rerank
- Оптимизируйте prompt и чат-модель
Вы можете подключить Crazyrouter embeddings API через OpenAI-совместимый SDK и использовать один base_url для embedding, rerank и чат-модели — удобно для построения полного RAG-процесса.
FAQ
Обязательно ли использовать text-embedding-3-large для RAG-базы знаний?
Нет. Он оптимален для продакшн, мультиязыка и задач с высокими требованиями к качеству. Для небольших проектов можно начать с более дешёвых embedding-моделей.
Какой размер чанка оптимален?
Нет универсального ответа. Для технической документации — 400-800 токенов, для FAQ — короче. Ориентируйтесь на качество поиска по реальным вопросам.
Можно ли использовать text-embedding-3-large с pgvector?
Да. Вектор можно сохранять в поле pgvector в PostgreSQL и искать по схожести.
Почему embedding находит нужный материал, а модель всё равно ошибается?
Возможно, среди найденных чанков есть “шум”, prompt не ограничивает модель, нет ссылок на источник или вопрос требует объединения нескольких документов. Добавьте rerank и контроль ссылок.
Нужно ли добавлять rerank в RAG?
В продакшне — да. Embedding быстро отбирает кандидатов, rerank точно сортирует. Вместе — надёжнее.

Top comments (0)