Більшість команд будують свою першу систему пошуку (retrieval system) однаково: розбивають кожен документ на фіксовані фрагменти (chunks) по 512 токенів, завантажують їх у векторну базу даних і сподіваються, що модель ембедінгу (embedding model) зробить усю важку роботу. Ця надія допоможе вам пройти демо-показ. Але вона не витримає зіткнення з реальними користувачами.

У реальних умовах юридичний контракт розвалюється, коли ви відриваєте пункт про відповідальність від його винятків. Документація API стає марною, коли приклад коду відокремлюється від сигнатури функції. Стрічка підтримки клієнтів перетворюється на шум, коли ви вириваєте одну скаргу з контексту розмови. Проблема рідко полягає в мовній моделі, яка стоїть наприкінці конвеєра. Проблема в тому, чим ви її годуєте.

Ми вивчили це на власному гіркому досвіді. Наш початковий рівень пошуку виглядав стандартно, але працював нестабільно. Тому ми перебудували його навколо простої ідеї: ставитися до пошуку як до вимірюваної інфраструктури, а не як до магії. Ось що саме змінилося і як ми підвищили повноту пошуку (recall) до 95 відсотків, одночасно скоротивши затримку (latency) на 95-му перцентилі з 850 мс до 320 мс.

Пастка фіксованих фрагментів

Однакову кількість токенів легко кодувати та легко пояснювати. Ця зручність приховує базову істину: документи мають структуру. Коли ви ігноруєте цю структуру, ви знищуєте корисний сигнал.

Розглянемо десятисторінкову генеральну угоду про надання послуг. Фіксований зріз у 512 токенів припаде на середину зобов'язання, відокремивши пункт від тієї самої таблиці обмежень, яка його обмежує. Тоді крок пошуку поверне лише половину думки, а генератор довигадає решту (галюцинує). У документації API занадто великий фрагмент розмиває ембедінг шаблонними заголовками, ховаючи конкретний метод, який потрібен розробнику. У тікетах підтримки фіксоване вікно сприймає розмову як «мішок речень», позбавляючи її взаємодії, яка пояснює, що саме пішло не так.

Ми перестали сприймати розмір фрагмента як гіперпараметр, який ми підбираємо навмання. Ми почали ставитися до нього як до процесу зіставлення типу документа з його внутрішньою інформаційною архітектурою.

Підбирайте метод фрагментації під дані

Рішення полягає не в одному ідеальному розмірі фрагмента. Рішення — у трьох різних стратегіях, налаштованих під три різні типи даних.

Юридичні документи тепер проходять через рекурсивне розбиття. Алгоритм спочатку шукає найбільші природні межі — розділи, потім підрозділи, потім нумеровані пункти — і вдається до менших фрагментів лише за потреби. Це дозволяє тримати пункт про припинення дії договору разом із умовами його збереження. Крок пошуку бачить цілісні логічні одиниці, що різко зменшує спокусу моделі вигадувати відсутні винятки.

Документація API та коду отримує фрагментацію з урахуванням структури. Заголовки Markdown, блоки коду та таблиці параметрів розбираються як атомарні одиниці. Ми не розбиваємо код всередині блоку коду. Ми тримаємо docstrings поруч із їхніми сигнатурами. У результаті запит конкретного методу класу повертає повний контекст, необхідний розробнику: опис, типізовані параметри та робочий приклад.

Дані підтримки та розмовні дані використовують семантичну фрагментацію. Замість підрахунку токенів ми шукаємо зміни теми або наміру. Якщо клієнт описує помилку в третьому повідомленні, а в сьомому вставляє stack trace, ми розбиваємо дані за змістом, а не за індексом повідомлення. Тоді рівень пошуку повертає повну історію проблеми, а не окреме речення без контексту.

Чому одного векторного пошуку недостатньо

Навіть ідеальні фрагменти програють у чистому векторному пошуку. Щільні ембедінги (dense embeddings) чудово вловлюють значення та синонімічність, але вони відомі своєю неточністю щодо точних рядків. Якщо інженер шукає конкретний код помилки ERR_CONNECTION_REFUSED, векторна схожість може повернути десяток концептуально схожих варіантів і пропустити точне співпадіння, яке заховане на чотирнадцятому місці.

