Привет, Хабр!
Мы живем в удивительное время. Попросить LLM написать для нас код стало так же естественно, как гуглить ошибку. Но у этой магии есть предел. Попросите модель написать quickSort
, и она справится блестяще. А теперь попросите ее: «Добавь метрики Prometheus в метод processOrder
в нашем проекте».
И тут магия рушится. LLM — это гениальный, но страдающий амнезией стажер. Она знает все языки мира, но не имеет ни малейшего понятия о вашем проекте. Она не знает, какой у вас логгер, как вы обрабатываете ошибки и что у вас уже есть готовый MetricsService
. В лучшем случае вы получите общий, неидиоматичный код. В худшем — сломаете половину логики.
Стандартный RAG (Retrieval-Augmented Generation) — это как дать стажеру доступ к одному файлу. Полезно, но картину целиком он все равно не увидит. А что, если мы могли бы дать ему не просто файл, а полный доступ к знаниям тимлида-архитектора? Что, если бы LLM могла видеть не просто строки кода, а всю паутину связей, зависимостей и паттернов вашего проекта?
Сегодня я расскажу о проекте code-graph-rag-mcp — это не просто очередной RAG-пайплайн. Это полноценный MCP-сервер, который строит граф знаний вашего кода и дает LLM «архитектурное зрение», превращая ее из простого кодера в настоящего цифрового ассистента.
Архитектурные решения: Почему именно так?
Чтобы построить систему, которая была бы быстрой, локальной и умной, пришлось принять несколько ключевых архитектурных решений. Давайте разберем каждое из них.
1. Почему не REST API, а MCP (Model Context Protocol)?
Можно было бы поднять обычный Express/Fastify сервер с REST-эндпоинтами. Но это плохой выбор для такой задачи.
Проблема: REST — это протокол без состояния (stateless). Каждый запрос — новая история. А нам нужна постоянная, «живая» связь между LLM и нашим кодом. LLM должна иметь возможность задавать уточняющие вопросы в рамках одной сессии, сохраняя контекст.
Решение:
@modelcontextprotocol/sdk
. Это специализированный протокол, созданный Anthropic именно для таких задач. Он работает поверх WebSocket или IPC, обеспечивая постоянное соединение. Это позволяет LLM не просто «дергать» эндпоинты, а вести полноценный диалог с инструментами, кэшировать результаты и строить сложные цепочки вызовов. Это нативный язык общения для Claude.

