EKG + RAG/LLM para retroalimentación de códigoTrabajo de Fin de Máster · UNED
El grafo de conocimiento

El grafo de conocimiento

Subpáginas

Las páginas de detalle que cuelgan de este hub. Cada una desarrolla una pieza del grafo.

La ontología EKG

El modelo educativo del dominio Python con perfil OWL 2 RL, SKOS y SHACL.

Inferencia y razonamiento

La materialización OWL 2 RL que lleva de 1772 a 4786 triples.

Validación con SHACL

Las formas que garantizan la integridad, con cero violaciones.

Consulta con SPARQL

Las consultas que interrogan el grafo y la federación con Wikidata.

Enlazado a Wikidata

Los 30 enlaces skos:exactMatch hacia la web de datos.

Grafos de propiedades

La comparación con Neo4j y Cypher, declarada como camino no recorrido.

2El grafo de conocimiento (EKG)

El EKG modela el dominio de la programación en Python en RDF/OWL 2 RL. Es la base semántica del proyecto y la fuente de contexto de los sistemas C y D.

Grafoteca interactiva — todos los grafos del proyecto

Un único lienzo para ver y manipular los grafos del proyecto. Elige el tipo, busca un concepto, agrúpalo por tema, alterna la inferencia o compara el grafo RDF con su versión de propiedades. Para recorrerlo tienes zoom, un minimapa en la esquina y, al pasar el ratón sobre un nodo, un rótulo con su tema y dificultad. Todos los datos proceden del .ttl canónico y van embebidos, sin red. El detalle argumentado está en la memoria.

Alternativa sin arrastrar y operable por teclado: puedes explorar el grafo entero con los desplegables "Tipo de grafo" y "Filtrar por tema", el campo "Buscar concepto" y el desplegable "Resaltar concepto" (más abajo), sin necesidad de arrastrar ni de gestos. Los botones "Encajar", "+" y "−" sustituyen al arrastre y al zoom de rueda. El contenido equivalente está además en forma textual: los bloques Turtle, las tablas y la lista de conceptos.

Selecciona un tipo de grafo y, si quieres, un nodo para ver sus relaciones.

concepto tema error / contraste clase · Wikidata · broader Motor: Cytoscape.js (embebido, offline). Equivalente textual en el desplegable.
Equivalente textual y conteos (accesibilidad)

El grafo modela 157 conceptos en 16 temas, con 174 prerrequisitos, 14 contrastes, 16 errores conceptuales, la malla SKOS 19/19/16 (broader/narrower/related), 30 enlaces skos:exactMatch a Wikidata y 10 formas SHACL. La tabla de conceptos por tema (consulta 01) y las 8 consultas con su salida ofrecen el detalle textual.

Métricas canónicas del grafo (ekg-python-150.ttl), recertificadas con rdflib + owlrl + pyshacl.
MétricaValorObjetivo
Conceptos (skos:Concept)157≥150
Triples afirmados → tras OWL 2 RL1772 → 4786≥1500
Validación SHACLConforme (0 violaciones)
Enlaces skos:exactMatch a Wikidata30≥15
Relaciones SKOS internas (broader/narrower · related)19 · 16
Clases · prop. objeto · prop. datos20 · 21 · 7
Decisión semántica: skos:exactMatch, no owl:sameAs

El enlazado a Wikidata usa skos:exactMatch (no owl:sameAs) de forma deliberada: así el razonador OWL 2 RL no fusiona el concepto propio con la entidad de Wikidata. Por eso la consulta ?x a pyedu:Concepto con inferencia devuelve 157 (solo los conceptos propios).

Un subgrafo de ejemplo

Subgrafo del concepto "Recursión" El concepto Recursión requiere Funciones, contrasta con Bucle for, se relaciona con Pila de llamadas, pertenece al tema Funciones y control, y tiene asociado el error frecuente "caso base ausente". requierecontrasta se relacionatemaerror Recursión Funciones Bucle for Pila dellamadas Funcionesy control caso base ausente
Subgrafo recuperado e inyectado en el prompt de los sistemas C/D (representación esquemática con la paleta del proyecto).

SPARQL: consulta federada a Wikidata

La consulta 08_federada_wikidata.rq recupera, para cada concepto enlazado, su descripción en español desde el endpoint de Wikidata. Ejecutada en vivo, devuelve 20 filas reales.

