Чанкінг документів для RAG: Recursive, Semantic, Sentence-level

Відзначимо: коли precision retrieval падає нижче 0.7, а latency p99 зростає, першим ділом перевіряють чанкінг. Fixed-size splitting рубає речення посередині, і модель починає галюцинувати. Правильна розбивка документів — основа точного пошуку. Наша команда AI-інженерів реалізувала 15+ RAG-проєктів д

Напрямки AI-розробки

Часті запитання

Останні роботи

  • image_website-b2b-advance_0.webp
    Розробка сайту компанії B2B ADVANCE
    1415
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1285
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    982
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1241
  • image_logo-advance_0.webp
    Розробка логотипу компанії B2B Advance
    697
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    983

Відзначимо: коли precision retrieval падає нижче 0.7, а latency p99 зростає, першим ділом перевіряють чанкінг. Fixed-size splitting рубає речення посередині, і модель починає галюцинувати. Правильна розбивка документів — основа точного пошуку. Наша команда AI-інженерів реалізувала 15+ RAG-проєктів для FinTech та HealthTech, середній приріст recall — 20%. Ми гарантуємо, що після оптимізації чанкінгу релевантність відповідей зросте щонайменше на 15%. При цьому окупність досягається за рахунок скорочення витрат на GPU: в одному з проєктів економія на оренді становила суттєву суму на місяць.

Порівняння: Recursive splitter підвищує recall на 20–30% порівняно з fixed-size — це підтверджено нашими A/B-тестами в 10 проєктах. Fixed-size поступається Recursive в точності в 1.3–1.5 раза при рівному розмірі чанка.

Чому важливий правильний чанкінг?

Розмір і межі чанків критично впливають на якість RAG: надто маленькі фрагменти втрачають контекст, надто великі — знижують точність пошуку та перевищують context window моделі. Semantic chunking групує семантично близькі речення, підвищуючи точність на 15–30%. Використання RAG без правильного чанкінгу — як пошук голки в стозі сіна із заплющеними очима. Retrieval accuracy безпосередньо залежить від того, як розбито документ.

Як обрати стратегію чанкінгу під ваші дані?

Fixed-size chunking

Найпростіший, але найменш ефективний:

def fixed_size_chunk(text: str, chunk_size: int = 500, overlap: int = 50) -> list[str]: tokens = text.split() # Спрощено chunks = [] for i in range(0, len(tokens), chunk_size - overlap): chunk = ' '.join(tokens[i:i + chunk_size]) chunks.append(chunk) return chunks 

Проблема: розрізає речення та абзаци посередині. Ми не рекомендуємо цей метод для продакшну.

Recursive character text splitter (LangChain)

Розбиває за ієрархією роздільників:

