Skip to content
 
 

Latest commit

 

History

6 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

RAG Data Preparation Pipeline

Подготовка данных для RAG без chunking, embeddings и vector DB.

Пайплайн делает только подготовительные этапы:

  1. загрузка источников через LlamaIndex SimpleDirectoryReader;
  2. парсинг pdf, txt, html через Unstructured и строковый парсинг csv через структурированный CSV parser;
  3. очистка текста regex-правилами;
  4. нормализация Unicode/регистра и sentence statistics через spaCy;
  5. дедупликация exact hash + near-duplicate MinHash LSH через datasketch;
  6. структурирование в text + metadata и LlamaIndex Document;
  7. экспорт в JSON и JSONL;
  8. orchestration через Prefect и логирование артефактов/метрик в MLflow.

Выходной формат подготовлен для следующих этапов RAG: chunking, embeddings и vector DB. Эти этапы здесь намеренно не реализованы, но metadata уже содержит lineage, hierarchy и quality signals, чтобы downstream-процессы могли сохранять связь чанков с исходниками и элементами.

Быстрый запуск

Зависимости и локальный пакет уже установлены в глобальный Python. Запуск из корня проекта:

rag-prep --config config/default.yaml

Прямой запуск без Prefect, но с теми же классами этапов:

rag-prep --config config/default.yaml --no-prefect

Результаты пишутся в data/prepared/:

  • documents.json
  • documents.jsonl
  • manifest.json

MLflow tracking по умолчанию пишется в mlruns/. В manifest.json сохраняются параметры запуска, числовые счётчики и диагностический блок parse_failures для файлов, которые не удалось разобрать при parser.fail_on_error: false. Файлы результата пишутся через временный файл и атомарную замену, поэтому частично записанный JSON/JSONL не остаётся на месте целевого файла.

Установка

Проект рассчитан на установку в текущий глобальный Python, без создания отдельного окружения. Команды нужно выполнять из корня проекта.

Обновить зависимости:

python -m pip install --upgrade -r requirements.txt

Установить локальный пакет в editable-режиме, чтобы команда rag-prep была доступна после изменения исходников:

python -m pip install -e . --no-deps

Флаг --no-deps уместен, если зависимости уже установлены через requirements.txt. Если проект переносится на новую машину, сначала ставится requirements.txt, затем локальный пакет.

Проверить целостность глобального окружения:

python -m pip check

На Windows для Unstructured дополнительно установлен python-magic-bin, чтобы unstructured.partition.auto корректно определял типы файлов. Для OCR-режимов PDF могут понадобиться системные Tesseract и Poppler, но для текстовых PDF достаточно parser.strategy: fast. Команда rag-prep перед запуском Prefect выставляет NO_PROXY=*, чтобы локальный временный сервер Prefect не ломался из-за системных proxy-настроек. Импорт rag_prep.flow сам по себе переменные окружения не меняет.

Примеры входных данных

В data/raw/ лежат русскоязычные примеры, похожие на реальные документы организации:

  • sample.txt - регламент обработки заявок в ИТ-службе;
  • sample.html - инструкция по передаче документов в электронный архив;
  • sample.csv - табличный реестр этапов service desk, где каждая строка становится отдельным элементом, а в конце есть exact и near-дубли для проверки дедупликации;
  • sample.pdf - памятка по подготовке комплекта документов к архивированию.

Эти файлы нужны для smoke-проверки пайплайна. Для своих данных можно заменить содержимое data/raw/ или указать другой paths.input_dir в config/default.yaml.

Формат результата

Каждая запись:

