Содержание
время чтения : 12 minutes

Ваша кодовая база растёт. Зависимости между модулями множатся. Архитектурная документация устаревает ещё до того, как будет написана. А что, если ваша сборка Gradle могла бы автоматически генерировать актуальные диаграммы на основе реальной структуры кода? Вот что делает пайплайн Graphify + PlantUML Gradle Plugin :`graphify . --no-viz`извлекает Knowledge Graph,`./gradlew generateKnowledgeGraphDiagram`Он преобразует его в диаграммы PlantUML. Ноль LLM, ноль руководства, 100% детерминированный.

Проблема: диаграммы всегда отстают от кода

Любой проект, превышающий несколько тысяч строк, сталкивается с этим синдромом:

  1. В начале проекта рисуется архитектурная диаграмма

  2. Код меняется, зависимости меняются

  3. Диаграмма становится декоративной ложью

  4. Никто не обновляет его, потому что это утомительно

  5. Новые прибывающие полагаются на это и совершают ошибки

@startuml
skinparam backgroundColor #FEFEFE
skinparam ActivityBackgroundColor #FDEDEC
skinparam ActivityDiamondBackgroundColor #FFF3CD

start

:Диаграмма архитектуры создана\n(вручную);

:Код развивается\n(новые классы, новые зависимости);

:Устаревшая диаграмма\n(больше не отражает код);

if (Qui met à jour ?) then (Personne)
    :Ложная документация;
    note right: Новые разработчики\n основываются на этом... и допускают ошибки