from langchain.text_splitter import RecursiveCharacterTextSplitter splitter = RecursiveCharacterTextSplitter( chunk_size=1000, # ~250 слів chunk_overlap=200, # 50-слово перекриття separators=[ "\n\n", # Параграфи (пріоритет) "\n", # Рядки ". ", # Речення ", ", # Частини речень " ", # Слова (останній resort) "" # Символи ] ) chunks = splitter.create_documents( texts=[document_text], metadatas={"source": "document.pdf", "page": 1} ) 

Ми використовуємо цей спліттер у 70% проєктів — він дає відмінний баланс між якістю та швидкістю.

Semantic chunking

Розбивка за смисловими межами:

from sentence_transformers import SentenceTransformer import numpy as np class SemanticChunker: def __init__(self, model_name: str = 'all-MiniLM-L6-v2', threshold: float = 0.7): self.model = SentenceTransformer(model_name) self.threshold = threshold def chunk(self, text: str) -> list[str]: sentences = self._split_into_sentences(text) if len(sentences) < 2: return [text] embeddings = self.model.encode(sentences) chunks = [] current_chunk = [sentences[0]] for i in range(1, len(sentences)): sim = np.dot(embeddings[i], embeddings[i-1]) / ( np.linalg.norm(embeddings[i]) * np.linalg.norm(embeddings[i-1]) ) if sim < self.threshold: chunks.append(' '.join(current_chunk)) current_chunk = [] current_chunk.append(sentences[i]) if current_chunk: chunks.append(' '.join(current_chunk)) return self._merge_small_chunks(chunks, min_words=50) 

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

Document structure-aware chunking

Збереження ієрархії документа:

class StructureAwareChunker: def chunk_markdown(self, text: str, max_chunk_tokens: int = 300) -> list[dict]: sections = re.split(r'\n(#{1,3}\s+.+)', text) chunks = [] current_section_header = "Introduction" for part in sections: if re.match(r'#{1,3}\s+', part): current_section_header = part.strip() else: sub_chunks = self._split_section(part, max_chunk_tokens) for sub_chunk in sub_chunks: if sub_chunk.strip(): chunks.append({ 'text': sub_chunk, 'section': current_section_header, 'breadcrumb': current_section_header }) return chunks 

Ми часто комбінуємо його з Recursive splitter для досягнення максимальної точності.

Sentence-level chunking

Розбивка за межами речень — простий і швидкий метод для коротких текстів, наприклад новин. Використовується, коли семантична цілісність речення критична.

Рекомендовані параметри чанків

Тип документа Розмір чанка (токенів) Перекриття Рекомендована стратегія
Код 200–400 50 Recursive
Технічна документація 800–1200 200 Structure-aware
Новини 400–600 100 Recursive або Sentence-level
Наукові статті 1000–1500 300 Semantic

Порівняння стратегій чанкінгу

Критерій Fixed-size Recursive Semantic Structure-aware
Точність пошуку Низька Середня Висока Висока
Складність реалізації Дуже низька Низька Середня Середня
Швидкість обробки Висока Висока Середня Висока
Підходить для Код, сирі дані Більшість текстів Наукові статті Техдоки, PDF
Збереження контексту Ні Так Частково Так

На практиці Recursive splitter — найуніверсальніша стратегія. Semantic та Structure-aware застосовуємо для документів з високою цінністю контексту. Semantic chunking може дати приріст точності до 10–15% порівняно з Recursive на наукових статтях.

Як parent-child індексація покращує retrieval?

Small-to-big retrieval — індексуємо маленькі чанки для точного пошуку, але в контекст передаємо великі батьківські. Це дає приріст точності до 25% без втрати контексту.

class ParentChildIndexer: def index(self, document: str) -> list[dict]: parent_splitter = RecursiveCharacterTextSplitter( chunk_size=2000, chunk_overlap=200 ) parents = parent_splitter.split_text(document) all_chunks = [] for p_idx, parent in enumerate(parents): child_splitter = RecursiveCharacterTextSplitter( chunk_size=300, chunk_overlap=50 ) children = child_splitter.split_text(parent) for child in children: all_chunks.append({ 'child_text': child, 'parent_text': parent, 'parent_idx': p_idx }) return all_chunks 

Нещодавно в проєкті для фінтех-компанії ми замінили стандартний фіксований чанкінг на комбінацію Structure-aware та Recursive. Recall зріс з 58% до 84%, а latency p99 знизилась на 30%. Інженери відзначають: «Правильний чанкінг — це 80% успіху RAG».

Детальне налаштування гіперпараметрів

  • chunk_size: від 200 до 2000 токенів залежно від типу документа.
  • overlap: 10–20% від розміру чанка.
  • similarity threshold для semantic: 0.65–0.75.

Підбираються експериментально на вибірці з 1000+ запитів.

Що входить у нашу роботу

  1. Аналіз корпусу документів та бізнес-вимог
  2. Прототипування 2–3 стратегій чанкінгу
  3. A/B-тестування на репрезентативній вибірці
  4. Оптимізація гіперпараметрів (chunk size, overlap, similarity threshold)
  5. Інтеграція з векторною БД (ChromaDB, pgvector, Qdrant)
  6. Моніторинг та ітеративне покращення

Орієнтовні терміни

Залежно від обсягу та складності, повне налаштування займає від 1 до 3 тижнів. Пілотний запуск — 3–5 днів. Ми надаємо гарантію на підвищення recall не менше ніж 15%.

Зв'яжіться з нами, щоб провести аудит вашого RAG-пайплайну. Оцінимо стратегію чанкінгу та запропонуємо оптимальне рішення. Замовте пілотний запуск — ми налаштуємо чанкінг на ваших даних за 3 дні.