Несколько лет я занимаюсь разработкой корпоративных RAG-систем. В последнее время от коллег и заказчиков всё чаще слышу, что векторный поиск — это устаревшее решение, а будущее за графовыми системами. Решил проверить это на практике. В статье делюсь впечатлениями, результатами экспериментов и инструкцией по воспроизведению.
Почему Ollama?
Выделить отдельные вычислительные ресурсы под RAG в реальном проекте — непростая задача. Мне нужно было понять, насколько слабое «железо» может потянуть такие системы. В итоге выяснилось: даже 4-битные модели кое-как справляются.
Зачем тут киберпанк?
Я большой поклонник жанра и люблю тестировать RAG и LLM на знакомых текстах. Для экспериментов с Microsoft GraphRAG выбрал рассказ Уильяма Гибсона «Johnny Mnemonic» — сначала на английском. Проектная директория с результатами доступна на GitHub.
Граф знаний: первые впечатления
Граф построен с помощью Gephi и плагина Лейдена на основе сущностей и связей, извлечённых GraphRAG. Все данные — в файле graph.graphml в папке output. Подробная инструкция по визуализации прилагается. Результат впечатляет: такой граф вручную аналитик мог бы строить несколько дней.
Заметно, что сущности объединяются только при точном совпадении имён. Например, Molly Millions и Molly, Lo Tek и Lo Teks, Ralfi Face и Ralfi — не склеились. Однако алгоритм Лейдена в Gephi выделил сообщества, а GraphRAG — свои тематические кластеры, которые можно изучить в файле community_reports.parquet. Вот три примера высокоранговых сообществ (описания сокращены):
- Сообщество: Yakuza, Sons of the Neon Chrysanthemum, Ono-Sendai
Описание: В центре — Якудза, влиятельная криминальная организация. Конфликт с Sons of the Neon Chrysanthemum, промышленный шпионаж против Ono-Sendai и другие связи. - Сообщество: Johnny, Ralfi Face и связанные сущности
Описание: Ядро — Джонни, центральный персонаж, втянутый в конфликты с Ральфи, Якудзой и Молли Миллионы. - Сообщество: Molly Millions, Ralfi, Drome
Описание: Молли Миллионы — ключевая фигура в Дроме, где пересекаются преступные и бизнес-операции. Связи с Ральфи, Льюисом, Magnetic Dog Sisters и другими.
На мой взгляд, результат выглядит содержательно и логично.
Поиск по графу: глобальный и локальный
Ответы можно получать на русском, если подредактировать промпты в файле local_search_system_prompt.txt и global_search_reduce_system_prompt.txt и использовать LLM с поддержкой русского. Однако формулировать запросы лучше на английском — граф и описания элементов всё ещё на английском, и это повышает точность.
Глобальный поиск работает по принципу MapReduce: анализирует описания сообществ. Хорошо подходит для вопросов, требующих понимания всей картины. Например, на запрос о киберпанковских элементах в рассказе система выдала структурированный ответ с 8 пунктами:
- Интеграция технологий и биологии: кибернетические импланты, нейропертурбаторы — технологии проникают в тело. [Data: Reports (5, 7, 12, 2, +more)]
- Дистопические городские ландшафты: Найттаун, Дром — символы социального распада. [Data: Reports (1, 8, 2, +more)]
- Корпоративная конкуренция: шпионаж, нелегальные сделки — борьба за технологии и власть. [Data: Reports (0, 12, 2, +more)]
- Технологическое неравенство: LO TEK используют устаревшие фильтры, в то время как элита — киберимпланты. [Data: Reports (9, 2, +more)]
- Киберорганические существа: Джонс — кибердельфин, Молли — боевой киборг. [Data: Reports (11, 7, 2, +more)]
- Идентичность и анонимность: Джонни использует алиби, что отражает размытие личности в цифровом мире. [Data: Reports (1, 12, 2, +more)]
- Насилие и власть: жестокие столкновения, контроль через технологии. [Data: Reports (5, 7, 2, +more)]
- Сюрреалистические пространства: FUNLAND и торговые центры — смесь разрухи и функциональности. [Data: Reports (8, 11, 2, +more)]
Цифры в квадратных скобках — это human_readable_id из parquet-файлов. Их можно открыть, например, с помощью pandas.
Локальный поиск комбинирует данные из графа с фрагментами исходного текста. Подходит для вопросов о конкретных связях. Например, на запрос о персонаже Jones система выдала детальный ответ:
Jones — кибернетический дельфин с сенсорами и доступом к пресному резервуару в Funland. Сотрудничает с Кальмой, читающей следы. Играет ключевую роль в извлечении данных с чипа Джонни. [Data: Entities (28, 33); Relationships (44, 51, +more)]
Его основные связи:
- С Molly Millions: совместная операция по извлечению данных. [Data: Entities (33); Relationships (44, 51, +more)]
- С Johnny: использование сенсоров для доступа к информации. [Data: Entities (33); Relationships (44, 51, +more)]
- С Squid: устройство на основе военных технологий НАВАЛЬНОЙ силы. [Data: Entities (19); Relationships (25, +more)]
- С Yakuza: косвенная связь — Якудза опасаются, что Squid извлечёт их данные из мозга Джонни. [Data: Relationships (25); Entities (19)]
В реальной системе потребуется агент, определяющий, какой тип поиска использовать — глобальный или локальный.
В GraphRAG есть ещё два режима: BASIC и DRIFT. Базовый — обычный векторный поиск по чанкам. Полезно иметь для сравнения. DRIFT Search напоминает Query expansion, но работает очень медленно — учтите при экспериментах.
Попробовал DRIFT на вопросе про Jones. За 40 минут получил результат, похожий на локальный поиск. Возможно, вопрос был слишком простым, или нужна модель побольше.
Возникает вопрос: что делать с такими ответами в продакшене? Варианты: передавать эксперту или пропускать через ещё один LLM-запрос с обработкой human_readable_id и учётом истории диалога.
Какие модели работают?
Тестируемые модели:
- Mistral 7B: не поддерживает глобальный поиск из-за проблем с JSON-выводом. Map-запросы падают.
- Gemma3 4B и 12B: результаты похожи, но формулировки корявые. Граф проще, а Jones — человек, а не дельфин.
- Qwen3 14B: результат устроил полностью. Все примеры в статье — на этой модели.
В качестве эмбеддинг-модели использовал user-bge-m3 от deepvk. Модель отлично работает на русском и справляется с английским.
Как воспроизвести эксперимент?
- Склонируйте репозиторий с проектной папкой. Переименуйте
env.exampleв.env. - Создайте и активируйте Python-окружение (например, через conda).
- Установите GraphRAG.
- Проверьте версию LiteLLM: должна быть не выше 1.82.6. Версии 1.82.7 и 1.82.8 — скомпрометированы.
- Установите пакеты для
embedding_proxy(об этом ниже). Установитеtorchс CUDA или без — на CPU тоже будет работать. - В отдельной консоли запустите прокси для эмбеддингов.
- В
settings.yamlпроверьте пути к LLM и эмбеддингу в секцияхcompletion_modelsиembedding_models. Укажитеapi_base. - В отдельной консоли загрузите LLM в Ollama.
- Остановите Ollama, установите переменные окружения:
OLLAMA_HOST=0.0.0.0:11434(если на другой машине)OLLAMA_CONTEXT_LENGTH=13000(по умолчанию — 4K, маловато для промптов) - Запустите Ollama:
ollama serve.
Чтобы использовать свои тексты или перестроить индексы:
- Удалите мой текст и поместите свои файлы в папку
input. - Удалите папки
cache,logs,output. - Запустите Ollama и
embedding_proxy. - Выполните:
graphrag index.
На машине с 16 ГБ GPU (не самой новой) обработка 38k слов («Johnny Mnemonic») занимает чуть больше часа.
Проблемы и костыли
GraphRAG использует LiteLLM как прокси. Ollama поддерживается, но иногда приходит запрос с некорректной ссылкой api/generation/api/show — возникает ошибка 404. К счастью, это не ломает пайплайн.
Запросы к LLM обрабатываются через очередь. Если ожидание превысит 10 минут — клиент отвалится. Параметр request_timeout в новых версиях GraphRAG не работает. Проблема в LiteLLM: у него таймаут 600 секунд по умолчанию для OpenAI-совместимых вызовов.
Самая серьёзная проблема — с эмбеддингами. Модели вроде bge-m3 в Ollama иногда выдают ошибку сериализации из-за Inf и NaN в JSON. Проблема известна с января 2026 года, но в версии Ollama 0.18.2 всё ещё актуальна. Поэтому я использую отдельный эмбеддинг-прокси: взял код из стороннего проекта, заменил вызов Ollama на langchain HF embedding. Криво, но зато можно использовать любые эмбеддинги с HuggingFace. По умолчанию — user-bge-m3. Чтобы сменить модель, используйте ключ model в CLI.
Если настраиваете эмбеддинг через Ollama — запускайте его отдельно, лучше на другой машине. Эмбеддинг используется в конце пайплайна, и если он упадёт — обидно после часа ожидания.
Важные настройки
Файл settings.yaml создаётся командой graphrag init. Его нужно отредактировать:
- В секциях моделей укажите провайдера, модель и
api_baseс URL. - В
vector_storeзадайте размер вектора. По умолчанию — 3072. Дляbge-m3нужно 1024, иначе пайплайн упадёт. - В
snapshotsустановитеgraphml: true, если хотите визуализировать граф.
Плюсы и минусы GraphRAG
Если не считать, что под капотом всё равно есть векторный поиск, графовая система субъективно даёт более релевантные ответы, чем QA-система на чистом векторе. Но это не замена векторному поиску — скорее специализированное решение для сложных семантических текстов, где не нужна мгновенная реакция.
Плюсы:
- Фреймворк сам выделяет сущности, связи, сообщества и генерирует описания.
- Есть API и тестовое приложение на Streamlit — можно быстро собрать MVP.
- Генератор потенциальных вопросов позволяет создавать бенчмарки и кешировать ответы (хотя я не рекомендую кешировать в RAG).
Минусы:
- Очень высокая ресурсоёмкость. На обработку одного небольшого документа уходит столько времени, сколько хватило бы на векторную базу из тысяч текстов.
- Примитивный чанкинг — по токенам или предложениям. Замена на семантический сплиттер или markdown-анализатор улучшила бы качество.
- Хочется заменить векторное хранилище.
Генерацию ответов не тестировал — ноутбук с примерами опубликован с ошибками (например, «файл не найден»). Возможно, попробую позже. Также планирую переделать промпты и построить граф на русском. Если получится — выйдет апдейт.
Злоупотребление GraphRAG может вызвать графовую зависимость. Шутка. Берегите себя!