Реалізація семантичного пошуку по текстових документах
Стандартний повнотекстовий пошук (BM25) не справляється з синонімами, перефразуваннями та помилками. Запит «як підвищити мотивацію команди» знаходить документи про «методи управління персоналом» — без жодного збігу за словами. Це принципово інша архітектура, що потребує векторних представлень та ANN-індексів. Ми реалізуємо такі системи під ключ, починаючи з аудиту даних і закінчуючи деплоєм у продакшен. Вартість таких проектів варіюється від $10,000 до $50,000 залежно від обсягу корпусу та складності. Докладніше про концепцію можна прочитати в статті про семантичний пошук.
Семантичний пошук: архітектура та порівняння моделей
Bi-encoder — основний робочий режим: окремі моделі кодують запит і документи в загальний векторний простір. Пошук зводиться до знаходження найближчих векторів через ANN (Approximate Nearest Neighbor). Cross-encoder працює на етапі reranking: приймає пару «запит+документ» і видає точний score релевантності. Bi-encoder швидший за cross-encoder в 10 разів (10 мс проти 100 мс на 1M документів), але cross-encoder перевершує bi-encoder в точності в 1.2 рази за NDCG@10. Комбінація bi-encoder (retrieve) + cross-encoder (rerank) — стандарт production-систем. Згідно з роботою Reimers & Gurevych (2019), такий дует значно перевершує кожен з методів окремо.
Порівняємо основні підходи до ембеддінгів:
| Параметр | Bi-encoder | Cross-encoder |
|---|---|---|
| Швидкість на 1M документів | <10 мс | >100 мс (для топ-100) |
| Точність (NDCG@10) | 0.75-0.85 | 0.90-0.95 |
| Застосування | Первинний пошук | Переранжування топ-K |
Яку модель ембеддінгів вибрати?
Для російської мови ми використовуємо cointegrated/rubert-tiny2 як baseline — швидкий, компактний (312-вимірний вектор). Для максимальної якості — intfloat/multilingual-e5-large або sbert-base-ru-mean-tokens (768-вимірний вектор). Fine-tuning на ваших даних дає приріст 5-10% за NDCG. Ми підбираємо модель під обсяг корпусу та latency requirements (p99 до 100 мс).
from sentence_transformers import SentenceTransformer, CrossEncoder # Bi-encoder bi_encoder = SentenceTransformer("cointegrated/rubert-tiny2") # Для лучшего качества: "intfloat/multilingual-e5-large" # Cross-encoder cross_encoder = CrossEncoder("cross-encoder/ms-marco-MiniLM-L-6-v2") # Для русского: "DiTy/cross-encoder-russian-msmarco" Qdrant vs FAISS: що обрати для продакшену?
Qdrant — production-grade, підтримує гібридний пошук, фільтри, реплікацію. Рекомендуємо для корпоративних рішень. FAISS — in-memory індекс, не потребує окремого сервісу. Ідеальний для прототипів та малих корпусів (< 1M векторів).
| Характеристика | Qdrant | FAISS |
|---|---|---|
| Тип | Зовнішня БД | In-memory індекс |
| Гібридний пошук | Вбудований | Потребує доопрацювання |
| Latency p99 (1M векторів) | < 10 мс | < 5 мс |
| Масштабування | Кластер/шардинг | Однопотоковий |
Приклад індексування в Qdrant:
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)) ]) Гібридний пошук: переваги та реалізація
Семантичний пошук + BM25 перевершують кожен з методів окремо. BM25 ловить точні збіги (номери, унікальні терміни), а ембеддінги — смислові близькі. Гібридний підхід покращує NDCG@10 у 2-3 рази порівняно з чистим BM25. Ми використовуємо RRF (Reciprocal Rank Fusion) для об'єднання результатів.
from rank_bm25 import BM25Okapi bm25 = BM25Okapi([doc.split() for doc in corpus]) semantic_scores = cosine_similarity([query_emb], doc_embeddings)[0] 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) Оцінка якості пошуку за допомогою метрик
Для оцінки якості використовуємо метрики ранжування: NDCG@10 (нормалізований дисконтований кумулятивний виграш з урахуванням порядку), MAP (середня точність за всіма запитами), MRR (обернений ранг першого релевантного результату). Для обчислення потрібен qrels (набір запитів з релевантністю). Ми автоматизуємо його створення: LLM генерує питання для кожного документа, сам документ — «золотий» відповідь. Це дає репрезентативну вибірку для метрик.
Процес впровадження та терміни
- Аудит даних: обсяг, формат, мова, специфічні терміни. Попередня обробка включає очищення, лематизацію та чанкінг (розмір чанка ~512 токенів з overlap 128).
- Вибір архітектури: bi-encoder + cross-encoder, гібрид, кастомна модель. Для великих корпусів (>10M документів) застосовуємо кластеризацію Qdrant з шардуванням.
- Розробка пайплайну: чанкінг, ембеддінг, індексування з моніторингом latency p99.
- Налаштування та деплой: кластер Qdrant (Helm-чарти), A/B тестування, канарейковий rollout.
- Передача документації, навчання команди (2 сесії по 2 години), гарантія 3 місяці.
Терміни: від 2 тижнів для прототипу, від 2 місяців для production-рішення. Вартість розраховується індивідуально — зв'яжіться з нами для безкоштовної оцінки.
Що входить у результат
- Розгорнута архітектурна документація.
- Вихідний код пайплайну з коментарями.
- Інтеграція з вашою інфраструктурою (Elasticsearch, БД, хмари).
- Деплой з Helm-чартами та CI/CD.
- Навчання команди (2 сесії по 2 години).
- Підтримка на етапі промислової експлуатації (1 місяць).
Чому довіряють нам
Нам довіряють завдяки 5-річному досвіду та 20+ реалізованим проектам. Всі рішення покриті unit-тестами та benchmarks. Наші інженери — автори open-source інструментів для ембеддінгів та ANN. Отримайте консультацію щодо вашого проекту — ми оцінимо завдання за 1 день. Замовте пілотний проект, щоб побачити результат на ваших даних.