{
  "text": "...",
  "metadata": {
    "id": "stable-document-id",
    "source": "absolute/path/to/file",
    "section": "full_document",
    "file_name": "sample.txt",
    "file_type": "txt",
    "source_hash": "...",
    "text_hash": "...",
    "parent_ids": ["source-id"],
    "origin_element_ids": ["element-id-1", "element-id-2"],
    "lineage": {
      "source_id": "source-id",
      "source_hash": "...",
      "origin_element_ids": ["element-id-1", "element-id-2"],
      "element_range": [0, 3],
      "pipeline_stage": "prepared_document"
    },
    "hierarchy": {
      "section_path": ["Раздел", "Подраздел"],
      "section_depth": 2,
      "document_order": 0
    },
    "element_start": 0,
    "element_end": 3,
    "element_types": ["NarrativeText", "Title"],
    "page_number": null,
    "char_count": 100,
    "word_count": 16,
    "sentence_count": 2,
    "pipeline_run_id": "...",
    "parsed_at": "...",
    "extra": {}
  }
}

Downstream Readiness

Поля, которые нужны следующим этапам без реализации самих этапов:

  • metadata.id - стабильный идентификатор подготовленного документа;
  • metadata.parent_ids - родительские сущности, сейчас это идентификатор source;
  • metadata.origin_element_ids - элементы парсинга, из которых собран документ;
  • metadata.lineage - цепочка source -> parsed elements -> prepared document;
  • metadata.hierarchy.section_path - путь секции для будущего document tree;
  • metadata.hierarchy.document_order - порядок документа внутри исходника;
  • metadata.extra.quality - диагностические scores для boilerplate/OCR garbage/menu leftovers.

Quality scoring не удаляет документы сам по себе. Он даёт сигнал будущим этапам chunking/retrieval, чтобы можно было фильтровать мусор, расследовать retrieval misses и объяснять происхождение ответа.

Конфигурация

Основные параметры находятся в config/default.yaml:

  • paths.input_dir и paths.output_dir;
  • loader.allowed_extensions;
  • parser.strategy: fast, auto, hi_res, ocr_only;
  • parser.fail_on_error: false пропускает проблемный файл и пишет failure в manifest, true останавливает пайплайн;
  • cleaning.drop_patterns для удаления boilerplate;
  • normalization.spacy_language;
  • deduplication.threshold, num_perm, shingle_size;
  • structuring.group_by_section;
  • logging.mlflow_enabled.

Относительные пути в paths.* считаются относительно корня проекта, если конфиг лежит в папке config/. Если конфиг расположен в другой папке, относительные пути считаются относительно папки этого YAML-файла.

Для OCR PDF на Windows могут дополнительно понадобиться системные Tesseract/Poppler. Для текстовых PDF используется strategy: fast.

RAG Chunking Pipeline

Второй пайплайн делает только chunking подготовленных документов. Он не считает embeddings, не пишет vector DB и не запускает retrieval.

Вход берётся из результата первого пайплайна:

  • data/prepared/documents.jsonl - структурированные документы text + metadata;
  • lineage, hierarchy, source metadata и quality signals переносятся в каждый чанк.

Выход пишется в data/chunks/:

  • chunks.json
  • chunks.jsonl
  • manifest.json

Стек Chunking

  • LlamaIndex SentenceSplitter - основной semantic-aware splitter, который старается не резать текст внутри предложений;
  • LlamaIndex TokenTextSplitter - альтернативная стратегия для строгого token-based режима;
  • tiktoken - подсчёт токенов под text-embedding-3-small;
  • Pydantic - схема чанка и metadata;
  • Prefect - воспроизводимый orchestration;
  • MLflow - параметры запуска, метрики размера чанков и артефакты экспорта.

Логика Chunking

Пайплайн не режет весь документ одной плоской строкой. Он работает в таком порядке:

  1. читает PreparedDocument из первого пайплайна;
  2. восстанавливает semantic blocks внутри секции по границам абзацев/элементов, которые были сохранены как \n\n;
  3. упаковывает блоки в chunk до chunk_size;
  4. применяет chunk_overlap только внутри текущей секции;
  5. режет LlamaIndex splitter'ом только oversized-блок, который сам больше token budget;
  6. считает chunk-level quality signals и сохраняет span metadata.

Так чанки не пересекают границы prepared document/section, а table/list/code-like блоки и повторяющиеся абзацы получают стабильные semantic_block_ids. Для обычных block-aware чанков offsets берутся из заранее посчитанных block spans, без глобального text.find() по всему документу. Если fallback splitter не сможет точно найти подстроку для oversized-блока, чанк получит offset_strategy с estimated_*, warning в логах и отдельный estimated_offsets_count в validation.