PREFIX pyedu: <https://w3id.org/ekg-python/schema#>
PREFIX skos:  <http://www.w3.org/2004/02/skos/core#>
SELECT ?concepto ?etiqueta ?descripcionWikidata WHERE {
  ?concepto skos:exactMatch ?wd ; rdfs:label ?etiqueta .
  FILTER(LANG(?etiqueta) = "es")
  SERVICE <https://query.wikidata.org/sparql> {
    ?wd schema:description ?descripcionWikidata .
    FILTER(LANG(?descripcionWikidata) = "es")
  }
}
# → 20 filas. Ej.: python → "lenguaje de programación de alto nivel"

El grafo en Turtle

El EKG canónico se serializa en Turtle bajo el perfil OWL 2 RL. Estos son los prefijos y cuatro bloques representativos, literales del fichero ekg-python-150.ttl.

@prefix dcterms: <http://purl.org/dc/terms/> .
@prefix foaf: <http://xmlns.com/foaf/0.1/> .
@prefix owl: <http://www.w3.org/2002/07/owl#> .
@prefix pyedu: <https://w3id.org/ekg-python/schema#> .
@prefix pyr: <https://w3id.org/ekg-python/id/> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix schema: <http://schema.org/> .
@prefix skos: <http://www.w3.org/2004/02/skos/core#> .
@prefix wd: <http://www.wikidata.org/entity/> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .

Visualización del grafo completo

Los 157 conceptos agrupados por tema, con sus prerrequisitos, su jerarquía skos:broader y sus contrastes. Elija un concepto para resaltar sus relaciones (o pase el cursor por un nodo).

Grafo de conocimiento completo: 157 conceptos en 16 temas Cada nodo es un concepto de programación en Python agrupado por tema; las aristas representan prerrequisitos (requierePrerrequisito), jerarquía conceptual (skos:broader) y contraste (contrastaCon). Use el selector para resaltar un concepto y sus relaciones. Algoritmos Complejidad algorítmica Concurrencia Control de flujo Estructuras de datos Excepciones Ficheros y E/S Programación funcional Funciones Fundamentos Módulos y paquetes Programación orientada a objetos Recursión Cadenas de texto Pruebas y estilo Sistema de tipos Apertura de ficheros (open) Clase abstracta (ABC) Ámbito de variables (scope) Anotaciones de tipo Árbol *args y **kwargs Argumentos por nombre Asignación assert async/await asyncio Atributo de instancia Backtracking Recorrido en anchura (BFS) break y continue Bucle for Bucle while Búsqueda binaria Búsqueda lineal Clase Clausura (closure) Cola (queue) collections Complejidad temporal Composición Comprensión de diccionarios Comprensión de listas Comprensión de conjuntos Condicional (if) Conjunto (set) Gestor de contexto Conversión de tipos Copia profunda Copia superficial Corrutina collections.Counter Módulo csv Dataclass Decorador collections.defaultdict collections.deque Desempaquetado Recorrido en profundidad (DFS) Diccionario Docstring Métodos especiales (dunder) else en bucles Encadenar excepciones Encapsulación Codificación de texto Entornos virtuales (venv) Enumeración (Enum) enumerate() Escritura de ficheros Excepción Excepción personalizada Expresión generadora filter() Formateo de cadenas from ... import f-strings Función Función lambda Funciones de orden superior functools.wraps Generador (yield) GIL Grafo (estructura) Montículo (heapq) Herencia Herencia múltiple Hilos (threading) Identidad de objetos (is) import Indexación Indexación negativa Constructor __init__ Iterables Iterador (iter, next) itertools Jerarquía de excepciones Módulo json Lectura de ficheros Lista map() match-case Memoización (lru_cache) Método Método de clase Método estático Métodos de cadena Mixin Módulo datetime Módulo math Módulo os Módulo random Módulo sys Orden de resolución (MRO) Multiproceso Mutabilidad Mutable vs inmutable namedtuple Notación O grande Objeto / instancia Operador * y ** Ordenación de burbuja Ordenación por mezcla Ordenación rápida Paquete Parámetros y argumentos functools.partial pathlib PEP 8 (estilo) Pila (stack) Gestor de paquetes (pip) Polimorfismo Programación dinámica Propiedad (@property) pytest Python raise range() Recursión Recursión de cola functools.reduce() Expresiones regulares (re) Retorno múltiple Parámetro self Slicing (rebanado) Slicing de cadenas Sobrecarga de operadores sorted() y sort() __str__ y __repr__ super() Tabla hash Operador ternario Booleano (bool) Bytes Complejo (complex) Flotante (float) Entero (int) NoneType (None) Cadena (str) Valores de verdad try/except/else/finally Tupla Comprobación de tipos (mypy) Genéricos (TypeVar) typing.Optional typing.Union unittest Argumentos por defecto Variable Operador morsa (:=) Sentencia with yield from zip()

