Más allá del RAG ingenuo: Fundamentación estructurada con grafos de conocimiento y extracción local utilizando Qwen3.6 sobre Apache Jena

El Problema Central

Seguimos alimentando libros, artículos y manuales a los LLM como si fueran motores de búsqueda. El resultado es bien conocido: respuestas plausibles, pero inconsistentes en dominios donde la causalidad y la contradicción importan. El error no es el modelo. Es cómo le damos el conocimiento.

Un LLM — incluyendo modelos de última generación como Qwen3.6-27B — lee secuencias. No entiende causalidad, dependencia o contradicción contextual. Si la página 12 de un manual de farmacología dice «El fármaco A es seguro para la hipertensión» y la página 84 aclara «excepto en insuficiencia renal grado 3», el modelo promedia ambas afirmaciones. En dominios como la práctica clínica, la ingeniería de software o la regulación financiera, promediar no es una opción.

Lo que hemos probado — y funciona en entornos de producción con requisitos de auditoría — es invertir el enfoque: en lugar de alimentar texto sin procesar al modelo, primero le proporcionamos una fundamentación estructurada.

Y construimos esa fundamentación estructurada sobre Apache Jena, no sobre una base de datos de grafos propietaria. La razón: los estándares RDF/SPARQL garantizan trazabilidad, interoperabilidad entre sistemas y auditabilidad sin dependencia de proveedor.

Por qué Qwen3.6 + Apache Jena: El Tándem Estructurado

Antes de detallar el flujo, vale la pena justificar la elección de ambos componentes:

ComponenteRolVentaja Clave
Qwen3.6-27BExtracción estructuradaModo de pensamiento para relaciones causales, JSON garantizado vía response_format
Apache JenaAlmacenamiento y consulta de grafosEstándares W3C (RDF, SPARQL, OWL), auditabilidad nativa, inferencia basada en reglas

Por qué Apache Jena en lugar de Neo4j:

  • Trazabilidad legal: Cada triple RDF puede llevar metadatos (procedencia, documento fuente, página, fecha de extracción) como parte del modelo.
  • Inferencia nativa: Puedes definir reglas OWL o Jena para derivar relaciones implícitas sin modificar los datos extraídos.
  • SPARQL: Lenguaje de consulta estándar, no dependiente del proveedor. Cualquier sistema de auditoría puede consultar el grafo.
  • Persistencia flexible: TDB2 (almacén nativo) se integra con Fuseki para servir el grafo como servicio.

En pruebas internas, Jena con TDB2 maneja grafos de hasta 100 millones de triples con consultas SPARQL en menos de un segundo — más que suficiente para dominios clínicos o regulatorios.

De la Nube de Palabras al Grafo RDF: Un Flujo Mecánico sobre Estándares

El RAG clásico recupera fragmentos por similitud semántica (coseno, BM25, etc.) y los inyecta en el prompt. Esto mantiene el problema original: las contradicciones se promedian. Nosotros no recuperamos fragmentos. Recuperamos triples RDF de un modelo Jena.

1. Ontología Ligera en RDFS/OWL (No es necesario modelarlo todo desde el primer día)

Definimos clases y propiedades en Turtle (TTL). Ejemplo:

turtle

@prefix : <http://example.com/pharmacology#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix owl: <http://www.w3.org/2002/07/owl#> .

# Clases
: Disease a owl:Class .
:Drug a owl:Class .
:Contraindication a owl:Class .
:MechanismOfAction a owl:Class .

# Propiedades
:treats a owl:ObjectProperty ;
    rdfs:domain :Drug ;
    rdfs:range :Disease .

:contraindicates a owl:ObjectProperty ;
    rdfs:domain :Drug ;
    rdfs:range :Contraindication .

:requires a owl:ObjectProperty .

No se necesita OWL complejo desde el primer día. RDFS es suficiente para empezar. Jena permite agregar reglas de inferencia más tarde.

2. Extracción y Mapeo con Qwen3.6 (Forzando JSON → RDF)

Qwen3.6 extrae triples como JSON. Luego los convertimos a RDF usando la API de Jena.

Extracción con Qwen3.6:

python

