opencode/README_RAG.md
Кирилл Блинов c756a5766b Add RAG pipeline: LightRAG indexer, OpenCode API, VLM describer, and test tools
- Add rag_indexer.py: build LightRAG index from OCR with OpenCode API
- Add rag_query.py: query the knowledge graph
- Add vlm_describer.py: generate VLM descriptions via LM Studio
- Add test_model.py: quick check for LightRAG-compatible models
- Add run_pipeline.sh and run_pipeline.bat: full OCR → VLM → RAG pipeline
- Fix rapidocr import (rapidocr_onnxruntime)
- Fix process_any_pdf.py paths for cross-platform use
- Add .env.example, README_RAG.md, AGENTS.md
- Update .gitignore for outputs and secrets
2026-05-29 09:54:37 +03:00

10 KiB
Raw Blame History

RAG / LightRAG для анализа чертежей

Этот модуль добавляет Retrieval-Augmented Generation (RAG) поверх существующего OCR-pipeline. Он превращает распознанный текст из full_ocr_results.json в граф знаний и позволяет задавать вопросы по чертежам на естественном языке.

Архитектура

PDF -> PyMuPDF + RapidOCR -> full_ocr_results.json
                                    |
                                    v
                         rag_indexer.py (LightRAG)
                                    |
                                    v
                         lightrag_cache/ (граф знаний)
                                    |
                                    v
                         rag_query.py (вопросы)

Почему LightRAG?

  • Строит граф сущностей и отношений (этажи, оси, размеры, помещения)
  • Поддерживает 4 режима поиска: naive, local, global, hybrid
  • Для чертежей рекомендуется hybrid — объединяет локальный поиск по соседним сущностям и глобальный контекст проекта

Установка

# 1. Установите зависимости
pip install -r requirements.txt

# 2. Выберите LLM backend:

Вариант A: OpenAI (быстро, требует интернет)

export OPENAI_API_KEY="sk-..."
# или в Windows: set OPENAI_API_KEY=sk-...

Вариант B: Ollama (локально, приватно, требует RAM/VRAM)

# Установите Ollama: https://ollama.com

# Скачайте модели (пример)
ollama pull qwen2.5:14b      # LLM для ответов
ollama pull nomic-embed-text # embeddings

# Запустите сервер
ollama serve

Вариант C: LM Studio (локальный GUI, подходит для VLM)

# Установите LM Studio: https://lmstudio.ai
# 1. Загрузите VLM модель (например, qwen3-vl-4b) и запустите Local Server
# 2. Убедитесь, что сервер доступен: http://127.0.0.1:1234/v1

# Переменные окружения (при необходимости)
export LMSTUDIO_URL="http://127.0.0.1:1234/v1"
export LMSTUDIO_API_KEY="lm-studio"

Вариант D: OpenCode API (DeepSeek V4 Flash Free)

# Требуется API-ключ OpenCode (бесплатный тариф)
# 1. Получите ключ на https://opencode.ai
# 2. Экспортируйте переменные:

export OPENCODE_API_KEY="ваш-ключ"
export OPENCODE_URL="https://opencode.ai/zen/v1"  # опционально, уже задан по умолчанию

Использование

Шаг 1: OCR (если еще не сделано)

python process_any_pdf.py "Кронштадтский 16-18 НК1_ОСК (v3).pdf" output_kronshtadt

Результат: папка output_kronshtadt/ с full_ocr_results.json.

Шаг 2: Построение индекса

OpenAI:

python rag_indexer.py output_kronshtadt --backend openai --model gpt-4o-mini

Ollama (локально):

python rag_indexer.py output_kronshtadt --backend ollama --model qwen2.5:14b --embed-model nomic-embed-text

Результат: папка output_kronshtadt/lightrag_cache/ с графом знаний.

LM Studio (VLM для описаний):

# Сгенерировать VLM-описания PNG (локальная Qwen)
python vlm_describer.py output_kronshtadt --model qwen/qwen3-vl-4b

# Построить индекс через OpenCode API (LLM)
python rag_indexer.py output_kronshtadt --backend opencode --vlm-desc

OpenCode API (DeepSeek V4 Flash Free):

# Построение индекса через бесплатный DeepSeek
python rag_indexer.py output_kronshtadt --backend opencode --model opencode/deepseek-v4-flash-free

# С VLM-описаниями (локальная Qwen + бесплатный DeepSeek)
python rag_indexer.py output_kronshtadt --backend opencode --vlm-desc