else (Quelqu'un)
    :Ручное обновление\n(2-3 часа работы);
    note right: Уже устарело\nк следующему PR
endif

stop

@enduml

Вопрос не в нужны ли диаграммы? — все знают, что да. Вопрос в том, что:Кто поддерживает их в актуальном состоянии?

Ответ: никто. За исключением случая, когда это автоматическое.

Решение: детерминированный pipeline Knowledge Graph → PlantUML

Принцип прост: вместо того чтобы рисовать диаграммы от руки, мы ихгенерирует из реальной структуры кода.

@startuml
skinparam backgroundColor #FEFEFE
skinparam componentStyle rectangle

actor Développeur
component "Graphify\n(pip install graphifyy)" as Graphify
collections "graphify-out/graph.json\n(Knowledge Graph)" as KGJSON
component "PlantUML Gradle Plugin\n(generateKnowledgeGraphDiagram)" as Plugin
component "KnowledgeGraphParser" as Parser
component "KnowledgeGraphRenderer" as Renderer
component "PlantumlService\n(validation + rendu PNG)" as PS
collections "diagrams/knowledge-graph/\n(.puml + .png)" as Output

Développeur --> Graphify : графически представлять . --no-viz
Graphify --> KGJSON : извлекает структура\nисходного кода
Développeur --> Plugin : ./gradlew generateKnowledgeGraphDiagram
Plugin --> Parser : parse(graph.json)
Parser --> Plugin : KnowledgeGraph\n(узлы + ребра + сообщества)
Plugin --> Renderer : render(graph, filters)
Renderer --> Plugin : code PlantUML\nдетерминированный
Plugin --> PS : validateSyntax + generateImage
PS --> Output : .puml + .png

note bottom of Plugin
Конвейер DÉTERMINISTE
Нет вызова LLM
Воспроизводимый результат
end note

@enduml

Две команды. Вот и всё.

# Étape 1 : extraire le Knowledge Graph
graphify . --no-viz

# Étape 2 : générer les diagrammes PlantUML
./gradlew generateKnowledgeGraphDiagram

Результат ? Файлы`.puml` et .png`в`diagrams/knowledge-graph/, версиированные в Git, всегда актуальны вместе с кодом.

Шаг 1: Установить Graphify и извлечь граф знаний

Установка

Graphify — это инструмент Python, который анализирует вашу кодовую базу и строит структурированный граф знаний:

# Méthode recommandée
uv tool install graphifyy && graphify install --platform opencode

# Alternative avec pip
pip install graphifyy && graphify install --platform opencode

Настроить исключения

Создать файл`.graphifyignore`в корне проекта для исключения файлов, которые не являются частью бизнес-логики :

# Secrets — JAMAIS dans le graphe
*-context.yml
*.env

# Fichiers générés
build/
.gradle/

# Tests fonctionnels
src/functionalTest/

Извлечь граф знаний

graphify . --no-viz

Флаг`--no-viz`пропускает генерацию HTML (бесполезно в конвейере Gradle). Результат — файл`graphify-out/graph.json`содержит :

  • Узлы: классы, функции, файлы — с их типом и сообществом

  • кромки: отношения между узлами (EXTRACTED из кода, INFERRED LLM)

  • Сообщества: автоматические группировки связанных узлов

Пример структуры`graph.json`
{
  "nodes": [
    {"id": "0", "label": "LlmService", "file_type": "code", "community": 0},
    {"id": "1", "label": "ApiKeyPool", "file_type": "code", "community": 0},
    {"id": "2", "label": "PlantumlService", "file_type": "code", "community": 1}
  ],
  "links": [
    {"source": "1", "target": "0", "relation": "uses", "confidence": "EXTRACTED", "weight": 0.9},
    {"source": "0", "target": "2", "relation": "calls", "confidence": "INFERRED", "weight": 0.7}
  ]
}

Исходный код (.kt, .java) анализируется локально с помощью tree-sitter,без апелляции LLM. Только файлы документации (.adoc, .md) требуют вызова LLM для семантического извлечения. Поэтому`--update`код Kotlin почти мгновенный

Шаг 2: Плагин Gradle преобразует Knowledge Graph в PlantUML

Архитектура пайплайна

Плагин`com.cheroliv.plantuml`включает задачу`generateKnowledgeGraphDiagram`который преобразует`graph.json`в диаграммах PlantUML способомполностью детерминированный :

@startuml
skinparam backgroundColor #FEFEFE

participant "Gradle" as G
participant "GenerateKnowledgeGraphDiagramTask" as Task
participant "KnowledgeGraphParser" as Parser
participant "KnowledgeGraphRenderer" as Renderer
participant "PlantumlService" as PS

collections "graphify-out/graph.json" as JSON

G -> Task : выполнять
Task -> JSON : читать
Task -> Parser : parse(graph.json)
Parser --> Task : KnowledgeGraph\n(узлы + рёбра + сообщества)
Task -> Renderer : render(graph, filters)
Renderer --> Task : код PlantUML\n(детерминированный)
Task -> PS : validateSyntax(plantumlCode)
alt syntaxe valide
    Task -> PS : generateImage → .png
else syntaxe invalide
    Task -> Task : сохранить .puml с предупреждением
end

@enduml

Внутренние компоненты

Компонент Роль

KnowledgeGraphParser

Разбор`graph.json`— поддерживает 3 формата: graphify natif (nodes+links), legacy (communities), плоский. Преобразует числовые ID в метки.

KnowledgeGraphRenderer

Детерминированно преобразует`KnowledgeGraph`в коде PlantUML. Группы по типу, сообщества в пакетах, автоматическая легенда.

GenerateKnowledgeGraphDiagramTask

Задача Gradle, которая оркестрирует: parse → render → validate → PNG. Настраивается через свойства Gradle.

kgmodels.kt

Модели данных:`KnowledgeGraph`, KnowledgeGraphNode, KnowledgeGraphEdge, KnowledgeGraphCommunity, EdgeType.

PlantumlService

Синтаксическая проверка + рендеринг PNG (повторно используется всеми задачами плагина).

Правила рендеринга

Рендерер применяет детерминированные визуальные соглашения :

Тип ребра Нотация PlantUML Значение

извлечённый

-→(сплошная линия, чёрный)

Связь, извлечённая из исходного кода (достоверность)

выведен

..>(пунктирная линия)

Связь, выведенная LLM (оценка уверенности)

неоднозначный

--x(красная пунктирная линия)

Неоднозначное отношение (требует проверки)

Сообщества отображаются как пакеты PlantUML с автоматической палитрой цветов.

Шаг 3: Ежедневное использование

Полная диаграмма

# Générer le diagramme du Knowledge Graph complet
./gradlew generateKnowledgeGraphDiagram

Выход:`diagrams/knowledge-graph/knowledge-graph-full.puml`+.png

Фильтровать по сообществу

# Une seule communauté
./gradlew generateKnowledgeGraphDiagram \
  -Pplantuml.kg.community=community_0

# Limiter le nombre de noeuds (lisibilité)
./gradlew generateKnowledgeGraphDiagram \
  -Pplantuml.kg.community=community_0 \
  -Pplantuml.kg.maxNodes=15

Фильтровать по типу ребра

# Uniquement les relations certaines (EXTRACTED)
./gradlew generateKnowledgeGraphDiagram \
  -Pplantuml.kg.edgeTypes=EXTRACTED

# Relations certaines + inférées
./gradlew generateKnowledgeGraphDiagram \
  -Pplantuml.kg.edgeTypes=EXTRACTED,INFERRED

Фильтровать по типу узла и уверенности

# Uniquement les classes de code
./gradlew generateKnowledgeGraphDiagram \
  -Pplantuml.kg.nodeTypes=code

# Seuil de confiance minimum (pour les INFERRED)
./gradlew generateKnowledgeGraphDiagram \
  -Pplantuml.kg.minConfidence=0.7

Пользовательский каталог вывода

./gradlew generateKnowledgeGraphDiagram \
  -Pplantuml.kg.outputDir=docs/architecture

Полный справочник свойств

собственность дефект Описание

plantuml.kg.community

(все)

Фильтровать сообщества по имени (совпадение подстроки)

plantuml.kg.edgeTypes

(все)

Типы рёбер, разделённые запятыми:`EXTRACTED`, INFERRED, AMBIGUOUS

plantuml.kg.minConfidence

0.0

Минимальный порог доверия для рёбер

plantuml.kg.maxNodes

(без ограничений)

Максимальное количество узлов для отображения

plantuml.kg.nodeTypes

(все)

Типы узлов, разделенные запятыми (пример`class`, code)

plantuml.kg.outputDir

diagrams/knowledge-graph

Выходная директория для файлов`.puml` et .png

Полный конвейер в рабочем процессе Gradle

Тип рабочего процесса

@startuml
skinparam backgroundColor #FEFEFE
skinparam ActivityBackgroundColor #E8F4FD
skinparam ActivityDiamondBackgroundColor #FFF3CD
skinparam ActivityBorderColor #2C3E50

start

partition "EXTRACTION" {
    :graphify. --no-viz;
    note right: Анализ исходного кода\ntree-sitter (локально, без LLM)

    :graphify-out/graph.json;
    note right: Узлы + рёбра\n+ сообщества
}

partition "GÉNÉRATION" {
    :./gradlew generateKnowledgeGraphDiagram;
    note right: Конвейер детерминированный\nпарсинг → рендер → валидация → PNG

    if (Filtres actifs ?) then (Oui)
        :Применить фильтры\n(community, edgeTypes,\nmaxNodes, nodeTypes);
    else (Non)
        :полная диаграмма;
    endif

    :диаграммы/knowledge-graph/\nграфа знаний-full.puml\nграфа знаний-full.png;
}

partition "INTÉGRATION" {
    :git add diagrams/knowledge-graph/;
    :git commit;
    note right: Версионированные диаграммы\nвсегда актуальны по отношению к коду
}

stop

@enduml

Интеграция в цикл разработки

Конвейер естественно вписывается в ключевые этапы разработки :

@startuml
skinparam backgroundColor #FEFEFE

state "Разработка" as dev
state "Извлечение\ngraphify . --no-viz" as extract
state "Генерация\n./gradlew generateKnowledgeGraphDiagram" as generate
state "Commit\nдиаграммы версионированные" as commit

[*] --> dev
dev --> extract : Измененный код
extract --> generate : graph.json актуален
generate --> commit : .puml + .png сгенерированы
commit --> dev : Диаграммы в repo

note right of extract
Почти мгновенно на коде Kotlin (tree-sitter, без LLM)
end note

note right of generate
Детерминированный
Нет LLM
Воспроизводимый результат
end note

@enduml

Инкрементальное обновление

Когда код меняется, мы не перестраиваем весь граф с нуля :

# Mise à jour incrémentale (fichiers modifiés uniquement)
graphify . --update

# Puis régénérer les diagrammes
./gradlew generateKnowledgeGraphDiagram

--update`извлекает заново только изменённые файлы (обнаруженные по SHA256 в`graphify-out/cache/). Для кода Kotlin это почти мгновенно потому что tree-sitter работает локально без обращения к LLM.

