Продвинутый

Graphify: локальный граф знаний из кода без векторов

Алексей Кузнецов
Алексей Кузнецов
Системный администратор5 ноября 2026 г.13 мин чтения

Graphify превращает код, документацию, SQL-схемы и PDF в граф знаний. Локальный детерминированный парсинг AST без векторного хранилища. Навык для Claude Code

Graphify: локальный граф знаний из кода без векторов

Graphify превращает код, документацию, SQL-схемы и PDF в граф знаний. Локальный детерминированный парсинг AST без векторного хранилища. Навык для Claude Code

Что делает Graphify и зачем это нужно

Что делает Graphify и зачем это нужно

Graphify — это навык для Claude Code, который собирает локальный граф знаний из кодовой базы, документации, SQL-схем и PDF. Нет векторного хранилища, нет эмбеддингов, нет зависимости от внешних API. Парсинг AST детерминированный: один и тот же коммит даёт один и тот же граф.

Для меня это важно по одной причине. Когда прихожу в новый проект, первое, что делаю — пытаюсь понять, как части связаны между собой. Открываю файл, ищу импорты, перехожу по определениям, смотрю, кто вызывает эту функцию. Graphify автоматизирует этот процесс и фиксирует результат в графе, который можно обходить и запрашивать.

От кодовой базы к графу знаний

Graphify сканирует репозиторий, парсит поддерживаемые языки через AST и извлекает три вещи: определения (функции, классы, таблицы), связи (импорты, вызовы, внешние ключи) и метаданные (путь к файлу, номер строки, язык). Для PDF и Markdown он вытаскивает текст и заголовки, для SQL — таблицы и связи между ними.

Установить навык можно через репозиторий с навыками для ИИ-агентов. Подключение выглядит стандартно — описание в YAML, список парсеров, источники и выходной формат.

Пример конфига:

yaml
# .claude/skills/graphify.yaml
name: graphify
description: Строит граф знаний из кодовой базы
parsers:
  - python
  - typescript
  - go
  - sql
sources:
  - path: ./src
  - path: ./docs
  - path: ./schema.sql
output:
  format: graphml
  path: ./knowledge-graph.graphml
exclude:
  - node_modules
  - .git
  - dist
  - "*.test.ts"
  - "*.spec.py"

После запуска навыка в Claude Code получается файл графа. Каждый узел — это отдельный элемент кода или документации, каждое ребро — связь между ними. Пример узла в JSON:

json
{
  "id": "src/services/user.service.ts:UserService.createUser",
  "type": "method",
  "language": "typescript",
  "file": "src/services/user.service.ts",
  "line": 42,
  "calls": [
    "src/services/email.service.ts:EmailService.send"
  ],
  "called_by": [
    "src/controllers/user.controller.ts:UserController.register"
  ]
}

С практической точки зрения, это значит, что я могу спросить у агента: «Кто вызывает функцию createUser?» и получить точный ответ, а не предположение на основе семантического сходства.

Если нужно подключить навык к агенту, порядок действий описан в руководстве по подключению навыков из mattpocock/skills к ИИ-агенту, а обзор самих навыков — в этой статье.

Почему без векторного хранилища

Векторные хранилища ищут по сходству. Введи запрос «оплата», модель найдёт всё, что семантически близко к оплате: функции, комментарии, переменные. Это удобно для поиска по документации, но не для понимания кода. Мне нужно знать, что process_payment вызывает validate_card, а не то, что где-то рядом упоминается слово «карта».

Векторный поиск напоминает поиск книги в библиотеке по обложке: иногда попадается нужное, но чаще — что-то похожее. Граф знаний работает как оглавление с указателем: видно точно, где что находится и как связано.

Graphify работает иначе. Он не угадывает, он читает AST. Результат детерминированный: тот же код даёт тот же граф, и этот граф можно версионировать вместе с репозиторием.

Разница на примере:

Векторный поискGraphify
Находит «похожие» фрагментыПоказывает точные вызовы и зависимости
Требует эмбеддингов и хранилищаРаботает локально на ноутбуке
Результат может меняться от модели к моделиРезультат одинаковый при одинаковом коде
Показывает сходство, а не связьПоказывает ребра графа: кто вызывает, кто наследует, кто ссылается

Для кода предсказуемость важнее, чем семантическое приближение. Если я рефакторю сервис, хочу видеть все места, где он используется, а не те, которые «похожи» на использование.

Ещё один нюанс — локальность. Весь парсинг происходит на машине, код не уходит никуда. Это важно, если работаешь с закрытыми репозиториями или под NDA.

Если хочешь посмотреть, как Graphify выглядит в действии с кодом, конфигами и PDF, есть отдельная статья: Graphify: код, конфиги и PDF в графе знаний.

Как работает локальный парсинг AST

Как работает локальный парсинг AST

Векторные хранилища решают задачу семантического поиска — но вносят неопределённость. Одна и та же функция может получить разный эмбеддинг в зависимости от модели, версии библиотеки или даже случайного зерна. Для анализа кода это проблема: вы не можете воспроизвести результат парсинга через месяц с теми же исходниками.

Graphify идёт другим путём — он строит граф знаний из кода, SQL-схем и PDF с помощью детерминированного обхода AST. На входе — файлы репозитория, на выходе — узлы и рёбра, которые не меняются без изменения исходников. Никаких векторов, никаких приближений.

Детерминированный обход кода

Парсер Graphify проходит по исходному коду и строит абстрактное синтаксическое дерево. Каждый узел — это конкретная сущность: функция, класс, импорт, вызов. Каждое ребро — точная связь, например «функция A вызывает функцию B» или «модуль C импортирует модуль D».

С практической точки зрения это означает предсказуемость. Если вы запустили парсинг сегодня и завтра на одном и том же коммите, получите идентичный граф. Это важно для ИИ-агентов: Claude Code может полагаться на структуру, которая не «плывёт» от запуска к запуску.

Пример. Допустим, есть простой Python-модуль:

python
# services/users.py
from database import SessionLocal

def get_user(user_id: int):
    session = SessionLocal()
    return session.query(User).filter_by(id=user_id).first()

Graphify извлечёт из этого узлы:

  • модуль services.users;
  • функцию get_user с аргументом user_id;
  • импорт SessionLocal из database;
  • вызов SessionLocal();
  • вызов session.query(...).

И рёбра между ними. Результат можно посмотреть в формате, удобном для Claude Code:

json
{
  "nodes": [
    {"id": "file:services/users.py", "type": "file", "language": "python"},
    {"id": "func:get_user", "type": "function", "file": "services/users.py"},
    {"id": "import:SessionLocal", "type": "import", "source": "database"}
  ],
  "edges": [
    {"from": "file:services/users.py", "to": "func:get_user", "label": "contains"},
    {"from": "func:get_user", "to": "import:SessionLocal", "label": "calls"},
    {"from": "func:get_user", "to": "import:SessionLocal", "label": "imports"}
  ]
}

Во-первых, такой обход не зависит от того, как называются переменные внутри функции — важна структура. Во-вторых, изменение комментария не пересоздаёт граф, если не меняется сама сигнатура или вызовы.

Для Claude Code это навык: агент запрашивает у Graphify текущее состояние графа и получает точную карту зависимостей, а не предположение векторной модели. Подробнее про подключение навыков можно прочитать в руководстве по навыкам из mattpocock/skills.

Связи между файлами, схемами и PDF

Код редко существует изолированно. Функция из примера выше работает с таблицей users в базе, её поведение описано в технической документации, а архитектурное решение — в PDF с отчётом по рефакторингу. Graphify связывает всё это в один граф.

Обход кода даёт узлы для каждого файла. Далее Graphify разрешает ссылки между ними:

  • импорты и require — ребра между исходниками;
  • SQL-схемы — таблицы и колонки становятся узлами, а запросы в коде — рёбрами «использует таблицу» или «читает колонку»;
  • PDF и Markdown — извлекаются заголовки и блоки кода, которые ссылаются на конкретные функции или таблицы.

Пример конфигурации, которую я пишу вручную, даже если в проекте есть GUI:

yaml
# graphify.yaml
sources:
  - path: ./src
    language: python
    parser: ast
  - path: ./schema.sql
    parser: sql
  - path: ./docs
    include:
      - "*.md"
      - "*.pdf"
    parser: text

output:
  format: json
  file: ./knowledge-graph.json

После запуска Graphify получаем связный граф. Например, функция get_user связана с таблицей users через SQL-запрос, а таблица — с разделом «Схема пользователей» в PDF-документации. Если разработчик переименует колонку в SQL, изменится только соответствующий узел и инцидентные рёбра, остальной граф останется нетронутым.

Это важно для аудита. Если мы посмотрим на граф через шесть месяцев, мы увидим те же связи, что и сегодня, при условии что код не менялся. Векторное хранилище такого гарантировать не может.

Ограничение здесь очевидное: динамически сгенерированный код или языки без поддержки парсера не попадут в граф. Если в проекте используется метапрограммирование, которое создаёт функции в рантайме, Graphify увидит только то, что записано в исходниках, а не то, что появится при выполнении.

Если нужно подключить навык к агенту, смотрите коллекцию навыков для ИИ-агентов из репозитория mattpocock/skills. А если интересно, как другие инструменты работают с кодом, есть разбор Momentic: QA-агенты ищут баги в приложениях.

Навык /graphify для Claude Code и других CLI

Подключение к Claude Code

Claude Code подключает навыки через файл CLAUDE.md в корне проекта. Graphify поставляется как готовый набор правил и скриптов, которые агент подхватывает автоматически.

Во-первых, добавьте навык в проект. Я использую git-субмодуль — так проще откатиться, если новая версия парсера сломает совместимость:

bash
cd ~/projects/my-app
git submodule add https://github.com/example/graphify-skill.git .claude/skills/graphify

Затем укажите путь к навыку в CLAUDE.md:

markdown
## Навыки

- .claude/skills/graphify/skill.yaml

Перезапустите Claude Code — появится команда /graphify.

С практической точки зрения, навык состоит из трёх частей: парсер AST, построитель графа и набор промптов. Парсинг происходит локально, код не уходит на сторонние серверы. Это критично, если вы работаете с закрытым репозиторием.

Cursor, Codex, Gemini CLI

Graphify не привязан только к Claude Code. Формат навыка совместим с любым CLI-агентом, который читает системные инструкции из markdown-файлов.

Для Cursor добавьте правило в .cursor/rules:

json
{
  "rules": [
    {
      "name": "graphify",
      "path": ".claude/skills/graphify/skill.yaml"
    }
  ]
}

Codex и Gemini CLI подключаются по аналогии — достаточно подключить файл с правилами в контекст сессии. Я проверял на Codex: навык заработал без правок, потому что Graphify следует стандартному формату из экосистемы навыков для ИИ-агентов из репозитория mattpocock/skills.

Если вы уже подключали навыки из этого репозитория, порядок действий будет таким же. Подробнее про подключение навыков из mattpocock/skills написано в отдельной инструкции здесь.

Пример запроса к графу

После подключения навыка вы можете спрашивать у агента не только про отдельные файлы, но и про связи между ними.

Пример запроса:

/graphify найди все функции, которые вызывают authenticate, и покажи, какие SQL-запросы они выполняют

Агент обойдёт граф, найдёт вершины функций и рёбра вызовов, и вернёт ответ:

Функция `handle_login` в `src/auth/service.py` вызывает `authenticate` и выполняет запрос:
SELECT * FROM users WHERE email = $1

Функция `refresh_session` в `src/auth/tokens.py` вызывает `authenticate` и выполняет:
UPDATE sessions SET expires_at = $1 WHERE user_id = $2

Можно запрашивать и по SQL-схемам:

/graphify какие таблицы зависят от таблицы users и через какие внешние ключи

Graphify строит граф детерминированно: тот же код даст тот же граф. Нет векторного хранилища, нет приблизительного поиска, нет неожиданных результатов после переиндексации. Для меня это главное — предсказуемость конфигурации.

Пересобрать граф после изменений в коде можно командой:

bash
graphify build --source ./src --output ./graph.json

Команда обходит директорию, парсит AST поддерживаемых языков и сохраняет граф в JSON. Поддерживаются Python, JavaScript, TypeScript, Go и SQL-схемы.

Если хотите посмотреть, как Graphify работает с конфигами и PDF, читайте основную статью Graphify: код, конфиги и PDF в графе знаний.

Итоги и ограничения подхода

Итоги и ограничения подхода

Что работает, а что нет

Graphify строит граф на основе AST — абстрактного синтаксического дерева. Это даёт точные связи: импорты, вызовы функций, определения классов, таблицы и внешние ключи в SQL-схемах. Всё детерминировано: один и тот же код даёт один и тот же граф. Нет эмбеддингов, нет векторного хранилища, нет неопределённости с косинусным сходством.

С практической точки зрения это выглядит так:

  • В Python-коде from models import User становится ребром между модулем и моделью. Не «похоже на», а именно так.
  • SQL-схема превращается в узлы таблиц и рёбра внешних ключей. Можно пройти от orders к users по user_id и увидеть все промежуточные объекты.
  • PDF и документация извлекаются как текстовые узлы, привязанные к исходному файлу.

Graphify позиционируется как навык для Claude Code. Если вы ещё не подключали навыки из репозитория mattpocock/skills, порядок действий описан в руководстве Подключение навыков из mattpocock/skills к ИИ-агенту, а обзор самого репозитория — в материале Навыки для ИИ-агентов из репозитория mattpocock/skills.

Обратная сторона — Graphify не понимает смысл. Если спросить «как работает аутентификация?», а в коде нет функции с таким именем, граф не поможет. Векторное хранилище нашло бы описание OAuth2 по смысловому сходству, а AST — только при точном совпадении символа или импорта.

Динамический код остаётся за скобками. eval, importlib, рефлексия в Java — ничего из этого не видно в статическом дереве. Например:

python
# Graphify не увидит эту связь при статическом парсинге
module_name = get_module_name_from_config()
handler = importlib.import_module(module_name)

Если код меняется чаще, чем пересобирается граф, узлы устаревают. Я встречал случаи, когда разработчики вносили правки в ветку, а граф всё ещё показывал старую сигнатуру функции — и действовали, основываясь на неактуальных данных.

Когда граф знаний перестанет быть актуальным

Граф теряет ценность, когда код перестаёт быть основным источником истины. Вот конкретные ситуации.

Во-первых, команда переходит на семантический поиск. Если разработчики привыкли спрашивать у ИИ «где происходит списание средств?» и получать ответ по смысловому сходству, детерминированный граф покажется ограниченным. Без векторов Graphify не найдёт «списание», если в коде используется слово charge или deduct.

Во-вторых, код меняется быстрее, чем пересобирается граф. На активном проекте с десятком коммитов в день актуальность падаёт через несколько часов после сборки. Если нет хука в CI, который пересобирает граф при каждом пуше, команда получит ложную уверенность в связях, которые уже не существуют.

В-третьих, большая часть логики спрятана за динамикой. Микросервисы на Node.js с динамическим импортом, плагины, загружаемые в рантайме, конфигурация через YAML, которая определяет порядок вызовов — всё это не попадёт в AST. Граф получится фрагментарным, и доверять ему нельзя.

Если мы посмотрим на микросервисную архитектуру, связь между репозиториями тоже остаётся за скобками. Graphify работает в рамках одного набора файлов. Если order-service вызывает payment-service через HTTP, граф этого не покажет. Для такого уровня нужен отдельный инструмент инфраструктурного отслеживания, а не парсинг кода.

И последнее: PDF без текстового слоя. Сканированные инструкции, скриншоты с текстом, чертежи — Graphify не распознает. Нужен предварительный OCR, иначе узел будет пустым.

С практической точки зрения я использую Graphify там, где важна предсказуемость: навигация по кодовой базе, аудит зависимостей, построение документации по API. Для семантических вопросов — «почему эта функция работает так?» — лучше подходит векторное хранилище или обычный поиск по grep с контекстом.

Часто задаваемые вопросы

Зачем строить граф, если в IDE уже есть поиск по импортам и определениям?

IDE показывает связи только внутри одного проекта, а Graphify связывает код с документацией, SQL-схемой и PDF в одном графе. Вы получаете карту зависимостей, которую можно обходить вне редактора. С практической точки зрения, это даёт предсказуемость: один и тот же коммит всегда порождает одинаковый граф.

Можно ли использовать полученный граф без Claude Code?

Навык записывает результат в GraphML, поэтому граф открывается любым инструментом для визуализации — Gephi, yEd или собственным скриптом на Python. Claude Code нужен только для сборки, а дальше файл лежит в репозитории как обычный артефакт. Если нужно, можно сверять версии графа между коммитами и видеть, как изменилась архитектура.

Почему нет векторного хранилища и эмбеддингов?

Векторный поиск возвращает вероятностный результат: один и тот же запрос может выдать разные узлы в зависимости от модели эмбеддингов. Graphify парсит AST детерминированно, поэтому одинаковый код даёт одинаковый граф без случайности. Для меня это критично, когда нужно точно знать, какая функция вызывает какую, а не угадывать по косинусному сходству.

Как часто нужно перестраивать граф после изменений в коде?

Граф не перестраивается автоматически при каждом коммите, его нужно запускать вручную или по расписанию. Для небольшого проекта достаточно обновлять его перед началом работы или после слияния ветки. Если репозиторий большой, исключите тесты и папку сборки, иначе парсинг займёт лишнее время.

Какие языки и форматы поддерживает Graphify?

Из коробки поддерживаются Python, TypeScript, Go и SQL, плюс извлечение текста из Markdown и PDF. Список источников и исключений настраивается в YAML-конфиге, так что можно добавить свои парсеры или убрать лишнее. Если язык не поддерживается, навык просто пропустит такие файлы и продолжит работу с остальными.

Поделиться:TelegramX / TwitterVK

Читайте также