EKG + RAG/LLM para retroalimentación de códigoTrabajo de Fin de Máster · UNED
Grafos de propiedades

Grafos de propiedades (Neo4j)

Cuento aquí cómo reexpresé el grafo de conocimiento, que en el proyecto vive en RDF/OWL, como un grafo de propiedades etiquetado (LPG) para Neo4j. Lo hice exportando el .ttl canónico a un guion de Cypher, sin tocar el RDF, de modo que ambas representaciones describan el mismo dominio desde dos paradigmas distintos.

Adrián Bueno JunqueroUNED · TFMPágina autocontenida, se abre sin conexión

1El export ekg_lpg.cypher

El artefacto ekg_lpg.cypher se genera desde ekg-python-150.ttl y no modifica el RDF. La idea es directa, cada concepto del grafo pasa a ser un nodo con etiqueta :Concepto y sus atributos (identificador, etiqueta legible, tema, dificultad) viven dentro del nodo como propiedades; las relaciones de prerrequisito y de contraste pasan a ser aristas tipadas entre nodos.

157
nodos :Concepto (sentencias CREATE)
174
aristas :REQUIERE (prerrequisitos)
14
aristas :CONTRASTA (contrastes)
2
tipos de relación exportados

Las cifras coinciden con el grafo canónico, que declara 157 conceptos y, en su malla de prerrequisitos y contrastes, las mismas relaciones que la visualización del EKG. Los conteos de arriba salen de contar las sentencias del propio fichero exportado.

Sentencias CREATE (nodos con propiedades)

Cada nodo se crea con sus propiedades entre llaves. Estos cuatro son literales del export y muestran el patrón completo, incluyendo el caso de python, que es el único concepto sin dificultad.

// EKG-Python como PROPERTY GRAPH (Neo4j/Cypher) — re-expresión del grafo RDF canónico.
// Generado desde ekg-python-150.ttl (no modifica el RDF). Las propiedades viven en el nodo;
// las relaciones de prerrequisito/contraste/error son aristas tipadas.

CREATE (:Concepto {id:'recursion', label:'Recursión', tema:'Recursión', dificultad:4});
CREATE (:Concepto {id:'busqueda_binaria', label:'Búsqueda binaria', tema:'Complejidad algorítmica', dificultad:4});
CREATE (:Concepto {id:'herencia', label:'Herencia', tema:'Programación orientada a objetos', dificultad:4});
CREATE (:Concepto {id:'python', label:'Python', tema:'Fundamentos'});

Aristas tipadas (MATCH … CREATE)

Las relaciones se crean emparejando dos nodos por su id y tendiendo una arista entre ellos. El export usa :REQUIERE para los prerrequisitos y :CONTRASTA para los contrastes. Estas líneas son literales del fichero.

// Prerrequisitos → aristas :REQUIERE
MATCH (a:Concepto{id:'recursion'}),(b:Concepto{id:'funcion'})
  CREATE (a)-[:REQUIERE]->(b);
MATCH (a:Concepto{id:'busqueda_binaria'}),(b:Concepto{id:'bucle_while'})
  CREATE (a)-[:REQUIERE]->(b);

// Contrastes → aristas :CONTRASTA
MATCH (a:Concepto{id:'recursion'}),(b:Concepto{id:'bucle_for'})
  CREATE (a)-[:CONTRASTA]->(b);
MATCH (a:Concepto{id:'busqueda_lineal'}),(b:Concepto{id:'busqueda_binaria'})
  CREATE (a)-[:CONTRASTA]->(b);

Ejemplos de MATCH para consultar el grafo

Una vez cargado el guion, el grafo de propiedades se interroga con consultas de patrón. Estas son ilustrativas y reproducen, en clave Cypher, recorridos que en el lado RDF resuelvo con SPARQL, por ejemplo los prerrequisitos directos de un concepto o sus contrastes. Las marco como ejemplos porque no se ejecutaron en este entorno (ver la última sección).

// Prerrequisitos directos de la búsqueda binaria
MATCH (c:Concepto{id:'busqueda_binaria'})-[:REQUIERE]->(p:Concepto)
RETURN p.label, p.tema, p.dificultad;

// Cadena de prerrequisitos (transitiva) hacia un concepto base
MATCH (c:Concepto{id:'busqueda_binaria'})-[:REQUIERE*]->(p:Concepto)
RETURN DISTINCT p.label;

// Conceptos que conviene contrastar con la recursión
MATCH (a:Concepto{id:'recursion'})-[:CONTRASTA]->(b:Concepto)
RETURN b.label;

// Conceptos avanzados de cada tema (filtra por propiedad del nodo)
MATCH (c:Concepto)
WHERE c.dificultad >= 4
RETURN c.tema, count(c) AS n
ORDER BY n DESC;