Dogfooding: плагин сам документирует себя

Плагин PlantUML существует для преобразования запросов в диаграммы. Он также может преобразовать Knowledge Graph своей собственной кодовой базы в диаграммы документации. Это dogfooding : плагин использует свой собственный сервис.

@startuml
skinparam backgroundColor #FEFEFE
skinparam componentStyle rectangle

package "Обычный конвейер\n(пользователь → диаграммы)" as normal {
    [Fichier .prompt] as prompt
    [LlmService\n+ ApiKeyPool] as llm1
    [ProcessPlantumlPromptsTask] as task1
    [Diagramme PNG] as out1

    prompt --> task1
    task1 --> llm1
    llm1 --> out1
}

package "Конвейер графа знаний\n(детерминированный, без LLM)" as kg {
    [graphify-out/graph.json] as kgjson
    [KnowledgeGraphParser] as parser
    [KnowledgeGraphRenderer] as renderer
    [PlantumlService] as ps
    [Diagramme PNG\n(documentation du plugin)] as out2

    kgjson --> parser
    parser --> renderer
    renderer --> ps
    ps --> out2
}

package "Конвейер Dogfooding\n(LLM → документация плагина)" as dogfood {
    [GraphifyPromptAdapter] as gpa
    [Fichiers .prompt\nauto-générés] as auto_prompt
    [LlmService\n+ ApiKeyPool] as llm2
    [ProcessPlantumlPromptsTask] as task2
    [Diagramme PNG\n(documentation LLM)] as out3

    kgjson --> gpa
    gpa --> auto_prompt
    auto_prompt --> task2
    task2 --> llm2
    llm2 --> out3
}