Pase el cursor o seleccione un concepto para ver sus prerrequisitos, su tema, sus contrastes y sus errores asociados.

prerrequisitoskos:broadercontrastaContema (16) Grafo generado del .ttl canónico y embebido (sin red).

Las 8 consultas SPARQL, con salida real

Cada consulta se ejecuta sobre el grafo inferido (4786 triples) y su salida procede de scripts/consultar.py (la federada, de salidas/08_federada_wikidata.txt).

Las ocho consultas del entregable y su resultado real.
#ConsultaQué demuestraSalida real
0101_conceptos_por_tema.rqSELECT + GROUP BY + GROUP_CONCAT + FILTER(LANG)16 temas con sus conceptos
0202_inferencia_conceptos.rqSELECT diagnóstico de la inferencia de tipos0 sin inferencia → 157 con OWL-RL
0303_prerrequisitos_transitivos.rqproperty path transitivo (+)5 prerrequisitos de búsqueda binaria
0404_ruta_aprendizaje.rqproperty path + OPTIONAL + ORDER BY dificultad1 fila (Función, dificultad 2)
0505_errores_por_concepto.rqOPTIONAL sobre reificación rdf:Statement16 errores (uno con fuente Keuning 2019)
0606_construct_prereq_directos.rqCONSTRUCT que materializa el cierre transitivo356 triples construidos (340 sin inferencia)
0707_diagnostico_envio.rqSELECT diagnóstico-RAG (caso de uso del TFM)2 filas (off-by-one→Indexación; iteración/recursión→Recursión)
0808_federada_wikidata.rqconsulta federada SERVICE a Wikidata20 filas reales (descripción @es)
Código de 02_inferencia_conceptos.rq
PREFIX pyedu: <https://w3id.org/ekg-python/schema#>
PREFIX rdfs:  <http://www.w3.org/2000/01/rdf-schema#>

# Consulta DISEÑADA para evidenciar la inferencia RDFS.
# Recupera todo lo que es pyedu:Concepto.
#   - Inferencia NONE: 0 resultados (ningún individuo se tipa a mano
#     como Concepto; solo por su subcategoría).
#   - Inferencia RDFS/OWL-RL: 157 resultados (rdfs:subClassOf propaga el tipo).
SELECT ?concepto ?etiqueta
WHERE {
  ?concepto a pyedu:Concepto ;
            rdfs:label ?etiqueta .
  FILTER(LANG(?etiqueta) = "es")
}
ORDER BY ?etiqueta
Código de 03_prerrequisitos_transitivos.rq
PREFIX pyedu: <https://w3id.org/ekg-python/schema#>
PREFIX pyr:   <https://w3id.org/ekg-python/id/>
PREFIX rdfs:  <http://www.w3.org/2000/01/rdf-schema#>

# Prerrequisitos directos e indirectos de "búsqueda binaria" mediante
# un property path (+). Esta consulta NO necesita razonador: el camino
# transitivo se calcula en tiempo de consulta. Es el contraste didáctico
# con la propiedad owl:TransitiveProperty, que materializa los mismos
# hechos por inferencia.
SELECT DISTINCT ?prerequisito ?etiqueta
WHERE {
  pyr:busqueda_binaria pyedu:requierePrerrequisito+ ?prerequisito .
  ?prerequisito rdfs:label ?etiqueta .
  FILTER(LANG(?etiqueta) = "es")
}
ORDER BY ?etiqueta
Código de 06_construct_prereq_directos.rq
PREFIX pyedu: <https://w3id.org/ekg-python/schema#>

# CONSTRUCT: materializa como triples directos el cierre transitivo de
# prerrequisitos. Devuelve un grafo nuevo (pyedu:tienePrerrequisitoTransitivo)
# que aplana la representación indirecta, útil para exportar o cargar
# en un sistema que no razone.
CONSTRUCT {
  ?concepto pyedu:tienePrerrequisitoTransitivo ?prerequisito .
}
WHERE {
  ?concepto pyedu:requierePrerrequisito+ ?prerequisito .
}

