Опубликовано
Что не пишут в статьях типа RAG за 5 минут. Часть вторая
Автор: Гусев Николай [портфолио]
Дата обновления не указана в исходнике.
Что не пишут в статьях типа "RAG за 5 минут". Часть вторая
В первой части описан один RAG: CRUD-индексация, адаптивные пороги, CRAG с веб-фоллбэком, content-hash дедупликация, отдельная коллекция сессий. 127 тестов. Корпус - 2000 документов, 750 сессий.
Но это была одна из двух систем. Вторая работала параллельно - на другом сервере, с другой архитектурой.
Два RAG, два подхода
На HQ: 92 тысячи чанков из 27 тысяч markdown-файлов. Три источника - wiki, почта (IMAP), Telegram-история (state.db). Автоиндексация каждые 30 минут через cron с flock. Плагин с pre_llm_call hook - RAG контекст встраивается в каждый ответ LLM автоматически. Восемь категорий чанков с фильтрацией на уровне базы: email, tg, concept, entity, raw, verified, llm-wiki, wiki.
На Autolycus: 2.5 тысячи чанков. CRUD-индексация (delete-before-upsert), content-based дедупликация, детерминистичные ID. Отдельная коллекция сессий (906 чанков). Source boost для приоритизации источников. Конфигурация через переменные окружения.
Оба работали независимо. Каждый обслуживал своего агента.
Сравнение
Сильные стороны HQ:
-
Категоризация: 8 категорий, фильтрация на уровне ChromaDB. На Autolycus категорий нет.
-
Автоиндексация: 3 cron-задачи с flock. Новые данные в поиске через 15 минут. На Autolycus - ручная индексация.
-
Плагин: pre_llm_call hook, автоматическое обогащение каждого ответа LLM. На Autolycus - только CLI-скрипты.
-
Объём: 92K чанков из 27K файлов.
Сильные стороны Autolycus:
-
CRUD-индексация: delete-before-upsert, cleanup удалённых файлов, детерминистичные ID (MD5 от source+heading+seq). На HQ - полная переиндексация при изменениях, 650+ ошибок при сбое.
-
Content-based dedup: content_hash на каждом чанке. На HQ - дубликаты при каждой переиндексации.
-
Sessions collection: отдельная коллекция (906 чанков), свой embedding prefix. На HQ - всё в одной коллекции.
-
Source boost: concepts/ +0.05, adr/ +0.03, manuals/ +0.03. На HQ - все чанки равны.
-
Конфигурация: через env vars. На HQ - хардкод.
Модели
На Autolycus - LM Studio с тремя моделями:
-
e5-large-instruct - эмбеддинги (1024d, cosine similarity)
-
Qwen 2.5 7B - grading, reranking, query rewriting
-
Gemma 4B - классификация запросов, fallback при неуверенности основного классификатора
Основной классификатор на обоих узлах - TF-IDF+SVM (96.4% accuracy, <1ms). Gemma подключается при confidence < 0.9.
Всё локальное.
Структурное ограничение
При проектировании объединения проведён Prism-анализ. Результат: автономность × полнота × простота ≤ 2. Три свойства одновременно получить нельзя.
-
Автономность + Полнота = сложность (SSH тоннель, синхронизация, мониторинг)
-
Полнота + Простота = рассинхронизация (категории расходятся, схемы несовместимы)
-
Автономность + Простота = потеря полноты (каждый узел изолирован)
Выбраны: Автономность + Полнота. Сложность принята как данность.
Premortem
Проведён премортем на горизонте один месяц. Восемь причин провала, четыре критические:
-
SSH тоннель падает → autossh + systemd + health check каждые 5 минут
-
Плагин конфликтует → тестирование на тестовом инстансе до установки
-
Категории расходятся → единый config.yaml, git-синхронизация
-
Self-RAG threshold не откалиброван → калибровка на реальных запросах по F1-score
ADR обновлён. Чеклист: 9 пунктов. Мониторинг - обязателен до запуска.
Результат унификации
Из Autolycus в HQ: CRUD-индексация, content dedup, детерминистичные ID, source boost, sessions collection, env vars.
Из HQ в Autolycus: категоризация, автоиндексация, плагин.
Каждый узел остался автономным. Данные не перемещаются. Поиск маршрутизируется: эвристика + fallback на оба узла. Происхождение фактов сохраняется - метка узла в каждом чанке.
Итоговая архитектура
Компоненты
Хранилище: ChromaDB, коллекции wiki и sessions. Пространство cosine. Каждый чанк содержит метаданные: source, heading, category, content_hash, node.
Эмбеддинги: e5-large-instruct (1024d). Локально через LM Studio.
Классификатор запросов: TF-IDF+SVM (основной), Gemma 4B (fallback при confidence < 0.9).
Grading и reranking: Qwen 2.5 7B. Batch-оценка релевантности чанков.
Индексация: CRUD (delete-before-upsert), content-hash дедупликация, cleanup удалённых файлов. Детерминистичные ID (MD5 от source+heading+seq).
Категории: email, tg, concept, entity, raw, verified, llm-wiki, wiki, session. Авторасширение из путей файлов с валидацией.
Маршрутизация: эвристика по ключевым словам + fallback на оба узла.
Self-RAG: адаптивные пороги (factual 0.75, analytical 0.65, synthesis 0.60). Детектор шума. CRAG fallback на DuckDuckGo.
Мониторинг: JSONL-логи, 4 алерта (avg_relevance, elapsed_seconds, chunks_found, SSH tunnel).
Варианты развёртывания
Вариант A: Полный (GPU + LM Studio)
-
e5-large-instruct - эмбеддинги
-
Qwen 2.5 7B - grading, reranking
-
Gemma 4B - классификация
-
TF-IDF+SVM - основной классификатор
Требуется: GPU с 24GB+ VRAM или LM Studio на отдельном сервере.
Вариант B: Без GPU, с LM Studio на удалённом сервере
-
То же, что вариант A, но LM Studio на удалённом хосте
-
Подключение через HTTP API (RAG_EMBEDDING_URL, RAG_LLM_URL)
-
Задержка: +5-15ms на каждый вызов
Вариант C: Без LM Studio (облачный fallback)
-
Эмбеддинги: sentence-transformers/all-MiniLM-L6-v2 (локальный, CPU, 384d) вместо e5-large-instruct
-
Классификация: только TF-IDF+SVM (без Gemma)
-
Grading: cosine threshold без LLM (адаптивные пороги по типу запроса)
-
CRAG: DuckDuckGo (без изменений)
Требуется: только CPU. Качество ниже, но работает.
Вариант D: Минимальный (один узел, без федерации)
-
Одна коллекция ChromaDB
-
TF-IDF+SVM классификатор
-
Cosine threshold без адаптивных порогов
-
Без CRAG, без sessions collection
-
Без плагина (только CLI)
Требуется: Python 3.10+, ChromaDB, scikit-learn. Работает на любом сервере.
Конфигурация (config.yaml)
`# ChromaDB
chroma_path: ~/.cache/chroma
collection_name: wiki
session_collection_name: sessions
# Embedding
embedding_model: e5-large-instruct # или all-MiniLM-L6-v2 для CPU
embedding_dim: 1024 # или 384 для MiniLM
embedding_url: http://localhost:1234/v1/embeddings
# LLM (grading, reranking)
llm_model: qwen2.5-7b-instruct
llm_url: http://localhost:1234/v1/chat/completions
# Классификация
classifier: tfidf_svm # основной
fallback_classifier: gemma4b # при confidence < 0.9
# Self-RAG пороги
threshold_factual: 0.75
threshold_analytical: 0.65
threshold_synthesis: 0.60
# Индексация
chunk_size: 2000
chunk_overlap: 100
batch_size: 64
# Категории (авторасширение из путей)
category_rules:
.email_cache/: email
.tg_history/: tg
concepts/: concept
entities/: entity
raw/: raw
verified/: verified
sessions/: session
# Источники
wiki_paths:
- ~/wiki
- ~/llm-wiki
- ~/wiki/.email_cache
- ~/wiki/.tg_history
# Cron (автоиндексация)
cron_schedule: "0 * * * *" # каждый час
cron_lock: /tmp/rag-index.lock
# Мониторинг
log_path: ~/.cache/chroma/search.log
alert_avg_relevance: 0.3
alert_elapsed_seconds: 5
alert_chunks_found_zero_ratio: 0.3`
Воспроизведение
- Установить зависимости:
pip install chromadb scikit-learn requests pyyaml2. Скопировать config.yaml, указать пути к данным 3. Запустить индексацию:python3 rag_indexer.py4. Запустить автоиндексацию: добавить cron-задачу из config 5. Для поиска:python3 rag_search.py --query "запрос"
При отсутствии LM Studio - использовать вариант C (sentence-transformers + cosine threshold).