note "Тот же LlmService, тот же ApiKeyPool,\nтот же PlantumlService\n— НОЛЬ дублирования" as N

@enduml

Связанные задачи Gradle :

Задача LLM ? Описание

generateKnowledgeGraphDiagram

Нет

Преобразуй`graph.json`в PlantUML (детерминированный)

generateDiagramDocs

Да

генерируй некоторые`.prompt`из графа обрабатывает их через LLM (dogfooding)

# Documentation déterministe (rapide, pas de LLM)
./gradlew generateKnowledgeGraphDiagram

# Documentation LLM (plus riche, consomme des tokens)
./gradlew generateDiagramDocs

Почему это детерминировано (и почему это важно)

Ключевой момент конвейера`generateKnowledgeGraphDiagram`:Он не вызывает никакой LLM. Трансформация`graph.json`→ PlantUML — чистая функция.

@startuml
skinparam backgroundColor #FEFEFE
skinparam componentStyle rectangle

rectangle "ДЕТЕРМИНИРОВАННЫЙ конвейер\n(generateKnowledgeGraphDiagram)" as det {
    [graph.json] as json
    [KnowledgeGraphParser] as parser
    [KnowledgeGraphRenderer] as renderer
    [PlantUML code] as puml

    json --> parser : parse
    parser --> renderer : KnowledgeGraph
    renderer --> puml : render (fonction pure)
}