Validación SHACL: conforme, y 6 violaciones en el control negativo

El grafo canónico es CONFORME (0 violaciones). Para probar que la validación no es vacua, un fichero con seis defectos deliberados (ejemplo-invalido.ttl) dispara exactamente 6 violaciones, una por defecto:

Las 6 violaciones detectadas en el control negativo (pyshacl).
#NodoPropiedadViolación
1pyr:mal_conceptopyedu:tieneDificultaddificultad 8 fuera del rango 1–5 (sh:maxInclusive)
2pyr:mal_ejerciciopyedu:cubreConceptoejercicio sin ningún concepto cubierto (sh:minCount)
3pyr:mal_conceptopyedu:perteneceATemaconcepto sin tema (sh:minCount)
4pyr:mal_ejerciciopyedu:tieneDificultadejercicio sin dificultad declarada (sh:minCount)
5pyr:mal_conceptordfs:labelconcepto sin etiqueta rdfs:label (sh:minCount)
6pyr:mal_ejerciciopyedu:tieneEnunciadoejercicio sin enunciado (sh:minCount)

Contraste de inferencia OWL 2 RL

0 → 157
conceptos (consulta 02, sin → con inferencia)
1772 → 4786
triples (+3014 inferidos)
×2,7
factor de materialización OWL-RL

El salto procede de rdfs:subClassOf (toda subclase de concepto es pyedu:Concepto). Como el enlazado usa skos:exactMatch y no owl:sameAs, las 30 entidades de Wikidata no se infieren como concepto, por eso el resultado es 157 y no 187.

Federada a Wikidata: las 20 filas reales

Hay 30 skos:exactMatch, pero la federada devuelve 20 filas porque solo 20 entidades tienen schema:description en español recuperable.

Las 20 filas (id · etiqueta · descripción Wikidata @es)
Salida real de 08_federada_wikidata.rq.
idEtiquetaDescripción (Wikidata, es)
arbol_estrÁrboltipo abstracto de datos que imita la estructura jerárquica de un árbol
polimorfismoPolimorfismoinformática
pythonPythonlenguaje de programación de alto nivel
regexExpresiones regulares (re)secuencia de caracteres que forma un patrón de búsqueda en cómputo teórico y teoría de lenguajes formales
tabla_hashTabla hashestructura de datos que asocia claves con valores, utilizando una función hash
iteradorIterador (iter, next)En computación, un objeto que permite recorrer un contenedor.
ordenacion_burbujaOrdenación de burbujatipo de algoritmo de ordenamiento
ordenacion_rapidaOrdenación rápidaalgoritmo de ordenación
closureClausura (closure)función en informática que es evaluada en un entorno conteniendo una o más variables dependientes de otro entorno
complejidadComplejidad temporaltiempo de ejecución que describe la demora de una máquina en procesar una tarea
conjuntoConjunto (set)estructura de datos de tipo abstracto en informática
recursionRecursiónmétodo en ciencias de computación
bfsRecorrido en anchura (BFS)algoritmo de búsqueda no informada utilizado para recorrer o buscar elementos en un grafo (usado frecuentemente sobre árboles)
busqueda_binariaBúsqueda binariaalgoritmo de búsqueda
generadorGenerador (yield)rutina especial en informática
grafo_estrGrafo (estructura)tipo abstracto de datos que consiste en un conjunto de nodos
claseClaseen programación orientada a objetos, plantilla para la creación de objetos de datos según un modelo predefinido
diccionarioDiccionarioestructura de datos que asocia claves con valores
herenciaHerenciaconcepto en informática
listaListadato abstracto que representa una secuencia ordenada de valores en ciencias de la computación

VoID / FAIR

El grafo se publica como dato FAIR con una descripción void.ttl (Vocabulary of Interlinked Datasets):

Metadatos declarados en void.ttl.
void:triples1772
void:entities157
void:classes · void:properties20 · 28
Linkset a Wikidata (void:linkPredicate skos:exactMatch)30 enlaces
LicenciaCC BY 4.0
Vocabularios reutilizadosRDFS, OWL, SKOS, Dublin Core, FOAF, schema.org
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.