Как встроить ИИ-поиск в приложение с эмбеддингами

Практическое руководство: как добавить семантический поиск в приложение с помощью эмбеддингов и векторных баз данных.

Как встроить ИИ-поиск в приложение с эмбеддингами

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

Как работает семантический поиск

Алгоритм в трёх шагах. Первый: каждый документ превращается в вектор (массив из 768–3072 чисел) с помощью модели эмбеддингов. Вектор кодирует смысл текста. Второй: векторы сохраняются в специализированную базу данных. Третий: при поиске запрос пользователя тоже превращается в вектор, и база находит ближайшие по косинусному расстоянию документы.

Весь процесс — от запроса до результата — занимает 50–200 мс при базе до миллиона документов.

Выбор модели эмбеддингов

Для русского языка подходят несколько моделей:

OpenAI text-embedding-3-small — облачная модель, $0.02 за миллион токенов. Размерность: 1536. Хорошее качество на русском языке. Минус — данные уходят в облако.

OpenAI text-embedding-3-large — более точная версия, $0.13 за миллион токенов. Размерность: 3072. Для задач, где нужна максимальная точность.

intfloat/multilingual-e5-large — open-source модель, работает локально. 560 МБ, размерность 1024. Хорошо работает с русским. Не требует API-ключей и не отправляет данные наружу.

BAAI/bge-m3 — мультиязычная модель с поддержкой sparse и dense эмбеддингов. Размерность 1024. Одна из лучших open-source моделей для русского языка по бенчмаркам MTEB.

Реализация с OpenAI и ChromaDB

ChromaDB — простейшая векторная база для прототипирования. Работает как встроенная библиотека, без отдельного сервера:

pip install chromadb openai
import chromadb
from openai import OpenAI

client = OpenAI()
chroma = chromadb.PersistentClient(path="./search_db")
collection = chroma.get_or_create_collection("documents")

def get_embedding(text):
    response = client.embeddings.create(
        model="text-embedding-3-small",
        input=text
    )
    return response.data[0].embedding

# Индексация документов
documents = [
    {"id": "1", "text": "Оптимизация затрат на AWS: 10 проверенных стратегий", "url": "/aws-costs"},
    {"id": "2", "text": "Kubernetes: автоматическое масштабирование подов", "url": "/k8s-autoscale"},
    {"id": "3", "text": "PostgreSQL: настройка производительности для высоких нагрузок", "url": "/pg-tuning"},
]

for doc in documents:
    embedding = get_embedding(doc["text"])
    collection.add(
        ids=[doc["id"]],
        embeddings=[embedding],
        documents=[doc["text"]],
        metadatas=[{"url": doc["url"]}]
    )

# Поиск
query = "как уменьшить расходы на облако"
query_embedding = get_embedding(query)

results = collection.query(
    query_embeddings=[query_embedding],
    n_results=3
)

for doc, metadata, distance in zip(results["documents"][0], results["metadatas"][0], results["distances"][0]):
    print(f"[{1-distance:.2f}] {doc} → {metadata['url']}")

Продакшен: Qdrant

Для продакшена ChromaDB не подходит — нет горизонтального масштабирования, нет фильтрации по метаданным при поиске. Qdrant — зрелая векторная база с REST API, фильтрами и кластеризацией:

# Запуск через Docker
docker run -p 6333:6333 -v qdrant_data:/qdrant/storage qdrant/qdrant
from qdrant_client import QdrantClient
from qdrant_client.models import VectorParams, Distance, PointStruct, Filter, FieldCondition, MatchValue

qdrant = QdrantClient(host="localhost", port=6333)

# Создание коллекции
qdrant.create_collection(
    collection_name="articles",
    vectors_config=VectorParams(size=1536, distance=Distance.COSINE)
)

# Индексация
points = [
    PointStruct(
        id=i,
        vector=get_embedding(doc["text"]),
        payload={"text": doc["text"], "category": doc.get("category", "general")}
    )
    for i, doc in enumerate(documents)
]
qdrant.upsert(collection_name="articles", points=points)

# Поиск с фильтрацией
results = qdrant.search(
    collection_name="articles",
    query_vector=get_embedding("уменьшить расходы"),
    query_filter=Filter(
        must=[FieldCondition(key="category", match=MatchValue(value="infrastructure"))]
    ),
    limit=5
)

Локальные эмбеддинги без API

Для конфиденциальных данных используйте локальную модель через sentence-transformers:

from sentence_transformers import SentenceTransformer

model = SentenceTransformer("intfloat/multilingual-e5-large")

def get_embedding_local(text):
    return model.encode(f"query: {text}").tolist()

# Использование идентично облачному варианту
vector = get_embedding_local("как уменьшить расходы на облако")

Модель загружается при первом запуске (2 ГБ) и работает полностью офлайн. На CPU — 50–100 мс на запрос, на GPU — 5–10 мс.

Чанкинг документов