Пошук за ключовими словами з використанням BM25 має протилежну проблему. Він знаходить точні токени, але не враховує семантичний намір. Користувач, який запитує «чому моя база даних не працює», ніколи не отримає документ із фразою «усунення несправностей при тайм-аутах з'єднання».

Тепер ми використовуємо обидва методи. Результати векторного та ключового пошуку подаються в Reciprocal Rank Fusion, який змішує два ранжованих списки без необхідності калібрування оцінок. Потім об'єднаний список проходить через cross-encoder reranker. Переранжувальник (reranker) працює повільніше за початковий пошук, але він набагато точніший, оскільки оцінює релевантність запиту та документа безпосередньо, а не через стиснуті ембедінги. Тільки цей гібридний конвеєр підвищив нашу повноту пошуку на 15 відсотків.

Виправлення поганих запитів перед їх потраплянням до індексу

Users do not write ideal search queries. They paste truncated log lines. They type “it’s broken.” They use jargon your documentation never adopted. If you trust the raw query, you are trusting noise.

We now expand every incoming query into three to five variations before sending them to the retrieval layer. One variation might be a direct paraphrase. Another might be a hypothetical ideal document title. A third strips away conversational filler and isolates technical keywords. Each variant gets embedded and searched. We then deduplicate and merge the candidate pools.

This is not free. Those extra embedding calls cost money and add a few milliseconds. But the effect on recall was dramatic: we moved from 78 percent to 96 percent by expanding queries before retrieval. Because better retrieval shrinks the generation window and grounds the model in correct context, we ended up saving money downstream. A slightly more expensive retrieval step is cheaper than a long, hallucinated generation step.

Stop Guessing. Start Searching.

Once we had the right chunking, hybrid retrieval, and query expansion in place, we still faced a combinatorial mess. Chunk size, chunk overlap, top-k retrieval depth, reranker cutoffs, and fusion weights all interact. A manual grid search would have taken weeks and still left us with a local maximum.

We switched to Bayesian optimization to explore the space. Rather than exhaustively testing every combination, the search algorithm maintains a belief over which configurations are likely to perform well and progressively narrows in on promising regions.

The output is not a single perfect setting. It is a Pareto frontier of choices. On one end, we have a lean configuration optimized for our high-throughput API support endpoint: fast inference, moderate recall, and the lowest possible latency. On the other end, we have an aggressive configuration for legal review: deeper retrieval, heavier reranking, and tighter overlap, trading milliseconds for thoroughness. Because the frontier is explicit, we can pick the right point for the product instead of pretending one size fits all.

What the Numbers Actually Look Like

These changes moved the system from a brittle prototype to a measured production pipeline.

Recall at ten improved from 78 percent to 95 percent. That means when the correct answer exists in our corpus, we find it nineteen times out of twenty.

Latency at the 95th percentile dropped from 850 ms to 320 ms. The hybrid stack sounds heavier on paper, but smarter indexing, smaller rerankers, and the ability to serve aggressive chunks only when needed made the whole system faster.

Hallucination rate—tracked by human annotators on a held-out golden dataset—fell from 12 percent to 3 percent. When the model receives complete, relevant context, it stops inventing facts.

Cost per query dropped from $0.008 to $0.005. Better retrieval means shorter, more focused LLM prompts and fewer recovery attempts. The extra embedding spend on query expansion is dwarfed by the savings in generation.

Build a Golden Dataset and Treat Retrieval Like Code

If you take one thing from this, it should be the discipline of measurement. We built a small golden dataset of real questions and verified answer locations. Before any change hits production, it runs against that dataset. Recall and latency are monitored in real time, not eyeballed in a notebook.

Retrieval is not a research demo. It is infrastructure. It deserves unit tests, regression benchmarks, and automated optimization just like the rest of your stack. Chunk by document structure, not by token superstition. Combine vector and keyword search with a reranker. Expand the queries your users actually write. Then let a search algorithm tune the knobs instead of your intuition.

The pipeline we described is not theoretical. You can read the original write-up here, and if you want to discuss retrieval engineering with a community that cares about this stuff, the GyaanSetu AI group is open.