# El RAG

JARVIS responde desde los apuntes del usuario, no desde lo que el modelo
recuerde. Para 668 herramientas de pentesting, recordar sería inventar. Este
documento explica el método y los resultados; el índice en sí no se publica
—contiene los apuntes, que son privados—, pero el código que lo construye sí
(`nucleo/saber/`).

## Cómo funciona

Embeddings **estáticos** (`model2vec`, `potion-multilingual-128M`) sobre los
fragmentos de los apuntes, con una **búsqueda híbrida**: similitud coseno más un
bono por palabras exactas y por nombrar la herramienta. Los estáticos son
rápidos —un vector por token, precalculado, sin red neuronal en la consulta— a
cambio de ser más flojos en lo semántico; el bono por palabras compensa esa
debilidad. No hace falta una base de datos vectorial: con unos miles de
vectores, un producto escalar en `numpy` arranca antes y no añade dependencias.

El RAG entra de **dos formas**:

- **Pasivo**: en cada pregunta se le ponen delante al modelo los fragmentos más
  parecidos. Siempre dispara.
- **Agéntico**: `buscar_en_apuntes` es una herramienta que el modelo invoca
  cuando lo decide, reformulando la consulta con sus palabras y buscando varias
  veces. Salva los casos donde el usuario dice una cosa y el apunte está escrito
  de otra.

## El pipeline de enriquecimiento

En `nucleo/saber/enriquece/`, cuatro fases, reanudable y no destructivo:

```
apuntes ──cosecha──> fragmentos ──sintetiza──> preguntas
                         │                         │
                         └───── reindexa ──────────┘
                                   │
                             índice + vectores
                                   │
                       evalúa (hit@k) ──promociona SOLO si mejora
```

1. **Cosecha**: rescata la metodología que un indexado ingenuo se deja. En el
   caso real, de 72 notas de metodología escritas a mano, **0 estaban en el
   índice**: el filtro por nombre y por profundidad las descartaba. Un tope por
   herramienta evita que un repo a granel (una base de exploits con decenas de
   miles de ficheros) ahogue lo bueno.
2. **Síntesis** — *indexación multi-representación*: por cada fragmento se
   generan las preguntas que responde, y **se embebe la pregunta, se devuelve el
   fragmento**. Así una consulta coloquial ("cómo saco una shell reversa") se
   compara pregunta-contra-pregunta, no contra un comando técnico en otro
   idioma. El modelo solo escribe preguntas, nunca reescribe comandos: si
   inventa una pregunta rara, esa entrada recupera peor y ya está.
3. **Reindexa**: une todo, deduplica y calcula los vectores.
4. **Evalúa**: mide la recuperación con un conjunto de preguntas de prueba
   (`enriquece/eval_set.jsonl`). Determinista, sin juez-LLM.

## Resultados, medidos

Rescatar la metodología y añadir los ganchos-pregunta, sobre el mismo conjunto
de evaluación:

| | antes | después |
|---|---|---|
| hit@1 | 48 % | **82 %** |
| hit@5 | 77 % | **95 %** |
| similitud media | 0.689 | **0.802** |

## Un resultado negativo, útil

¿Y si se cambia el embedder estático por un transformer de verdad (e5, bge)?
Medido: **no compensa**. `intfloat/multilingual-e5-base` empató con el estático
(hit@1 86 %, hit@5 95 %), arreglando dos consultas y rompiendo otras dos, a
cambio de 50-150 ms por consulta y una dependencia de `torch` en cada búsqueda.
El trabajo de recuperación lo hace la búsqueda híbrida, no el embedder. Se
reproduce con `enriquece/experimento_embedder.py`.

## Por qué el índice no está en el repositorio

`saber.jsonl` contiene el TEXTO de los apuntes indexados, con rutas y
conocimiento personal; `diario.jsonl` es lo que se le dice al asistente. Son del
usuario. El `.gitignore` los bloquea. Lo reproducible —el método, el código y la
forma de medirlo— sí está; el corpus lo pone cada quien con sus notas.

## Qué se publica y qué no, en concreto

| | En el repo |
|---|---|
| El código: `indexa`, `busca`, `glosario`, `sistema`, `enriquece/` | **sí** |
| El eval (`enriquece/eval_set.jsonl`) | **sí** |
| El glosario de tareas de Linux en castellano (`glosario.py`) | **sí** (es conocimiento genérico, no personal) |
| El índice (`saber.jsonl`) y sus copias | **no** — los apuntes |
| El diario (`diario.jsonl`) | **no** — las conversaciones |
| Lo derivado (`cosecha.jsonl`, `sintesis.jsonl`) | **no** — salen de los apuntes |

Un detalle útil: `indexa.py` en un equipo recién clonado ya construye un índice
**útil sin ningún dato privado** —de las *man pages* del sistema y del glosario—,
así que se puede ver el RAG funcionando de inmediato; los apuntes de COFRE solo
lo enriquecen si los tienes.