Длинные документы нужно разбивать на фрагменты (чанки) перед индексацией. Оптимальный размер чанка — 200–500 токенов. Слишком мелкие — теряется контекст. Слишком крупные — снижается точность поиска.

Разбивайте по абзацам или семантическим блокам, а не по фиксированному числу символов. Добавляйте перекрытие 10–20% между чанками. Сохраняйте метаданные: заголовок документа, URL, дату — они нужны для ранжирования и фильтрации результатов.

Гибридный поиск

Наилучшие результаты даёт комбинация семантического и ключевого поиска. Qdrant и другие современные базы поддерживают гибридный поиск: запрос одновременно ищется по эмбеддингам (смысл) и по BM25 (точное совпадение ключевых слов). Результаты объединяются через reciprocal rank fusion.

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

Векторный поиск и эмбеддинги: как это работает

Традиционный поиск (BM25, TF-IDF) ищет совпадение ключевых слов. Семантический поиск на эмбеддингах ищет смысловую близость — запрос «как приготовить пасту» найдёт документ «рецепт макарон», хотя слов нет ни одного общего. Это революция для поиска по корпоративным базам знаний, FAQ и документации.

Выбор модели эмбеддингов

МодельРазмерностьЯзыкиСкоростьКачество (RU)
text-embedding-3-large (OpenAI)3072100+API★★★★★
text-embedding-3-small (OpenAI)1536100+API★★★★☆
multilingual-e5-large (Microsoft)1024100+Локально★★★★☆
paraphrase-multilingual-mpnet76850+Локально★★★★☆
E5-mistral-7b4096100+GPU required★★★★★

Векторные базы данных: сравнение

БДТипМасштабЦенаОсобенность
pgvectorPostgreSQL extensionДо ~1M векторовБесплатноSQL, ACID
ChromaSelf-hosted / CloudСреднийБесплатно (local)Простейший старт
WeaviateSelf-hosted / CloudБольшойОт $0 (self-hosted)Hybrid search
PineconeManaged cloudЛюбойОт $0 (free tier)Managed, prod-ready
MilvusSelf-hostedМиллиардыБесплатноEnterprise scale

Hybrid Search: лучшее из обоих миров

На практике лучшие результаты даёт комбинация семантического поиска (эмбеддинги) и ключевого (BM25). Semantic search хорош для концептуальных запросов, BM25 — для конкретных терминов и кодов. Weaviate и Elasticsearch имеют встроенный hybrid search. Для pgvector используйте pg_search (ParadeDB) для BM25.

Практические применения

  • RAG (Retrieval-Augmented Generation) — поиск релевантных документов для контекста LLM
  • Дедупликация — нахождение похожих записей в базе данных
  • Рекомендации — похожие товары, статьи, пользователи
  • Классификация без дообучения — zero-shot через cosine similarity с примерами классов
  • Умный поиск по FAQ — нахождение ответа даже при разных формулировках вопроса

Сравнение embedding-моделей для семантического поиска

МодельMTEB ScoreРазмерностьЦена (1M токенов)Self-hosted
text-embedding-3-large (OpenAI)64.63072$0.13Нет
text-embedding-3-small (OpenAI)62.31536$0.02Нет
voyage-3 (Anthropic)67.11024$0.06Нет
E5-large-v262.21024БесплатноДа
nomic-embed-text-v1.562.3768БесплатноДа
BGE-M3 (BAAI)66.81024БесплатноДа

Семантический поиск: базовая реализация

from openai import OpenAI
import numpy as np

client = OpenAI()

def embed(text):
    return client.embeddings.create(
        input=text,
        model='text-embedding-3-small'
    ).data[0].embedding

def cosine_similarity(a, b):
    return np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b))

# База документов
docs = [
    'Квантовые компьютеры используют кубиты',
    'Машинное обучение требует больших данных',
    'Python популярен среди data scientists',
]
doc_embeddings = [embed(d) for d in docs]

# Поиск
query = 'Как работают квантовые вычисления?'
query_emb = embed(query)
scores = [cosine_similarity(query_emb, de) for de in doc_embeddings]
best = docs[np.argmax(scores)]
print(f'Лучший результат: {best}')

Векторные базы данных: выбор

  • FAISS (Meta): локально, без сервера, идеально для прототипов и небольших баз (<10M векторов)
  • Qdrant: self-hosted или облако, отличная производительность, REST/gRPC API, фильтрация по metadata
  • Weaviate: встроенная векторизация, GraphQL API, подходит для enterprise
  • Pinecone: managed-сервис без инфраструктурных затрат; дороже при больших объёмах
  • pgvector: расширение PostgreSQL — если уже используете PG, минимальный overhead

Практические рекомендации

  • Для RAG-системы: voyage-3 + Qdrant — лучший баланс качества и цены в 2026
  • Без внешних API: BGE-M3 локально + FAISS — конкурентное качество бесплатно
  • Масштаб production: text-embedding-3-large + Pinecone или Qdrant Cloud
  • Гибридный поиск: комбинируйте векторный + BM25 (полнотекстовый) — улучшает recall на 10–20%

Читайте также