Шаг 3 (опционально): VLM-описания через LM Studio

Если у вас в LM Studio загружена VLM модель (например, qwen3-vl-4b), можно сгенерировать текстовые описания PNG перед индексацией:

python vlm_describer.py output_kronshtadt --model qwen/qwen3-vl-4b

Это создаст output_kronshtadt/vlm_descriptions.json — текстовое описание каждой страницы. При --vlm-desc в rag_indexer.py эти описания добавятся к OCR-тексту, давая RAG понимание пространственной компоновки.

Важно: 4B VLM хороша для базовых описаний, но на сложных А0-чертежах может галлюцинировать. Для лучшего качества используйте 7B+ VLM (Qwen2-VL 7B, InternVL 8B).

Шаг 4: Запросы

# Через OpenCode API (рекомендуется — бесплатно)
python rag_query.py output_kronshtadt "На каких страницах показан план 3-го этажа?" --backend opencode --mode hybrid

python rag_query.py output_kronshtadt "Какие размеры указаны между осями А и Б?" --backend opencode --mode hybrid

python rag_query.py output_kronshtadt "Какие квартиры есть на этаже 2?" --backend opencode --mode hybrid

# Режим local — ищет соседей сущностей в графе
python rag_query.py output_kronshtadt "Что находится рядом с осью В?" --backend opencode --mode local

# Режим global — общий контекст проекта
python rag_query.py output_kronshtadt "Опиши общую структуру здания по чертежам." --backend opencode --mode global

Как это работает под капотом

1. Формирование документов

rag_indexer.py читает full_ocr_results.json и для каждой страницы создает текстовый документ вида:

=== Чертеж: страница 5 из 120 ===
Изображение: page_005.png

--- Текстовый слой PDF ---
План 3-го этажа...

--- Распознанный текст (OCR) ---
  "3-й этаж" (confidence=0.95, bbox=[100, 200, 150, 220])
  "Ось А" (confidence=0.88, bbox=[300, 400, 350, 420])
  "5400 мм" (confidence=0.92, bbox=[500, 600, 580, 620])

--- Извлеченные сущности ---
Этажи: 3-й этаж
Оси: А
Размеры: 5400 мм

2. Построение графа (LightRAG)

LightRAG пропускает каждый документ через LLM и извлекает:

  • Entities: 3-й этаж, Ось А, 5400 мм
  • Relationships: 3-й этаж -> содержит -> Ось А, Ось А -> имеет размер -> 5400 мм

Это сохраняется в lightrag_cache/ как граф + векторные эмбеддинги.

3. Поиск ответа

При запросе "Какие размеры между осями А и Б?":

  • Hybrid = Local (ищет узлы "Ось А", "Ось Б" и их соседей) + Global (общий контекст "размеры между осями")
  • LLM генерирует ответ на основе найденных фрагментов

Советы по использованию для чертежей

  1. Качество OCR критично — если RapidOCR пропускает цифры размеров, RAG не найдет их. Проверяйте full_ocr_results.json.
  2. Структурируйте сущности — в rag_indexer.py уже встроен парсер этажей, осей, размеров. При необходимости расширьте регулярные выражения под ваши ГОСТ-овские обозначения.
  3. Страницы как узлы — если чертеж очень большой (А0), можно разбить PNG на фрагменты и индексировать их отдельно.
  4. VLM для сложных схем — если нужно понимать геометрию (а не только текст), добавьте описания от зрительной модели (Qwen2-VL, LLaVA) в текст документов перед индексацией.

Расширение: добавление VLM-описаний

Если у вас есть GPU, можно улучшить понимание чертежей:

# Псевдокод — добавить в rag_indexer.py перед insert
from transformers import Qwen2VLForConditionalGeneration, AutoProcessor

vlm = Qwen2VLForConditionalGeneration.from_pretrained("Qwen/Qwen2-VL-7B-Instruct")
processor = AutoProcessor.from_pretrained("Qwen/Qwen2-VL-7B-Instruct")

desc = vlm_describe_image(page_image_path, "Опиши этот чертеж: этажи, оси, размеры, помещения.")
doc_text += f"\n--- Описание модели зрения ---\n{desc}\n"
await rag.ainsert(doc_text)

Это даст RAG понимание пространственной компоновки, даже если OCR плохо распознал текст.

Файлы

Файл Назначение
rag_indexer.py Построение индекса LightRAG из OCR-результатов
rag_query.py Запросы к индексу
requirements.txt Зависимости (lightrag-hku, openai, etc.)