Реализация семантического поиска по текстовым документам

Проектируем и внедряем системы искусственного интеллекта: от прототипа до production-ready решения. Наша команда объединяет экспертизу в машинном обучении, дата-инжиниринге и MLOps, чтобы AI работал не в лаборатории, а в реальном бизнесе.
Показано 1 из 1 услугВсе 1566 услуг
Реализация семантического поиска по текстовым документам
Средняя
~3-5 рабочих дней
Часто задаваемые вопросы
Направления AI-разработки
Этапы разработки AI-решения
Последние работы
  • image_website-b2b-advance_0.png
    Разработка сайта компании B2B ADVANCE
    1218
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1161
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    853
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1047
  • image_logo-advance_0.png
    Разработка логотипа компании B2B Advance
    561
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    825

Реализация семантического поиска по текстовым документам

Семантический поиск понимает смысл запроса, а не только ключевые слова. Запрос «как повысить мотивацию команды» находит документы о «методах управления персоналом», которые не содержат ни одного слова из запроса. Это принципиальное отличие от BM25/TF-IDF.

Архитектура семантического поиска

Bi-encoder (основной рабочий режим): отдельные модели кодируют запрос и документы в векторное пространство. Поиск — нахождение ближайших векторов через ANN (Approximate Nearest Neighbor).

Cross-encoder (reranking): принимает пару запрос+документ, выдаёт relevance score. Медленнее (O(N) против O(log N)), но точнее. Применяется для переранжирования топ-K результатов bi-encoder.

Комбинация bi-encoder (retrieve) + cross-encoder (rerank) — стандарт production-систем.

Модели для русского языка

from sentence_transformers import SentenceTransformer, CrossEncoder

# Bi-encoder
bi_encoder = SentenceTransformer("cointegrated/rubert-tiny2")
# Для лучшего качества: "sbert-base-ru-mean-tokens"

# Cross-encoder
cross_encoder = CrossEncoder("cross-encoder/ms-marco-MiniLM-L-6-v2")  # английский
# Для русского: "DiTy/cross-encoder-russian-msmarco"

Векторное хранилище и индекс

Qdrant — рекомендуется для production:

from qdrant_client import QdrantClient
from qdrant_client.models import Distance, VectorParams, PointStruct

client = QdrantClient("localhost", port=6333)
client.create_collection(
    collection_name="documents",
    vectors_config=VectorParams(size=312, distance=Distance.COSINE),
)

# Индексирование
embeddings = bi_encoder.encode(documents, batch_size=64, show_progress_bar=True)
client.upload_points("documents", [
    PointStruct(id=i, vector=emb.tolist(), payload={"text": doc})
    for i, (emb, doc) in enumerate(zip(embeddings, documents))
])

FAISS — для in-memory индексов, быстрый, не требует внешнего сервиса:

import faiss
index = faiss.IndexFlatIP(312)  # Inner Product (cosine after normalization)
faiss.normalize_L2(embeddings)
index.add(embeddings)

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

Семантический + BM25 — лучше обоих по отдельности:

# BM25 компонент (Elasticsearch или rank_bm25)
from rank_bm25 import BM25Okapi
bm25 = BM25Okapi([doc.split() for doc in corpus])

# Семантический компонент
semantic_scores = cosine_similarity([query_emb], doc_embeddings)[0]

# RRF (Reciprocal Rank Fusion)
def rrf(bm25_ranks, semantic_ranks, k=60):
    scores = {}
    for rank, idx in enumerate(bm25_ranks):
        scores[idx] = scores.get(idx, 0) + 1/(k + rank)
    for rank, idx in enumerate(semantic_ranks):
        scores[idx] = scores.get(idx, 0) + 1/(k + rank)
    return sorted(scores, key=scores.get, reverse=True)

Query expansion и preprocessing

Качество поиска зависит от обработки запроса:

  • Spell correction: пользователи делают опечатки
  • Synonym expansion: «ДМС» → «добровольное медицинское страхование»
  • Query rewriting через LLM: «где купить ноут» → «ноутбук купить интернет-магазин»

Метрики качества

  • NDCG@10: нормализованный дисконтированный кумулятивный выигрыш
  • MAP (Mean Average Precision): средняя precision по всем запросам
  • MRR (Mean Reciprocal Rank): обратный ранг первого релевантного результата

Для оценки нужен набор запросов с размеченной релевантностью (qrels). Можно создать автоматически: GPT-4o генерирует вопросы для каждого документа, документ — «золотой» ответ.

Производительность

Qdrant с HNSW индексом: < 10ms на запрос при 1M векторов. FAISS IndexIVFFlat: < 5ms при 10M векторов. Bottleneck обычно — эмбеддинг запроса, не поиск.