209 lines
10 KiB
Markdown
209 lines
10 KiB
Markdown
|
|
# 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.) |
|