- Fix duplicate except blocks in model_loader.py - Mark G.2.5 (tests) as completed - Mark G.1.3 (dots.ocr integration) as completed
18 KiB
TODO: PARSER Agent + RAG Haystack Stack
Детальний план реалізації PARSER-агента на базі dots.ocr та RAG-системи на базі Haystack.
Статус: 🟡 Планування
G. PARSER Agent (dots.ocr)
G.1. Runtime моделі PARSER
-
G.1.1 Обрати runtime для dots.ocr ✅
- Рішення: Python 3.11 + PyTorch + FastAPI
- Обґрунтування:
- dots.ocr — torch-модель, потребує PyTorch
- FastAPI для HTTP-обгортки (інтеграція з G.2)
- Python 3.11 для сучасного синтаксису
- Структура модуля:
parser_runtime/model_loader.py— завантаження dots.ocrparser_runtime/schemas.py— ParsedDocument, Page, Chunkparser_runtime/inference.py— функціяrun_ocr(...)
- Формат інтерфейсу:
def parse_document( input: bytes | str, # bytes або path output_mode: Literal["raw_json", "markdown", "qa_pairs", "chunks"] ) -> ParsedDocument - Реалізація: ✅ Інтегровано з dots.ocr моделлю
model_loader.py- завантаження моделі через transformersinference.py- реальний inference з моделлюmodel_output_parser.py- парсинг виводу моделі в блоки- Підтримка CUDA/CPU/MPS з автоматичним fallback
-
G.1.2 Створити
parser-runtime/сервіс ✅app/runtime/__init__.pyapp/runtime/model_loader.py(lazy init, GPU/CPU fallback)app/runtime/inference.py(функції:parse_document,dummy_parse_document)- Конфігурація в
app/core/config.py
-
G.1.3 Додати конфіг ✅
PARSER_MODEL_NAME=rednote-hilab/dots.ocr(вже було)PARSER_DEVICE=cuda/cpu/mps(вже було)PARSER_MAX_PAGES=100(вже було)PARSER_MAX_RESOLUTION=4096x4096(вже було)PARSER_BATCH_SIZE=1(вже було)- Додано:
PDF_DPI=200,IMAGE_MAX_SIZE=2048,PAGE_RANGE - Додано:
USE_DUMMY_PARSER,ALLOW_DUMMY_FALLBACK
G.2. HTTP-сервіс parser-service
-
G.2.1 Створити сервіс
services/parser-service/(FastAPI) ✅app/main.py— FastAPI додатокapp/schemas.py— Pydantic моделі (ParsedDocument, ParsedBlock, ...)app/core/config.py— конфігураціяDockerfile— Docker образrequirements.txt— залежностіREADME.md— документація
-
G.2.2 Ендпоінти ✅
POST /ocr/parse— повертає raw JSON (з mock-даними)- Request:
multipart/form-data(file) +output_mode - Response:
ParseResponseзdocument,markdown,qa_pairs, абоchunks
- Request:
POST /ocr/parse_qa— Q&A-представлення (поки що mock)POST /ocr/parse_markdown— Markdown-версія (поки що mock)POST /ocr/parse_chunks— семантичні фрагменти для RAG (поки що mock)GET /health— health check
-
G.2.3 Підтримати типи файлів ✅
- PDF (розбиття по сторінках → зображення)
- Використано
pdf2imageз poppler-utils convert_pdf_to_images()вpreprocessing.py
- Використано
- PNG/JPEG
- Пряма обробка через
PIL/Pillow load_image()вpreprocessing.py
- Пряма обробка через
- TIFF, WebP (підтримка через PIL)
- Автоматичне визначення типу файлу (
detect_file_type()) - Валідація розміру файлу (
validate_file_size())
- PDF (розбиття по сторінках → зображення)
-
G.2.4 Додати pre-/post-processing ✅
- Нормалізація розміру зображень (
normalize_image(),prepare_images_for_model()) - Конвертація PDF → зображення (по сторінках) - вже в G.2.3
- Mapping вихідного JSON dots.ocr → внутрішню структуру
ParsedBlock(build_parsed_document()) - Валідація структури (перевірка наявності обов'язкових полів)
- Post-processing функції:
build_chunks()- семантичні фрагменти для RAGbuild_qa_pairs()- Q&A париbuild_markdown()- Markdown конвертаціяnormalize_text()- нормалізація тексту
- Нормалізація розміру зображень (
-
G.2.5 Додати базові тести ✅
- Створити структуру тестів (
tests/,pytest.ini) test_preprocessing.py- PDF/image loading, normalization, validationtest_postprocessing.py- chunks, QA pairs, markdown generationtest_inference.py- dummy parser and inference functionstest_api.py- API endpoint testsconftest.py- pytest fixtures- Створити
tests/fixtures/docs/з реальними тестовими документами (опційно) - Snapshot-тести JSON-структури (опційно, для регресійного тестування)
- Створити структуру тестів (
-
G.2.6 Додати в
docker-compose.yml- Сервіс
parser-serviceз залежностями - Environment variables
- Health check
- Volumes для тимчасових файлів
- Сервіс
G.3. Інтеграція PARSER у DAGI Router
-
G.3.1 Додати LLM-профіль у
router-config.ymlllm_profiles: parser_dots_ocr: model: "dots.ocr" base_url: "http://parser-runtime:11435" # або Ollama/vLLM endpoint timeout_s: 120 -
G.3.2 Додати provider
providers: parser: type: ocr base_url: "http://parser-service:9400" -
G.3.3 Додати routing rule
routing: - id: doc_parse when: mode: doc_parse use_provider: parser -
G.3.4 Розширити
RouterRequest(вrouter_client.pyабоtypes/api.ts)- Поля
doc_url: Optional[str] - Поля
doc_type: Optional[str](pdf,image) - Поля
output_mode: Optional[str](raw_json|qa_pairs|markdown|chunks) - Поля
file_bytes: Optional[bytes](для прямого завантаження)
- Поля
-
G.3.5 E2E curl-тест
curl -X POST http://localhost:9102/route \ -H "Content-Type: application/json" \ -d '{ "mode": "doc_parse", "dao_id": "daarion", "user_id": "test", "payload": { "doc_url": "https://.../example.pdf", "output_mode": "qa_pairs" } }'
G.4. Опис агента PARSER
-
G.4.1 Створити
docs/agents/parser.md✅ (вже створено)- Роль: Document Ingestion & Structuring Agent
- Вхід:
doc_url,file_id,raw bytes - Вихід:
ParsedDocument { blocks[], tables[], qa_pairs[] } - Обмеження: max pages, max file size
-
G.4.2 Додати
parser_agentу CrewAI/оркестратор- Створити
orchestrator/agents/parser_agent.py - Задачі:
parse_for_rag— підготовка chunk'ів для індексаціїparse_for_summary— підготовка структурованого огляду doc'аparse_for_qa— Q&A базу для SecondMe/microDAO
- Інтеграція з CrewAI workflow
- Створити
-
G.4.3 Зв'язати PARSER з RBAC
- Перевірка прав на інжест документів (
role: admin,role: researcher) - Обмеження на приватні/публічні документи
- Перевірка
dao_idдля ізоляції даних - Додати в
microdao/rbac.py(якщо потрібно)
- Перевірка прав на інжест документів (
H. RAG Haystack Stack (PARSER як головний агент)
Тут PARSER — головний агент інжесту, а RAG шар — Haystack-пайплайни для пошуку відповідей.
H.1. Вибір стеку RAG
-
H.1.1 Обрати фреймворк
- Варіант A: deepset Haystack 2.x (pipelines, retrievers, document stores)
- Переваги: готові компоненти, добра документація
- Недоліки: може бути overkill для простого випадку
- Варіант B: власний RAG поверх
pgvector/Qdrant + простий код- Переваги: легше, більше контролю
- Недоліки: треба писати більше коду
- Рекомендація: Почнути з варіанту B (pgvector вже є), потім можна перейти на Haystack
- Варіант A: deepset Haystack 2.x (pipelines, retrievers, document stores)
-
H.1.2 Обрати векторне сховище
- Postgres + pgvector (вже є в
city-db) - Або [ ] Qdrant / Weaviate / Milvus (якщо потрібна краща продуктивність)
- Рекомендація: Використати
pgvector(вже налаштовано)
- Postgres + pgvector (вже є в
-
H.1.3 Обрати embedding-модель
- Варіант A:
sentence-transformers/all-MiniLM-L6-v2(легка, швидка) - Варіант B:
intfloat/multilingual-e5-base(краще для української) - Варіант C: Qwen embedding (якщо є)
- Рекомендація: Почнути з
multilingual-e5-base
- Варіант A:
H.2. Інджест-пайплайн (PARSER → RAG)
-
H.2.1 Створити
services/rag_ingest_pipeline.py- Функція
ingest_document(doc_id, doc_url, dao_id, user_id) - Функція
ingest_chunks(chunks: List[ParsedChunk], dao_id, doc_id)
- Функція
-
H.2.2 Кроки пайплайну
- Виклик PARSER (
mode=doc_parse,output_mode=chunks) - Нормалізація блоків у формат для індексації:
content— текст блокуmeta—dao_id,doc_id,page,bbox,section_type
- Обчислення ембеддингів (окремий embedding-модель/провайдер)
- Створити
services/embedding_service.pyабо використатиsentence-transformers
- Створити
- Запис у document store (Postgres + pgvector)
- Створити таблицю
document_chunks:CREATE TABLE document_chunks ( id UUID PRIMARY KEY, doc_id TEXT NOT NULL, dao_id TEXT NOT NULL, chunk_text TEXT NOT NULL, embedding vector(768), -- або інший розмір page_num INTEGER, bbox JSONB, section_type TEXT, created_at TIMESTAMPTZ DEFAULT NOW() );
- Створити таблицю
- Виклик PARSER (
-
H.2.3 Тест
- Індексувати 1 PDF (наприклад, "Токеноміка MicroDAO")
- Перевірити, що в storage з'явилися Documents з meta
[dao_id=... , doc_id=...] - Перевірити, що embeddings обчислені та збережені
H.3. Query-пайплайн (RAG-відповіді)
-
H.3.1 Створити
services/rag_query_pipeline.py- Функція
answer_query(dao_id, user_id, question: str) -> RAGResponse
- Функція
-
H.3.2 RAG-пайплайн (якщо використовуємо Haystack)
retriever→ top-k документів/фрагментів поdao_id- Використати
pgvectorдля similarity search - Фільтр по
dao_idдля ізоляції даних
- Використати
- (опційно)
ranker→ rerank за релевантністю generator→ LLM (qwen3:8b або PokeeResearch-7B)- Формувати промпт з контекстом:
question + retrieved_chunks
- Формувати промпт з контекстом:
-
H.3.3 Вихід
RAGResponseclass RAGResponse: answer: str citations: List[Citation] confidence: float class Citation: doc_id: str doc_title: str page: int excerpt: str bbox: Optional[Dict] # для виділення в PDF -
H.3.4 E2E-тест
mode="rag_query"запит до Router:{ "mode": "rag_query", "dao_id": "daarion", "user_id": "test", "payload": { "question": "Поясни токеноміку microDAO і роль стейкінгу." } }- Очікування: відповідь + 2–3 посилання на індексовані документи
H.4. Оркестрація: PARSER як головний агент
-
H.4.1 Додати в CrewAI workflow
doc_ingest_workflow- Agent
parser_agent:- Перевіряє тип документа
- Вирішує: локальний PARSER vs хмарний (якщо важкі PDF)
- Викликає
rag_ingest_pipeline
- Agent
validation_agent:- Робить sanity-check: кількість блоків, чи є таблиці, чи коректна мова
- Перевіряє якість розпізнавання (confidence scores)
- Agent
-
H.4.2 Додати в workflow
rag_answer_workflow- Крок 1:
retrieval_agent(Haystack/pgvector)- Пошук релевантних фрагментів
- Крок 2:
answer_agent(LLM)- Генерація відповіді на основі контексту
- Крок 3: (опційно)
citation_checker- Верифікація відповідей по фрагментах
- Перевірка на галлюцинації
- Крок 1:
H.5. Інтеграція з DAARWIZZBot / microDAO
-
H.5.1 Додати команди для бота (в
gateway-bot/http_api.py)/upload_doc→ інжест документу в RAG через PARSER- Підтримка завантаження файлів через Telegram
- Виклик
doc_ingest_workflow
/ask_doc→ питання до бази документів DAO- Виклик
rag_answer_workflow - Відправка відповіді з цитатами
- Виклик
-
H.5.2 RBAC
- Хто може інжестити документи (
role: admin,role: researcher) - Хто може ставити питання до приватних документів
- Перевірка прав в
microdao/rbac.py
- Хто може інжестити документи (
-
H.5.3 UI для Console (опційно)
- Сторінка
/console/documents— список документів DAO - Завантаження документів через drag-and-drop
- Перегляд розпарсених документів
- Чат-інтерфейс для питань до документів
- Сторінка
Порядок виконання (рекомендований)
Фаза 1: PARSER Runtime (1-2 дні)
- G.1.1 — Обрати runtime
- G.1.2 — Створити
parser-runtime/ - G.1.3 — Додати конфіг
Фаза 2: PARSER Service (2-3 дні)
- G.2.1 — Створити FastAPI сервіс
- G.2.2 — Реалізувати ендпоінти
- G.2.3 — Підтримка PDF/зображень
- G.2.4 — Pre/post-processing
- G.2.5 — Тести
- G.2.6 — Docker Compose
Фаза 3: Інтеграція з Router (1 день)
- G.3.1 — LLM-профіль
- G.3.2 — Provider
- G.3.3 — Routing rule
- G.3.4 — Розширити RouterRequest
- G.3.5 — E2E тест
Фаза 4: RAG Ingest (2-3 дні)
- H.1.1 — Обрати стек
- H.2.1 — Створити ingest pipeline
- H.2.2 — Реалізувати кроки
- H.2.3 — Тест
Фаза 5: RAG Query (2-3 дні)
- H.3.1 — Створити query pipeline
- H.3.2 — Реалізувати RAG-пайплайн
- H.3.3 — Вихід з цитатами
- H.3.4 — E2E тест
Фаза 6: Оркестрація (1-2 дні)
- H.4.1 —
doc_ingest_workflow - H.4.2 —
rag_answer_workflow
Фаза 7: Інтеграція з ботом (1-2 дні)
- H.5.1 — Команди
/upload_doc,/ask_doc - H.5.2 — RBAC
Загальний час: ~10-15 днів (залежить від складності моделі та налаштування)
Залежності та ресурси
Python пакети
qwen3-asr-toolkit(якщо доступний)transformers/torch(для моделі)pdf2image/PyMuPDF(для PDF)Pillow(для зображень)sentence-transformers(для embeddings)pgvector(вже є)haystack(якщо використовуємо)
Системні залежності
ffmpeg(може знадобитися)poppler(для PDF → images)
GPU
- Рекомендовано GPU для dots.ocr (можна CPU fallback)