- docs/panel.png y docs/demo.webm: el panel en funcionamiento (telemetria, reactor, catalogo de acciones y las metricas de rendimiento). - docs/rag.md: el metodo del RAG, el pipeline de enriquecimiento y los resultados medidos (hit@1 48->82%, hit@5 77->95%), incluido el resultado negativo de que un embedder transformer no compensa. El indice NO se publica: contiene los apuntes del usuario. - hud.js: datos de ejemplo de la seccion de rendimiento para el modo demo.
3.9 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 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_apunteses 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
- 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.
- 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á.
- Reindexa: une todo, deduplica y calcula los vectores.
- 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.