Skeleton Indexing (KDD 2025) + HippoRAG 2 (ICML 2025) + VectorCypher + Datalog Reasoning + 10 итераций оптимизации
TL;DR
Я реализовал Graph RAG систему, которая комбинирует 5 техник из свежих научных статей в единый пайплайн с декларативным reasoning-движком, полной провенансной трассировкой и типизированным API. Результат: 174/180 (96.7%) на билингвальном бенчмарке из 30 вопросов, оценённых в 6 режимах retrieval. Три режима достигли 100%. Ноль persistent failures.
GitHub: vpakspace/agentic-graph-rag
Проблема: почему обычный RAG недостаточен
Классический RAG — “разбей документ на чанки, сделай embeddings, найди похожие” — работает для простых фактоидных вопросов. Но он ломается на:
-
Вопросах о связях: “Как метод X связан с компонентом Y?” — ответ разбросан по разным чанкам
-
Multi-hop рассуждениях: “Что произойдёт, если изменить A, учитывая что A влияет на B, а B на C?”
-
Глобальных вопросах: “Перечисли все 7 архитектурных решений” — ответ в 7 разных местах документа
-
Кросс-языковых запросах: русский вопрос о концепциях из английского документа
Моя цель — система, которая справляется со всеми этими типами вопросов, а не только с простыми.
Архитектура: 5 техник из 2025 года
1. Skeleton Indexing (KET-RAG, KDD 2025)
Проблема: извлечение сущностей из всех чанков — дорого (O(n) вызовов LLM).
Решение: строим KNN-граф по embeddings чанков → PageRank → извлекаем сущности только из top-25% “скелетных” чанков. Периферийные чанки привязываем через keyword matching.
Chunks → KNN Graph → PageRank → Top-β Skeletal (full extraction)
→ Peripheral (keyword linking only)
Результат: 75% меньше вызовов LLM при сопоставимом качестве. Это не трюк — это математика: PageRank выделяет чанки, которые наиболее “центральны” в семантическом пространстве документа.
2. Dual-Node Structure (HippoRAG 2, ICML 2025)
Проблема: обычный GraphRAG теряет контекст полных пассажей. Обычный RAG теряет связи между сущностями.
Решение: два типа узлов в Neo4j:
-
PhraseNode — сущность (имя, тип, PageRank score, embedding)
-
PassageNode — полный текст чанка (контент, embedding)
-
MENTIONED_IN — связывает сущности с пассажами
-
RELATED_TO — ко-вхождения между сущностями
Это даёт и навигацию по графу (через PhraseNode), и полный контекст (через PassageNode).
3. VectorCypher Retrieval
Гибридный retrieval в три фазы, вдохновлённый VectorCypherRetriever из Neo4j GraphRAG:
-
Vector Index → находим ближайшие PhraseNode через cosine similarity
-
Cypher Traversal → расширяем через RELATED_TO (до 3 хопов)
-
PassageNode Collection → собираем связанные пассажи → GraphContext
Ключевой инсайт: cosine re-ranking по реальным embeddings PassageNode из Neo4j бьёт RRF-фьюжн.
4. Agentic Router с Self-Correction
Три уровня маршрутизации с каскадным fallback:
|
Tier |
Метод |
Confidence |
Описание |
|---|---|---|---|
|
1 |
Mangle (Datalog) |
0.7 |
65 билингвальных ключевых слов |
|
2 |
LLM (GPT-4o-mini) |
0.85 |
Классификация нейросетью |
|
3 |
Pattern (regex) |
0.5 |
Regex-паттерны как fallback |
Если качество retrieval ниже порога (relevance < 2.0 из 5), система эскалирует по цепочке инструментов:
vector_search → cypher_traverse → hybrid_search → comprehensive_search → full_document_read
Каждая попытка перефразирует запрос через LLM. Лучшие результаты отслеживаются по всем попыткам.
5. PyMangle — Datalog-движок на Python
Полная реимплементация Google Mangle (2,919 строк):
-
Lark-based парсер с кастомной грамматикой
-
Semi-naive evaluation со стратифицированным отрицанием
-
35+ встроенных функций (арифметика, строки, списки, словари)
-
Temporal evaluation
-
Filter pushdown для внешних предикатов
Три файла правил:
-
routing.mg— маршрутизация запросов (65 ключевых слов) -
access.mg— RBAC (role inheritance + permit/deny) -
graph.mg— граф-инференс (reachable, common_neighbor, evidence)
% Транзитивное замыкание по графу
reachable(X, Y, 1) :- edge(X, R, Y).
reachable(X, Z, D) :- reachable(X, Y, D1), edge(Y, R, Z),
D = fn:plus(D1, 1), D < 5.
% Общие соседи двух сущностей
common_neighbor(A, B, N) :- edge(A, R1, N), edge(B, R2, N), A != B.
Бенчмарк: от 38% до 96.7% за 10 итераций
Дизайн бенчмарка
-
30 вопросов: 7 simple, 7 relation, 6 multi_hop, 6 global, 4 temporal
-
2 документа: Doc1 (русский, граф знаний) + Doc2 (английский, архитектура SCL)
-
6 режимов retrieval: vector, cypher, hybrid, agent_pattern, agent_llm, agent_mangle
-
180 оценок (30 × 6) через hybrid judge: embedding similarity + keyword overlap + LLM-as-judge
Эволюция результатов
v3: 38% ████░░░░░░░░░░░░░░░░ Baseline (вопросы на EN, документы на RU)
v4: 67% █████████░░░░░░░░░░░ +29pp — вопросы на RU (language match!)
v5: 73% ██████████░░░░░░░░░░ +6pp — comprehensive_search для global
v10: 65% █████████░░░░░░░░░░░ -8pp — добавили 15 новых вопросов
v11: 80% ████████████░░░░░░░░ +15pp — enumeration prompt
v12: 93% ██████████████████░░ +13pp — hybrid judge
v14: 96.7%███████████████████░ +3.7pp — semantic judge
Финальные результаты (v14)
|
Режим |
Результат |
|
|---|---|---|
|
Vector |
30/30 (100%) |
Чистый embedding search |
|
Hybrid |
30/30 (100%) |
Vector + Graph |
|
Agent (Mangle) |
30/30 (100%) |
Datalog правила |
|
Agent (LLM) |
29/30 (96%) |
GPT-4o-mini роутер |
|
Agent (Pattern) |
28/30 (93%) |
Regex паттерны |
|
Cypher |
27/30 (90%) |
Граф-траверсал |
|
Итого |
174/180 (96.7%) |
0 persistent failures |
10 уроков оптимизации
1. Язык вопросов = язык документов (+29pp)
Самое большое улучшение за всю историю проекта. Вопросы на английском о русском документе давали 38%. Переключение на русские вопросы — 67%. Embeddings хорошо справляются с кросс-языковым поиском, но LLM-генератор теряет контекст.
2. Failures — это не retrieval, а generation + evaluation
Ключевой инсайт v11: для глобальных вопросов ВСЕ нужные ключевые слова находились в top-30 чанков. Проблема была в том, что генератор не перечислял все пункты, а judge обрезал ответ до 500 символов.
3. CoT-промпт для judge — катастрофа
Попытка сделать judge “умнее” через Chain-of-Thought (“перечисли найденные ключевые слова → посчитай → выдай вердикт”) вызвала регрессию с 144/180 до 48/180. GPT-4o-mini буквально искал английские строки в русском тексте. Простой промпт “match CONCEPTS, not strings” работает в 3 раза лучше.
4. Cosine re-ranking бьёт RRF
Hybrid search с Reciprocal Rank Fusion давал худшие результаты, чем cosine re-ranking по реальным embeddings из Neo4j. RRF хорош для combining разных сигналов, но когда оба сигнала — embedding-based, прямое cosine similarity точнее.
5. Embedding similarity для judge (threshold 0.65)
Для вопросов с reference answer: cosine similarity между ответом системы и эталоном >= 0.65 → auto-PASS. Калибровка: правильный ответ ~0.677, неправильный (другой документ) ~0.570. Порог 0.65 идеально разделяет.
6. Кросс-языковая маршрутизация
Русский вопрос о концепциях из английского документа (Doc2/SCL) ломает vector_search — он возвращает Doc1. Решение: детектируем кросс-языковой глобальный запрос → напрямую full_document_read вместо vector_search.
7. Comprehensive search размывает результаты
comprehensive_search (multi-query fan-out) генерирует N подзапросов → каждый через vector_search → RRF merge. Но если все подзапросы возвращают Doc1, то единственный full_document_read результат для Doc2 тонет в RRF-merge.
8. Self-correction loop должен сохранять лучшее
Ранний баг: каждая попытка перезаписывала предыдущие результаты. Если attempt 1 дал score 2.5, а attempt 2 — score 1.8, система возвращала 1.8. Фикс: трекаем best_results и best_score по всем попыткам.
9. Enumeration prompt — специальный формат
Для глобальных вопросов (“перечисли все…”) обычный prompt генерирует текст, а не список. Специальный enumeration prompt: “Output a numbered list. Scan ALL chunks. Do not stop early.”
10. Judge limit 500 → 2000 символов
Обрезка ответа до 500 символов для judge убивала enumeration-ответы (7 пунктов ~ 1500 символов). Увеличение до 2000 — мгновенный +5pp.
Typed API и провенанс
Каждый запрос создаёт PipelineTrace:
{
"trace_id": "tr_abc123def456",
"router_step": {
"method": "mangle",
"decision": {"query_type": "simple", "suggested_tool": "vector_search"}
},
"tool_steps": [{
"tool_name": "vector_search",
"results_count": 10,
"relevance_score": 3.2,
"duration_ms": 150
}],
"escalation_steps": [],
"generator_step": {
"model": "gpt-4o-mini",
"confidence": 0.82
},
"total_duration_ms": 1800
}
API: FastAPI REST (/api/v1/) + FastMCP (SSE/MCP) — и для REST-клиентов, и для AI-агентов.
Цифры проекта
|
Метрика |
Значение |
|---|---|
|
Python LOC |
16,206 (118 файлов) |
|
Тесты |
586 (320 core + 108 PyMangle + 158 rag-core) |
|
Зависимости |
26 пакетов |
|
Итерации бенчмарка |
10 (v2 → v14) |
|
Файлы результатов |
15 JSON (~4.7 MB) |
|
Mangle-правила |
111 строк (3 файла) |
|
Классы |
36 (14 data models, 8 services, 6 config, 4 reasoning) |
Что дальше
-
Personalized PageRank для query-focused графового обхода
-
Human evaluation в дополнение к LLM-as-judge
-
Streaming ответы в Streamlit UI
-
Больше Mangle-правил — temporal reasoning, conflict resolution
Стек
Если вам интересны детали реализации или вы хотите обсудить Graph RAG — пишите в комментариях или открывайте issue на GitHub.
Автор: VladSpace
- Запись добавлена: 24.02.2026 в 12:16
- Оставлено в
Советуем прочесть:
- Геймер построил языковую модель с 5 млн параметров в Minecraft
- Моя RAG-система: как я за 8 дней собрал RAG для своего сайта визитки
- О пользе механической тренировки памяти для развития способности запоминания любой информации
- «Антиплагиат» научился распознавать сгенерированные ИИ тексты с точностью до 98%
- Production-ready архитектура AI-агента. Часть 1: ReAct, Advanced RAG, Tools, Prompts
- Как я написал production-ready PHP-роутер за один вечер с помощью ИИ
- Ушёл из жизни учёный, доктор физико-математических наук и автор более 20 книг и 250 научных статей Александр Горбань
- ИИ-система Locus превзошла экспертов в научных исследованиях
- Google разрабатывает AI-помощника для ускорения научных открытий
- Образные коды дней недели


