Звичайний пошук по сайту видає статті лише якщо в них зустрічаються точні слова запиту. Користувач шукає «як оплатити» — не знаходить статтю «способи розрахунку». Семантичний пошук вирішує цю проблему: він розуміє сенс, а не рядки. Ми впроваджуємо такі системи для інтернет-магазинів, документації та порталів. Наш досвід — 5+ років у AI-пошуку та понад 10 успішних проєктів. Команда сертифікованих інженерів PostgreSQL. Зв'яжіться з нами, щоб обговорити ваш сценарій.
Чому семантичний пошук кращий за повнотекстовий? — реалізація AI пошуку
Повнотекстовий пошук (PostgreSQL tsvector, Elasticsearch) шукає за збігом слів. Семантичний — за змістом. Він перетворює текст у векторні ембеддинги — числові масиви з 768–3072 чисел, що кодують семантичну близькість. Тексти з близькими векторами семантично схожі. Це дає приріст точності релевантних результатів у 2–3 рази, тобто семантичний пошук кращий за повнотекстовий у 2-3 рази за точністю, особливо для довгих та розмовних запитів.
Як ми це робимо: стек і кейс
Для інтернет-магазину з 50 000 товарів ми впровадили гібридний пошук на базі OpenAI embeddings та pgvector. Результат: середній час відповіді 0,3 секунди, точність 92%. Вартість базового рішення від $5000.
Вибір моделі. Використовуємо text-embedding-3-small (1536 вимірів) — оптимальний баланс швидкості та якості. Для української та російської мов він дає чудові результати.
Векторна база даних. PostgreSQL з розширенням pgvector та HNSW-індексом:
CREATE EXTENSION vector;
CREATE TABLE content_chunks (
id BIGSERIAL PRIMARY KEY,
content_id BIGINT REFERENCES content(id),
chunk_text TEXT NOT NULL,
chunk_index INT,
embedding vector(1536),
metadata JSONB
);
CREATE INDEX ON content_chunks USING hnsw (embedding vector_cosine_ops)
WITH (m = 16, ef_construction = 64);
Індексація контенту. Розбиваємо текст на чанки по 400 токенів з перекриттям 50 слів, отримуємо ембеддинги через OpenAI API та зберігаємо в таблицю:
import OpenAI from 'openai';
const openai = new OpenAI();
async function indexContent(contentItem) {
const chunks = chunkText(contentItem.body, { maxTokens: 400, overlap: 50 });
const { data: embeddings } = await openai.embeddings.create({
model: 'text-embedding-3-small',
input: chunks,
});
// Зберігаємо в pgvector батчами по 100
for (let i = 0; i < chunks.length; i += 100) {
const batchChunks = chunks.slice(i, i + 100);
const batchEmbeds = embeddings.slice(i, i + 100);
await db.query(`
INSERT INTO content_chunks (content_id, chunk_text, chunk_index, embedding, metadata)
VALUES ($1, $2, $3, $4::vector, $5)
`, [contentItem.id, batchChunks, /* ... */]);
}
}
Пошук за змістом. Комбінуємо векторний та повнотекстовий пошук через RRF (Reciprocal Rank Fusion):
async function semanticSearch(query, { limit = 10, threshold = 0.7 } = {}) {
const { data: [{ embedding }] } = await openai.embeddings.create({
model: 'text-embedding-3-small',
input: query,
});
const results = await db.query(`
WITH semantic AS (
SELECT content_id, chunk_text,
1 - (embedding <=> $1::vector) AS score,
ROW_NUMBER() OVER (ORDER BY embedding <=> $1::vector) AS rank
FROM content_chunks
ORDER BY embedding <=> $1::vector
LIMIT 20
),
fulltext AS (
SELECT id AS content_id, body AS chunk_text,
ts_rank(to_tsvector('russian', body), plainto_tsquery('russian', $2)) AS score,
ROW_NUMBER() OVER (ORDER BY ts_rank(...) DESC) AS rank
FROM content
WHERE to_tsvector('russian', body) @@ plainto_tsquery('russian', $2)
LIMIT 20
)
SELECT COALESCE(s.content_id, f.content_id) AS id,
COALESCE(s.chunk_text, f.chunk_text) AS text,
(COALESCE(1.0 / (60 + s.rank), 0) + COALESCE(1.0 / (60 + f.rank), 0)) AS rrf_score
FROM semantic s FULL OUTER JOIN fulltext f ON s.content_id = f.content_id
ORDER BY rrf_score DESC
LIMIT $3
`, [`[${embedding.join(',')}]`, query, limit]);
return results.rows;
}
Гібридний пошук: що це і навіщо?
Гібридний пошук об'єднує результати векторного та повнотекстового методів через RRF. Це компенсує слабкості кожного: векторний пошук знаходить за змістом, але може пропустити точне входження терміну; повнотекстовий — навпаки. Разом вони забезпечують високу релевантність навіть для складних запитів. Ми використовуємо цей підхід у всіх проєктах. Важливо: якісне впровадження потребує досвіду — наші інженери гарантують результат.
Вибір embedding-моделі для української/російської мови
Вибір моделі критичний. Multilingual-моделі (наприклад, Cohere) часто поступаються спеціалізованим на українській/російській. Ми тестували кілька варіантів і рекомендуємо:
| Модель | Розмірність | Якість для укр/рос | Швидкість | Вартість |
|---|---|---|---|---|
| OpenAI text-embedding-3-small | 1536 | відмінно | висока | низька |
| OpenAI text-embedding-3-large | 3072 | чудово | середня | середня |
| Cohere embed-multilingual-v3 | 1024 | добре | висока | середня |
| BGE-M3 (self-hosted) | 1024 | добре | залежить від GPU | безкоштовно |
Source: OpenAI Embeddings documentation
Процес роботи
- Аудит контенту — виділяємо типи текстів, розмір, частоту оновлень.
- Вибір моделі та векторної бази даних — визначаємо компроміс між якістю та бюджетом.
- Налаштування індексації контенту — чанкінг, конфігурація індексу, batch-обробка.
- Розробка API пошуку — endpoint з параметрами: запит, фільтри, пагінація.
- Створення UI — пошуковий рядок, снипети з підсвіткою, прогресивне завантаження.
- Тестування — A/B-тест з поточним пошуком, моніторинг метрик.
- Деплой та моніторинг — алерти по затримках, запити без результатів.
Приклад реалізації інкрементальної переіндексації
Щоб не переіндексовувати всі документи при кожній зміні, використовуємо тригери на таблиці контенту та чергу задач (Bull/PGBoss). При додаванні або оновленні запису ставимо задачу на переіндексацію лише цього документа. Фоновий воркер забирає задачу, отримує ембеддинги та оновлює відповідний чанк. Це дозволяє підтримувати актуальність без повної переіндексації навіть при тисячах змін на день.Строки орієнтовно
| Етап | Строк (днів) |
|---|---|
| Семантичний пошук по 10К документів (pgvector) | 4–5 |
| Гібридний пошук (вектор + повнотекст) | +1–2 |
| Переранжування через Cohere Rerank | +1 |
| UI з підсвіткою та аналітикою | +2–3 |
| Інкрементальна переіндексація | +1–2 |
Разом: від 8 до 12 робочих днів. Вартість розраховується індивідуально, орієнтовно від $5000.
Що входить в роботу
- Повна документація архітектури (схема БД, API специфікація, інструкція з розгортання).
- Вихідний код під ключ з CI/CD.
- Доступ до репозиторію, дампу даних, моніторинг-дашборду.
- Навчання команди (2–3 години).
- Технічна підтримка 3 місяці.
Типові помилки при впровадженні
- Неправильний чанкінг: занадто довгі чанки (>1000 токенів) знижують точність, занадто короткі — втрачають контекст. Оптимум: 300–500 токенів з перекриттям 50–100.
- Вибір моделі без урахування мови: multilingual-моделі (наприклад, Cohere) часто працюють гірше на українській/російській, ніж спеціалізовані OpenAI embeddings.
- Відсутність переранжування: навіть хороший векторний пошук іноді видає нерелевантні топ-результати. Cross-encoder rerank виправляє це.
Хочете впровадити семантичний пошук? Зв'яжіться з нами — обговоримо ваш проєкт. Отримайте консультацію за вашим сценарієм використання.