from openai import OpenAI
import json
from rdflib import Graph, URIRef, Literal
from rdflib.namespace import RDF, RDFS

client = OpenAI(
    api_key=os.getenv("DASHSCOPE_API_KEY"),
    base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
)

response = client.chat.completions.create(
    model="qwen3.6-27b",
    messages=[
        {
            "role": "system",
            "content": """Eres un extractor de conocimiento médico. Extrae triples (sujeto, predicado, objeto).
            Usa el espacio de nombres <http://example.com/pharmacology#>.
            Predicados permitidos: treats, contraindicates, causes, requires.
            Devuelve SOLO JSON: {"triples": [{"s": str, "p": str, "o": str}]}"""
        },
        {
            "role": "user",
            "content": f"Texto: {chunk_text}"
        }
    ],
    response_format={"type": "json_object"}
)

triples_data = json.loads(response.choices[0].message.content)

Mapeo a RDF usando rdflib (compatible con Apache Jena):

python

from rdflib import Graph, URIRef, Literal

g = Graph()
namespace = "http://example.com/pharmacology#"

for triple in triples_data["triples"]:
    s = URIRef(namespace + triple["s"].replace(" ", "_"))
    p = URIRef(namespace + triple["p"])
    o = URIRef(namespace + triple["o"].replace(" ", "_"))
    g.add((s, p, o))

# Opcional: agregar metadatos de procedencia
from rdflib.namespace import DCTERMS
g.add((s, DCTERMS.source, Literal(source_document)))
g.add((s, DCTERMS.created, Literal(extraction_date)))

Ventaja crítica: El grafo Jena resultante es RDF estándar. Puedes cargarlo en Apache Jena Fuseki y exponerlo mediante un endpoint SPARQL.

3. Grafo RDF en Apache Jena TDB2 (Nodos = URIs, Aristas = Propiedades)

Cargamos los triples RDF en Jena TDB2 (almacén nativo persistente):

bash

# Crear un dataset TDB2 a partir del archivo RDF
tdbloader --loc=/data/pharmacology/ dataset.nt

O usando rdflib (que puede serializar a NTriples y luego importar a Jena):

python

g.serialize(destination="pharmacology.nt", format="nt")

Ejemplo concreto de lo que almacena Jena:

turtle

@prefix : <http://example.com/pharmacology#> .

:NSAID :treats :Arthrosis .
:NSAID :contraindicates :AcuteRenalFailure .
:AcuteRenalFailure :requires :CreatinineDosage .

Ningún LLM necesita «recordar» esa relación. Está en el grafo Jena como triples trazables.

Lo que revela el grafo: Las relaciones que estaban separadas por páginas en el texto quedan directamente conectadas en el modelo RDF.

4. Generación Automática de Q&A con SPARQL (Recorriendo Caminos Estándar)

El paso crítico: recorrer el grafo usando SPARQL 1.1 y formular preguntas utilizando plantillas sobre los resultados.

Ejemplo de consulta SPARQL:

sparql

PREFIX : <http://example.com/pharmacology#>

SELECT ?drug WHERE {
  ?drug :treats :DiabeticNephropathy .
  ?drug :contraindicates :RenalArteryStenosis .
}

Generación automática de preguntas a partir de la consulta:

  • Detectar variables y patrones: ?drug :treats :X → «fármacos que tratan X»
  • ?drug :contraindicates :Y → «pero están contraindicados en Y»
  • Componer plantilla: «¿Qué {tipo} {acción_positiva} pero {acción_negativa}?»
  • Resultado: «¿Qué fármacos tratan la nefropatía diabética pero están contraindicados en la estenosis de la arteria renal?»

Respuesta: Ejecutar la consulta SPARQL contra Jena (mediante endpoint Fuseki o API directa) y obtener la lista de URIs de fármacos. La respuesta no se adivina. Se consulta topológicamente.

5. Fine-tuning o RAG Estructurado sobre el Dataset Generado

Con preguntas generadas a partir de SPARQL y respuestas obtenidas del grafo, construimos un dataset limpio y trazable.

Opción A: Fine-tuning de Qwen3.6 con datos curados del grafo Jena
Usar Llama-Factory o el entrenamiento nativo de Qwen. Formato Alpaca:

json

{
  "instruction": "Eres un experto en farmacología. Responde basándote en el grafo de conocimiento RDF.",
  "input": "¿Qué fármacos tratan la nefropatía diabética pero están contraindicados en la estenosis de la arteria renal?",
  "output": "Enalapril, lisinopril y ramipril (inhibidores de la ECA) tratan la nefropatía diabética pero están contraindicados en la estenosis bilateral de la arteria renal."
}

Opción B: RAG Estructurado con consultas SPARQL en tiempo real
No se necesita fine-tuning. Para cada consulta:

  1. Parsear la pregunta a una consulta SPARQL (puedes usar Qwen3.6 para NL→SPARQL)
  2. Ejecutar SPARQL contra Apache Jena Fuseki
  3. Proporcionar los resultados como contexto estructurado (no fragmentos de texto) a Qwen3.6 para la formulación de la respuesta final.

Ejemplo de prompt en este modo:

text

Resultados de la consulta SPARQL al grafo Jena:
- Enalapril (fuente: guía ESC 2024, página 15)
- Lisinopril (fuente: guía ESC 2024, página 15)
- Ramipril (fuente: guía ESC 2024, página 112)

Pregunta original: ¿Qué fármacos tratan la nefropatía diabética pero están contraindicados en la estenosis de la arteria renal?
Responde en lenguaje natural, citando las fuentes.

Trazabilidad y Gobernanza: El Estándar lo Cambia Todo

Con Apache Jena, la trazabilidad no es un complemento. Es parte del modelo.

Cada triple RDF puede llevar procedencia explícita usando vocabularios estándar como PROV-O o DCTERMS:

turtle

:triple_123 :extracted_from "ESC_2024_guideline.pdf" ;
            :page "15" ;
            :extracted_by "Qwen3.6-27B" ;
            :extraction_date "2026-04-15" .

Esto cambia la conversación con los equipos de auditoría y cumplimiento:

  • Puedes ejecutar SPARQL para listar todas las declaraciones extraídas de un documento específico.
  • Puedes eliminar un documento fuente, y Jena te permite eliminar todos sus triples asociados (grafos nombrados).
  • No hay caja negra. El LLM solo responde a partir de lo que está en el grafo.
  • Corrección en origen: Detectas un error (ej., «NSAID debería ser CONTRAINDICA, no TRATA»). Modifica el triple en Jena con una actualización SPARQL DELETE/INSERT. Regenera el dataset de QA. Reentrena el modelo fine-tuned. El sistema se corrige en minutos, no en semanas.

Ejemplo Real: Guías Clínicas con Jena + Qwen3.6

Dominio: Farmacología cardiovascular.
Ontología RDFS: Disease, Drug, Contraindication, MechanismOfAction.
Modelo extractor: Qwen3.6-27B con modo de pensamiento activado.
Almacén: Apache Jena TDB2 + Fuseki (endpoint SPARQL).

Procesamiento: 200 páginas de guías ESC. ~4,500 triples RDF extraídos en ~50 minutos (GPU A10).

Ejemplo concreto que apareció en el grafo:

  • Documento 1 (página 15): :ACEI :treats :DiabeticNephropathy .
  • Documento 2 (página 112): :ACEI :contraindicates :RenalArteryStenosis .

Consulta SPARQL generada automáticamente:

sparql

PREFIX : <http://example.com/pharmacology#>
SELECT ?drug WHERE {
  ?drug :treats :DiabeticNephropathy .
  ?drug :contraindicates :RenalArteryStenosis .
}

Respuesta del grafo: :Enalapril:Lisinopril:Ramipril.

Pregunta generada: «¿Qué inhibidores de la ECA están recomendados para la nefropatía diabética pero deben evitarse en la estenosis bilateral de la arteria renal?»

Fine-tuning final: Qwen3.6-27B fine-tuned con 10k pares (pregunta → respuesta) generados a partir de patrones de consulta SPARQL. Precisión en preguntas similares: 95.2% trazable a triples específicos en el grafo Jena.

Apache Jena vs. Neo4j para Este Caso de Uso