Запуск Chunking

Перед запуском chunking должен существовать файл data/prepared/documents.jsonl.

Через Prefect:

rag-prep chunk --config config/chunking.yaml

Прямой запуск без Prefect:

rag-prep chunk --config config/chunking.yaml --no-prefect

Старые команды подготовки данных остаются рабочими:

rag-prep prepare --config config/default.yaml
rag-prep prepare --config config/default.yaml --no-prefect

Legacy-форма тоже сохранена:

rag-prep --config config/default.yaml

Конфигурация Chunking

Параметры находятся в config/chunking.yaml:

  • paths.input_jsonl - JSONL из первого пайплайна;
  • paths.output_dir - директория экспорта чанков;
  • chunking.strategy - sentence или token;
  • chunking.chunk_size - целевой размер чанка в токенах;
  • chunking.chunk_overlap - overlap в токенах, должен быть меньше chunk_size;
  • chunking.tokenizer_model - модель токенизатора, по умолчанию text-embedding-3-small;
  • chunking.embedding_model - модель будущих embeddings, записывается в metadata, сами embeddings не считаются;
  • chunking.preserve_section_boundaries - не смешивать разные prepared documents/sections;
  • chunking.preserve_block_boundaries - сначала упаковывать semantic blocks и резать только oversized-блоки;
  • chunking.max_chunk_tokens - validation guardrail перед embeddings;
  • chunking.min_quality_score - порог для диагностического счётчика low-quality chunks;
  • chunking.fail_on_validation_error - останавливать пайплайн при пустых, слишком маленьких, слишком больших, low-quality чанках, estimated offsets или проблемах lineage.

Формат Чанка

Каждая запись в chunks.jsonl готова к следующему embeddings-пайплайну:

{
  "text": "...",
  "metadata": {
    "id": "stable-chunk-id",
    "document_id": "prepared-document-id",
    "source": "absolute/path/to/file",
    "section": "Раздел",
    "position": 0,
    "chunk_start_char": 0,
    "chunk_end_char": 430,
    "chunk_token_count": 128,
    "chunk_size": 220,
    "chunk_overlap": 40,
    "chunking_strategy": "sentence",
    "tokenizer_model": "text-embedding-3-small",
    "embedding_model": "text-embedding-3-small",
    "semantic_block_ids": ["semantic-block-id-1"],
    "semantic_block_start": 0,
    "semantic_block_end": 0,
    "offset_strategy": "semantic_block_span",
    "parent_ids": ["prepared-document-id"],
    "origin_element_ids": ["element-id-1"],
    "lineage": {
      "document_id": "prepared-document-id",
      "chunk_id": "stable-chunk-id",
      "chunk_position": 0,
      "semantic_block_ids": ["semantic-block-id-1"],
      "semantic_block_range": [0, 0],
      "pipeline_stage": "chunk"
    },
    "hierarchy": {
      "section_path": ["Раздел"],
      "document_order": 0,
      "chunk_position": 0,
      "semantic_block_count": 1,
      "semantic_block_range": [0, 0]
    },
    "source_hash": "...",
    "document_text_hash": "...",
    "text_hash": "...",
    "file_name": "sample.txt",
    "file_type": "txt",
    "quality": {
      "token_density": 0.37,
      "language_confidence": 0.99,
      "ocr_noise_score": 0.0,
      "structure_score": 0.82,
      "unique_token_ratio": 0.74,
      "semantic_block_count": 1,
      "is_low_quality_chunk": false
    },
    "chunked_at": "..."
  }
}

position, chunk_start_char, chunk_end_char, semantic_block_ids, parent_ids, origin_element_ids и lineage нужны для отладки retrieval misses, hallucinations и обратной трассировки ответа к исходному документу. embedding_model фиксируется заранее, чтобы следующий этап мог проверить совместимость чанков с выбранной моделью embeddings.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages