Средний

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

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

Как Graphify превращает исходники, документацию и схемы БД в запрашиваемый граф знаний через детерминированный AST-парсинг.

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

Как Graphify превращает исходники, документацию и схемы БД в запрашиваемый граф знаний через детерминированный AST-парсинг.

Как работает детерминированный парсинг

Как работает детерминированный парсинг

Когда мы говорим о понимании кода, большинство современных решений полагается на семантический поиск. Однако в сложных проектах «похожесть» векторов не гарантирует точности. Если вам нужно найти, какой именно метод вызывает функцию init_db() в конкретном модуле, векторный поиск может выдать десяток похожих по смыслу функций, но промахнуться мимо нужной реализации.

Graphify решает эту проблему через детерминированный подход. Вместо того чтобы превращать код в облако чисел, инструмент разбирает его на составляющие, используя абстрактное синтаксическое дерево (AST). Это позволяет строить связи, которые не зависят от того, насколько «похож» текст функции на запрос.

AST вместо эмбеддингов

Традиционный путь построения RAG-систем (Retrieval-Augmented Generation) для кода — это нарезка файлов на куски (chunks), создание эмбеддингов и хранение их в векторной базе. Проблема здесь в потере контекста: когда кусок кода обрывается на середине определения класса, связь между этим классом и его методами разрывается.

Graphify использует AST-парсинг. Процесс выглядит иначе: парсер обходит дерево синтаксиса и извлекает строго определённые сущности. Если мы анализируем Python-проект, парсер не просто видит текст, он понимает иерархию: Module $\rightarrow$ ClassDef $\rightarrow$ FunctionDef $\rightarrow$ Arguments.

С практической точки зрения это даёт предсказуемость. Если вы ищете зависимость, вы получаете точный путь по дереву, а не «наиболее вероятный вариант». Это критически важно при использовании локальных ИИ-агентов и инструментов, где цена галлюцинации агента — это сломанный билд или неверная правка в логике.

Для понимания того, как глубоко можно уйти в анализ структуры, стоит изучить подходы к автономной разработке на Rust с помощью Claw-Code. Там также важна точность интерпретации синтаксиса для корректного применения изменений.

Пример того, как выглядит упрощённое представление AST для функции на Python, которое затем превращается в узел графа:

python
# Исходный код
def calculate_total(price: float, tax: float) -> float:
    return price + (price * tax)

# Логическая структура, которую извлекает Graphify (упрощенно)
{
  "type": "FunctionDef",
  "name": "calculate_total",
  "args": ["price", "tax"],
  "returns": "float",
  "body": [
    {
      "type": "Return",
      "value": {
        "type": "BinOp",
        "left": "price",
        "op": "Add",
        "right": "..."
      }
    }
  ]
}

Типы узлов и рёбер в графе

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

Узлы (Nodes) представляют собой сущности кода или инфраструктуры:

  • CodeEntity: функции, классы, модули, переменные.
  • SchemaEntity: таблицы БД, колонки, индексы (извлекаются из миграций или схем).
  • DocEntity: блоки docstrings, README-файлы, комментарии.

Рёбра (Edges) описывают отношения между ними. Именно здесь кроется сила детерминизма:

  • CALLS: функция А вызывает функцию Б.
  • DEFINES: класс определяет метод.
  • REFERENCES: переменная использует значение из другого модуля.
  • DEPENDS_ON: модуль импортирует другой модуль.
  • MAPS_TO: поле в коде соответствует колонке в базе данных.

Если мы посмотрим на архитектуру оптимизации ИИ-агентов с помощью системы ECC, станет ясно, что точность навигации по графу напрямую влияет на производительность агента. Чем меньше лишних «путей» предлагает граф, тем быстрее и точнее работает агент.

Представьте, что вам нужно проследить путь данных от API-эндпоинта до конкретной таблицы в PostgreSQL. В векторной базе вы будете искать «эндпоинт, который пишет в таблицу». В графе Graphify вы пройдёте по цепочке: Endpoint (Node) $\xrightarrow{calls}$ ServiceMethod (Node) $\xrightarrow{executes}$ SQLQuery (Node) $\xrightarrow{targets}$ Table (Node).

Такая структура позволяет строить сложные запросы, которые невозможно реализовать через обычный поиск. Например, можно найти все функции, которые используют переменную user_id и при этом не имеют обработки исключений, опираясь на структуру AST и связи в графе.

Интеграция с AI-ассистентами

Интеграция с AI-ассистентами

Смысл использования Graphify проявляется тогда, когда вы перестаёте кормить нейросеть «сырым» контекстом и начинаете передавать структурированные знания. Если обычный RAG (Retrieval-Augmented Generation) часто ошибается, пытаясь найти похожие куски текста через векторный поиск, то детерминированный граф позволяет AI-агенту точно понимать связи между вызовом функции в одном модуле и определением схемы таблицы в другом. Это критически важно, когда вы используете локальные ИИ-агенты и инструменты, где точность контекста напрямую влияет на стоимость и скорость работы.

Навык /graphify для Claude Code

Claude Code — это мощный инструмент командной строки, но его понимание структуры проекта ограничено тем, что он успеет прочитать через grep или ls. Чтобы превратить его из простого помощника в полноценного архитектора, мы можем использовать механизм кастомных навыков (skills).

Для интеграции Graphify вам нужно создать конфигурационный файл, который будет описывать команду вызова графа. В корне вашего проекта создайте директорию .claude/ и добавьте туда файл skills.json.

json
{
  "skills": [
    {
      "name": "/graphify",
      "description": "Запрос графа знаний проекта для понимания связей между кодом, документацией и схемой БД",
      "command": "graphify query --context \"$1\" --format json"
    }
  ]
}

Теперь, когда вы спросите Claude: «/graphify как работает процесс авторизации пользователя?», агент не будет просто искать слово «auth» по всем файлам. Он выполнит команду, получит JSON-структуру связей (от контроллера к сервису, от сервиса к репозиторию и далее к SQL-схеме) и построит логически верный ответ. С точки зрения предсказуемости — это на порядок выше, чем попытки модели угадать зависимости по косвенным признакам.

Подключение к Cursor и Gemini CLI

С Cursor ситуация интереснее. Поскольку это форк VS Code, у него нет прямого API для внедрения произвольных команд в его внутренний индекс, но мы можем использовать подход через внешние инструменты анализа.

Если вы работаете в Cursor, наиболее эффективный способ — настроить Graphify как часть вашего рабочего процесса через терминал или скрипты автоматизации. Это особенно полезно при автономной разработке на Rust с помощью Claw-Code, где сложность типов и макросов требует глубокого понимания AST.

Для Gemini CLI (и других CLI-инструментов, работающих с LLM) интеграция сводится к конвейеру (pipeline). Вы можете передавать результат запроса к графу напрямую в качестве системного промпта или контекста.

Пример Bash-скрипта для анализа сложного рефакторинга:

bash
#!/bin/bash
# Скрипт для передачи контекста из графа в Gemini CLI

QUERY="связи между модулем оплаты и таблицей transactions"
CONTEXT=$(graphify query --context "$QUERY" --format markdown)

# Передаем полученный контекст в Gemini
gemini-cli prompt "Используя следующий контекст графа знаний, предложи план рефакторинга: $CONTEXT"

Такой подход позволяет избежать «галлюцинаций» модели, вызванных неполным контекстом. Вместо того чтобы пытаться оптимизировать ИИ-агентов с помощью системы ECC за счёт изменения промптов, вы решаете проблему на уровне данных, предоставляя агенту чистую, детерминированную структуру. Это делает работу с любыми автономными инструментами вроде Claw-code более стабильной и контролируемой.

Работа с документацией и схемами

Работа с документацией и схемами

Когда мы говорим о построении графа знаний, многие совершают одну и ту же ошибку: пытаются скормить всё подряд векторной базе данных. Но векторы хороши для поиска по сходству, а не для понимания структуры. Если вам нужно точно знать, какой внешний ключ связывает таблицу orders с users, векторный поиск может ошибиться, выдав «похожий» фрагмент текста. Graphify решает эту проблему через детерминированный разбор, превращая неструктурированные данные в четкие узлы и ребра.

Парсинг SQL-схем и конфигов

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

Процесс выглядит так: парсер обходит дерево синтаксиса, идентифицирует ключевые сущности (CREATE TABLE, ALTER TABLE) и, что самое важное, извлекает зависимости. Мы не просто записываем названия таблиц, мы фиксируем связи FOREIGN KEY, типы данных и ограничения UNIQUE или NOT NULL. Это обеспечивает предсказуемость графа: вы всегда можете построить запрос, который покажет путь от конкретного поля в одной таблице до связанного с ним поля в другой.

Для автоматизации этого процесса можно использовать CLI-инструментарий. Например, чтобы прогнать схему через анализатор, команда выглядит так:

bash
# Запуск парсинга SQL-схемы для построения графа связей
graphify analyze --type sql --input ./schemas/production_db.sql --output ./graph/db_structure.json

Если вы работаете с конфигурационными файлами (например, YAML в Kubernetes или Nginx), подход аналогичен. Мы разбираем структуру вложенности, превращая каждый уровень конфигурации в узел графа. Это позволяет, например, увидеть, какие сервисы зависят от конкретного ConfigMap. Такой системный подход к анализу инфраструктуры часто применяется при настройке локальных ИИ-агентов и инструментов, когда агенту нужно понимать контекст окружения без лишнего шума.

Извлечение связей из PDF и Markdown

Документация часто живет отдельно от кода, и это главная проблема при анализе кодовой базы. Markdown — идеальный формат для Graphify, так как его структура предсказуема. Мы парсим заголовки, списки и ссылки, превращая их в иерархические связи. Если в документации указано, что «Модуль А использует API Модуля Б», Graphify создает ребро между соответствующими узлами.

С PDF ситуация сложнее. Это формат, который не предназначен для машинного чтения, поэтому здесь мы применяем многослойный подход. Сначала происходит нормализация текста, а затем применяется семантический разбор структурных элементов (таблиц, списков, подзаголовков).

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

Пример конфигурации для задания правил извлечения связей из документации:

yaml
# Настройка правил парсинга документации
documentation_rules:
  markdown:
    extract_links: true        # Создавать узлы для всех внутренних ссылок
    hierarchy_depth: 3         # Глубина вложенности заголовков для графа
  pdf:
    ocr_enabled: false         # Используем текстовый слой, если он есть
    table_extraction: strict   # Строгий разбор таблиц для сохранения связей

Такая точность критически важна при использовании продвинутых методов разработки, таких как автономная разработка на Rust с помощью Claw-Code. Когда агент понимает не только синтаксис, но и архитектурные связи, описанные в документации, вероятность ошибок при рефакторинге или добавлении новых функций снижается в разы.

В конечном счете, работа с документацией в Graphify — это не просто «чтение текста», а перевод человеческих описаний на язык графовых структур, который понятен и человеку, и машине.

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

Производительность на больших репозиториях

Во-первых, парсинг всего дерева репозитория с десятками тысяч файлов требует значительных ресурсов. На практике я сталкивался с репозиторием объёмом ≈ 12 ГБ, где количество файлов превышало 35 000. При запуске парсера без ограничения потоков процесс занимал почти все ядра и потреблял более 8 ГБ оперативной памяти, а время завершения превышало 45 минут.

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

bash
# Ограничиваем количество файлов до 5000 и запускаем 6 потоков
time graphify parse \
  --input /path/to/big-repo \
  --max-files 5000 \
  --threads 6

Команда time показывает реальное время выполнения, что позволяет оценить влияние параметров на производительность. Если требуется ещё ускорить процесс, можно предварительно отфильтровать файлы по расширению:

bash
find /path/to/big-repo -type f \( -name "*.py" -o -name "*.js" -o -name "*.go" \) \
  | xargs -n 1000 -P 6 graphify parse --input -

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

yaml
cache:
  enabled: true
  directory: /var/cache/graphify
  max_age_hours: 24

Кэш позволяет повторно использовать уже построенные AST‑деревья без повторного парсинга, что особенно полезно при итеративных проверках изменений.

Ссылка на материалы, где обсуждаются подходы к оптимизации производительности: Оптимизация ИИ-агентов с помощью системы ECC и affaan-m/ECC: AI Agent Harness Performance Optimization.

Что теряется без векторного поиска

В Graphify используется детерминированный AST‑парсинг, который фиксирует структуру кода, но не его смысл. Поэтому при отсутствии вектора поиска вы теряете возможность выполнять семантическую (смысловую) фильтрацию. Например, запрос «как получить данные из API» не найдёт функции с похожим поведением, если её имя отличается («retrieve data», «fetch data», «load data»).

Пример запроса без векторного поиска:

bash
graphify search --query "получить данные" --repo /path/to/repo

Результат может быть пустым, потому что точные совпадения строк в комментариях или в названиях функций отсутствуют.

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

Связанные темы: Локальные ИИ-агенты и инструменты и Claw-code: автономный музейный экспонат на Rust рассматривают, как семантический поиск интегрируется в рабочие потоки.

Таким образом, без векторного поиска Graphify остаётся мощным инструментом для точного поиска по структуре, но теряет гибкость, необходимую для поиска по смыслу и контексту. Это ограничение следует учитывать при выборе сценария использования.

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

Как Graphify отличается от векторных поисковых систем при работе с кодом?

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

Что такое AST‑парсинг и почему он обеспечивает точность?

AST‑парсинг — это процесс обхода абстрактного синтаксического дерева, которое представляет структуру кода в виде иерархии узлов. Парсер понимает, как класс определяется, какие аргументы у метода, как связаны импорты. Благодаря этому связь между init_db() и его объявлением сохраняется, и поиск не разрывается на середине файла. Поэтому точность поиска не зависит от «похожести» текста, а определяется структурой.

Как Graphify обрабатывает большие проекты, где файлы разбиваются на куски?

При работе с крупными проектами Graphify не делит файлы на произвольные куски, а парсит каждое файловое дерево целиком, сохраняя родительские связи между модулями. Это позволяет построить граф, в котором узлы класса и функции находятся в контексте их пакетов. Поскольку дерево не обрывается, поиск остаётся предсказуемым даже при тысячах файлов. На практической точке это уменьшает риск «потерять» связь между классом и его методами.

Можно ли использовать Graphify для поиска зависимостей между модулями?

Да, Graphify строит граф знаний, где узлы представляют импорты, классы и функции, а ребра отражают зависимости. Поиск по графу позволяет ответить на вопрос, какой модуль импортирует init_db(). Такой подход полезен при аудите кода и планировании рефакторинга. В отличие от векторных поисков, результат будет точным и основан на реальном дереве импортов.

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

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