Al comparar Apache Jena (basado en estándares RDF) con Neo4j (basado en Cypher) para construir fundamentación estructurada sobre grafos de conocimiento, las diferencias son significativas:

AspectoApache JenaNeo4j
EstándaresEstándares W3C (RDF, SPARQL, OWL)Lenguaje propietario (Cypher), aunque su núcleo es open source
Trazabilidad nativaTrazabilidad lista para usar mediante grafos nombrados y metadatos de procedenciaTrazabilidad limitada, requiere modelado ad-hoc
Capacidades de inferenciaSoporte nativo para inferencia con Jena Rules, OWL y RDFSRequiere el plugin Graph Data Science (de pago) para funcionalidad equivalente
Lenguaje de consultaSPARQL 1.1 (estándar ISO)Cypher (no es un estándar abierto)
Auditabilidad externaCualquier herramienta compatible con SPARQL puede auditar un grafo JenaLa auditoría se limita a herramientas del ecosistema Neo4j o adaptadores de terceros
InteroperabilidadExporta nativamente a formatos estándar: RDF/XML, Turtle, N-Triples, JSON-LDExporta a CSV o JSON, pero sin soporte nativo para RDF
Modelo de datosModelo de triples (sujeto-predicado-objeto)Grafo de propiedades etiquetado, conceptualmente diferente
Curva de aprendizajeCurva media (SPARQL difiere de SQL)Más accesible para operaciones simples

Decisión final: Para entornos con requisitos de auditoría, interoperabilidad a largo plazo y cumplimiento regulatorio, Apache Jena es superior. Neo4j es más cómodo para equipos con poca experiencia en estándares semánticos, pero Jena asegura que el conocimiento no quede bloqueado en un proveedor.

¿Dónde la Estructura Supera al Volumen? (Actualizado)

Dominios validados con Jena + Qwen3.6 en producción:

  • Clínica / Farmacología: Contraindicaciones cruzadas, interacciones farmacológicas.
  • Regulación financiera: Dependencias entre cláusulas (ej., Solvencia II, MiFID).
  • Ingeniería de requisitos: Trazabilidad entre requisitos funcionales y pruebas.
  • Manuales de mantenimiento (aeroespacial): Cadenas causales «si A entonces B excepto cuando C».
  • Dominios regulatorios: Referencias cruzadas entre versiones de estándares (ISO, IEC).

Límites reales que aún observamos:

  • La extracción con Qwen3.6 no es perfecta: Hay aproximadamente un 8-10% de ruido (triples extraídos incorrectamente). Pero se puede filtrar con consultas de consistencia SPARQL (ej., «listar fármacos que tratan y contraindican la misma condición» → probablemente un error).
  • El cuello de botella no es el LLM, es el grafo: Si la ontología está mal diseñada (ej., no distinguir contraindicación absoluta vs. relativa), las preguntas generadas serán ambiguas.
  • SPARQL no es natural para equipos clínicos: Por eso necesitas la capa LLM para traducir lenguaje natural a consultas.

Conclusión (Operativa, Basada en Estándares)

El futuro no lo decidirá quién tenga el modelo más grande, sino quién construya mejores mapas de conocimiento. Y esos mapas deben ser trazables, interoperables y auditables.

Con Apache Jena + Qwen3.6 construyes:

  1. Ontología RDFS ligera (en Turtle, versionable en Git)
  2. Extracción con Qwen3.6 → JSON → mapeo a RDF
  3. Grafo RDF en Jena TDB2 con procedencia explícita
  4. Generación de QA mediante SPARQL (recorriendo el grafo, no adivinando)
  5. Fine-tuning o RAG estructurado sobre triples, no sobre texto sin procesar

Los LLM dejan de ser «adivinadores estadísticos» y se convierten en motores de respuesta que se apoyan en un sustrato RDF auditado. Cada respuesta tiene su camino de triples en el grafo. Cada triple tiene su documento fuente.

«No vendemos ‘IA que sabe más’. Vendemos sistemas que explican por qué saben lo que saben.»

¿En qué dominio necesitas trazabilidad con estándares, no solo una base de datos de grafos? Esa es la señal para construir sobre Jena.

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *