JARVIS/docs/rag.md
sito 9fa0a40edf saber: manuales cosechados e indice publico pre-construido
- manuales.py cosecha el --help de las herramientas que no traen man page
  (ffuf, sqlmap, suite impacket, netexec, hydra...) y lo escribe como markdown
  en conocimiento/09 - Manuales/. Es la referencia de opciones que la man page
  no da; complementa el cheatsheet de invocaciones comunes.
- indexa.py --publico construye saber-publico.jsonl: glosario + pack de
  conocimiento con los vectores ya calculados, 100% publico y portable.
- busca.py usa ese indice publico como fallback cuando no hay indice personal,
  asi un clon recien hecho consulta al instante sin reindexar nada.
- .gitignore deja pasar saber-publico.jsonl; el personal (saber.jsonl) sigue
  fuera.
- docs/rag.md y el README del pack documentan ambas cosas.
2026-08-16 18:43:56 +02:00

5.5 KiB

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 personal (los apuntes de cada quien) no se publica; sí se publica un índice público pre-construido —glosario de Linux más el pack de metodología y manuales, con los vectores ya calculados (nucleo/datos/saber-publico.jsonl)— para que un clon recién hecho consulte al instante, sin repetir el trabajo de indexar. El código que lo construye está entero en 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íntesisindexació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, manuales, enriquece/
El eval (enriquece/eval_set.jsonl)
El glosario de tareas de Linux en castellano (glosario.py) (genérico)
El pack de metodología y manuales (saber/conocimiento/) — limpio y genérico
El índice público pre-construido (datos/saber-publico.jsonl) — glosario + pack, con vectores
El índice personal (saber.jsonl) y sus copias no — los apuntes indexados
El diario (diario.jsonl) no — las conversaciones
Lo derivado (cosecha.jsonl, sintesis.jsonl) no — salen de los apuntes

Así que un equipo recién clonado consulta al instante, sin dato privado y sin reindexar: el saber-publico.jsonl ya trae el glosario y el pack de metodología —enumeración, explotación web, shells, privesc, Active Directory, cheatsheets— más los manuales de las herramientas que no traen man page (`saber/conocimiento/09

  • Manuales/, cosechados con manuales.pyde su--help). Cuando quiera sumar sus propias notas y las *man pages* de su máquina, corre indexa.pyy pasa a usar su índice personal. Los apuntes privados del usuario (susaber.jsonl`, su diario) siguen fuera; el pack los complementa.