rectangle "LLM-конвейер\n(processPlantumlPrompts)" as llm {
    [.prompt] as prompt
    [LlmService] as llmSvc
    [ChatModel] as model
    [PlantUML code] as puml2

    prompt --> llmSvc
    llmSvc --> model : API call
    model --> puml2 : réponse non-déterministe
}

note bottom of det
Тот же вход → тот же выход
Нет сетевой задержки
Нет затрат на токены
Воспроизводимо в CI
end note

note bottom of llm
Одинаковый ввод → переменный вывод
Сетевая задержка (1-5с)
Стоимость в токенах
Требуется API-ключ
end note

@enduml

Конкретные преимущества:

преимущество Влияние

воспроизводимость

даже`graph.json`→ тот же график. Точно. Каждый раз.

Нулевая стоимость

Без вызова LLM = без токенов = без счёта за API

Нулевая задержка

Разбор + рендеринг занимает ~100 мс, а не 1-5 секунд.

Совместимая CI

API-ключ не нужен. Нет неустойчивого теста из‑за переменного ответа LLM.

версионный

Le .puml`Сгенерировано — это текст. Его можно`diff, ревьюер в PR, версионировать в Git.

Диаграммы самого пайплайна

Чтобы замкнуть цикл, вот диаграмма конвейера, как она была бы сгенерирована плагином :

@startuml
skinparam backgroundColor #FEFEFE

participant "Разработчик" as Dev
participant "Gradle" as G
participant "GenerateKnowledgeGraphDiagramTask" as Task
participant "KnowledgeGraphParser" as Parser
participant "KnowledgeGraphRenderer" as Renderer
participant "PlantumlService" as PS
collections "graphify-out/graph.json" as JSON
collections "diagrams/knowledge-graph/" as Out

Dev -> G : ./gradlew generateKnowledgeGraphDiagram
G -> Task : выполнить
Task -> JSON : читать
Task -> Parser : parse(graph.json)
Parser --> Task : KnowledgeGraph

alt filtres actifs
    Task -> Task : применять фильтры\n(community, edgeTypes, maxNodes…)
end

Task -> Renderer : render(graph, filters)
Renderer --> Task : код PlantUML

Task -> PS : validateSyntax()
alt valide
    Task -> PS : generateImage()
    PS --> Out : .puml + .png
else invalide
    Task -> Out : .puml (с предупреждением)
end

@enduml

Интеграция в управление проектом

В нашем проекте этот конвейер интегрируется в стратегию управления контекстом EAGER/LAZY для ИИ-агента. Knowledge Graph заменяет ручную архитектурную документацию структурированным, запрашиваемым и автоматически обновляемым графом.

@startuml
skinparam backgroundColor #FEFEFE
skinparam componentStyle rectangle

package "EAGER\n(всегда заряжен)" as eager {
    [PROMPT_REPRISE.adoc\n(contexte session)] as pr
    [INDEX.adoc\n(vue d'ensemble)] as idx
    [GRAPH_REPORT.adoc\n(~50 lignes)] as gr
}

package "LAZY — Стратегия\n(по запросу)" as lazy_strat {
    [Méthodologies] as meth
    [Archives sessions] as sessions
}

package "LAZY — Graphify\n(целевые запросы)" as lazy_graph {
    [graph.json] as gj
    [Queries\n(query/path/explain)] as queries
}

package "Pipeline PlantUML\n(автоматически сгенерированный)" as puml {
    [generateKnowledgeGraphDiagram] as kgTask
    [generateDiagramDocs] as ddTask
    [Diagrammes PNG] as diagrams
}

Agent --> eager : Читать в начале сеанса
Agent ..> lazy_strat : Если тип обнаружен, загрузить
Agent ..> lazy_graph : Запрос при необходимости структурный
kgTask --> diagrams : Детерминированный
ddTask --> diagrams : Через LLM

note right of gr
Конденсат из Knowledge God
Узлы-боги + сообщества
Заменить ECOSYSTEM_OVERVIEW
(225 строк → 50 строк)
end note

@enduml

Сочетание двух систем является дополнительным:

  • Стратегия управляет когда и как— управление, рабочий процесс, архивация

  • Graphify управляет ЧТОМ и ГДЕ— структура кода, связи, целевые запросы

_ Стратегия сессии управляетКогда et le как(управление, workflow, пороги), Graphify управляетЧТО et le где(структура кода, связи, целевые запросы). Конвейер PlantUML обрабатываетчем(детерминированные диаграммы, версиионированные, всегда актуальные) _

Подготовка: чек‑лист за 5 минут

(empty) Этап Заказ

1

Установить Graphify

uv tool install graphifyy

2

Настроить исключения

Создать`.graphifyignore`

3

извлечь Knowledge Graph

graphify . --no-viz

4

Сгенерировать диаграммы

./gradlew generateKnowledgeGraphDiagram

5

Закоммитить результаты

git add diagrams/knowledge-graph/

# Script complet en 5 commandes
uv tool install graphifyy
cat > .graphifyignore << 'EOF'
*-context.yml
*.env
build/
.gradle/
EOF
graphify . --no-viz
./gradlew generateKnowledgeGraphDiagram
git add graphify-out/GRAPH_REPORT.adoc graphify-out/graph.json diagrams/knowledge-graph/

Подводные камни и меры по смягчению

Ловушка Описание Смягчение

Слишком плотный граф

Большой проект генерирует сотни нечитаемых узлов

Использовать`-Pplantuml.kg.maxNodes=30`и фильтровать по сообществу

Слишком широкие исключения

Слишком много файлов в`.graphifyignore`уменьшает значение графика

Начать с исключения только credentials и build

Направление стрелок

Ребра INFERRED могут иметь неоднозначное направление

Фильтровать по`-Pplantuml.kg.edgeTypes=EXTRACTED`только для определённых отношений

устаревший граф

Код меняется, но граф не перестраивается

Использовать`graphify . --update`регулярно или git-хук`graphify hook install`

Обновленная стоимость

Инкрементальные обновления практически бесплатны (tree-sitter local)

Только документы (.adoc) потребляют токены LLM

Что мы получаем в итоге

@startuml
skinparam backgroundColor #FEFEFE

rectangle "ДО" as avant {
    card "Диаграммы, нарисованные вручную\nВсегда устаревают\nНикто их не обновляет" as av1 #FDEDEC
}

rectangle "ПОСЛЕ" as apres {
    card "Автоматически генерируемые диаграммы\nВсегда актуальны относительно кода\nВерсионируются в Git\nДетерминированы и воспроизводимы" as ap1 #E8F8E8
}

avant --> apres : graphify . --no-viz\n+ ./gradlew generateKnowledgeGraphDiagram

@enduml

Конкретные выгоды :

Прибыль Деталь

Документация всегда актуальна

Диаграммы отражают текущий код, не ручной снимок

Нулевые усилия по обслуживанию

Диаграммы пересоздаются при каждой сборке

Снижение технического долга

Больше не нужно поддерживать диаграммы вручную

Визуальный контекст для новых

Новый разработчик понимает архитектуру, глядя на диаграммы

Потенциал тонкой настройки

Пары (подграф → диаграмма) являются примерами обучения ИИ

Автоматическая валидация

`PlantumlService.validateSyntax()`проверяет каждую сгенерированную диаграмму

Быстрый онбординг

5 диаграмм = полный вид архитектуры

__ Тот, у кого есть зачем может перенести всё как. </think>

Le почему : диаграммы всегда актуальны. Le как : две команды в конвейере Gradle.

Ссылки

Похожие статьи