2Un diagrama del grafo de propiedades

Este diagrama esquematiza cómo se ve un fragmento del grafo en el modelo LPG, con las propiedades anidadas en cada nodo y las relaciones tipadas como flechas. Es el mismo subgrafo de la recursión que muestro en la página del grafo, pero dibujado a la manera de Neo4j.

Fragmento del EKG como grafo de propiedades El nodo Concepto "recursion" (tema Recursión, dificultad 4) tiene una arista REQUIERE hacia el nodo "funcion" (tema Funciones, dificultad 2) y una arista CONTRASTA hacia el nodo "bucle_for" (tema Control de flujo, dificultad 2). Las propiedades viven dentro de cada nodo. :REQUIERE :CONTRASTA :Concepto id: 'recursion' tema: 'Recursión' dificultad: 4 :Concepto id: 'funcion' dificultad: 2 :Concepto id: 'bucle_for' dificultad: 2
Modelo LPG, las propiedades (id, tema, dificultad) viven dentro del nodo y las relaciones son aristas tipadas (:REQUIERE, :CONTRASTA). Representación esquemática con la paleta del proyecto.

3RDF frente a grafos de propiedades

Las dos representaciones modelan el mismo dominio, pero con compromisos distintos. En RDF todo es un triple sujeto–predicado–objeto y los atributos de un concepto son también triples; en un LPG los atributos son pares clave-valor que cuelgan del nodo o de la arista. Resumo las diferencias que más pesan en este proyecto.

RDF/OWL (representación canónica del EKG) frente a grafo de propiedades (export Neo4j).
AspectoRDF / OWL 2 RLGrafo de propiedades (LPG)
Unidad básicaTriple sujeto–predicado–objetoNodo y arista con propiedades clave-valor
Atributos de un conceptoTriples con literales (p. ej. pyedu:tieneDificultad 4)Propiedades dentro del nodo (dificultad:4)
Atributos sobre una relaciónRequiere reificación o entidad intermediaPropiedades directas en la arista
IdentificadoresIRIs globales (pyr:recursion)Identificadores locales del grafo (id:'recursion')
Inferencia formalRazonador OWL 2 RL (0 → 157 conceptos inferidos)Sin razonador estándar; recorridos por consulta
Validación de formaSHACL (conforme, 0 violaciones)Restricciones del motor (no exportadas aquí)
Enlazado externoskos:exactMatch a Wikidata (30 enlaces)No se materializa en este export
Lenguaje de consultaSPARQL (incluida consulta federada)Cypher (MATCH … RETURN)
Qué se conserva y qué no en el viaje a LPG

El export traslada la espina dorsal del grafo, los conceptos con sus propiedades y la malla de prerrequisitos y contrastes. No reproduce la inferencia OWL 2 RL, la validación SHACL ni el enlazado skos:exactMatch a Wikidata, que son fortalezas propias del lado RDF y que documento en la página del grafo. El LPG, a cambio, hace cómodo poner atributos sobre las propias relaciones y recorrer caminos con consultas de patrón.

4Lo que sí está hecho y lo que no

Declaración

Los artefactos Cypher están listos, el fichero ekg_lpg.cypher existe, se genera desde el .ttl canónico y contiene las 157 sentencias CREATE y las aristas :REQUIERE y :CONTRASTA que aquí enseño. En cambio, el diseño visual en arrows.app y la ejecución del guion en el GUI de Neo4j no se realizaron en este entorno, que no dispone de interfaz gráfica. Por eso esta página no incluye ninguna captura de Neo4j Browser ni de arrows.app, no se finge una pantalla que no se ha producido. El diagrama de más arriba es un SVG esquemático dibujado con la paleta del proyecto, no una captura de herramienta.

Cargar el guion y dibujarlo en una de esas herramientas queda como paso reproducible para quien disponga de un Neo4j con GUI, el fichero está preparado para ejecutarse tal cual.

Ver también

El grafo de conocimiento

La representación canónica en RDF/OWL, con la grafoteca interactiva, la malla SKOS, la inferencia OWL 2 RL y el enlazado a Wikidata.

El sistema RAG

Cómo se recupera el subgrafo de un concepto y se inyecta en el prompt de los sistemas C y D.

Recursos y artefactos

Ficheros del proyecto, entre ellos las serializaciones del grafo y los guiones de exportación.

Cómo citar

Si este trabajo te resulta útil y quieres referenciarlo, esta es la cita recomendada.

Bueno Junquero, A. (2026). Integración de un grafo de conocimiento educativo con un LLM mediante RAG. Trabajo Fin de Máster, Máster Universitario en Investigación en Inteligencia Artificial, UNED. Director, José Luis Fernández Vindel.