Індексація документів для RAG (PDF, DOCX, HTML, Markdown)

Типовий сценарій: клієнт завантажує 500-сторінковий PDF з таблицями та багатоколонковою версткою, а RAG-система повертає биті відповіді — текст із колонок зливається, заголовки втрачаються, таблиці перетворюються на кашу. Ми знаємо, як цього уникнути: якісний парсинг — фундамент будь-якого RAG-пайпл

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

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

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

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

Типовий сценарій: клієнт завантажує 500-сторінковий PDF з таблицями та багатоколонковою версткою, а RAG-система повертає биті відповіді — текст із колонок зливається, заголовки втрачаються, таблиці перетворюються на кашу. Ми знаємо, як цього уникнути: якісний парсинг — фундамент будь-якого RAG-пайплайну. За 5 років ми обробили понад 200 проєктів різної складності та переконалися: економія на парсингу обертається втратою точності відповідей.

Чому якість парсингу визначає успіх RAG?

Сучасні RAG-системи, такі як ті, що будуються на LangChain або LlamaIndex, вимагають чистого структурованого тексту для коректного чанкування та ембеддингу. Якщо на вході — «каша», то й пошук буде хаотичним. Дослідження показують, що до 30% помилок RAG викликані саме поганим парсингом вихідних документів. Ми використовуємо стек: PyTorch для кастомних моделей, pdfplumber для PDF, BeautifulSoup і markdownify для HTML, python-docx для DOCX.

Які формати ми підтримуємо? — індексація документів для

Формат Складність парсингу Особливості
PDF Висока Таблиці, колонки, скани (OCR)
DOCX Середня Вбудовані таблиці, стилі
HTML Низька Сміттєві теги, скрипти
Markdown Низька Готові заголовки та списки

Як ми парсимо складні PDF?

Візьмемо реальний кейс: PDF з бухгалтерською звітністю — 200 сторінок, на кожній таблиця з фінансовими даними. Стандартні бібліотеки PyPDF2 або pdfminer втрачають межі комірок. Ми застосовуємо pdfplumber з кастомною постобробкою:

from pathlib import Path from dataclasses import dataclass @dataclass class ParsedDocument: text: str metadata: dict source_format: str page_count: int = None class DocumentParser: def parse(self, file_path: str) -> ParsedDocument: path = Path(file_path) ext = path.suffix.lower() if ext == '.pdf': return self._parse_pdf(file_path) elif ext in ['.docx', '.doc']: return self._parse_docx(file_path) elif ext in ['.html', '.htm']: return self._parse_html(file_path) elif ext in ['.md', '.markdown']: return self._parse_markdown(file_path) else: raise ValueError(f"Unsupported format: {ext}") def _parse_pdf(self, path: str) -> ParsedDocument: # Для складних PDF (з таблицями, колонками) — pdfplumber import pdfplumber with pdfplumber.open(path) as pdf: pages_text = [] for page in pdf.pages: # Збереження таблиць як markdown tables = page.extract_tables() text = page.extract_text() or "" for table in tables: table_md = self._table_to_markdown(table) text += f"\n\n{table_md}\n\n" pages_text.append(text) full_text = "\n\n---PAGE BREAK---\n\n".join(pages_text) return ParsedDocument( text=full_text, metadata={"source": path, "pages": len(pdf.pages)}, source_format="pdf", page_count=len(pdf.pages) ) def _parse_docx(self, path: str) -> ParsedDocument: from docx import Document doc = Document(path) elements = [] for element in doc.element.body: if element.tag.endswith('p'): # Параграф para = element style = para.style.name if hasattr(para, 'style') else '' text = element.text_content() if style.startswith('Heading'): level = int(style.split()[-1]) if style[-1].isdigit() else 1 elements.append('#' * level + ' ' + text) elif text.strip(): elements.append(text) elif element.tag.endswith('tbl'): # Таблиця table = self._extract_table_from_docx(element) elements.append(table) return ParsedDocument( text='\n\n'.join(elements), metadata={"source": path}, source_format="docx" ) def _parse_html(self, path: str) -> ParsedDocument: from bs4 import BeautifulSoup with open(path, 'r', encoding='utf-8') as f: soup = BeautifulSoup(f.read(), 'html.parser') # Видалення скриптів і стилів for tag in soup(['script', 'style', 'nav', 'footer', 'header']): tag.decompose() # Вилучення структурованого тексту from markdownify import markdownify text = markdownify(str(soup), heading_style="ATX") return ParsedDocument( text=text, metadata={"source": path, "title": soup.title.string if soup.title else ""}, source_format="html" ) 