2. Почему не Neo4j, а SQLite + sqlite-vec?
Граф кода — значит, нужна графовая база данных, верно? Не всегда.
Проблема: Профессиональные графовые СУБД (Neo4j, TigerGraph) — это тяжеловесные серверные решения. Они требуют отдельной установки, настройки и потребляют много ресурсов. Для локального инструмента, который каждый разработчик запускает на своей машине, это избыточно.
-
Решение:
better-sqlite3
и расширениеsqlite-vec
. Это гениальное в своей простоте решение:Zero-Configuration: SQLite — это просто файл. Никаких серверов, портов и паролей. Запустил — и работает.
Производительность:
better-sqlite3
— одна из самых быстрых реализаций SQLite для Node.js. Для локальных задач ее скорости более чем достаточно.Все в одном: Расширение
sqlite-vec
добавляет векторный поиск прямо в SQLite! Нам не нужно поднимать отдельную векторную базу (Chroma, Weaviate), что радикально упрощает стек. Граф связей и семантические векторы живут в одном файле.
3. Почему не Regex, а Tree-sitter?
Как разобрать код на десятке языков и не сойти с ума?
Проблема: Регулярные выражения — хрупкий и ненадёжный способ парсинга кода. Они ломаются на любой нестандартной конструкции. Использовать отдельные парсеры для каждого языка (Babel для JS, AST для Python) — сложно и громоздко.
-
Решение:
web-tree-sitter
. Это универсальный парсер, который:Сверхбыстрый: Написан на C и скомпилирован в WebAssembly.
Устойчив к ошибкам: Если в коде есть синтаксическая ошибка (а она есть почти всегда в процессе редактирования), Tree-sitter не падает, а строит частичное дерево. Это критически важно для инструмента, работающего в реальном времени.
Мультиязычный: Достаточно подключить готовую грамматику для нужного языка, и он работает. Это позволяет проекту легко поддерживать JS, TS, Python и добавлять новые языки в будущем.
4. Сердце системы: Код как граф в SQLite
А теперь самое главное. Где и как живет этот граф? Может, для этого нужна тяжелая графовая СУБД вроде Neo4j? Нет, и это осознанное решение.
Проблема: Профессиональные графовые СУБД — это избыточность для локального инструмента. Они требуют отдельной установки, настройки и потребляют много ресурсов.
Решение: Гениальная простота SQLite. Мы эмулируем графовую структуру с помощью двух обычных реляционных таблиц. Это классический подход, известный как "список смежности" (Adjacency List).
В файле project.db создаются всего две таблицы:
-
entities (Сущности) — это УЗЛЫ (NODES) нашего графа.
Каждая строка в этой таблице — это отдельная сущность в коде: функция, класс, переменная, интерфейс.
Хранятся ее имя, тип, путь к файлу и координаты в коде.
-
relationships (Отношения) — это РЁБРА (EDGES) нашего графа.
Каждая строка — это связь между двумя сущностями (sourceId → targetId).
-
Самое важное здесь — тип связи:
calls: функция A вызывает функцию B.
extends: класс Cat наследует класс Animal.
implements: класс UserService реализует интерфейс IUserService.
imports: файл A импортирует сущность из файла B.
Как этот граф строится?
Этот процесс автоматизирован с помощью агентной системы:
CollectorAgent сканирует файлы, с помощью Tree-sitter парсит их в AST и находит все узлы (сущности), записывая их в таблицу entities.
AnalysisAgent снова проходит по AST, но теперь ищет связи между уже найденными узлами. Находит вызов функции — создает ребро calls. Видит extends — создает ребро extends. И так далее, наполняя таблицу relationships.
В результате, без внешних зависимостей и сложных серверов, мы получаем полную, подробную и готовую к запросам карту всего нашего проекта в одном файле.
5. Семантический поиск в code-graph-rag-mcp: Поиск по смыслу, а не по словам
Представьте, что вы ищете в проекте код, отвечающий за обработку платежей. Вы можете использовать обычный поиск (Ctrl+F) по слову "payment". Но что, если разработчик назвал соответствующие функции handleTransaction
, processCharge
или executeOrder
? Обычный поиск их не найдет.
Семантический поиск решает именно эту проблему. Он ищет код не по точному совпадению ключевых слов, а по смысловой близости (семантике).
Как это реализовано в проекте (под капотом)
Процесс состоит из трех основных этапов: векторизация, хранение и поиск.
Этап 1: Векторизация кода (Создание эмбеддингов)
За этот этап отвечает SemanticAgent
.
Что происходит: Агент проходит по всем сущностям в коде (функциям, классам, методам).
-
Что он берет: Он берет не только сам код, но и, что важнее, контекстную информацию:
Название функции/класса (
authenticateUser
).Комментарии и JSDoc/Docstrings (
/** ... */
).Иногда даже имена переменных.
-
Преобразование в векторы: Используя библиотеку
@xenova/transformers
(Transformers.js), агент загружает предварительно обученную языковую модель (например,all-MiniLM-L6-v2
). Эта модель преобразует собранный текст в эмбеддинг — числовой вектор (например, массив из 384 чисел), который представляет семантическое значение этого фрагмента.Важно: Весь этот процесс происходит полностью локально на вашей машине. Ни ваш код, ни его метаданные никуда не отправляются.
Этап 2: Хранение векторов в SQLite
Куда сохранять эти числовые векторы, чтобы по ним можно было быстро искать?
Технология: Проект использует гениальное расширение для SQLite под названием
sqlite-vec
.Что оно делает:
sqlite-vec
добавляет в обычную базу данных SQLite возможность хранить векторные эмбеддинги и выполнять по ним сверхбыстрый поиск ближайших соседей (ANN, Approximate Nearest Neighbor).Преимущество: Это избавляет от необходимости поднимать отдельную векторную базу данных (Pinecone, Chroma, Weaviate). Все данные — и граф кода, и семантические векторы — лежат в одном легковесном файле
project.db
.
Этап 3: Выполнение поиска
Это происходит, когда вы задаете вопрос LLM.
Ваш запрос: Вы пишете в Claude: "Найди код, который отвечает за аутентификацию пользователя".
Действие LLM: Модель понимает, что это поисковый запрос, и вызывает инструмент
semantic_search
с вашим текстом в качестве аргумента.-
Что делает инструмент:
Он берет ваш запрос ("аутентификация пользователя") и с помощью той же модели из
@xenova/transformers
превращает его в такой же вектор.Затем он выполняет SQL-запрос к
sqlite-vec
, говоря: "Найди мне топ-5 векторов в базе, которые наиболее близки (по косинусному расстоянию) к вектору моего запроса".sqlite-vec
мгновенно находит самые релевантные фрагменты кода. Это могут быть функции с именамиlogin
,verifyToken
,jwtMiddleware
— даже если в них нет слова "аутентификация", их семантические векторы будут близки к вектору вашего запроса.
Результат: Инструмент возвращает LLM список найденных сущностей, и модель формирует для вас осмысленный ответ.
Схема работы
Ваш запрос Инструмент `semantic_search` База данных SQLite
("user auth code") ───> 1. "user auth code" → [0.1, 0.9, ...] (Вектор запроса)
2. ЗАПРОС К SQLITE:
"Найти векторы, близкие к [0.1, 0.9, ...]"
▲
│
3. ПОЛУЧИТЬ РЕЗУЛЬТАТЫ: │ (sqlite-vec)
- login() │
- verifyToken() ▼
Хранилище векторов
(для login, verifyToken, ...)
Ключевые преимущества этого подхода
Поиск по намерению: Вы можете описывать то, что ищете, своими словами, а не угадывать точные названия функций.
Устойчивость к синонимам и разным формулировкам:
payment
,charge
,transaction
— для семантического поиска это близкие по смыслу понятия.Естественный язык: Позволяет LLM использовать свою сильную сторону — понимание естественного языка — для поиска по кодовой базе.
Приватность и локальность: Весь процесс, от создания эмбеддингов до поиска, происходит на вашей машине.
В итоге, семантический поиск идеально дополняет графовый анализ. Если граф отвечает на вопрос "Как код устроен?", то семантический поиск отвечает на вопрос "Где в коде находится нужная мне функциональность?". Вместе они дают LLM беспрецедентную глубину понимания вашего проекта.
Итоговая архитектура
5. Почему не монолит, а многоагентная система?
Индексация большого проекта — сложный процесс. Его можно реализовать как один большой скрипт, но это плохая идея.
Проблема: Монолитный индексатор сложно отлаживать и масштабировать. Если на этапе анализа зависимостей произойдет сбой, весь процесс остановится.
-
Решение: Декомпозиция на агентов, каждый со своей зоной ответственности:
Collector Agent: «Разведчик». Быстро сканирует файлы и строит базовый AST. Его задача — собрать сырые данные.
Analysis Agent: «Аналитик». Берет сырые данные и обогащает их, находя связи: кто кого вызывает, кто от кого наследуется.
Semantic Agent: «Лингвист». Создает векторные эмбеддинги для семантического поиска.
Refactoring Agent: «Техлид». Ищет дубликаты, анализирует сложность и находит «узкие места». Такой подход позволяет распараллелить работу, сделать систему отказоустойчивой и легко добавлять новые виды анализа в будущем.
Итоговая архитектура
╔════════════════════╗ ╔════════════════════╗
║ Claude Desktop ║<═════║ MCP Server ║
║ (или другой клиент)║ ║ (на Node.js + SDK) ║
╚════════════════════╝ ╚═════════╦══════════╝
║ (Запросы через 13 инструментов)
▼
╔════════════════════════════════════════════════════════════════════════╗
║ База знаний (Knowledge Base) ║
║ ┌────────────────────────────────┐ ║
║ │ SQLite файл (project.db) │ ║
║ │ │ ║
║ │ • Граф кода (таблицы) │ ║
║ │ • Векторы (sqlite-vec) │ ║
║ └────────────────────────────────┘ ║
╚═══════════════════════════════════════╦════════════════════════════════╝
║ (Построение и обновление)
┌──────────────────┐ ┌─────────────┴────────────┐ ┌──────────────────────────┐
│ Кодовая база ├─────▶│ Парсинг (Tree-sitter) ├─────▶│ Агентная система │
│ (JS, TS, Python) │ └──────────────────────────┘ │ (Collector, Analyzer...) │
└──────────────────┘ └──────────────────────────┘
13 мощных инструментов: Арсенал вашего AI-ассистента
Сервер предоставляет 13 специализированных инструментов, которые LLM может использовать для анализа вашего проекта:
Основные:
get_entities
,semantic_search
,find_similar_code
,get_entity_details
,search_by_pattern
.Анализ связей:
get_relationships
,analyze_dependencies
,get_call_graph
,impact_analysis
.Рефакторинг:
suggest_refactoring
,find_duplicates
,analyze_complexity
,find_hotspots
.
Производительность: 5.5x быстрее нативных инструментов
Метрика |
Нативный Claude |
MCP CodeGraph |
Улучшение |
---|---|---|---|
Время выполнения |
55.84 с |
<10 с |
5.5x быстрее |
Потребление памяти |
Зависит от процесса |
~65MB |
Оптимизировано |
Количество функций |
Базовые паттерны |
13 инструментов |
Комплексный анализ |
Точность |
На основе паттернов |
Семантическая |
Превосходящая |
Технические характеристики:
Скорость индексации: 100+ файлов в секунду.
Время ответа на запросы: <100 мс.
Практический пример: От вопроса к инсайту за секунды
Запрос в Claude Desktop:
> «Что сломается, если я изменю интерфейс IUserService
?».
Что происходит «под капотом»:
LLM видит ключевые слова «изменю» и «интерфейс» и вызывает инструмент
impact_analysis
с аргументомIUserService
.Сервер мгновенно выполняет запрос к своему графу в SQLite: «Найти все сущности, которые реализуют или напрямую зависят от
IUserService
».Сервер возвращает список классов (
UserService
,AdminController
) и файлов, которые будут затронуты.LLM получает этот структурированный список и генерирует человекочитаемый ответ: «Изменение
IUserService
затронет классUserService
, который его реализует, иAdminController
, который использует его для инъекции зависимостей. Вам потребуется обновить реализацию в этих файлах».
Это не просто поиск по тексту. Это глубокий анализ, основанный на понимании структуры кода.
Установка и интеграция
Начать работу проще простого.
Установка:
npm install -g @er77/code-graph-rag-mcp
Настройка Claude Desktop:
npx @modelcontextprotocol/inspector add code-graph-rag \
--command "npx" \
--args "@er77/code-graph-rag-mcp /path/to/your/codebase"
После этого просто выберите code-graph-rag
в списке инструментов в Claude и начинайте задавать вопросы о своем проекте.
Заключение
Проект code-graph-rag-mcp
— это шаг от «LLM как генератора текста» к «LLM как члена команды». Предоставляя модели глубокий контекст через графы знаний, мы открываем совершенно новые возможности для автоматизации рутинных задач, анализа сложных систем и безопасного рефакторинга.
Выбранный технологический стек — MCP, Tree-sitter и SQLite — не случаен. Он является результатом поиска баланса между производительностью, простотой использования и мощностью. Это локальный, приватный и невероятно быстрый инструмент, который может стать вашим незаменимым помощником в разработке.
Попробуйте сами и дайте своей LLM «суперсилу» — знание вашего кода.
Комментарии (0)
Dharmendra
17.09.2025 20:38идея отличная, но есть немного дегтя сугубого имхо:
- заваливать LLM еще одной кучей тулов через MCP - путь к запутыванию модели, ей и так не просто если прицепили Jira, Github и еще парочку.
- про rerank я что-то не нашел в статье ничего
- использование простеньких моделей для embedding - медленно, печально, слабо и частая зарядка ноута :), а хороших - нужен хоть какой то GPU с VRAM (или mac studio m3).
- скорость отработки через mcp - имхо, это не нативно юзать фреймворки а-ля LangChain/Graph иже с ними. Но, конечно, статья не про написание своего агента, а про юзание готового с расширением обвески-фичей. Тут наверное идея правильная от безысходности. Спасибо за идею, будем думать.
SweetDreamBoss
Интересно, но всё таки достаточно сложный процесс для обычных обывателей, которые хотят просто ввести свой проект
Я разработал нечто подобное, но назвал Капсула памяти, суть в том что в конце каждой сессии ты прописываешь триггер, в том же чате, и капсула обновляется учитывая сегодняшним доработки, сохраняя знания, опыт и контекст
AppCrafter
А можно подробнее?
SweetDreamBoss
Я написал статью здесь, жду когда пройдет модерацию.
Если кратко:
Капсула памяти Sh/AIDS v6.2 — это система сохранения и передачи контекста для ИИ, которая решает ключевую проблему «потери памяти» между сессиями(LLM).
Ключевые преимущества:
· Мгновенное восстановление контекста — ИИ сразу работает с вашими задачами, без повторных объяснений
· Экономия 90% времени — исключает рутину онбординга в каждом новом диалоге
· Персонализированные ответы — ИИ учитывает ваш стиль мышления, цели и предпочтения
· Масштабируемость — подходит для личного использования, командной работы и бизнес-процессов
· Повышение качества решений — за счёт полного контекста ИИ дает более точные и релевантные ответы
Основная польза: Превращает разрозненные диалоги с ИИ в непрерывный осмысленный процесс,где каждое взаимодействие строится на основе предыдущего опыта. Это особенно ценно для сложных задач, требующих долгосрочной работы: обучение, проектирование, анализ данных и карьерное развитие.