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

209 lines
10 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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`** — объединяет локальный поиск по соседним сущностям и глобальный контекст проекта
## Установка
```bash
# 1. Установите зависимости
pip install -r requirements.txt
# 2. Выберите LLM backend:
```
### Вариант A: OpenAI (быстро, требует интернет)
```bash
export OPENAI_API_KEY="sk-..."
# или в Windows: set OPENAI_API_KEY=sk-...
```
### Вариант B: Ollama (локально, приватно, требует RAM/VRAM)
```bash
# Установите Ollama: https://ollama.com
# Скачайте модели (пример)
ollama pull qwen2.5:14b # LLM для ответов
ollama pull nomic-embed-text # embeddings
# Запустите сервер
ollama serve
```
### Вариант C: LM Studio (локальный GUI, подходит для VLM)
```bash
# Установите 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)
```bash
# Требуется API-ключ OpenCode (бесплатный тариф)
# 1. Получите ключ на https://opencode.ai
# 2. Экспортируйте переменные:
export OPENCODE_API_KEY="ваш-ключ"
export OPENCODE_URL="https://opencode.ai/zen/v1" # опционально, уже задан по умолчанию
```
## Использование
### Шаг 1: OCR (если еще не сделано)
```bash
python process_any_pdf.py "Кронштадтский 16-18 НК1_ОСК (v3).pdf" output_kronshtadt
```
Результат: папка `output_kronshtadt/` с `full_ocr_results.json`.
### Шаг 2: Построение индекса
**OpenAI:**
```bash
python rag_indexer.py output_kronshtadt --backend openai --model gpt-4o-mini
```
**Ollama (локально):**
```bash
python rag_indexer.py output_kronshtadt --backend ollama --model qwen2.5:14b --embed-model nomic-embed-text
```
Результат: папка `output_kronshtadt/lightrag_cache/` с графом знаний.
**LM Studio (VLM для описаний):**
```bash
# Сгенерировать 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):**
```bash
# Построение индекса через бесплатный 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 перед индексацией:
```bash
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: Запросы
```bash
# Через 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, можно улучшить понимание чертежей:
```python
# Псевдокод — добавить в 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.) |