Структуроване вилучення метаданих

class MetadataExtractor: def extract(self, doc: ParsedDocument) -> dict: metadata = doc.metadata.copy() # Вилучення заголовків для навігації headers = re.findall(r'^#{1,3}\s+(.+)$', doc.text, re.MULTILINE) metadata['headers'] = headers[:20] # Перші 20 заголовків # Вилучення дат date_pattern = r'\b\d{1,2}[./]\d{1,2}[./]\d{2,4}\b' dates = re.findall(date_pattern, doc.text) if dates: metadata['dates_mentioned'] = dates[:5] # Мова документа from langdetect import detect try: metadata['language'] = detect(doc.text[:1000]) except Exception: metadata['language'] = 'unknown' return metadata 

Підготовка до індексації

Після парсингу документи чанкуються (розбиваються на фрагменти), ембеддяться та завантажуються у векторну БД. Ключовий момент: збереження структурних маркерів (заголовки, номери сторінок) у метаданих чанків для забезпечення атрибуції джерела у відповідях RAG.

Для 1000-сторінкового PDF повний цикл (парсинг → чанкінг → ембеддинг → індексація): 5-15 хвилин при використанні OpenAI Embeddings API. Власні GPU на базі Triton Inference Server прискорюють ембеддинг у 2-3 рази.

Що входить у роботу?

  • Аудит документів: аналіз типів, обсягу, складності.
  • Розробка пайплайну: парсери, чанкер, ембеддер, завантажувач.
  • Інтеграція з векторною базою: Qdrant, ChromaDB, pgvector — на ваш вибір.
  • Тестування на метриках: recall@k, precision@k, latency p99.
  • Документація та навчання: передача коду, опис архітектури, навчання вашої команди.
  • Підтримка: 3 місяці гарантії на баги та адаптацію під нові формати.

Порівняння: готові сервіси проти кастомного рішення

Критерій Готові сервіси (наприклад, Unstructured.io) Наше кастомне рішення
Якість вилучення таблиць Середня (до 70%) Висока (95%+)
Підтримка рідкісних форматів Обмежена Будь-які формати під замовлення
Контроль над метаданими Мінімальний Повний контроль
Вартість на 10 000 сторінок ~$500/міс Одноразово + підтримка
Інтеграція з вашим стеком Через API Глибоке вбудовування

Процес роботи

  1. Аналітика: ви надсилаєте 2-3 зразки документів, ми оцінюємо складність і терміни.
  2. Проектування: обираємо стек (Hugging Face Embeddings, vLLM, тощо), проектуємо пайплайн.
  3. Реалізація: пишемо код парсерів та інтеграцію з вашою RAG-системою.
  4. Тестування: прогоняємо на ваших даних, підганяємо чанкінг та ембеддинги.
  5. Деплой: розгортаємо у вашій інфраструктурі (AWS, GCP, on-prem).

Орієнтовні терміни: від 2 тижнів до 2 місяців залежно від обсягу та складності. Вартість розраховується індивідуально під кожен проєкт.

Стратегії чанкування: як розбивка впливає на точність RAG

Вибір стратегії чанкування безпосередньо впливає на recall@5 у вашому RAG. Занадто великі чанки (2000+ токенів) знижують точність пошуку. Занадто малі (64 токени) втрачають контекст.

Перевірені стратегії:

  • Fixed-size з overlap: чанки по 512 токенів, перекриття 64 токени. Добре для однорідних текстів без складної структури.
  • Sentence window: чанк = речення + 2–3 речення по боках. Високий recall, підходить для FAQ.
  • Heading-based: розбивка за заголовками документа. Ідеально для технічної документації та нормативів.
  • Semantic chunking: нарізка за семантичними межами (SBERT cosine similarity). Найкраща якість, але потребує додаткових обчислень.

Ми тестуємо кілька стратегій на ваших документах і обираємо за метриками recall@5 та MRR.

Отримайте консультацію — надішліть зразки документів, і ми протягом 1 робочого дня дамо оцінку. Наш досвід: 200+ проєктів, 5 років на ринку, гарантія якості на кожен етап.