# Sistema RAG + Fine-tuning — Motor de feedback del TFM

Implementación del experimento comparativo de tres condiciones que contrasta la
hipótesis central del TFM (la integración de un grafo de conocimiento educativo
mejora la calidad del feedback automático frente al LLM solo):

- **Sistema A** — LLM base local, sin contexto (Ollama).
- **Sistema B** — LLM con fine-tuning QLoRA sobre el dataset, sin grafo.
- **Sistema C** — LLM (base o fine-tuned) + GraphRAG, con el subgrafo del EKG
  recuperado e inyectado en el prompt.
- **Sistema D** — híbrido (B+C): fine-tuning QLoRA + GraphRAG.

## Componentes

```
sistema-rag/
├── scripts/
│   ├── generar_dataset.py      # dataset sintético anclado al EKG (AST + radon)
│   ├── preparar_finetuning.py  # dataset -> formato chat (train/val) para SFT
│   ├── entrenar_qlora.py       # fine-tuning QLoRA (Sistema B); modo --smoke en CPU
│   ├── rag_ekg.py              # recuperador GraphRAG (embeddings + subgrafo SPARQL)
│   ├── generar_feedback.py     # genera feedback con el Sistema A, B o C
│   └── evaluar_ab.py           # arnés de evaluación comparativa A/B/C/D
├── datos/
│   ├── dataset_feedback.jsonl  # 2625 registros (código con error -> feedback)
│   ├── ft_train.jsonl / ft_val.jsonl
│   └── embeddings_conceptos.json  # caché de embeddings de conceptos
├── modelos/                    # adaptadores QLoRA entrenados
└── salidas/                    # informes de evaluación
```

## Pila y entorno verificado

- GPU NVIDIA RTX 5090, 32 GB (Blackwell, sm_120).
- Inferencia con Ollama (`llama3.1:8b` para A/C; embeddings `nomic-embed-text`, 768-dim).
- Fine-tuning con `transformers` + `peft` (LoRA) + `bitsandbytes` (4-bit) + `datasets`.
- Grafo en `rdflib` sobre `../grafo-ekg/ontologia/ekg-python.ttl`; la recuperación
  por SPARQL vale también contra el endpoint de GraphDB.

## Cómo ejecutarlo

```bash
# 1. Dataset y preparación
python scripts/generar_dataset.py
python scripts/preparar_finetuning.py        # 2100 train / 525 val (formato chat)

# 2. Sistemas A y C (inferencia, requieren Ollama activo)
python scripts/generar_feedback.py --sistema A --indice 0
python scripts/generar_feedback.py --sistema C --indice 0

# 3. Evaluación comparativa
python scripts/evaluar_ab.py --sistemas A,B,C,D -n 50

# 4. Fine-tuning del Sistema B
python scripts/entrenar_qlora.py --smoke     # verificación del pipeline (CPU)
python scripts/entrenar_qlora.py             # entrenamiento real (requiere GPU CUDA)
```

## Resultados verificados

- Fine-tuning QLoRA v3 sobre `Qwen2.5-Coder-7B` rinde en held-out `eval_loss`
  1.028 y precisión de token 0.842.
- La recuperación GraphRAG ante un error off-by-one recupera *Indexación* (con su
  prerrequisito *Lista* y el error *off-by-one*), *Bucle for* y *Función*.
- Benchmark held-out A/B/C/D (n=50, semilla=11, juez `qwen2.5:32b`):

| Métrica | Sistema A | Sistema B | Sistema C | Sistema D |
|---|---|---|---|---|
| Acierto de categoría | 0.26 | 0.70 | 0.36 | **0.76** |
| Acierto de concepto | 0.18 | 0.50 | 0.48 | **0.54** |

> El Sistema D (B+C) lidera ambas métricas, por delante de B y de C; el LLM base A
> queda muy por detrás.

## Restricciones del entorno (honestas)

- Fine-tuning en GPU. La build de PyTorch de PyPI es CPU-only y
  `download.pytorch.org` está bloqueado en este sandbox. Para entrenar en la
  RTX 5090 (Blackwell), instala en tu shell con red completa
  `pip install torch --index-url https://download.pytorch.org/whl/cu128`
  y ejecuta `python scripts/entrenar_qlora.py`.
- Modelos gated. `Qwen/Qwen2.5-Coder-7B-Instruct` (por defecto) no requiere
  token; `meta-llama/Llama-3.1-8B-Instruct` sí. `huggingface.co` es alcanzable.
- GraphDB. La recuperación funciona con `rdflib` local; para usar el endpoint
  de GraphDB en el Sistema C basta apuntar el recuperador a su SPARQL endpoint.

## Recomendación de modelos

- Para inferencia A/C y servir B, `llama3.1:8b` (baseline del anteproyecto) o
  `qwen2.5-coder:32b` (mejor en código, entra en 32 GB).
- Para fine-tuning de B, QLoRA 4-bit sobre un 7-8B (holgado en 32 GB). El 32B no
  es entrenable por QLoRA en una sola tarjeta de 32 GB.
- Tras entrenar: fusionar adaptador, convertir a GGUF y `ollama create` para
  servir el Sistema B con el mismo backend que A y C.

## Próximos pasos

- Entrenar el Sistema B en GPU y re-evaluar A/B/C sobre la muestra completa.
- Rúbrica de 5 dimensiones (panel/LLM-juez) además de las métricas automáticas.
- Interfaz web de demostración para el profesor (envío de código → feedback).
