tiempo de lectura : 12 minutes

Tu base de código crece. Las dependencias entre módulos se multiplican. La documentación de la arquitectura se vuelve obsoleta antes incluso de estar escrita. ¿Y si tu build de Gradle pudiera automáticamente generar diagramas actualizados a partir de la estructura real del código? Eso es exactamente lo que hace el pipeline Graphify + Plugin Gradle PlantUML :`graphify . --no-viz`extrae el Knowledge Graph,`./gradlew generateKnowledgeGraphDiagram`lo transforma en diagramas PlantUML. Cero LLM, cero manual, 100% determinista.

El problema: los diagramas siempre están por detrás del código

Todo proyecto que supera algunos miles de líneas conoce este síndrome:

  1. Se dibuja un diagrama de arquitectura al inicio del proyecto

  2. El código evoluciona, las dependencias cambian

  3. El diagrama se convierte en una mentira decorativa

  4. Nadie lo actualiza porque es tedioso

  5. Los recién llegados se basan en ello y cometen errores

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

start

:Diagrama de arquitectura creado\n(manualmente, a mano);

:El código evoluciona\n(nuevas clases, nuevas dependencias);

:Diagrama obsoleto\n(ya no refleja el código);

if (Qui met à jour ?) then (Personne)
    :Documentación engañosa;
    note right: Los nuevos desarrolladores\nse basan en eso... y cometen errores
else (Quelqu'un)
    :Actualización manual\n(2-3 horas de trabajo);
    note right: Ya obsoleto\nen la próxima PR
endif

stop

@enduml

La pregunta no es ¿es necesario tener diagramas? — todo el mundo sabe que sí. La pregunta es :¿Quién los mantiene actualizados?

La respuesta : nadie. Excepto si es automático.

La solución: un pipeline determinista Knowledge Graph → PlantUML

El principio es simple : en lugar de dibujar los diagramas a mano, losgenera desde la estructura real del código.

@startuml
skinparam backgroundColor #FEFEFE
skinparam componentStyle rectangle

actor Développeur
component "Graphify\n(pip install graphifyy)" as Graphify
collections "graphify-out/graph.json\n(Gráfico de Conocimiento)" as KGJSON
component "PlantUML Gradle Complemento\n(generateKnowledgeGraphDiagram)" as Plugin
component "KnowledgeGraphParser" as Parser
component "KnowledgeGraphRenderer" as Renderer
component "PlantumlService\n(validación + renderizado PNG)" as PS
collections "diagramas/knowledge-graph/\n(.puml + .png)" as Output

Développeur --> Graphify : graphify . --no-viz
Graphify --> KGJSON : extrae la estructura\ndel código fuente
Développeur --> Plugin : ./gradlew generateKnowledgeGraphDiagram
Plugin --> Parser : parse(graph.json)
Parser --> Plugin : KnowledgeGraph\n(nodos + aristas + comunidades)
Plugin --> Renderer : render(graph, filters)
Renderer --> Plugin : código PlantUML\ndeterminista
Plugin --> PS : validateSyntax + generateImage
PS --> Output : .puml + .png

note bottom of Plugin
Pipeline DÉTERMINISTE
Ninguna llamada LLM
Resultado reproducible
end note

@enduml

Dos comandos. Eso es todo.

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

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

¿El resultado? Archivos`.puml` et .png`en`diagrams/knowledge-graph/, versionados en Git, siempre actualizados con el código.

Paso 1: Instalar Graphify y extraer el Knowledge Graph

Instalación

Graphify es una herramienta de Python que analiza su codebase y construye un grafo de conocimiento estructurado:

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

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

Configurar las exclusiones

Crear un archivo`.graphifyignore`en la raíz del proyecto para excluir los archivos que no forman parte de la lógica de negocio:

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

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

# Tests fonctionnels
src/functionalTest/

Extraer el Knowledge Graph

graphify . --no-viz

La bandera`--no-viz`Omite la generación HTML (inútil en un pipeline Gradle). El resultado es un archivo`graphify-out/graph.json`conteniendo :

  • nudos: clases, funciones, archivos — con su tipo y comunidad

  • Aristas: relaciones entre nodos (EXTRACTED desde el código, INFERRED por el LLM)

  • Comunidades: agrupamientos automáticos de nodos vinculados

Ejemplo de estructura`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}
  ]
}

El código fuente (.kt, .java) se analiza localmente por tree-sitter,inapelable LLM. Sólo los archivos de documentación (.adoc, .md) requieren una llamada LLM para la extracción semántica. Así que`--update`sobre código Kotlin es casi instantáneo.

Paso 2 : El plugin Gradle transforma el Knowledge Graph en PlantUML

Arquitectura del pipeline

El plugin`com.cheroliv.plantuml`incluye una tarea`generateKnowledgeGraphDiagram`que transforma`graph.json`en diagramas PlantUML de maneratotalmente determinista :

@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 : execute
Task -> JSON : leer
Task -> Parser : parse(graph.json)
Parser --> Task : KnowledgeGraph\n(nodos + aristas + comunidades)
Task -> Renderer : render(graph, filters)
Renderer --> Task : código PlantUML\n(determinista)
Task -> PS : validateSyntax(plantumlCode)
alt syntaxe valide
    Task -> PS : generateImage → .png
else syntaxe invalide
    Task -> Task : guardar .puml con advertencia
end

@enduml

Los componentes internos

Componente rol

KnowledgeGraphParser

Analizar`graph.json`— soporta 3 formatos : graphify nativo (nodes+links), legado (communities), plano. Resuelve los IDs numéricos en etiquetas.

KnowledgeGraphRenderer

Transforma deterministamente un`KnowledgeGraph`en código PlantUML. Grupos por tipo, comunidades en paquetes, leyenda automática.

GenerateKnowledgeGraphDiagramTask

Tarea Gradle que orquesta: parse → render → validate → PNG. Configurable mediante propiedades Gradle.

kgmodels.kt

Modelos de datos:`KnowledgeGraph`, KnowledgeGraphNode, KnowledgeGraphEdge, KnowledgeGraphCommunity, EdgeType.

PlantumlService

Validación sintáctica + renderizado PNG (reutilizado por todas las tareas del plugin).

Reglas de renderizado

El renderer aplica convenciones visuales deterministas:

Tipo de arista Notación PlantUML Significado

EXTRACTED

-→(línea continua, negro)

Relación extraída del código fuente (certeza)

DEDUCIDO

..>(línea punteada)

Relación inferida por el LLM (puntuación de confianza)

AMBIGUOUS

--x(línea roja punteada)

Relación ambigua (por verificar)

Las comunidades se representan como paquetes PlantUML con una paleta de colores automática.

Paso 3: Uso diario

diagrama completo

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

Salida :`diagrams/knowledge-graph/knowledge-graph-full.puml`+.png

Filtrar por comunidad

# 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

Filtrar por tipo de arista

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

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

Filtrar por tipo de nodo y confianza

# 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

Directorio de salida personalizado

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

Referencia completa de las propiedades

Propiedad Defecto Descripción

plantuml.kg.community

(todas)

Filtrar las comunidades por nombre (coincidencia de subcadena)

plantuml.kg.edgeTypes

(todos)

Tipos de aristas separados por comas :`EXTRACTED`, INFERRED, AMBIGUOUS

plantuml.kg.minConfidence

0.0

Umbral mínimo de confianza para las aristas

plantuml.kg.maxNodes

(ilimitado)

Número máximo de nodos a mostrar

plantuml.kg.nodeTypes

(todos)

Tipos de nodos separados por comas (ej.class, code)

plantuml.kg.outputDir

diagrams/knowledge-graph

Directorio de salida para los archivos`.puml` et .png

El pipeline completo en un flujo de trabajo Gradle

Tipo de flujo de trabajo

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

start

partition "EXTRACTION" {
    :graphify . --no-viz;
    note right: Analice el código fuente\n tree-sitter (local, sin LLM)

    :graphify-out/graph.json;
    note right: Nodos + aristas\n+ comunidades
}

partition "GÉNÉRATION" {
    :./gradlew generateKnowledgeGraphDiagram;
    note right: Pipeline determinista\nparse → render → validate → PNG

    if (Filtres actifs ?) then (Oui)
        :Aplicar filtros\n(community, edgeTypes,\nmaxNodes, nodeTypes);
    else (Non)
        :Diagrama completo;
    endif

    :diagramas/knowledge-graph/\nknowledge-graph-full.puml\nknowledge-graph-full.png;
}

partition "INTÉGRATION" {
    :git add diagrams/knowledge-graph/;
    :git commit;
    note right: diagramas versionados\nsiempre actualizado con el código
}

stop

@enduml

Integración en el ciclo de desarrollo

El pipeline se integra naturalmente en las etapas clave del desarrollo:

@startuml
skinparam backgroundColor #FEFEFE

state "Desarrollo" as dev
state "Extracción\ngraphify . --no-viz" as extract
state "Generación\n./gradlew generateKnowledgeGraphDiagram" as generate
state "Commit\ndiagramas versionados" as commit

[*] --> dev
dev --> extract : Código modificado
extract --> generate : graph.json actualizado
generate --> commit : .puml + .png generados
commit --> dev : Diagramas en el repo

note right of extract
Casi instantáneo en código Kotlin
(tree-sitter, sin LLM)
end note

note right of generate
Deterministico
No LLM
Resultado reproducible
end note

@enduml

Actualización incremental

Cuando el código cambia, no se reconstruye todo el grafo desde cero:

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

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

--update`reextrae únicamente los archivos modificados (detectados por SHA256 en`graphify-out/cache/). En código Kotlin, es casi instantáneo porque tree-sitter trabaja localmente sin llamada a LLM.

El dogfooding : el plugin se documenta a sí mismo

El plugin PlantUML existe para transformar prompts en diagramas. También puede transformar el Knowledge Graph de su propre codebase en diagramas de documentación. Es dogfooding: el plugin consume su propio servicio.

@startuml
skinparam backgroundColor #FEFEFE
skinparam componentStyle rectangle

package "Pipeline normal\n(usuario → diagramas)" 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 "Grafo de conocimiento del pipeline\n(determinista, sin 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 "Pipeline Dogfooding\n(LLM → documentación del plugin)" 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 "Mismo LlmService, mismo ApiKeyPool,\nmismo PlantumlService\n— CERO duplicación" as N

@enduml

Tareas Gradle asociadas :

tarea LLM ? Descripción

generateKnowledgeGraphDiagram

No

Transforma`graph.json`en PlantUML (determinista)

generateDiagramDocs

Sí

Genera de`.prompt`desde el grafo, los trata a través de LLM (dogfooding)

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

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

¿Por qué es determinista (y por qué importa)?

El punto clave del pipeline`generateKnowledgeGraphDiagram`:Él no llama ningún LLM. La transformación`graph.json`PlantUML es una función pura.

@startuml
skinparam backgroundColor #FEFEFE
skinparam componentStyle rectangle

rectangle "Canalización DÉTERMINISTE\n(generateKnowledgeGraphDiagram)" as det {
    [graph.json] as json
    [KnowledgeGraphParser] as parser
    [KnowledgeGraphRenderer] as renderer
    [PlantUML code] as puml

    json --> analizar : analiza
    parser --> renderer : KnowledgeGraph
    renderer --> puml : render (función pura)
}

rectangle "Canalización LLM\n(processPlantumlPrompts)" as llm {
    [.prompt] as prompt
    [LlmService] as llmSvc
    [ChatModel] as model
    [PlantUML code] as puml2

    prompt --> llmSvc
    llmSvc --> model : API llamada
    model --> puml2 : respuesta no determinista
}

note bottom of det
Misma entrada → misma salida
Sin latencia de red
Sin costo en tokens
Reproducible en CI
end note

note bottom of llm
Misma entrada → salida variable
Latencia de red (1-5s)
Costo en tokens
Requiere una clave API
end note

@enduml

Los beneficios concretos:

Ventaja Impacto

Reproducibilidad

incluso`graph.json`→ mismo diagrama. Exactamente. Todas las veces.

Costo cero

Sin llamada LLM = sin tokens = sin factura API

Cero latencia

Parse + render tarda ~100ms, no 1-5 segundos.

Compatible con CI

No se necesita una clave API. No hay prueba inestable debido a una respuesta variable del LLM.

Versionable

Le .puml`generado es texto. Podemos lo`diff, el revisor en PR, versionarlo en Git

Diagramas del propio pipeline

Para cerrar el ciclo, aquí tienes el diagrama del pipeline tal como sería generado por el plugin :

@startuml
skinparam backgroundColor #FEFEFE

participant "Desarrollador" 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 : ejecutar
Task -> JSON : leer
Task -> Parser : parse(graph.json)
Parser --> Task : KnowledgeGraph

alt filtres actifs
    Task -> Task : aplicar filtros\n(community, edgeTypes, maxNodes…)
end

Task -> Renderer : render(graph, filters)
Renderer --> Task : código PlantUML

Task -> PS : validateSyntax()
alt valide
    Task -> PS : generateImage()
    PS --> Out : .puml + .png
else invalide
    Task -> Out : .puml (con advertencia)
end

@enduml

Integración en la gobernanza del proyecto

En nuestro proyecto, este pipeline se integra en una estrategia de gestión de contexto EAGER/LAZY para el agente IA. El Knowledge Graph reemplaza la documentación de arquitectura manual por un grafo estructurado, consultable y autoactualizado.

@startuml
skinparam backgroundColor #FEFEFE
skinparam componentStyle rectangle

package "EAGER\n(siempre cargado)" 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 — Estrategia\n(bajo demanda)" as lazy_strat {
    [Méthodologies] as meth
    [Archives sessions] as sessions
}

package "LAZY — Graphify\n(consultas dirigidas)" as lazy_graph {
    [graph.json] as gj
    [Queries\n(query/path/explain)] as queries
}

package "Pipeline PlantUML\n(generado automáticamente)" as puml {
    [generateKnowledgeGraphDiagram] as kgTask
    [generateDiagramDocs] as ddTask
    [Diagrammes PNG] as diagrams
}

Agent --> eager : Leído al inicio de la sesión
Agent ..> lazy_strat : Cargar si se detecta el tipo
Agent ..> lazy_graph : Consulta si es estructuralmente necesario
kgTask --> diagrams : Determinista
ddTask --> diagrams : A través de LLM

note right of gr
Resumen del Knowledge Graph
Nodos God + comunidades
Reemplaza ECOSYSTEM_OVERVIEW
(225 líneas → 50 líneas)
end note

@enduml

El matrimonio de los dos sistemas es complementario :

  • La estrategia gestiona el CUÁNDO y el CÓMO— gobernanza, workflow, archivado

  • Graphify gestiona el QUOI y el OÙestructura del código, relaciones, consultas dirigidas

__ La estrategia de sesión gestiona elCUANDO et le CÓMO(gobernanza, flujo de trabajo, umbrales), Graphify gestiona elqué et le DÓNDE(estructura del código, relaciones, consultas específicas). El pipeline PlantUML gestiona elCON QUÉ(diagramas deterministas, versionados, siempre actualizados). (No output needed because no source text provided.)

Preparación: checklist en 5 minutos

# Etapa Orden

1

Instalar Graphify

uv tool install graphifyy

2

Configurar las exclusiones

Crear`.graphifyignore`

3

Extraer el Knowledge Graph

graphify . --no-viz

4

Generar los diagramas

./gradlew generateKnowledgeGraphDiagram

5

Confirmar los resultados

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/

Trampas y mitigaciones

Trampa Descripción Mitigación

Grafo demasiado denso

Un gran proyecto genera cientos de nodos ilegibles

Utilizar`-Pplantuml.kg.maxNodes=30`et filtrar por comunidad

exclusiones demasiado amplias

Demasiados archivos en`.graphifyignore`reduce el valor del grafo

Comenzar por excluir solo credentials y build

Dirección de las flechas

Las aristas INFERRED pueden tener una dirección ambigua

Filtrar por`-Pplantuml.kg.edgeTypes=EXTRACTED`Solo para las relaciones ciertas

Grafo obsoleto

El código cambia pero el grafo no se reconstruye

usar`graphify . --update`regularmente o el hook git`graphify hook install`

Costo actualizado

Las actualizaciones incrementales son prácticamente gratuitas (tree-sitter local)

Sólo los docs (.adoc) consumen tokens LLM

Lo que se obtiene al final

@startuml
skinparam backgroundColor #FEFEFE

rectangle "AVANT" as avant {
    card "Diagramas dibujados a mano\nSiempre obsoletos\nNadie los actualiza" as av1 #FDEDEC
}

rectangle "APRÈS" as apres {
    card "Diagramas generados automáticamente\nSiempre actualizados con el código\nVersionados en Git\nDeterministas y reproducibles" as ap1 #E8F8E8
}

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

@enduml

Los beneficios concretos:

Beneficio Detalle

Documentación siempre actualizada

Los diagramas reflejan el código actual, no una instantánea manual

Cero esfuerzo de mantenimiento

Los diagramas se regeneran en cada compilación

Reducción de la deuda técnica

Ya no es necesario mantener los diagramas a mano

Contexto visual para los nuevos

Un nuevo desarrollador comprende la arquitectura mirando los diagramas

potencial de ajuste fino

Los pares (subgrafo → diagrama) son ejemplos de entrenamiento IA

Validación automática

`PlantumlService.validateSyntax()`verifica cada diagrama generado

Onboarding rápido

5 diagramas = vista completa de la arquitectura

__ El que tiene un por qué puede soportar todos los cómo. </think>

El porqué : diagramas siempre actualizados. El cómo : dos comandos en un pipeline de Gradle.

enlaces

Artículos relacionados