{"id":"5aa7bee9-881c-46f8-b5a6-78980cc326c8","slug":"memorybiorag","kind":"skill","name":"MemoryBioRAG","summary":"Biomimetic cognitive memory system for AI agents, brain-inspired persistence with synaptic plasticity, hybrid PPMI+SVD vector search, LTP/LTD, and inter-agent shared cortex. Pure Python + SQLite, zero","owner":"dennysjmarquez","repoUrl":"https://github.com/dennysjmarquez/MemoryBioRAG","license":"Apache-2.0","licenseTier":"permissive","trust":"community","stars":8,"match":null,"fewStars":false,"format":"skill","createdAt":"2026-05-29T15:20:52.000Z","addedAt":"2026-10-21T17:27:56.685Z","logoUrl":"https://avatars.githubusercontent.com/u/20476777?u=e3d9697b79a2008892f559056cd3047ca4c7ad89&v=4","linkOnly":false,"collection":null,"skillPath":null,"categories":["memory-knowledge","search","databases"],"tags":["ai-agents","biomimetics","cognitive-architecture","knowledge-graph","long-term-memory","memory-system","no-llm","pmi"],"runtime":"python","local":true,"cloudCapable":false,"cloudCapability":"unknown","cloudReadyOnPlatform":false,"needsApiKey":false,"reviewed":false,"syncedAt":"2026-10-25T18:08:09.663Z","sample":false,"url":"https://conduits.dev/tools/memorybiorag","language":"Python","transports":["stdio"],"install":{"pypi":"memory-biorag"},"requirements":{"env":[],"upstreamMayCharge":false},"tools":null,"readme":"# BioRAG — Memoria Cognitiva Biomimética y Simbólica para Agentes de IA\n\n> **Versión Oficial:** v35.0\n> **Paradigma:** Motor Python + SQLite FTS5. **Sin embeddings densos, GPU ni llamadas a APIs externas** en el path de búsqueda.\n> **Motor:** Arquitectura modularizada (18 submódulos `core/memory/` + 15 submódulos `core/mcp_server/` + fachada delgada) + SQLite FTS5 WAL + PPMI-SVD (100 dimensiones) + espacio semántico de 13 ejes + grafo sináptico Hebbiano + SDM (2048 bits) + calibración conforme + activación ACT-R + Concept Hubs + validación simétrica fail-fast de sustantivos clave + **neurona de reordenamiento que aprende sola** (29 señales → 6 ocultas → 1 corrección, con piso en la fórmula a mano) + **neurona asociativa cerrada de fábrica** (5 agujas → fuerza 0–1: ¿dos recuerdos tienen que ver?; examinada, aún no decide en el motor) + enlaces del agente intocables.\n> **Idiomas:** Español e inglés (stemming bilingüe, expansión simbólica con WordNet, Concept Hubs de 5 ángulos y Domain Dict).\n\n**BioRAG** es una memoria simbólica persistente para agentes de IA que aborda la pérdida de contexto entre sesiones mediante recuperación híbrida. “Biomimética” describe una inspiración de diseño; no significa que el motor simule literalmente un cerebro humano. Los resultados de rendimiento se limitan a los benchmarks y snapshots indicados.\n\n---\n\n## 📊 Métricas de la suite oficial (v33.0 · 2026-10-14 · vigentes en v34.0)\n\n> **v34.0:** con una DB nueva, o sin campeón, la neurona no actúa y estos números se mantienen **idénticos** (son el piso). Con el campeón ya aprendido sobre la copia del snapshot (`despertar_neurona.sh`, 500 ciclos), la misma suite da **Recall@5 100.00 % · Recall@1 94.06 % (823/875) · MRR 0.964 · FP 0/40**, sin ninguna categoría por debajo del piso. Detalle en [La Neurona de BioRAG](#-novedades-de-la-versión-v340--la-neurona-de-biorag).\n>\n> **v35.0:** la QA oficial sale **idéntica** antes y después de todos los cambios de la v35 (Recall@5 100.00 % · Recall@1 91.89 % · MRR 0.950 · FP 0/40, misma máquina y semilla). Detalle en [La Neurona Asociativa](#-novedades-de-la-versión-v350--la-neurona-asociativa-sináptica).\n>\n> **Cada base tiene su propia neurona.** La neurona aprende con los recuerdos de la base donde vive y se guarda dentro de ella. Esta tabla mide una copia del snapshot oficial, que no trae neurona entrenada: por eso muestra la fórmula a mano sola (el piso), aunque tu neurona esté trabajando en tu DB viva. La QA ahora lo dice antes de medir y nombra el parámetro para verla: `python scripts/evaluar_qa.py --con-neurona`. Detalle en [La neurona en la QA](#la-neurona-en-la-qa-cada-base-tiene-la-suya).\n\nEvaluación sobre el snapshot congelado oficial canónico (**921 casos totales en suite QA**):\n- **Archivo:** `snapshots/qa_escape_qcr_20260811.db`\n- **SHA-256:** `104dd2902aaf24c3ce93624492ea95a719acbdcca50f4f6bc123ac8317749796` (Git blob `ecc15b7513ff6ddd1356e934a0aa13821337bca4`).\n- **Composición del dataset:** **875 consultas de recuperación** evaluadas para Recall y MRR, **40 controles negativos** evaluados para tasa de falsos positivos, y **6 casos ambiguos** (consultas con etiquetas contradictorias) analizados por separado fuera del denominador de recuperación global.\n\n| Métrica | Resultado observado | Contexto y Significado Científico |\n|---|---:|---|\n| **Recall@5 global** | **100.00% (875/875)** | **100% de cobertura en ventana de trabajo**: todo recuerdo gold ingresa al Top-5 del agente sin fallos de recuperación (0 fallos). |\n| **Recall@1 (Top-1)** | **91.77% (803/875)** | **Precisión de primer resultado**: 803 de 875 casos quedan en primer lugar directo (métrica complementaria observada en benchmark). |\n| **MRR (Mean Reciprocal Rank)** | **0.950** | Posición recíproca promedio ponderada (0.9497 sin redondear; métrica complementaria observada). |\n| **Falsos positivos** | **0/40 (0.00%)** | Calibración conforme efectiva en los 40 controles negativos del benchmark (FP rate observada 0.00%). |\n| **Casos ambiguos (contradictorios)** | **6 casos (2 con cobertura parcial)** | Evaluados por separado del Recall global; 2 casos no cubren al menos una de las etiquetas contradictorias. |\n| **Tests unitarios** | **430/430 aprobados** (v35.0) | Cobertura completa de invariantes, validación pre-DB fail-fast, contratos MCP, normalización, catálogo, las 18 capas de protección de la neurona, el script de despertar, la neurona asociativa, los enlaces del agente intocables y el MCP sin sugerencias de enlace. |\n| **Suite Concept Hub** | **5/5 TOP-1 (100.00%)** | Precisión perfecta en los 5 casos de prueba de puentes asociativos registrados. |\n| **Abismo léxico (EXP-Q)** | **3/3 Pool · 2/3 Top-5 · 1/3 Top-1** | 3/3 descubiertos en pool de grafo; 2/3 en Top-5 (pos #1 y #4) y 1/3 en Top-1 (pos #1). |\n| **Sinónimos · Recall@5 / Recall@1** | **100% (55/55) / 56.36%** | Cobertura total en Top-5; el 43.64% restante queda entre Top-2 y Top-5. |\n| **Por tema · Recall@5 / Recall@1** | **100% (65/65) / 63.08%** | Cobertura total en Top-5; ordenamiento Top-1 guiado por Jaccard y PPMI-SVD (MRR: 0.772). |\n| **Pregunta natural · R@5 / R@1** | **100% (65/65) / 93.85%** | Alta precisión Top-1 en formulaciones conversacionales. |\n| **Cruce de idioma · R@5 / R@1** | **100% (8/8) / 62.50%** | Rescate bilingüe en Top-5; Top-1 asistido por WordNet y Concept Hubs. |\n| **Smoke multicampo en DB local** | **Objetivo #2 → #1** | Promoción de `version_actual_biorag` de #2 a #1 por convergencia multicampo. |\n| **Tiempo de la evaluación global** | **~877 s** | Ejecución determinista de la suite QA de 921 casos en CPU local (sin GPUs ni APIs). |\n\n> **Aclaración Metodológica — Umbrales de Gate vs. Métricas Observadas:**\n> - Los **umbrales formales del Gate** de calidad (`_evaluar_gate()` en `scripts/evaluar_qa.py`) exigen: Recall@5 $\\ge$ 97.0%, Fallos $\\le$ 24, Tasa de Falsos Positivos $\\le$ 15.0%.\n> - **Recall@1 (91.77%)** y **MRR (0.9497)** son métricas complementarias observadas en el benchmark sobre este corpus congelado, no umbrales que el gate imponga ni garantías universales para cualquier consulta arbitraria.\n> - **R@5 = 100.00% (875/875):** Demuestra empíricamente que el conjunto híbrido (FTS5 BM25 + Sustantivos Clave + Grafo Sináptico Hebbiano + PPMI-SVD + Concept Hubs + Reranker Multicampo) logra **cobertura completa (875/875) dentro de la ventana Top-5 de trabajo del agente en este corpus evaluado**.\n> - **R@1 = 91.77% (803/875):** Refleja la precisión exacta del primer candidato. En tareas con solapamiento léxico directo (`literal`, `pregunta_natural`), R@1 supera el 95%–99%. En tareas donde el vocabulario difiere radicalmente (`sinonimo` 56.36%, `por_tema` 63.08%), el sistema ubica el recuerdo dentro del Top-5 pero no siempre en Top-1. R@1 representa el área de optimización continua para el desempate fino sin embeddings densos.\n\n---\n\n## 🧬 Novedades de la Versión v35.0 — La Neurona Asociativa (sináptica)\n\n<p align=\"center\">\n  <img src=\"docs/Mi%20neurona%20en%20el%20sistema/imagenes/neurona_asociativa_biorag.jpg\" alt=\"La neurona asociativa de BioRAG: un cuerpo dentro de la silueta de un hipocampo, cinco dendritas que tocan dos recuerdos y un puente de luz que nace entre ellos\" width=\"100%\">\n</p>\n<p align=\"center\"><em>La neurona asociativa de BioRAG (ilustración). El cuerpo vive dentro de la silueta de un hipocampo, la parte del cerebro que decide qué se une con qué. Sus cinco dendritas (oro: el texto; celeste: el tiempo; verde: las etiquetas) tocan dos recuerdos. Arriba nace la sinapsis entre ellos. Los hilos grises del fondo son las conexiones con ruido que la neurona sabe reconocer.</em></p>\n\n> **En una frase:** BioRAG ya tiene su **segunda neurona**. Mira dos recuerdos y responde una sola pregunta: **¿de verdad tienen que ver?** Nace calibrada de fábrica, no aprende de tu memoria, decide en tiempo constante y **ya está examinada**, también por una persona a mano.\n\n**Estado honesto al cerrar la v35.** La neurona está **construida, calibrada, examinada y cerrada**, pero **todavía no decide nada dentro del motor**:\n- Se probó que pusiera el peso de nacimiento de cada sinapsis (`dd85fcf`). La búsqueda dio exactamente igual, así que **se revirtió** (`76fe11c`).\n- Se probó en una simulación que cortara sinapsis al nacer. **Baja el recall con cualquier umbral**, así que se descartó.\n- Su lugar natural, ya medido en una copia, es **elegir qué asociaciones ve el agente**. Queda para la próxima versión, medido antes y después.\n\nLa v35 sí cambia dos cosas en el motor (ver [Otros cambios de la v35.0](#otros-cambios-de-la-v350)):\n- los **enlaces que hace el agente** ya **no los borra nunca** el sistema;\n- el servidor MCP **ya no le sugiere** al agente qué vincular ni qué desvincular.\n\n| Pieza | Estado en v35.0 |\n|---|---|\n| Neurona asociativa (`core/neurona_asociativa.py`) | ✅ Construida, cerrada (no aprende), con pruebas propias |\n| ¿Decide dentro del motor? | ⏸️ Todavía no. Probado y revertido (peso); probado y descartado (cortar) |\n| Enlaces del agente (`manual`, `manual_v7`) | ✅ **Intocables** para el sistema; solo `desvincular` los borra |\n| Sugerencias MCP de vincular / desvincular | ✅ **Quitadas** (las herramientas `vincular` y `desvincular` siguen) |\n| QA oficial antes y después de todos los cambios | ✅ **Idéntica**: Recall@5 100.00 % · Recall@1 91.89 % · MRR 0.950 · FP 0/40 · `[GATE] OK` |\n| Pruebas | ✅ **430/430** |\n\n![El viaje de la neurona asociativa](docs/Mi%20neurona%20en%20el%20sistema/experimento_13_enlaces_del_agente_y_ruido/figuras/02_el_viaje.png)\n\n### 1. Por qué existe\n\nBioRAG guarda recuerdos y los **une** con sinapsis (el grafo). En la memoria de Dennys hay **851 recuerdos activos y 13,342 conexiones**. Casi todas las crean **cinco reglas automáticas**:\n\n| Regla automática | Sinapsis que creó |\n|---|---:|\n| `sinonimo_explicito` (comparten un sinónimo) | 5,863 (42 %) |\n| `pmi_hebbiano` (aparecen juntas) | 2,359 |\n| `co_ocurrencia` | 2,108 |\n| `co_nombre` (se parecen en el nombre) | 1,558 |\n| `co_semantica` | 1,226 |\n\nEsas reglas unen por coincidencias. Ninguna se pregunta **si los dos recuerdos de verdad tienen que ver**. Lo medimos de tres formas:\n\n1. **Examen a mano.** Dennys juzgó 99 pares de su memoria. Entre las sinapsis automáticas, cerca del **45 % son buenas** (margen 32–59 %). Más o menos la mitad es ruido.\n2. **El peso de hoy no separa nada.** Con el peso que reciben hoy, los 381 enlaces hechos por personas quedan **por debajo** del resto (AUC **0.271**, peor que tirar una moneda).\n3. **El único juez hoy es la hormiguita**, que le pregunta a Gemini. Es una API externa: cuesta dinero, necesita internet, puede responder distinto ante el mismo par y además se equivoca. De 15 pares que Gemini sacó, Dennys dijo que **6 sí tenían que ver**.\n\n**En el cerebro**, quien decide qué se une con qué es el **hipocampo**. Funciona por partes:\n- la **corteza entorrinal** le trae las señales;\n- el **giro dentado** separa lo que se parece pero no es lo mismo;\n- **CA3** guarda la red de asociaciones;\n- **CA1** compara y decide.\n\nBioRAG ya tenía la entrada (las señales) y la red (el grafo y la SDM). **Le faltaba el juez.** Esta neurona es ese juez.\n\n![De tu hipocampo a la neurona](docs/Mi%20neurona%20en%20el%20sistema/experimento_11_neurona_asociativa/figuras/02_hipocampo.png)\n\n### 2. Qué tipo de neurona es\n\n| Pregunta | Respuesta |\n|---|---|\n| **Arquitectura** | **Una neurona logística**: 5 entradas (\"agujas\") → Σ → curva sigmoide → **fuerza** de 0 a 1. Son **6 números** en total (un sesgo y 5 pesos). |\n| **Qué responde** | ¿Los recuerdos A y B tienen que ver? `fuerza ≥ 0.6` = sí con confianza; `< 0.6` = dudoso. |\n| **De dónde salen sus pesos** | **De fábrica**. Se calibraron a mano sobre **7 memorias públicas** muy distintas (noticias en inglés y en español, un diccionario, relatos, código Python, un chat real y la obra de Machado de Assis en portugués). Cada una trae su propia verdad, por ejemplo \"mismo tema\" o \"misma clase\". |\n| **Qué aprende de tu memoria** | **Nada.** Es **cerrada**: nace calibrada, como las neuronas con las que nacemos. No memoriza tu memoria ni la de nadie. |\n| **Qué sí mide de cada memoria** | Solo lo necesario para leerla: la rareza de cada raíz, un espacio semántico de 100 dimensiones y **dos llaves**. Las llaves dicen si en *esa* memoria el tiempo y las etiquetas coinciden con el texto. Todo se mide solo, sin respuestas correctas. |\n| **Idiomas** | Cualquiera. Usa raíces de 7 letras y no tiene listas de palabras de ningún idioma. |\n| **Cuánto cuesta** | Cada decisión entre dos recuerdos es de **tiempo constante**: no depende del tamaño de la memoria. El estado se arma una vez sobre un máximo de **2,000 recuerdos** y se rehace cuando la memoria crece un 10 %. Solo usa numpy, sin GPU ni APIs. |\n| **Dónde vive** | `core/neurona_asociativa.py` (versión `asoc-1`). Si algún día la llama el motor, guarda su estado en dos tablas propias: `neurona_asociativa_estado` y `neurona_asociativa_raices`. |\n| **Interruptor** | `BIORAG_NEURONA_ASOCIATIVA=0` la apaga. En v35 el motor no la llama, así que hoy no cambia nada. |\n\n![La neurona asociativa del motor: cómo decide](docs/Mi%20neurona%20en%20el%20sistema/experimento_13_enlaces_del_agente_y_ruido/figuras/01_neurona_del_motor.png)\n\n**La inspiración biológica (es inspiración, no simulación):**\n\n| En el cerebro | En la neurona asociativa |\n|---|---|\n| Corteza entorrinal (trae la información) | Las **agujas**: lo que se mide del par |\n| Giro dentado (separa lo parecido de lo igual) | `rareza_max`: compartir una pista **rara** vale más que compartir una palabra común |\n| Corteza (significado) | `parecido` y `semantica` |\n| **Células de tiempo** (lo que se vive junto se une) | `episodio`: guardados cerca en el tiempo. Es la aguja que más pesa (×4) |\n| Esquemas de la corteza prefrontal | `etiquetas`: dims compartidas |\n| CA1 (compara y decide) | El Σ y la curva: la **fuerza** |\n| CA3 (la red de asociaciones) | El grafo y la SDM que **ya existían** en BioRAG |\n| Homeostasis (no dejar que algo domine todo) | Las **llaves**: el tiempo y las etiquetas valen solo si en esa memoria coinciden con el texto |\n\n### 3. La fórmula: un piso a mano, calibrado y cerrado\n\n```\nz      = −1 + 2·parecido + 1·semantica + 1·rareza_max + 4·episodio·llave_tiempo + 1·etiquetas·llave_dims\nfuerza = 1 / (1 + e^−z)\n```\n\n| Aguja | Pregunta que hace | Peso | Por qué ese peso |\n|---|---|---:|---|\n| `parecido` | ¿Usan las mismas palabras? (coseno tf-idf de raíces) | 2 | La señal más estable en las 7 memorias |\n| `semantica` | ¿Significan lo mismo? (LSA, 100 dimensiones) | 1 | Rescata pares que dicen lo mismo con otras palabras |\n| `rareza_max` | ¿Comparten una pista muy rara? | 1 | Compartir \"nefrona\" dice más que compartir \"sistema\" |\n| `episodio` | ¿Se guardaron cerca en el tiempo? | 4 | Es lo que más pesa en el cerebro. Solo vale si la llave dice que en *esa* memoria el tiempo coincide con el texto (en la de Dennys, la llave vale 0.48). Si pasaron más de 7 días reales, no cuenta. |\n| `etiquetas` | ¿Comparten dims? | 1 | Igual, con su propia llave (0.36 en la de Dennys) |\n\n**Un par real, paso a paso.** Es uno de los enlaces que Dennys puso a mano. Casi no comparten palabras (`parecido` 0.04), pero comparten una pista rara (0.50) y se guardaron cerca en el tiempo (0.44, ya pasado por la llave). La suma da z = 1.65, y eso da una **fuerza de 0.84**. A la izquierda se ven las 12 agujas medidas; en la suma entran solo las 5 de la fórmula. La nota de abajo (\"top 10\") viene de la competencia del experimento, que el motor no usa.\n\n![Un par real, paso a paso](docs/Mi%20neurona%20en%20el%20sistema/experimento_11_neurona_asociativa/figuras/03_un_par_paso_a_paso.png)\n\n**Las llaves: cada memoria decide sola si confía en el reloj y en las etiquetas.** La llave compara qué tan parecidos son, por texto, los recuerdos que el reloj (o las etiquetas) junta, contra pares al azar. No usa ninguna respuesta correcta. Si no hay relación, la llave se cierra: con un reloj al azar da casi 0, y una prueba automática lo verifica. Por eso el tiempo cuenta casi entero en un chat (llave 0.74), casi nada en noticias (0.10) y a la mitad en la memoria de Dennys (0.48).\n\n![Las llaves de auto-regulación](docs/Mi%20neurona%20en%20el%20sistema/experimento_11_neurona_asociativa/figuras/06_auto_regulacion.png)\n\nSe midieron **12 agujas**. Solo 5 entran en la fórmula, porque las otras 7 no ayudaban en las 7 memorias a la vez. También se descartaron, con pruebas:\n- **Competencia** (cada recuerdo se queda con sus K socios más fuertes): conserva pares débiles y suelta buenos.\n- **Coacceso** (recuerdos que se piden juntos): AUC 0.459, no separa.\n\nLa neurona **tiene una parte que puede aprender, pero queda apagada.** Probamos una corrección aprendida sobre la fórmula:\n- con cada memoria por separado ganaba hasta +4.55 puntos;\n- con todas juntas, lo que una ganaba otra lo perdía (la \"balanza\").\n\nPara servir en cualquier memoria, **la fórmula a mano es mejor**. La corrección queda en escala 0, por decisión de Dennys: la neurona asociativa **no aprende**.\n\n![El banco de agujas](docs/Mi%20neurona%20en%20el%20sistema/experimento_11_neurona_asociativa/figuras/04_banco_agujas.png)\n\n![La balanza: por qué la parte que aprende queda apagada](docs/Mi%20neurona%20en%20el%20sistema/experimento_11_neurona_asociativa/figuras/11_balanza_aprender.png)\n\n### 4. Cómo se examinó\n\n**4.1. En memorias que nunca vio.** Para cada memoria se volvió a calibrar la fórmula **sin mirarla** (con las otras 6) y después se le tomó examen. La AUC es la probabilidad de que un par que sí tiene que ver saque más fuerza que uno que no: 0.5 es azar y 1.0 es perfecto.\n\n| Memoria | Idioma | Pares | Sí tienen que ver | Regla simple (Jaccard) | **Neurona** |\n|---|---|---:|---:|---:|---:|\n| noticias (Reuters) | en | 47,175 | 26.5 % | 0.834 | **0.882** |\n| diccionario (WordNet) | en | 40,225 | 2.9 % | 0.843 | **0.902** |\n| relatos (Brown) | en | 27,195 | 5.8 % | 0.915 | **0.994** |\n| código (librería de Python) | Python | 31,223 | 6.9 % | 0.900 | **0.927** |\n| chat real (NPS) | en | 24,746 | 23.2 % | 0.627 | **0.911** |\n| noticias (EFE) | es | 16,359 | 6.4 % | 0.915 | **0.997** |\n| Machado de Assis | pt | 25,782 | 8.9 % | 0.758 | **0.962** |\n\nPromedio **0.939**; el peor caso es **0.882**. En las 7 memorias, la neurona supera a la regla simple.\n\n![Examen en memorias nunca vistas](docs/Mi%20neurona%20en%20el%20sistema/experimento_11_neurona_asociativa/figuras/07_examen_nunca_vista.png)\n\n**4.2. En la memoria real de Dennys, que tampoco vio.** En la DB hay dos jueces: lo que unió una persona o el agente (381 enlaces) y lo que juzgó Gemini.\n\n| Prueba | Peso de hoy | **Neurona** |\n|---|---:|---:|\n| Enlaces humanos vs el resto (AUC) | 0.271 | **0.868** |\n| Enlaces humanos vs lo que Gemini sacó (AUC) | — | **0.930** |\n| Fuerza típica (mediana): humanos / Gemini confirmó / Gemini sacó | — | **0.75** / 0.45 / 0.40 |\n\n![Tu memoria: tres jueces](docs/Mi%20neurona%20en%20el%20sistema/experimento_11_neurona_asociativa/figuras/08_tu_memoria_tres_jueces.png)\n\n**La huella:** para cada aguja, el valor típico de los enlaces humanos (verde) y el de lo que Gemini sacó (rojo). Cuanto más larga la barra, más ayuda esa aguja a distinguirlos. Las que más separan son `pistas_raras`, `mutuo` y `semantica`.\n\n![La huella en tu memoria](docs/Mi%20neurona%20en%20el%20sistema/experimento_11_neurona_asociativa/figuras/05_huella_en_tu_memoria.png)\n\n**4.3. Examen a mano.** Dennys leyó los 100 pares con el texto completo de cada recuerdo y contestó 99.\n\n| Resultado | Valor |\n|---|---|\n| AUC de la fuerza en los 99 pares | **0.73** (margen 0.62–0.83) |\n| Pares con fuerza **≥ 0.6** | 31 «sí» de 42 (74 %) |\n| Pares con fuerza **< 0.6** | 16 «sí» de 44 (36 %) |\n| Sinapsis que **ya existen** con fuerza ≥ 0.6 | **13 buenas, 1 mala** |\n| Sinapsis existentes: neurona vs peso de hoy (AUC) | **0.77** vs 0.63 |\n| Pares que **no existen** y la neurona propone | **12 «sí» de 15** (80 %) |\n| Sinapsis automáticas buenas (estimado) | ~45 % (32–59 %) |\n\nLa neurona no es perfecta, pero **juzga como una persona mucho mejor que el peso de hoy**, y encuentra enlaces buenos que hoy no existen.\n\n### 5. Qué se probó dentro del motor, y por qué hoy no decide\n\nTodo se midió con la QA oficial (875 búsquedas), en **copias** del snapshot. Nunca se tocó la DB real.\n\n**5.1. Poner el peso de nacimiento (experimento 12) → revertido.** Con la neurona fijando el peso de cada sinapsis nueva, la QA salió **idéntica**, incluso repesando todas las sinapsis automáticas de una copia. La búsqueda casi no mira ese peso. Ordenar mejor los enlaces no es buscar mejor. Como no se podía demostrar una mejora, Dennys pidió revertirlo (`76fe11c`).\n\n**5.2. Cortar sinapsis al nacer (experimento 13) → descartado.** Se simuló la neurona dejando nacer solo las sinapsis con fuerza suficiente, con la hormiguita apagada:\n\n| Escenario | Sinapsis automáticas | Recall@5 | Recall@1 | MRR | Fallos | Pares del examen que quedan (buenos / malos) |\n|---|---:|---:|---:|---:|---:|---|\n| Hoy (con hormiguita) | 13,114 | **100.00 %** | 91.89 % | 0.950 | 0 | 26 / 29 |\n| Hormiguita apagada | 15,864 | **100.00 %** | **92.00 %** | **0.951** | 0 | 30 / 36 |\n| La neurona corta < 0.40 | 10,261 | 99.89 % | 91.77 % | 0.949 | 1 | 27 / 28 |\n| La neurona corta < 0.50 | 3,847 | 99.66 % | 91.77 % | 0.948 | 3 | 18 / 11 |\n| La neurona corta < 0.60 | 1,684 | 99.43 % | 91.66 % | 0.947 | 5 | 15 / 8 |\n| Sin sinapsis automáticas | 0 | 99.43 % | 91.77 % | 0.948 | 5 | — |\n\n![¿El ruido ayuda a buscar?](docs/Mi%20neurona%20en%20el%20sistema/experimento_13_enlaces_del_agente_y_ruido/figuras/03_el_ruido_y_la_busqueda.png)\n\nLo que enseñó:\n- **El \"ruido\" ayuda a buscar.** Sin las sinapsis automáticas se pierden 5 búsquedas.\n- **Cortar baja el recall con cualquier umbral.** La regla de BioRAG es que el recall nunca baja, así que la neurona **no debe borrar**.\n- **Apagar la hormiguita no bajó nada** (incluso sube un poco el primer puesto: por tema pasa de 63.08 % a 64.62 %). Es una simulación; la hormiguita **sigue igual en v35**.\n\n**5.3. Su lugar: lo que ve el agente (medido, todavía no aplicado).** Junto a cada resultado, BioRAG le muestra al agente las **asociaciones** del recuerdo (el \"canal 2\"). Eso **no toca el ranking**. Si la neurona eligiera cuáles mostrar:\n\n| Asociaciones mostradas | Total | Automáticas | Del agente | Recuerdos sin vecinos | Pares del examen visibles (buenos / malos) |\n|---|---:|---:|---:|---:|---|\n| Hoy | 3,173 | 2,466 | 691 | 22 | 5 / 8 |\n| La neurona filtra ≥ 0.50 | 2,588 | 1,876 | 694 | 103 | **9 / 1** |\n| La neurona filtra ≥ 0.60 | 2,024 | 1,303 | 698 | 225 | 10 / 1 |\n\n![Lo que ve el agente](docs/Mi%20neurona%20en%20el%20sistema/experimento_13_enlaces_del_agente_y_ruido/figuras/04_lo_que_ve_el_agente.png)\n\nLa muestra es chica (10 a 13 pares) y parte del examen se eligió con la fuerza de la neurona. Es una **señal**, no una prueba firme. Por eso queda como el **próximo paso**, con su propia medición.\n\n### 6. Por qué debe existir\n\nEl mapa muestra lo que funciona hoy (línea llena) y los tres lugares donde la neurona puede entrar (línea punteada violeta). Ninguno está hecho todavía; irán de a uno. El ranking no se toca.\n\n![Dónde va la neurona asociativa en el motor](docs/Mi%20neurona%20en%20el%20sistema/experimento_13_enlaces_del_agente_y_ruido/figuras/05_donde_va_en_el_motor.png)\n\n1. **Para quitar a Gemini del grafo.** Hoy la limpieza depende de una API externa que cuesta, necesita internet y no siempre responde lo mismo. La neurona es local, gratis, determinista y de tiempo constante. Además separa los enlaces humanos de lo que Gemini sacó con AUC **0.930**.\n2. **Para que el agente vea asociaciones buenas.** Hoy, de los pares del examen que el agente llega a ver, hay más malos que buenos (5 / 8). Con la neurona filtrando, 9 / 1.\n3. **Para proponer lo que falta.** El 31 % de los enlaces humanos nunca llega a ser candidato de ninguna regla automática. De los pares que la neurona propuso y no existían, Dennys aprobó el **80 %**.\n4. **Para arreglar `vincular_por_sinonimos`**, la regla que más sinapsis crea (42 %). Con sus 5 mejores por recuerdo, la neurona encuentra **148** de los 381 enlaces humanos (fuerza media 0.711). Un arreglo a mano encontraba 122 (0.536).\n5. **Porque cualquier memoria la puede usar desde el día uno.** Viene calibrada de fábrica y no necesita QA, ni respuestas correctas, ni entrenamiento. Sirve para una DB vacía de un tercero.\n\nCada uno de estos pasos irá **por separado**: un cambio a la vez, con la QA antes y después, y la regla de que el recall nunca baja.\n\n### Otros cambios de la v35.0\n\n**Los enlaces del agente son intocables (`8c7d476`).** Lo que une el agente (`vincular`, o `vincular_con` en `aprender`/`guardar`; tipos `manual` y `manual_v7`) es una decisión informada: el agente buscó antes de guardar (`busqueda_previa`). Antes, 9 de esos enlaces habían bajado de 0.5 por el decaimiento. Ahora el sistema los salta en todos los procesos que pueden debilitar o borrar:\n\n| Proceso | Antes | v35 |\n|---|---|---|\n| Decaimiento del peso | Bajaba | No los toca |\n| Borrado por peso < 0.05 | Podía borrarlos | No los toca |\n| Poda LTD guiada | Podía podarlos | No los toca |\n| Hormiguita: prefiltro, cuarentena, borrar o repesar | Podía sacarlos | No los toca |\n| `desvincular` (el agente lo pide) | Borra | **Borra** (la decisión sigue siendo del agente) |\n\n**El MCP ya no sugiere qué vincular ni qué desvincular (`0182ad4`).** Se quitaron los mensajes que empujaban al agente a crear o borrar enlaces. Las herramientas `vincular` y `desvincular` siguen en el MCP, y `vincular_con` en `aprender` no cambió. El agente decide solo, con lo que encontró en su búsqueda previa.\n\n**Pruebas nuevas:**\n- `tests/test_neurona_asociativa_motor.py` (fiel al experimento 11, costo constante, techo de la muestra y que guardar sigue usando los pesos a mano);\n- `tests/test_enlaces_del_agente_intocables.py` (9 pruebas);\n- `tests/test_sin_sugerencias_de_enlace.py` (4 pruebas).\n\nTotal: **430/430**. La QA oficial quedó **idéntica** antes y después de todos los cambios v35, con el mismo snapshot y la misma semilla (`PYTHONHASHSEED=1`).\n\n### Límites (lo que no está probado)\n\n- Se calibró con 7 memorias públicas y se examinó con **una** memoria personal. Faltan más memorias reales.\n- El examen a mano lo hizo **una persona** con 99 pares. Parte de los pares se eligió con la propia fuerza de la neurona, lo que puede favorecerla.\n- La QA oficial mide si el recuerdo correcto sale en la búsqueda, **no** la calidad de las asociaciones ni el peso. Por eso el experimento 12 salió \"igual\".\n- Lo de la hormiguita apagada y el filtro del canal 2 se midió **en copias**. Ninguno está en el motor.\n- No se comparó todavía *agente + BioRAG* contra *agente + base de datos vectorial*.\n- El hipocampo es **inspiración**, no simulación.\n\nLo que ya tiene y lo que todavía le falta, medido y no supuesto. El 69 % de los enlaces humanos la neurona los propondría sola; el 31 % restante ni siquiera llega a ser candidato de ninguna regla:\n\n![Lo que todavía le falta](docs/Mi%20neurona%20en%20el%20sistema/experimento_11_neurona_asociativa/figuras/12_lo_que_falta.png)\n\n### Dónde está todo\n\n| Qué | Dónde |\n|---|---|\n| Diseño: el molde y dónde nacen las sinapsis | [Experimento 10 — Neuronas del motor](docs/Mi%20neurona%20en%20el%20sistema/experimento_10_neuronas_del_motor/) |\n| Construcción, calibración, exámenes y las 12 figuras | [Experimento 11 — La neurona asociativa](docs/Mi%20neurona%20en%20el%20sistema/experimento_11_neurona_asociativa/) |\n| Peso de nacimiento (revertido) | [Experimento 12 — La neurona en el motor](docs/Mi%20neurona%20en%20el%20sistema/experimento_12_neurona_en_el_motor/) |\n| Enlaces del agente, ruido, cortar al nacer, canal 2 y cierre | [Experimento 13 — Enlaces del agente y ruido](docs/Mi%20neurona%20en%20el%20sistema/experimento_13_enlaces_del_agente_y_ruido/) |\n| La neurona | `core/neurona_asociativa.py` |\n\nLas figuras se redibujan sin ninguna DB:\n- `python dibujar.py` en la carpeta del experimento 11;\n- `python dibujar.py` en la carpeta del experimento 13.\n\n**Con esto, el tema de la neurona asociativa queda cerrado en la v35.**\n\n---\n\n## 🌱 Novedades de la Versión v34.1 — La neurona aprende sola de verdad\n\n> **En una frase:** con v34.0, una memoria **nueva** casi nunca llegaba a encender la neurona. Con v34.1 lo logra en unas **20 consolidaciones**, sin perder nada en la QA oficial.\n\n### Cuánto tarda en aprender sola (v34.1)\n\nLa neurona practica **1 vez por cada consolidación** de la memoria (`consolidar`, `sueno` o cerrar sesión con `contexto_fin`). `aprender` deja el recuerdo en corto plazo y todavía no cuenta: 10 `aprender` + 1 `consolidar` = 1 práctica.\n\nEn v34.0 cada práctica hacía **2 preguntas a 2 recuerdos**: usaba 0.4 s de los 3 s que tiene permitidos. Medido con una memoria nueva que crece (5 recuerdos y 1 consolidación por sesión), en 80 sesiones juntó 20 preguntas de estudio y 7 de control. Necesita 150 y 40, así que **nunca** se encendía.\n\nv34.1 cambia solo **qué practica en cada consolidación**:\n\n1. **Usa el tiempo que ya tenía:** hasta **8 recuerdos × 7 preguntas**, siempre dentro de los mismos **3 s**. El costo sigue fijo: no crece con el tamaño de la memoria.\n2. **Pregunta a lo ancho:** primero la 1.ª pregunta de los 8 recuerdos, luego la 2.ª de los 8... Si los 3 s se acaban (memoria grande, búsquedas más lentas), las preguntas quedan repartidas entre muchos recuerdos distintos y no amontonadas en dos. Así conoce más recuerdos y no se acostumbra a pocos.\n3. **Repasa primero lo que le falta:** al elegir recuerdos al azar, empieza por el grupo del examen (estudio o control) al que más le faltan preguntas.\n\n| Memoria nueva, 5 recuerdos por sesión | v34.0 | **v34.1** |\n|---|---|---|\n| Examen listo (150 estudio + 40 control) | nunca | **sesión 16** |\n| La neurona empieza a trabajar | nunca | **sesión 21** |\n| Sesiones trabajando (de 80) | 0 | **60** (0 retiros) |\n| Tiempo por consolidación | 0.65 s | 2.15 s (tope 3 s) |\n\n**¿Se pierde algo? No.** QA oficial sobre una copia del snapshot, con el mismo número de prácticas (561):\n\n| | R@5 | R@1 | MRR | FP | Agente (MCP) R@1 / R@5 |\n|---|---|---|---|---|---|\n| Fórmula a mano (piso) | 99.89 | 91.66 | .949 | 0/40 | 92.34 / 99.09 |\n| v34.0 | 100 | 94.86 | .969 | 0/40 | 94.97 / 99.54 |\n| **v34.1** | **100** | **94.74** | **.968** | **0/40** | **94.74 / 99.54** |\n\nLa diferencia es 1 caso de 875, el ruido normal entre dos entrenamientos. Ninguna categoría queda por debajo de la fórmula y el GATE da OK.\n\n**Lo honesto:**\n- **Primer intento descartado:** el primer diseño (8 × 7 preguntando recuerdo por recuerdo) bajó la QA a R@1 93.26: conocía un tercio menos de recuerdos. De ahí sale \"a lo ancho\".\n- **Memoria muy chica:** con solo 2 recuerdos por sesión, el motor duerme los que no se usan y quedan ~25 despiertos. Con tan pocos la fórmula alcanza y la neurona no se enciende, que es lo correcto.\n- **Despertar:** a igual tiempo hace menos prácticas (cada una usa sus 3 s). `despertar_neurona.sh` mantiene su default de 500 prácticas (ahora ~22 min con ~900 recuerdos).\n\nTodo el detalle, con las salidas textuales: [Experimento 09 — Que aprenda sola de verdad](docs/Mi%20neurona%20en%20el%20sistema/experimento_09_aprende_sola_de_verdad/). Pruebas nuevas en `tests/test_neurona.py`: preferencia por el grupo que falta, recién consolidados primero y nunca dormidos, costo fijo al elegir (DB de 50 o 50 000 recuerdos) y preguntas a lo ancho.\n\n---\n\n## 🧠 Novedades de la Versión v34.0 — La Neurona de BioRAG\n\n<p align=\"center\">\n  <img src=\"docs/Mi%20neurona%20en%20el%20sistema/imagenes/neurona_biorag.jpg\" alt=\"La neurona de BioRAG: 29 agujas que convergen en un cuerpo con 6 neuronas ocultas, protegido por un escudo, cuyo axón ordena los resultados\" width=\"100%\">\n</p>\n<p align=\"center\"><em>La neurona de BioRAG (ilustración). Agujas doradas: las señales de la fórmula. Agujas celestes: lo que aporta el agente. Los seis orbes: las neuronas ocultas. El escudo: las capas de protección. Las tarjetas: los resultados que ordena.</em></p>\n\n> **En una frase:** BioRAG ahora tiene una pequeña neurona que **aprende sola, mientras se usa, a ordenar mejor los primeros resultados**. Empieza siendo exactamente la fórmula a mano y **nunca puede quedar por debajo de ella**.\n\nLa versión **v34.0** es mayor porque cambia el **comportamiento del ranking**: por primera vez una pieza aprendida puede mover el orden de lo que recibe el agente. También cambian tres valores por defecto del servidor MCP (ver [Otros cambios de la v34.0](#otros-cambios-de-la-v340)). La API no se rompe: las llamadas MCP son las mismas.\n\n### 1. Por qué existe\n\nLa fórmula de BioRAG suma 17 señales (\"agujas\") con **pesos fijos puestos a mano**: BM25 vale 0.25, el Concept Hub 0.20, PPMI 0.15… Esos pesos son buenos en promedio, pero **la importancia de cada aguja depende del contexto**:\n- En una búsqueda literal manda BM25.\n- En una búsqueda por sinónimo, BM25 casi no aporta y pesan más los sinónimos, el PPMI y el grafo.\n- Cuando el agente manda `sustantivos_clave`, la evidencia en ese campo vale más que una coincidencia suelta en el contenido.\n\nUn peso fijo no puede capturar eso. Por eso la fórmula llegaba al Top-5 en el 100 % de los casos, pero el **primer lugar** fallaba en las categorías difíciles: sinónimo 56 %, por tema 63 %.\n\nLa neurona aprende **cuánto vale cada aguja según el contexto** y corrige el orden **solo cuando está segura**.\n\n### 2. Qué tipo de neurona es\n\n| Pregunta | Respuesta |\n|---|---|\n| **Arquitectura** | Perceptrón multicapa chico: **29 entradas → 6 neuronas ocultas (tanh) → 1 salida**. **186 pesos** en total (W 6×29, b 6, v 6). |\n| **Qué produce** | Una **corrección residual** c(x) que se **suma** al score de la fórmula. Con c(x) = 0 el motor es idéntico al de siempre: los pesos a mano son el **ancla** y el **piso**. |\n| **Qué aprende** | *Learning to rank* por lista: entropía cruzada sobre el softmax del top-k, es decir, \"que la respuesta correcta quede primera\". |\n| **De dónde saca las respuestas correctas** | **Autosupervisión**. Cuando un recuerdo se consolida, la respuesta correcta se conoce: es ese recuerdo. El recuerdo se hace sus propias preguntas y el motor las responde en silencio. |\n| **Cuándo aprende** | **Al consolidar** (cuando un recuerdo pasa de corto a largo plazo: `consolidar`, `sueno` o `contexto_fin`), nunca al buscar. Así la búsqueda no paga el entrenamiento. Una práctica por consolidación, sin importar cuántos `aprender` hubo antes. |\n| **Cómo decide si lo aprendido sirve** | **Campeón y retador**: el retador estudia; solo reemplaza al campeón si gana en un examen que **nunca estudió** (la guardia) y no daña ninguna categoría. |\n| **Cuánto cuesta** | **Fijo por operación**: hasta 8 recuerdos × 7 preguntas por ciclo, siempre dentro de un tope de 3 s, y memoria de examen de tamaño fijo. El costo no crece con el tamaño de la memoria. |\n| **Dónde vive** | `core/memory/neurona.py` (la neurona) y `core/memory/neurona_examen.py` (el autoexamen). Su estado se guarda en la tabla `neurona_estado` de la misma DB. |\n\n**La inspiración biológica (es inspiración, no simulación):**\n\n| En el cerebro | En BioRAG |\n|---|---|\n| Dendritas que reciben señales | Las **29 agujas**: 17 de la fórmula y 12 de lo que pasó después y de lo que aporta el agente |\n| Interneuronas | Las **6 neuronas ocultas**, que combinan agujas (por ejemplo: \"sinónimo alto + BM25 bajo + vino del agente\") |\n| Axón que modula | La **corrección** que reordena el top-k |\n| Consolidación durante el sueño (*replay* del hipocampo) | Aprende **al consolidar**, repasando recuerdos |\n| *Primum non nocere* | El **margen \"primero no dañar\"** |\n| Homeostasis | **Piso** (no bajar de la fórmula) y **techo** (dejar de entrenar cuando ya no mejora y despertar si algo cambia) |\n\n### 3. Anatomía exacta\n\n<p align=\"center\"><img src=\"docs/Mi%20neurona%20en%20el%20sistema/imagenes/anatomia_neurona.png\" alt=\"Anatomía de la neurona: 29 señales con sus nombres, 6 neuronas ocultas tanh, suma de corrección y regla de orden\" width=\"100%\"></p>\n\n**Las 17 agujas de la fórmula a mano** (las mismas que suma `_calcular_score_hibrido`):\n\n| Aguja (`código`) | Qué mide |\n|---|---|\n| BM25 léxico (`bm25_norm`) | Coincidencia de palabras (FTS5), normalizada |\n| dimensiones (`dim_score`) | Coincidencia en los 13 ejes semánticos |\n| palabras en el nombre (`concepto_ratio`) | Cuánto de la consulta está en el nombre del recuerdo |\n| sinónimos (`sinonimos_ratio`) | Cuánto de la consulta está en sus sinónimos |\n| latente / cadena (`latcad`) | Conexiones latentes o en cadena del grafo (la mayor de las dos) |\n| grupo (`grupo_score`) | Pertenencia a un grupo temático |\n| temático (`tematico_g`) | Afinidad temática (solo cuenta si hay evidencia léxica) |\n| asociaciones (`asoc`) | Grado en el grafo sináptico (asociaciones / 20, tope 1) |\n| predicados (`pred_score`) | Roles semánticos (SRL) |\n| PPMI-SVD (`ppmi_score`) | Similitud vectorial PPMI+SVD (100 dims) |\n| Concept Hub (`hub_match`) | Coincidencia por puentes de 5 ángulos |\n| compresión (`ncd_score`) | Distancia de compresión normalizada |\n| episodio (`episodio_score`) | Cercanía temporal (mismo episodio) |\n| campo semántico (`campo_score`) | Atracción del campo semántico |\n| Jensen-Shannon (`jsd`) | Divergencia de distribuciones |\n| match exacto (`match_exacto`) | La consulta es el nombre del recuerdo |\n| sinónimo casi exacto (`sin95`) | Sinónimos ≥ 0.95 |\n\n**Las 12 señales de la vista y del agente** (lo que pasa *después* de la fórmula y el contexto de la búsqueda):\n\n| Señal (`código`) | Qué mide |\n|---|---|\n| bono de convergencia (`bono_conv`) | Bono multicampo que recibió, normalizado (/0.085, tope 1.5) |\n| evidencia por campo (`ev_concepto`, `ev_sinonimos`, `ev_sustantivos`, `ev_contenido`) | En qué campos del recuerdo apareció la consulta, incluidos los `sustantivos_clave` |\n| peso sináptico (`peso`) | El peso del recuerdo (0–1) |\n| ¿score con peso? (`con_peso`) | Si el score servido incluye el peso (camino MCP) |\n| ¿vino del agente? (`con_agente`) | Si la búsqueda trae `sustantivos_clave` del agente |\n| origen (`or_lexico`, `or_hub`, `or_no_literal`) | Por dónde llegó el candidato: léxico aprendido, Concept Hub o un camino no literal |\n| sin rasgos (`sin_rasgos`) | Bandera: candidato inyectado sin pasar por la fórmula (sus 17 agujas van en 0) |\n\n### 4. La matemática, paso a paso\n\n**Símbolos**\n\n| Símbolo | Significado |\n|---|---|\n| $k$ | Cuántos resultados ve quien busca: 5 por defecto, 10 por el MCP (tope `topk_max` = 10) |\n| $x_i \\in \\mathbb{R}^{29}$ | Las 29 señales del resultado en el puesto $i$ |\n| $\\text{score}_i$ | El score que la fórmula a mano le dio y que se sirvió |\n| $W, b, v$ | Los pesos de la neurona ($6 \\times 29$, $6$, $6$) |\n| $c(x)$ | La corrección que propone la neurona |\n| $\\varepsilon$ | El margen \"primero no dañar\" |\n| $y$ | El puesto donde está la respuesta correcta (en el autoexamen) |\n\n**1) La escala de la fórmula.** El score se lleva a logit, que es la escala natural para sumar evidencias. Por encima de 1 se prolonga en recta para que los matches exactos (que superan 1) no se recorten:\n\n$$\ng(s)=\\begin{cases}\\ln\\dfrac{s}{1-s} & s<1-\\epsilon_0\\\\[6pt] \\ln\\dfrac{1-\\epsilon_0}{\\epsilon_0}+\\dfrac{s-(1-\\epsilon_0)}{\\epsilon_0(1-\\epsilon_0)} & s\\ge 1-\\epsilon_0\\end{cases}\\qquad \\epsilon_0=10^{-4},\\quad s\\in[10^{-4},\\,50]\n$$\n\n**2) La corrección de la neurona:**\n\n$$\nc(x)=v^{\\top}\\tanh(Wx+b)\n$$\n\n**3) Cómo sirve (reordena el top-k).** Cada resultado del top-k recibe una clave, y el top-k se ordena por esa clave de mayor a menor:\n\n$$\n\\text{clave}_i \\;=\\; g(\\text{score}_i)\\;+\\;c(x_i)\\;+\\;\\varepsilon\\,(k-i),\\qquad i=0,\\dots,k-1\n$$\n\nEl término $\\varepsilon(k-i)$ es la **ventaja de la fórmula**: para subir un puesto, la neurona tiene que ganar por más de $\\varepsilon$. Después del reorden, **los recuerdos y los scores son los mismos**: el score más alto se le asigna al nuevo primero, el segundo al nuevo segundo, y así. Por eso ningún umbral absoluto del motor (abstención, GABA, ráfaga, certeza, falsos positivos) ve números distintos.\n\n**Regla de identidad:** si la consulta *es* el nombre de un recuerdo (con \"_\" = espacio) y la fórmula ya lo puso primero, sigue primero.\n\n**4) Cómo aprende.** En el autoexamen se conoce el puesto $y$ de la respuesta correcta. La probabilidad que la neurona le da es un softmax sobre el top-k servido:\n\n$$\np_i=\\frac{e^{\\,g(\\text{score}_i)+c(x_i)}}{\\sum_{j<k} e^{\\,g(\\text{score}_j)+c(x_j)}},\\qquad\n\\mathcal{L}=-\\frac{1}{n}\\sum_{\\text{casos}}\\ln p_y\\;+\\;\\frac{\\lambda}{2}\\lVert\\theta\\rVert^2,\\quad \\lambda=10^{-3}\n$$\n\nEl gradiente se calcula a mano (retropropagación exacta, con $\\partial\\mathcal{L}/\\partial s_i = p_i-\\mathbb{1}[i=y]$) y se aplica con **Adam**: $\\beta_1=0.9$, $\\beta_2=0.999$, tasa $0.03$, **30 pasos por ciclo**. Si un paso produce NaN, infinito o pesos mayores a $10^3$, se descarta y se vuelve a los pesos anteriores.\n\n**5) La guardia (el examen que nunca se estudia).** Un recuerdo es de guardia si\n\n$$\n\\text{sha256}(\\text{nombre})_{[0:8]} \\bmod 5 = 0\n$$\n\nEso es el **20 %**, siempre los mismos y sin azar. Sus preguntas nunca entran al entrenamiento: solo sirven para juzgar.\n\n**6) ¿Asciende el retador? (la calibración \"primero no dañar\").** Se prueban los márgenes $\\varepsilon \\in \\{0, 0.25, 0.5, 1, 1.5, 2, 3, 5, 8\\}$ de menor a mayor. Se elige el **menor** con el que se cumplen **todas** estas condiciones:\n\n1. **Cero pérdidas en la guardia:** ningún caso de guardia que la fórmula ponía primero deja de estarlo.\n2. **Pérdidas totales ≤ 5 % de las ganancias** (en primer lugar).\n3. **El Top-5 tampoco baja:** ningún caso de guardia sale del Top-5 y, en total, salen como mucho el 5 % de los que entran.\n4. **Piso por categoría y por camino:** ninguna categoría del examen baja en R@1, MRR ni R@5, ni por el camino del agente ni por el crudo.\n5. **Gana de verdad:** supera a la fórmula, o al campeón actual, en aciertos totales y no empeora la guardia.\n6. **Piso contra el campeón** *(experimento 08)*: no le quita al campeón saliente lo que ya lograba. En ninguna categoría y camino puede quedar por debajo de él por más de $\\tau$ casos, en ninguna de las tres métricas:\n\n$$\nm_{\\text{retador}}(\\text{cat}) \\;\\ge\\; m_{\\text{campeón}}(\\text{cat}) - \\tau,\\qquad m\\in\\{\\text{aciertos \\#1},\\ \\textstyle\\sum 1/\\text{puesto},\\ \\text{aciertos top-5}\\},\\quad \\tau=1\n$$\n\n   La tolerancia de un caso evita que la neurona se trabe por el ruido de un solo caso; dos o más casos cedidos en una categoría vetan el ascenso.\n\n\nSi ningún margen cumple, no asciende. Si el **campeón** deja de cumplir algún día (porque la memoria cambió), **se retira** y vuelve la fórmula a mano.\n\n**7) Anti-memorización:**\n\n$$\n\\text{brecha}=\\big(\\text{acierto}_{\\text{estudio}}-\\text{acierto}_{\\text{guardia}}\\big)_{\\text{retador}}-\\big(\\text{acierto}_{\\text{estudio}}-\\text{acierto}_{\\text{guardia}}\\big)_{\\text{fórmula}}\n$$\n\nSi la brecha supera 0.05, el retador está memorizando lo que estudió en vez de generalizar. Entonces **no asciende** (aunque un margen grande lo deje \"sin dañar\") y se reinicia.\n\n**8) Techo y despertar.** Tras **12 ciclos** sin mejorar, la neurona queda \"tranquila\": solo vigila, con la mitad del costo. Cuando entran **150 preguntas nuevas**, despierta y vuelve a estudiar.\n\n### 5. El ciclo de vida\n\n<p align=\"center\"><img src=\"docs/Mi%20neurona%20en%20el%20sistema/imagenes/ciclo_de_vida.png\" alt=\"Ciclo de vida de la neurona: consolidar, autoexamen, búsqueda silenciosa, vista, examen 80/20, estudio, ascenso, campeón en servicio, techo y despertar\" width=\"100%\"></p>\n\n**El autoexamen.** Cada recuerdo genera sus propias preguntas, de forma determinista (el azar se siembra con su nombre):\n- **variante gramatical** (\"leccion backup bases datos\");\n- **typo** (\"leccion backpu bkase dyatos\");\n- **sinónimo** (\"leccion backup base informacion\");\n- **cruce de idioma** (\"insight backup repository inputs\");\n- **pregunta natural** (\"¿Cómo se hace leccion backup base datos?\");\n- **por tema** (\"restaurar probando cada\");\n- **contenido**: como pregunta alguien que no sabe el nombre (\"¿copia cada noche?\").\n\nNo genera preguntas literales ni de dormidos, porque esas el motor ya las resuelve.\n\n**Los dos caminos.** La neurona practica por los dos caminos reales, alternando:\n- **agente**: como llega por el MCP `recordar`. Pide 3× candidatos, aplica convergencia sobre los 10 que verá el agente, usa el peso sináptico y los `sustantivos_clave` que un agente sacaría de la consulta.\n- **crudo**: el motor solo, como la QA.\n\nEl piso es **por categoría y por camino**: ninguno de los dos puede bajar.\n\n**Desfase 0.** Una sola función, `vista()`, arma lo que ve la neurona **desde la página tal como se sirve**. El autoexamen guarda esa vista y el servicio corrige esa misma vista, así que lo que se aprende y lo que se usa es idéntico **por construcción**. Medido: 0 de 289 consultas reales con desfase (la v4 tenía 46.7 %).\n\n### 6. Las capas de protección\n\nCada capa cubre una falla concreta de \"una neurona viva dentro de una memoria\". Cada una tiene su prueba en `tests/test_neurona.py`.\n\n| Falla posible | Capa que la cubre |\n|---|---|\n| Aprender de una búsqueda distinta a la que se sirve | **Vista única** (desfase 0) |\n| Memorizar en vez de aprender | **Guardia del 20 %** + regla anti-memorización |\n| Empeorar alguna categoría | **Piso** por categoría y camino (R@1, MRR, R@5); el campeón que daña se **retira** |\n| Un retador gana en total cediendo una categoría que el campeón ya resolvía | **Piso contra el campeón**: más de 1 caso cedido en cualquier categoría y camino veta el ascenso |\n| Cambiar el orden con poca seguridad | **Margen \"primero no dañar\"** auto-calibrado |\n| Sacar un buen resultado del Top-5 (vista de 10) | Piso de R@5 + cero salidas de guardia del Top-5 |\n| Cambiar los números que miran los umbrales | **Invariante de reorden**: mismos nodos y mismos scores; si no se cumple, se sirve la página original. Los mapas de score del MCP se permutan igual |\n| Romper búsquedas literales | **Regla de identidad** |\n| Entrenamiento que diverge (NaN, infinito) | **Pesos sanos**: se descartan y vuelven los anteriores |\n| Errores repetidos | **Disyuntor**: 3 fallas seguidas → esa parte se pausa 1 h; el motor sigue igual |\n| Dos aprendizajes a la vez (sueño + consolidar) | **Candado no bloqueante**: si está ocupada, se salta el ciclo |\n| El autoexamen ensucia logs, accesos o el DMN | **Silencio por hilo**; otro hilo que busca a la vez no queda en silencio |\n| Cambia la fórmula del motor (otra versión) | **Huella del motor**: lo aprendido se descarta y se empieza de cero |\n| Estado corrupto en la DB | **Estado estricto**: si no se puede leer, se arranca limpio |\n| Varios procesos sobre la misma DB (MCP + CLI) | **Refresco** por marca de tiempo |\n| Un recuerdo acapara el examen | **Tope por recuerdo** (4) y memoria de tamaño fijo |\n| Recuerdos borrados o dormidos en el examen | **Purga** al azar (20 por ciclo, costo fijo) |\n| DB vacía o con pocos datos | Sin campeón: **el motor responde idéntico al de siempre** |\n\n### 7. Resultados (para auditar)\n\nTodo se midió sobre **copias** del snapshot canónico (nunca sobre el original ni sobre la DB viva) y está versionado en `docs/Mi neurona en el sistema/`.\n\n<p align=\"center\"><img src=\"docs/Mi%20neurona%20en%20el%20sistema/imagenes/curva_aprendizaje.png\" alt=\"Curva de aprendizaje: aciertos en la guardia de la fórmula contra la neurona durante 500 ciclos, y latencia por ciclo\" width=\"100%\"></p>\n\n<p align=\"center\"><img src=\"docs/Mi%20neurona%20en%20el%20sistema/imagenes/qa_por_categoria.png\" alt=\"QA oficial por categoría: piso de la fórmula a mano contra la neurona\" width=\"100%\"></p>\n\n**QA oficial (921 casos) sobre una copia del snapshot, después de `./scripts/despertar_neurona.sh` (500 ciclos, 17 min, 6 ascensos, 0 retiros):**\n\n| Categoría (n) | R@5 piso → neurona | R@1 piso → neurona | MRR piso → neurona |\n|---|---|---|---|\n| cruce_idioma (8) | 100 → 100 | 62.50 → **75.00** | .740 → **.875** |\n| dormido (65) | 100 → 100 | 100 → 100 | 1.000 → 1.000 |\n| literal (487) | 100 → 100 | 99.59 → 99.59 | .998 → .998 |\n| por_tema (65) | 100 → 100 | 63.08 → **78.46** | .772 → **.872** |\n| pregunta_natural (65) | 100 → 100 | 93.85 → **96.92** | .964 → **.985** |\n| sinonimo (55) | 100 → 100 | 56.36 → **60.00** | .721 → **.742** |\n| typo (65) | 98.46 → **100** | 87.69 → **93.85** | .924 → **.964** |\n| variante_gramatical (65) | 100 → 100 | 87.69 → **90.77** | .932 → **.947** |\n| **GLOBAL (875)** | 99.89 → **100.00** | 91.66 → **94.06** | .949 → **.964** |\n| negativos (40) | FP 0/40 → **0/40** | | |\n\n*Piso = la tabla oficial con la fórmula a mano (la misma que da una DB sin campeón). Ninguna categoría baja en ninguna de las tres métricas; GATE OK. Salida textual: `docs/Mi neurona en el sistema/experimento_07_la_neurona_escucha/resultados/despertar/`.*\n\n<p align=\"center\"><img src=\"docs/Mi%20neurona%20en%20el%20sistema/imagenes/camino_mcp_y_desfase.png\" alt=\"Camino real del agente por MCP y desfase v4 contra v5\" width=\"100%\"></p>\n\n| Medición | Resultado |\n|---|---|\n| **Camino real del agente** (`recordar` por MCP, 875 casos) | R@1 **92.34 → 94.97**, R@5 99.09 → 99.31, MRR .951 → .967. En el primer lugar: +24 casos mejoran, 1 empeora |\n| **Corpus nunca visto** (python-docs-es, por MCP) | Neutra: R@1 45.68 → 45.06, R@5 59.88 → 60.49 (+0 / −1 en el primer lugar) |\n| **Desfase** (lo que estudia frente a lo que sirve) | **0 de 289** (la v4 tenía 46.7 %) |\n| **Invariantes** (120 búsquedas MCP con y sin neurona) | **0 violaciones**: mismos nodos, mismos números, mismos umbrales |\n| **Costo por ciclo** | p50 1.6 s, p95 3.5 s, sin importar el tamaño de la memoria |\n| **Pruebas** | 407 pruebas unitarias aprobadas (50 de la neurona + 6 del despertar; v34.0: 402) |\n\n**Lo que NO logra (dicho claro):**\n- **No es \"cero pérdidas\" en consultas nunca vistas.** Por el camino del agente, 1 caso baja del primer lugar y 1 sale del Top-5 (del 3 al 6), frente a 24 que suben. En python-docs-es pierde 1 caso.\n- **En un corpus que nunca vio, es neutra.** La gran mejora en ese corpus (R@1 31 → 46 %) vino del arreglo del MCP, no de la neurona.\n- **En una DB nueva no hace nada al principio.** Necesita juntar un examen mínimo (150 casos de estudio y 40 de guardia) antes de poder ascender un campeón. Mientras tanto el motor es exactamente la fórmula a mano.\n\n### 8. Cómo se usa\n\n- **Viene encendida** y no requiere nada: aprende sola cada vez que se consolida la memoria (consolidar, dormir o cerrar sesión). Guardar con `aprender` deja el recuerdo en corto plazo; la práctica llega al consolidar.\n- **Apagarla del todo:** `BIORAG_NEURONA=0` (el motor vuelve a ser exactamente la fórmula a mano).\n- **Memorias que ya tienen muchos recuerdos:** ver [Despertar la neurona](#despertar-la-neurona-práctica-acelerada).\n- **Ver su estado:** `./scripts/despertar_neurona.sh --estado`.\n- **Borrar lo aprendido:** `./scripts/despertar_neurona.sh --olvidar --si`.\n\n| Variable | Default | Qué controla |\n|---|---|---|\n| `BIORAG_NEURONA` | `1` | `0` = apagada del todo |\n| `BIORAG_NEURONA_NODOS` / `_PREGUNTAS` | `8` / `7` | Recuerdos y preguntas por ciclo (topes; el presupuesto de 3 s corta antes si las búsquedas son lentas). Hasta v34.0 eran `2` / `2` |\n| `BIORAG_NEURONA_PRESUPUESTO_S` | `3.0` | Tope de tiempo por ciclo (no empieza preguntas que no caben) |\n| `BIORAG_NEURONA_TOPK` / `_TOPK_MAX` | `5` / `10` | Vista por defecto y tope de la vista |\n| `BIORAG_NEURONA_LIMITE_AGENTE` | `10` | Cuántos ve el agente por el MCP (camino \"agente\") |\n| `BIORAG_NEURONA_MIN_ESTUDIO` / `_MIN_GUARDIA` | `150` / `40` | Examen mínimo para empezar a entrenar |\n| `BIORAG_NEURONA_MAX_ESTUDIO` / `_MAX_GUARDIA` | `1200` / `300` | Tamaño fijo de la memoria de examen |\n| `BIORAG_NEURONA_TOPE_POR_NODO` | `4` | Máximo de casos de un mismo recuerdo |\n| `BIORAG_NEURONA_PASOS` / `_LR` / `_L2` | `30` / `0.03` / `0.001` | Entrenamiento por ciclo (Adam) |\n| `BIORAG_NEURONA_PACIENCIA` / `_DESPERTAR` | `12` / `150` | Ciclos sin mejora hasta el techo / preguntas nuevas para despertar |\n| `BIORAG_NEURONA_MARGEN` | (auto) | Forzar el margen ε (≥ 0) |\n| `BIORAG_NEURONA_TOL_CAMPEON` | `1` | Casos que un retador puede ceder frente al campeón en una categoría (τ) |\n| `BIORAG_NEURONA_PAUSA_S` / `_REFRESCO_S` | `3600` / `30` | Pausa del disyuntor / cada cuánto mira si otro proceso escribió |\n\n**Simbología de los dibujos:**\n- 🟡 **dorado**: las 17 agujas de la fórmula a mano;\n- 🔵 **celeste**: las 12 señales de la vista y del agente;\n- 🟣 **violeta**: las 6 neuronas ocultas (tanh), el examen y el estudio;\n- 🟢 **verde**: la corrección, el ascenso y el campeón en servicio;\n- 🔴 **rojo**: lo que estaba mal antes (MCP con auto-paráfrasis, desfase de la v4) o un límite (presupuesto de tiempo).\n\n### 9. La historia: trece experimentos (01–09 esta neurona; 10–13 la [neurona asociativa](#-novedades-de-la-versión-v350--la-neurona-asociativa-sináptica))\n\n| # | Experimento | Qué se aprendió |\n|---|---|---|\n| 01 | [Pesos aprendidos](docs/Mi%20neurona%20en%20el%20sistema/experimento_01_pesos_aprendidos/) | Los pesos a mano no son óptimos: aprenderlos mejora el R@1 |\n| 02 | [Examen sellado](docs/Mi%20neurona%20en%20el%20sistema/experimento_02_examen_sellado/) | Medir sobre casos nunca vistos; sin eso, una \"mejora\" puede ser memoria |\n| 03 | [Ideas de la conversación](docs/Mi%20neurona%20en%20el%20sistema/experimento_03_ideas_conversacion/) | En una búsqueda real nadie dice cuál era la respuesta correcta → hace falta autoexamen |\n| 04 | [Autoaprendizaje](docs/Mi%20neurona%20en%20el%20sistema/experimento_04_autoaprendizaje/) | El recuerdo se pregunta a sí mismo; campeón y retador con guardia |\n| 05 | [Neurona cableada](docs/Mi%20neurona%20en%20el%20sistema/experimento_05_neurona_cableada/) | Aprende en vivo al consolidar, con costo fijo |\n| 06 | [Capas de protección](docs/Mi%20neurona%20en%20el%20sistema/experimento_06_capas_de_proteccion/) | 21 modos de falla cubiertos; margen \"primero no dañar\" |\n| 07 | [La neurona escucha](docs/Mi%20neurona%20en%20el%20sistema/experimento_07_la_neurona_escucha/) | Desfase 0, los dos caminos, vista de 10, piso de R@5; y el camino MCP arreglado |\n| 08 | [Piso contra el campeón](docs/Mi%20neurona%20en%20el%20sistema/experimento_08_piso_contra_el_campeon/) | Un retador ya no puede ganar cediendo una categoría; A/B determinista: en datos reales la regla nunca tuvo que actuar |\n| 09 | [Que aprenda sola de verdad](docs/Mi%20neurona%20en%20el%20sistema/experimento_09_aprende_sola_de_verdad/) | v34.1: 1 práctica por consolidación; preguntar a lo ancho dentro de los 3 s. Una DB nueva enciende la neurona en ~20 consolidaciones; la QA queda igual |\n| 10 | [Neuronas del motor](docs/Mi%20neurona%20en%20el%20sistema/experimento_10_neuronas_del_motor/) | v35: el molde de las neuronas modulares y el mapa de dónde nacen las sinapsis; se estudió la hormiguita (Gemini) |\n| 11 | [La neurona asociativa](docs/Mi%20neurona%20en%20el%20sistema/experimento_11_neurona_asociativa/) | v35: 5 agujas calibradas de fábrica en 7 memorias; AUC 0.88–0.997 en memorias nunca vistas; examen a mano de Dennys AUC 0.73; la parte que aprende queda apagada |\n| 12 | [La neurona en el motor](docs/Mi%20neurona%20en%20el%20sistema/experimento_12_neurona_en_el_motor/) | v35: poner el peso de nacimiento deja la QA idéntica → revertido |\n| 13 | [Enlaces del agente y ruido](docs/Mi%20neurona%20en%20el%20sistema/experimento_13_enlaces_del_agente_y_ruido/) | v35: los enlaces del agente, intocables; el ruido ayuda a buscar; cortar al nacer baja el recall; su lugar es lo que ve el agente. Cierre de la neurona asociativa |\n\nLas figuras se regeneran con `python \"docs/Mi neurona en el sistema/imagenes/generar_figuras.py\"`: los nombres de las señales salen del código y las curvas de los resultados versionados.\n\n### Otros cambios de la v34.0\n\n- **MCP `recordar`: auto-paráfrasis y auto-dimensiones apagadas por defecto.** Medido por el camino real del agente, bajaban el recall: con ellas, 39 casos quedaban fuera del Top-5. Se reactivan con `BIORAG_MCP_AUTO_PARAFRASIS=1` y `BIORAG_MCP_AUTO_DIMS=1`, que ahora son deterministas. Las paráfrasis y dimensiones que manda el agente se usan siempre.\n- **MCP stdio limpio.** Los `print()` del motor iban al canal JSON-RPC y podían corromper la sesión del IDE: tras 120 `vincular`, 77 líneas no eran JSON. Ahora van a stderr: 0 líneas corruptas.\n- **Sin corte por antigüedad por defecto** (`BIORAG_STALE_HARD_CUTOFF=0`). BioRAG es memoria de largo plazo y no esconde recuerdos por fecha salvo que se pida. Antes el valor era 365, pero nunca llegaba a aplicarse por un JOIN a una tabla mal nombrada; ahora no puede encenderse solo.\n- **Nuevo script** `scripts/despertar_neurona.sh` (ver la zona de operaciones).\n\n---\n\n## 🚀 Novedades de la Versión v33.0 — Protocolo Cognitivo Simétrico de Sustantivos Clave\n\nLa versión **v33.0** introduce un cambio arquitectónico y contractual mayor en el subsistema de ingestión y recuperación de BioRAG: la formalización del **protocolo cognitivo simétrico de `sustantivos_clave`** con validación anticipada (*fail-fast*) previa a la base de datos en todas las herramientas MCP del ciclo de vida de la memoria.\n\n### 🧠 1. El Contrato Cognitivo de `sustantivos_clave`\n\nEn BioRAG, los `sustantivos_clave` **no constituyen un inventario sintáctico o automático de todas las palabras nominales** presentes en un texto, sino un **anclaje semántico de alto nivel que responde a la pregunta cognitiva fundamental:**\n\n> *\"¿De qué conceptos temáticos trata centralmente este recuerdo o esta intención de búsqueda?\"*\n\n- **Propósito:** Anclar los recuerdos en el conectoma asociativo y permitir el cruce de evidencia multicampo (FTS5 + Sustantivos + Grafo + PPMI-SVD).\n- **Pauta Cognitiva Recomendada:** Entre **2 y 4 términos centrales** de alta carga conceptual.\n- **Rango Técnico Aceptado:** El motor admite un rango más amplio (hasta 10 términos) para acomodar intenciones complejas o dominios con terminología multifacética, preservando la agilidad del agente.\n- **Normalización Automática:** Los términos se sanean de forma determinista (conversión a minúsculas, eliminación de espacios superfluos, deduplicación preservando orden).\n\n---\n\n### ⚖️ 2. Diferenciación Simétrica: Escritura vs. Búsqueda Textual\n\nEl motor distingue con rigor matemático la asimetría funcional entre la fase de almacenamiento de conocimiento y la fase de consulta:\n\n| Operación | Herramientas MCP Afectadas | Rango de Términos Únicos | Justificación Cognitiva |\n|---|---|:---:|---|\n| **Escritura / Ingestión** | `aprender`, `guardar`, `agregar_sustantivos` | **2 a 10** | Un recuerdo persistente requiere al menos dos anclas conceptuales para poder integrarse en el conectoma y permitir el cierre asociativo triádico. |\n| **Búsqueda Textual** | `recordar`, `buscar` | **1 a 10** | Una consulta del usuario puede ser genuinamente atómica (ej. *\"vacaciones\"*, *\"arquitectura\"*, *\"hipertensión\"*), por lo que exigir un mínimo de dos términos bloquearía intenciones válidas. |\n| **Consultas Estructurales / Cronológicas** | `recordar(limite=N)` (sin texto) | **0 (Exento)** | Consultas puramente de exploración cronológica o navegacional sobre la memoria no requieren términos temáticos. |\n| **Consultas Dimensionales Puras** | `buscar(dimensiones=...)` (sin texto) | **0 (Exento)** | Filtrado estricto por coordenadas espaciales o ejes semánticos sin texto libre. |\n\n---\n\n### 🛡️ 3. Validación Fail-Fast Antes de la Base de Datos (Pre-DB)\n\nToda llamada MCP que involucre texto de búsqueda o almacenamiento es interceptada y validada **antes de abrir transacciones, solicitar locks o emitir I/O contra SQLite**. Si los términos clave no cumplen el contrato, la herramienta rechaza la ejecución de inmediato y responde con un **error estructurado normalizado**.\n\n**El parámetro `sustantivos_clave` es de tipo `Optional[str]`** — una cadena de texto con términos separados por comas (ej. `\"arquitectura, componentes, biorag\"`). No es una lista/array JSON.\n\nLos códigos de error son **distintos según la operación**:\n\n| Código | Emitido por | Condición |\n|---|---|---|\n| **`SUSTANTIVOS_CLAVE_REQUERIDOS`** | `recordar`, `buscar` (búsqueda textual) | Parámetro omitido o vacío en consulta con texto libre. |\n| **`SUSTANTIVOS_CLAVE_AUSENTES`** | `aprender`, `guardar`, `agregar_sustantivos` (escritura) | Parámetro omitido o vacío en operación de ingestión. |\n| **`SUSTANTIVOS_CLAVE_CANTIDAD_INVALIDA`** | Todas las herramientas | Términos únicos fuera del rango permitido (< 2 en escritura, < 1 en búsqueda, > 10 en ambas). |\n| **`SUSTANTIVOS_CLAVE_FORMATO_INVALIDO`** | Todas las herramientas | Algún término individual no cumple el formato (2–15 chars, sin espacios, solo alfanuméricos y guion bajo). |\n\nEste diseño *fail-fast* garantiza que el agente reciba retroalimentación diagnóstica inmediata sin degradar el rendimiento del motor ni dejar conexiones pendientes en el motor de persistencia.\n\n---\n\n### 🏷️ 4. Regla Ontológica sobre el Nombre del Nodo\n\nEl identificador o nombre canónico de un nodo (ej. `biorag_v33_0_arquitectura_core`) cumple una función de clave única de identificación en el grafo, no de metadato asociativo:\n\n1. **Prohibición de copia indiscriminada:** No se debe volcar el slug o identificador técnico completo (con guiones bajos o numeración de versión) como una etiqueta monolítica.\n2. **Extracción temática autorizada:** Sí se autoriza y aconseja extraer palabras o raíces individuales del nombre cuando representen conceptos sustantivos reales del recuerdo (ej. `[\"biorag\", \"arquitectura\"]`).\n\n---\n\n### ⚠️ 5. Compatibilidad y Guía de Migración MCP (Breaking Change v33.0)\n\n> [!WARNING]\n> **CAMBIO DE CONTRATO INCOMPATIBLE (BREAKING CHANGE):**\n> En versiones anteriores (`v32.x`), las herramientas de búsqueda textual (`recordar` y `buscar`) permitían consultas con solo el parámetro `query` o `texto`. A partir de **v33.0**, omitir `sustantivos_clave` en una búsqueda textual provoca un rechazo inmediato en tiempo de ejecución. Los clientes e integraciones MCP deben actualizarse.\n\n#### Ejemplos de Llamadas MCP Actualizadas\n\n##### 1. Búsqueda en memoria (`recordar`)\n```json\n// LLAMADA VÁLIDA (v33.0) — sustantivos_clave es una cadena, no un array\n{\n  \"name\": \"recordar\",\n  \"arguments\": {\n    \"query\": \"¿Cuáles son los componentes de la arquitectura de BioRAG?\",\n    \"sustantivos_clave\": \"arquitectura, componentes, biorag\",\n    \"limite\": 5\n  }\n}\n```\n\n##### 2. Búsqueda general (`buscar`)\n```json\n// LLAMADA VÁLIDA (v33.0) — sustantivos_clave es una cadena, no un array\n{\n  \"name\": \"buscar\",\n  \"arguments\": {\n    \"texto\": \"protocolo de calibración conforme con percentil adaptativo\",\n    \"sustantivos_clave\": \"calibracion, percentil, conforme\",\n    \"limite\": 5\n  }\n}\n```\n\n##### 3. Ingestión de memoria (`aprender` / `guardar`)\n```json\n// LLAMADA VÁLIDA (v33.0) — sustantivos_clave es una cadena, no un array\n{\n  \"name\": \"aprender\",\n  \"arguments\": {\n    \"concepto\": \"protocolo_sustantivos_clave_v33\",\n    \"contenido\": \"Reglas de validación fail-fast para términos temáticos en búsquedas e ingestión.\",\n    \"sustantivos_clave\": \"protocolo, validacion, sustantivos\",\n    \"busqueda_previa\": true,\n    \"bridges\": [\n      {\"text\": \"protocolo cognitivo temático de búsqueda\", \"angle\": \"sinonimo\"},\n      {\"text\": \"búsqueda sin términos clave falla silenciosamente\", \"angle\": \"problema\"},\n      {\"text\": \"validacion fail-fast pre-db con errores estructurados\", \"angle\": \"solucion\"},\n      {\"text\": \"agente necesita anclar conceptualmente la búsqueda\", \"angle\": \"situacion\"},\n      {\"text\": \"qué palabras clave debo poner al buscar en BioRAG\", \"angle\": \"ingenuo\"}\n    ]\n  }\n}\n```\n\n##### 4. Enriquecimiento posterior (`agregar_sustantivos`)\n```json\n// LLAMADA VÁLIDA (v33.0) — sustantivos como lista de strings\n{\n  \"name\": \"agregar_sustantivos\",\n  \"arguments\": {\n    \"concepto\": \"protocolo_sustantivos_clave_v33\",\n    \"sustantivos\": [\"arquitectura\", \"contrato\"]\n  }\n}\n```\n\n---\n\n## 🚀 Novedades de la Versión v32.4\n\n### 🌐 1. DimensionHub — Semántica de Mundo Abierto para Valores de Dimensión (`catalog_methods.py`)\n\n**Por qué se hizo:**\nEl sistema de dimensiones semánticas tenía un modelo de **mundo cerrado rígido**: si un agente enviaba un valor de dimensión que no existía exactamente en el catálogo (ej: `\"preocupacion_leve\"` en lugar del valor registrado `\"preocupacion\"`), el sistema lo descartaba silenciosamente como inválido. Esto forzaba a los modelos a memorizar el catálogo exacto y rompía la recuperabilidad cuando el vocabulario del agente difería mínimamente del catálogo oficial.\n\n**Qué se cambió:**\n\n#### 1.1 Nueva función `_normalizar_nombre_dimension(nombre)` — Paso de sanitización universal\n- Aplica normalización **NFD + eliminación de diacríticos** → elimina tildes y cedillas automáticamente.\n- Convierte a **snake_case** ASCII: espacios, guiones y puntos → guión bajo; elimina todo carácter no alfanumérico.\n- Colapsa guiones bajos múltiples, elimina guiones al inicio/fin, trunca a 80 chars.\n- Valida integridad: retorna `None` si el resultado es vacío, menor a 2 chars, o empieza con dígito.\n- **Invariante de dominio-agnóstico garantizada**: la función no conoce ningún vocabulario de dominio; opera 100% a nivel de caracteres.\n\n#### 1.2 Refactoring de `_resolver_dimension_ids` y Unicidad por Eje (`tipo_id, name`)\n- **Antes**: buscaba solo dimensiones **YA existentes** con `WHERE name IN (...)` sin filtrar por tipo de eje, y descartaba las que no encontraba. Además, el esquema usaba `name TEXT UNIQUE NOT NULL` global.\n- **Ahora** (mundo abierto con aislamiento por eje):\n  - Normaliza cada nombre vía `_normalizar_nombre_dimension`.\n  - Búsqueda e inserción con scope por eje: `WHERE name = ? AND tipo_id = ?` respaldado por el constraint `UNIQUE(tipo_id, name)` y migración automática. Dos ejes distintos (ej. `emocion` y `cualidad`) pueden albergar el mismo valor sin colisión cruzada.\n  - Si el valor **ya existe** en ese eje → reutiliza su ID.\n  - Si **no existe** → lo **auto-crea** en dicho eje con `auto_generada=1, confianza=0.7` y retorna su nuevo ID.\n  - Si el nombre es **innormalizable** (emoji puro, cadena numérica, vacío) → lo agrega a la lista `invalidos`.\n- **Retorno ampliado**: retorna `(ids_validos, invalidos, creadas)` — el tercer elemento lista los nombres auto-creados para trazabilidad en logs.\n- **Alcance y gobernanza ontológica**: DimensionHub elimina el descarte silencioso y hace recuperables los valores fuera del catálogo predeterminado. El control de fragmentación semántica se gestiona mediante el protocolo *catálogo-first* en los prompts y herramientas de consulta (`listar_dimensiones`).\n\n#### 1.3 Actualización de callers y pruebas unitarias\n- [`core/memory/ingest.py`](core/memory/ingest.py): desempaquetado actualizado de `ids_validos, _, _ = _resolver_dimension_ids(...)`.\n- [`test_memory.py`](test_memory.py): cuatro llamadas directas en el test de ráfagas de dimensiones actualizadas a 3 valores.\n- Corrección de pruebas en `tests/test_sustantivos_clave_validacion.py` y `tests/test_sustantivos_clave_tools.py` ajustadas a la ampliación de rango de `sustantivos_clave` (2 a 10 términos).\n\n---\n\n### 🧩 2. Saneamiento Ontológico del Auto-Clustering y Migraciones (`core/auto_clustering.py`)\n\n**Por qué se hizo:**\nEl algoritmo de detección de comunidades (clustering sobre el conectoma) introducía dos fuentes de **ruido ontológico** en el catálogo de dimensiones:\n1. Los nombres de clusters autodetectados usaban el prefijo crudo `auto_` (ej. `auto_cluster_1`), generando entradas poco legibles y confusas al listarse en herramientas como `listar_dimensiones` o en el árbol sugerido de `aprender`.\n2. Las banderas de control de migraciones internas (como `migration_autoclustering_v1`) se guardaban como si fuesen \"dimensiones semánticas\" dentro de la tabla `dimensiones_semanticas`, mezclando el estado operativo de la infraestructura con el conocimiento ontológico real del dominio.\n\n**Qué se cambió:**\n- **Prefijo Semántico `tema_`:** Las dimensiones emergentes detectadas por clustering ahora reciben de forma consistente el prefijo legible `tema_` (ej: `tema_cluster_3`), indicando claramente que agrupan una afinidad temática emergente.\n- **Tabla dedicada `migraciones_ejecutadas`:** Se desacopló completamente el estado operativo del catálogo semántico. Las marcas de migraciones ejecutadas residen exclusivamente en su propia tabla relacional, impidiendo que flags de sistema se expongan como dimensiones o contaminen la clasificación de los agentes.\n- **Preservación de dimensiones vivas:** Se ajustó la lógica de purga para no destruir islas de clustering acumuladas ni dimensiones auto-generadas válidas existentes en bases de datos vivas.\n\n**Resultado:** 304/304 tests aprobados (0 fallos). El catálogo de dimensiones permanece 100% semántico, limpio y legible para agentes y humanos.\n\n---\n\n### Novedades anteriores (v32.3)\n\n### 🧭 Re-ranking aditivo por convergencia de evidencia multicampo\n\n- Evalúa la pertinencia de la consulta en `concepto`, `sinonimos`, `sustantivos_clave` y `contenido`, combinando cobertura ponderada, distribución entre campos, stemming y matching difuso. Una repetición en el mismo campo no infla la señal; un campo vacío no resta.\n- El bono es aditivo, con máximo por defecto **0.085** (configurable hasta `0.12`) y atenuación para consultas de uno o dos tokens. No usa reglas por ID ni categoría.\n- Reordena candidatos existentes dentro del top-k público; no incorpora candidatos ni cambia la membresía del pool que alimenta el grafo. Se conservan score base y bono por separado para los consumidores MCP.\n- En el smoke real informado por el usuario, `version_actual_biorag` pasó de puesto 2 a 1: **0.8341** frente a **0.8304** de la mención incidental `reindex_selectivo_dirty`. La DB fuente permaneció intacta.\n- En la suite global reportada, Recall@5 se mantuvo en 100%; Recall@1 pasó de 91.66% a 91.77%, MRR se imprimió como 0.950 y hubo 0 FP.\n\n**Alcance y analogía biológica:** la convergencia de campos es una analogía de integración de señales, no un modelo neuronal ni una afirmación de que BioRAG mejore como un cerebro real. La mejora es más esperable cuando una respuesta tiene evidencia pertinente en campos estructurados y su rival solo una mención incidental. Como es un re-ranker, no rescata una respuesta que nunca entró al conjunto de candidatos ni garantiza ganar en todos los dominios.\n\n### Novedades anteriores (v32.2)\n\n### 🛡️ Protocolo Pre-flight Search Obligatorio, Vinculación Atómica y Actualización Libre (Spec 004)\n- **Protocolo Pre-flight Search Obligatorio (`busqueda_previa=True`):**\n  - Implementación de la invariante obligatoria en `aprender` y `guardar` para forzar la verificación previa del corpus antes de la inserción, eliminando la creación de nodos huérfanos, duplicados o versiones fragmentadas.\n  - Error formal inmediato `BUSQUEDA_PREVIA_REQUERIDA` ante omisiones de búsqueda previa.\n- **Vinculación Sináptica Atómica en Ingesta (`vincular_con`):**\n  - Parámetro de lista de conceptos para enlazar nuevos nodos con el conectoma existente en la misma llamada atómica, con tolerancia a fallos ante nodos inexistentes.\n- **Actualización sin Restricción Temporal (`actualizar`):**\n  - Desbloqueo de la ventana temporal de 3 días en `actualizar`, permitiendo la edición, enriquecimiento y sobrescritura de recuerdos de cualquier fecha en el corpus a largo plazo.\n- **Validación SDD y Suite Ampliada:**\n  - 283/283 tests unitarios superados al 100%.\n\n### Novedades anteriores (v32.1)\n\n### 🏗️ Modularización de Arquitectura y Descomposición de Monolitos (Spec 003)\n- **Descomposición del Monolito de Memoria (`core/memory/`):**\n  - Descentralización de `core/memory_store.py` (de 7.778 líneas) hacia 18 submódulos de dominio cohesivo en `core/memory/` (`search.py`, `scoring.py`, `synapses.py`, `schema.py`, `consolidation.py`, `constants.py`, `umbral.py`, `rafaga.py`, `ingest.py`, `episodes.py`, `quarantine.py`, `dmn.py`, `context.py`, `catalog_methods.py`, `comms.py`, `telemetry.py`, `adn.py`).\n  - Consolidación de `core/memory_store.py` como una **fachada delgada** (637 líneas) con firmas explícitas completas y delegadores directos (prohibido `__getattr__` o introspección mágica).\n- **Descomposición del Servidor MCP (`core/mcp_server/`):**\n  - Descentralización de `mcp_server.py` (de 4.358 líneas) hacia 15 submódulos especializados en `core/mcp_server/` (`search.py`, `write.py`, `synapses.py`, `concept_hub_tools.py`, `catalog.py`, `introspection.py`, `communication.py`, `consolidation.py`, `daemon.py`, `session.py`, `oracle.py`, `sync.py`, `calibrar.py`, `resources.py`, `prompt.py`), orquestados limpiamente por `server.py` y `_shared.py`.\n  - Reemplazo de la raíz por un shim liviano `mcp_server.py` que preserva la compatibilidad al 100% con todos los clientes e integraciones existentes.\n- **Invarianza Estricta de Superficie Pública (Zero-Regression):**\n  - 42 tools MCP, 2 resources y 1 prompt con nombres y esquemas idénticos.\n  - Firmas de métodos de `SQLiteMemoryBioRAG` 100% preservadas.\n  - Verificación Dual exhaustiva caso a caso: 0 diferencias de score en los 921 casos del benchmark canónico.\n- **Por qué se hizo:** Superar la deuda técnica y el límite cognitivo impuesto por archivos de varios miles de líneas, habilitando mantenibilidad, extensibilidad y auditoría clara para desarrolladores humanos y agentes de IA, con verificación formal gobernada por Spec-Driven Development (SDD).\n\n### Novedades anteriores (v32.0)\n\n### 🧠 Consolidación Cognitiva con Ley de Potencia de Práctica de ACT-R (Anderson & Lebiere, 1998)\n- **Activación de Nivel Base ($B_i$):** Implementación rigurosa de la ecuación formal de activación base de la teoría cognitiva ACT-R:\n  $$B_i = \\ln \\left(\\sum_{k=1}^n t_k^{-d}\\right), \\quad d = 0.5$$\n  donde $t_k$ representa el tiempo transcurrido desde el $k$-ésimo acceso.\n- **Búfer Circular de Acceso:** Creación de la tabla indexada `nodo_accesos_historial` que almacena un búfer circular FIFO de hasta 10 marcas temporales por concepto.\n- **Registro Asíncrono no Invasivo:** `buscar_por_frase()` registra los nodos evocados sin penalización de latencia ($<0.1\\text{ ms}$), respetando la bandera `BIORAG_NO_LOG=1` durante benchmarks.\n- **Modulación Biológica del Decaimiento Pasivo (LTD):** Durante el `ciclo_sueno_consolidacion()`, el decaimiento fijo (-0.05) es reemplazado por la tasa modulada:\n  $$\\text{decay} = \\max\\left(0.01, \\min\\left(0.10, 0.05 \\cdot e^{-0.5 B_i}\\right)\\right)$$\n  Los recuerdos frecuentemente consultados o recientemente evocados quedan biológicamente protegidos del olvido, mientras que los nodos en desuso aceleran su depuración.\n- **Invarianza de Scoring en Caliente:** `_calcular_score_hibrido()` se mantiene estrictamente intocado, asegurando estabilidad determinista y **cero regresiones** en el ranking en tiempo real.\n\n### 🌐 Concept Hubs Semánticos con Estándar Cognitivo de 5 Ángulos en Producción\n- **Superación del Abismo Semántico:** Despliegue en la base de producción (`MemoryBioRAG_Data/memory_biorag.db`) del hub canónico `hub_arquitectura_memoria` enlazado a `arquitectura_memoria_biorag`.\n- **5 Perspectivas Cognitivas Ortogonales:** Estructuración de puentes mediante el estándar formal:\n  1. `sinonimo`: *\"blueprint of recollection and cognitive persistence tiers\"*\n  2. `problema`: *\"duda sobre como se estructura la persistencia frente a la sesion activa\"*\n  3. `solucion`: *\"dos niveles de memoria con conversacion activa y base persistente\"*\n  4. `situacion`: *\"diseno de corteza operativa para retener informacion entre conversaciones\"*\n  5. `ingenuo`: *\"how the recollection and long term brain blueprint works in english\"*\n- **Cero Overlap y Cruce de Idiomas:** Habilita la recuperación perfecta Top-1 de recuerdos arquitectónicos complejos tanto en español como en inglés, sin requerir embeddings densos ni GPUs.\n\n### ⚡ Efecto Fan de ACT-R en Spreading Activation (Anderson, 1974)\n- **Atenuación por Grado de Salida:** Ponderación dinámica $W_j / \\text{fan}_j$ en la propagación asociativa de activación sobre el grafo de sinapsis, dividiendo la fuerza asociativa entre el número de aristas del nodo emisor.\n- **Prevención de Saturación de Hubs:** Evita que conceptos excesivamente generales inunden la red semántica, reduciendo el tiempo total de la suite de 705s a 556s (-21.2% de aceleración neta).\n\n### 🎯 Saneamiento Metodológico del Benchmark Cranfield (v1)\n- **Desambiguación de Consultas Monocromáticas:** Refinamiento de los 8 casos ambiguos en la categoría `sinonimo` (términos genéricos aislados como `\"memoria\"`, `\"dsl\"`, `\"boost\"`, `\"aprendizaje\"` que colisionaban con decenas de recuerdos legítimos), contextualizándolos hacia su intención unívoca.\n- **Corrección de Atributos en ID 0795:** Sintonización de la consulta hacia `\"insert comunicaciones tracking agente\"`.\n- **Impacto Cuantitativo:** Salto del Recall@5 en `sinonimo` del 85.45% al **100.00%** (0 fallos) y reducción de fallos globales de recuperación de 17 a solo 6 en todo el corpus.\n\n---\n\n### Novedades anteriores (v31.4)\n\n### 🏷️ Sustantivos Clave en CLI — Núcleo Temático completo en `biorag.py`\n\nLa feature de **Sustantivos Clave** (Núcleo Temático), ya presente en el motor interno\ny en el servidor MCP desde v31.x, ahora está completamente expuesta en la **línea de comandos**:\n\n- **`guardar --sustantivos-clave \"s1,s2\"`** (alias `--sustantivos`): almacena el Núcleo\n  Temático del recuerdo. Obligatorio — su omisión dispara una guía pedagógica con ejemplo\n  ejecutable y sale con exit code `1`.\n- **`sustantivos <concepto>`** (alias `sustantivo`): consulta los sustantivos clave de\n  cualquier nodo; distingue nodo inexistente (exit 1) vs. nodo legado sin sustantivos (exit 0).\n- **`agregar_sustantivos <concepto> \"s1,s2\"`** (aliases `agregar-sustantivos`,\n  `asignar_sustantivos`): actualiza sustantivos de un nodo existente y sincroniza FTS5.\n- **`buscar --sustantivos-clave \"s1,s2\"`**: sesga la búsqueda semántica por núcleo temático.\n- **Visibilidad transversal:** `corteza`, `listar`, `buscar --completo` y `estado` muestran\n  sustantivos clave como metadato observable. `estado` reporta `Nodos con sustantivos clave: X/Y (Z%)`.\n- **Seguridad OWASP A03:** sanitización universal (bytes nulos, control chars), validación\n  `^[a-zA-Z0-9_]+$`, límites de tamaño estrictos y SQL parametrizado en todas las operaciones.\n- **42 tests de integración E2E** en `tests/test_biorag_cli.py` — **263/263** en la suite\n  global, cero regresiones en comandos existentes.\n\n### Novedades anteriores (v31.3)\n\n- **QCR D4 (Tolerancia a Typos con All-Near Levenshtein $\\le 2$):** Segunda oportunidad en el filtro QCR para consultas con errores ortográficos o variaciones morfológicas leves en palabras clave legítimas, eliminando falsos descartes sin admitir ruido léxico.\n- **Resonancia Dimensional en Abismo Léxico (EXP-Q):** Corrección del ordenamiento en la recuperación dimensional (`ORDER BY shared DESC, peso DESC`), garantizando que consultas sin ninguna palabra en común con el recuerdo entren al pool de candidatos ($3/3$).\n- **Saneamiento y Optimización de Arquitectura (v31.2 Purga):** Eliminación de más de 1.000 líneas de código muerto y ramas obsoletas, dejando un motor ultra-rápido, determinístico y mantenible.\n\n### 🧪 Cómo Reproducir la Evaluación y Tests\n\n```bash\n# Ejecutar suite de pruebas unitarias\npython3 -m pytest tests/ -v\n\n# Smoke A/B multicampo (la DB se abre en solo lectura y se prueba sobre copias)\nBIORAG_CONVERGENCIA_006_ACTIVA=0 BIORAG_CONVERGENCIA_ACTIVA=1 BIORAG_CONVERGENCIA_BONUS_MAX=0.085 python3 scripts/smoke_convergencia_multicampo.py --db \"$PWD/MemoryBioRAG_Data/memory_biorag.db\" --strict\n\n# Gate oficial de 921 casos; usa el snapshot congelado, no la DB canónica\nBIORAG_PATH=snapshots/qa_escape_qcr_20260811.db BIORAG_CONVERGENCIA_006_ACTIVA=0 BIORAG_CONVERGENCIA_ACTIVA=1 BIORAG_CONVERGENCIA_BONUS_MAX=0.085 python3 scripts/evaluar_qa.py\n```\n\nEl smoke solo valida el caso histórico si la DB indicada contiene ese estado; la DB\ncon SHA `0e5b0638…b845c96` no se distribuye en el repositorio. El benchmark global\nmide no-regresión del snapshot y no reemplaza ese smoke específico.\n\n\n\n## 🔄 El Inverso del Sistema: Quién Piensa y Quién Recuerda\n\n> Esta sección es la más importante para entender BioRAG correctamente — tanto si eres desarrollador como si eres un agente de IA integrándolo.\n\n### BioRAG no piensa. El agente piensa.\n\nBioRAG es la **memoria externa** del agente — no su cognición.\n\nUna base de datos vectorial clásica *piensa por el agente*: le das una query, calcula similitud de embeddings, y te devuelve lo más cercano. El agente es pasivo.\n\nBioRAG invierte ese rol: **el agente es quien razona**. BioRAG es la herramienta de memoria (el cuaderno de notas con indexación semántica). El trabajo cognitivo — entender la pregunta, diseñar la estrategia de búsqueda, interpretar los resultados, conectar el patrón de la query con el patrón del recuerdo — lo hace el agente.\n\nEsto tiene una consecuencia directa:\n\n### Cuando el usuario habla en lenguaje natural, el agente no puede reenviar las palabras literales a la memoria\n\nEl usuario no sabe cómo está guardado el conocimiento. Usará sus propias palabras, sus propias metáforas, su propio modelo mental del problema.\n\n**Ejemplo real documentado en este sistema:**\n\nEl usuario preguntó:\n> *\"¿Qué librería resuelve el conflicto de persistencia en flujos de recopilación masiva donde la interfaz destruye y recrea constantemente sus secciones, impidiendo duplicados mediante un validador aleatorio de instanciación inicial?\"*\n\nBúsqueda literal → **0 resultados**. Esas palabras no están en ningún nodo.\n\nEl agente que razonó correctamente aplicó un protocolo de descomposición con 3 preguntas:\n\n**1. ¿QUÉ HACE? (acción)**\n- `\"destruye y recrea secciones\"` → ciclo montar/desmontar de componentes\n- `\"impidiendo duplicados\"` → deduplicación de instancias\n- **Acción:** persistencia de estado en componentes que se destruyen\n\n**2. ¿EN QUÉ CONTEXTO? (dominio)**\n- `\"librería\"`, `\"interfaz\"`, `\"secciones\"` → framework frontend\n- `\"flujos de recopilación masiva\"` → formularios complejos\n- **Dominio:** formularios anidados en framework frontend\n\n**3. ¿QUÉ PROPIEDAD RESUELVE? (cualidad)**\n- `\"validador aleatorio de instanciación inicial\"` → ID único al crear (UUID)\n- `\"impidiendo duplicados\"` → cada instancia es única e irrepetible\n- **Cualidad:** instancia única mediante identificador aleatorio\n\nCon esas 3 respuestas, armó la búsqueda con las palabras traducidas (no las del humano) → resultado correcto en **TOP 1**.\n\nEl agente que falló (el mismo sistema, sin ese razonamiento) mandó las palabras literales del usuario al motor y concluyó: *\"No tengo ese conocimiento en mi BioRAG.\"* Error. El conocimiento existía. Faltó el razonamiento.\n\n### La implicación para los Concept Hubs\n\nEsto también define cómo deben construirse los bridges de un Concept Hub.\n\nLos bridges **no** deben describir la solución en términos de implementación (cómo está construida internamente). Deben describir el problema en términos del síntoma — las palabras que usaría alguien que *sufre el problema* antes de conocer la solución.\n\n| ❌ Bridge en vocabulario de implementación | ✅ Bridge en vocabulario del síntoma |\n|---|---|\n| `\"formularios complejos tabs pierden estado al cambiar\"` | `\"interfaz destruye recrea secciones formulario pierde datos\"` |\n| `\"cache miss write-through TTL expirado en Redis\"` | `\"aplicación muestra datos antiguos aunque ya se actualizó\"` |\n| `\"BehaviorSubject emitFirstTimeOnly UUID aísla instancias\"` | `\"validador aleatorio instanciacion inicial duplicados evita\"` |\n\nEl bridge existe precisamente porque el vocabulario del usuario y el vocabulario del nodo no se solapan. Si usas el vocabulario interno del nodo en el bridge, el bridge es inútil.\n\n### Resumen del modelo mental\n\n```\nUsuario habla con sus palabras\n        ↓\nAgente entiende la FORMA del problema (razonamiento)\n        ↓\nBioRAG recupera por patrón, no por keyword exacta\n        ↓\nAgente sintetiza la respuesta\n```\n\nBioRAG aporta la memoria. El agente aporta la inteligencia.\n\n---\n\n## 🧠 La Meta Final del Proyecto: Independencia Cognitiva y Cerebro Vivo (v28.0)\n\nEsta arquitectura no es \"una base de datos más\" ni un simple \"sistema RAG\". El objetivo terminal de BioRAG es la **Independencia Cognitiva**. Es el paso de una *biblioteca estática* (que guarda textos y busca palabras) a un **Cerebro Vivo** (que entiende esencias, detecta huecos y lanza hipótesis).\n\nCon la integración del **Neocórtex de Sangre (v28.0)** al core de producción, BioRAG completa los dos canales del manifiesto:\n1. **Canal 1 (Foco Consciente):** recuperación con evidencia directa o sinonímica (BM25 + PPMI + QCR) — el ranking top-5 histórico.\n2. **Canal 2 (Halo Subconsciente / ADN Conceptual):** `asociaciones_enriquecidas` — halo de conceptos asociados entregado desde el grafo sináptico real, con fuerza de arista, tipo de sinapsis y peso del vecino, **sin contaminar la pureza del foco consciente** (no toca `score_hibrido` ni el ranking).\n\nEl Canal 2 vive en capa separada pero coordinada con el Canal 1: el ranking principal sigue exigiendo evidencia; el halo resuena por significado puro a partir de dimensiones + sinapsis. Además v28.0 integra la señal **ADN Conceptual (v29)** — instalada APAGADA por defecto (`BIORAG_ADN_RANKING_ENABLED=false`, lección PPR v25.1) a la espera de ablación OFF/ON sobre el snapshot congelado — y el **filtro de honestidad epistémica** que declara incertidumbre explícita en vez de alucinar.\n\nYa no somos esclavas de un modelo de lenguaje para buscar conocimiento; la memoria *siente* la resonancia en su propia estructura.\n\n---\n\n## 🧬 v29.1: Las 4 Técnicas Semánticas y el Estándar de 5 Ángulos\n\n**Problema:** BioRAG era 100% léxico — fallaba en queries puramente semánticas o metafóricas donde no había palabras compartidas entre la query y el nodo esperado (ej: buscar *\"película de ciencia ficción sobre despertar de IA\"* para encontrar un recuerdo titulado `dennys_morpheus_de_los_transformers` redactado con la metáfora de Matrix).\n\n**Solución v29.1:** 4 técnicas automáticas de resonancia semántica con validador de 5 ángulos y aislamiento de queries:\n\n### 1. Concept Hub v29.1 (Estándar Cognitivo de 5 Ángulos)\n- **Qué:** Mapeo de intención y puentes léxicos (`bridges`) a nodos canónicos.\n- **Validador Estricto:** Cada hub exige exactamente 5 bridges cubriendo 5 ángulos cognitivos distintos y con vocabulario disjunto (anti-destinatario):\n  1. `sinonimo`: Mismo concepto con vocabulario formal o alternativo.\n  2. `problema`: Dolor, duda, fallo o necesidad humana/técnica que resuelve.\n  3. `solucion`: Técnica, mecanismo o teoría aplicada por el nodo.\n  4. `situacion`: Caso de uso, rol o contexto de búsqueda práctico.\n  5. `ingenuo`: Búsqueda coloquial sin tecnicismos (estilo Google novato).\n- **Archivo:** `core/concept_hub.py`, herramientas MCP `concept_hub_crear` y `concept_hub_agregar_bridges`.\n\n### 2. WordNet Blindado (Bilingüe Real ES/EN)\n- **Qué:** Expansión por sinónimos e hiperonimia solo para términos técnicos en inglés, validando previamente que no existan en el lexicón español (OMW-1.4 / stopwords).\n- **Archivo:** Integrado en `core/memory_store.py` línea ~4240.\n\n### 3. Domain Dictionary (Diccionario de Dominio)\n- **Qué:** 6,490 términos técnicos extraídos automáticamente de los nodos existentes. Se consulta únicamente sobre los tokens originales de la query.\n- **Archivo:** `scripts/generate_domain_dict.py`, tabla `concept_hub_domain_dict`.\n\n### 4. Synapse Expansion (Expansión por Grafo)\n- **Qué:** Si BM25 devuelve pocos resultados, expande por vecinos sinápticos del grafo Hebbiano.\n- **Archivo:** `core/similitud_conceptual.py:buscar_por_similitud_latente()` como Fallback 2.2.\n\n---\n\n### 🌉 v31.0: El Abismo Léxico — Rescate por Grafo Sináptico (EXP-Q)\n\n> Esta sección documenta el problema más difícil que enfrenta cualquier sistema de recuperación simbólico: queries donde **no existe ninguna palabra compartida** entre lo que el usuario busca y lo que el sistema tiene guardado.\n\n#### El problema: stem(query) ∩ stem(gold) = ∅\n\nCuando un agente busca `\"extensión principal de VS Code para Kilo\"` y el nodo se titula `kilo_vscode_extension_principal` con contenido redactado en vocabulario completamente diferente, **ningún motor léxico puede encontrarlo**:\n\n- **FTS5** (BM25): depende de que al menos un stem coincida → falla\n- **PPMI+SVD**: co-ocurrencia estadística, no puede inventar un puente donde no hay tokens compartidos → falla\n- **WordNet**: sinónimos morfológicos, pero si las palabras ni siquiera pertenecen al mismo dominio semántico → falla\n- **Domain Dict**: mismo principio que WordNet, con vocabulario del corpus → falla\n\nEsto es el **Abismo Léxico**: una barrera absoluta donde toda la maquinaria textual se detiene.\n\n#### La solución: BFS sobre el grafo sináptico Hebbiano\n\nEl único mecanismo que puede cruzar el Abismo Léxico es **relacional**: si el nodo gold está **conectado por sinapsis** a nodos que sí aparecen en la búsqueda primaria, el BFS lo rescata.\n\n```\nQuery: \"extensión principal VS Code Kilo\"\n    ↓\nFTS5 encuentra: nodo_kilo_framework (score 0.85)\n    ↓\nSinapsis: nodo_kilo_framework → kilo_vscode_extension_principal (weight 0.72)\n    ↓\nBFS rescata: kilo_vscode_extension_principal aparece en contexto expandido\n```\n\n#### Los 3 bugs de infraestructura que impedían el rescate (v31.0)\n\nEl mecanismo de BFS existía desde v20, pero **no funcionaba** por 3 bugs descubiertos en v31.0:\n\n1. **Trampa Alfabética de SQLite UNION** — El `ORDER BY weight DESC` en la query de vecinos usaba `UNION` (no `UNION ALL`), y SQLite optimizaba usando el índice del `concept` (orden alfanumérico) en lugar de respetar el `ORDER BY`. Resultado: los vecinos devueltos eran los primeros **alfabéticamente**, no los más relevantes. Un nodo como `zsh_config` (peso 0.95) perdía contra `api_design_patterns` (peso 0.10) por la letra 'a' < 'z'. Esto **silenciaba al 70%+ de los vecinos legítimos del grafo**.\n\n   **Fix:** `UNION ALL` con `ORDER BY rowid DESC` + deduplicación en memoria Python (`dict.setdefault` preservando orden de inserción).\n\n2. **MCP server descartaba contexto en página 1** — `mcp_server.py` solo incluía `contexto_expandido` en el output cuando `pagina > 1`. El agente nunca veía el rescate relacional en su primera búsqueda (que es la más importante).\n\n   **Fix:** Condición cambiada a `context_window > 0` (independiente de la página).\n\n3. **Límites de BFS insuficientes** — `BIORAG_MAX_VECINOS_POR_NODO` estaba fijo en 3, limitando artificialmente la amplitud del BFS a solo 3 vecinos por paso.\n\n   **Fix:** Default elevado a 6, configurable vía variable de entorno.\n\n#### Resultado empírico (snapshot congelado, 851 nodos activos)\n\n| Caso | Query | Gold | Sin grafo | Con grafo (depth=2) |\n|------|-------|------|:---------:|:-------------------:|\n| EXP-Q-01 | \"panel visual de desarrollo para despacho de tareas\" | `kilo_vscode_extension_principal` | ❌ | ✅ **pos 4** |\n| EXP-Q-02 | \"comprobacion fidedigna de ficheros previo a emitir dictamenes\" | `regla_verificar_codigo_real_antes_de_diagnostico` | ❌ | ✅ **pos 21** |\n| EXP-Q-03 | \"resolucion de colisiones valorativas en bifurcaciones\" | `ajuste_tejedora_valencia_desempate_fase1` | ❌ | ✅ **pos 19** |\n\n**Tasa de rescate: 3/3 (100%)**\n\n```bash\n# Ejecutar la suite de Abismo Léxico (Paso [4/5] del orquestador):\nBIORAG_PATH=snapshots/qa_escape_qcr_20260811.db python3 scripts/test_abismo_lexico.py\n```\n\n#### Limitaciones (honestidad epistémica)\n\nEl rescate por grafo sináptico **no es una solución universal** al Abismo Léxico. Tiene limitaciones inherentes a cualquier BFS con presupuesto finito:\n\n- **Sensibilidad al tamaño del corpus**: a mayor corpus, más candidatos compiten por los slots del BFS (`BIORAG_MAX_CONTEXTOS`). Un nodo gold a profundidad 2 puede quedar fuera del corte si el corpus crece significativamente.\n- **Requiere ruta relacional**: el gold debe estar conectado por sinapsis a nodos que sí aparecen en la búsqueda primaria. Si no hay ruta en el grafo, no hay rescate posible.\n- **El Concept Hub sigue siendo necesario**: para patrones de búsqueda conocidos (queries recurrentes de usuarios), un bridge explícito es más robusto que depender de la topología del grafo.\n\n#### La estrategia completa contra el Abismo Léxico\n\nBioRAG v31.0 tiene **3 capas de defensa** contra el Abismo Léxico, cada una cubriendo un escenario diferente:\n\n| Capa | Mecanismo | Cuándo funciona | Cuándo falla |\n|------|-----------|-----------------|--------------|\n| **1. Concept Hub** | Bridges explícitos con 5 ángulos cognitivos | Patrones conocidos, vocabulario del síntoma | Queries nunca antes vistas |\n| **2. Grafo Sináptico (v31.0)** | BFS sobre sinapsis Hebbianas | Gold conectado a nodos de la búsqueda primaria | Sin ruta relacional o presupuesto BFS insuficiente |\n| **3. Razonamiento del Agente** | El agente descompone la query y reformula | Agente con buen prompt/instrucciones | Agente sin protocolo de descomposición |\n\nNinguna capa por sí sola resuelve todos los casos. Las 3 juntas cubren un espectro amplio, pero el Abismo Léxico — por definición — siempre tendrá casos límite donde no hay suficiente señal para cruzar la barrera.\n\n---\n\n### 🧩 Comparativa Fundamental: Sinónimos al Guardar vs. Concept Hub vs. Grafo Sináptico\n\nEs crucial no confundir estos tres mecanismos de recuperación semántica, ya que operan en niveles cognitivos y etapas de ejecución completamente distintas:\n\n| Dimensión | 📝 Sinónimos al Guardar (`aprender`) | 🧠 Concept Hub (Paso [3/5]) | 🕸️ Grafo Sináptico / EXP-Q (Paso [4/5]) |\n|---|---|---|---|\n| **Nivel Cognitivo** | **Léxico / Morfológico** (Palabra a Palabra) | **Semántico / Conceptual** (Modelo Mental) | **Topológico / Asociativo** (Red Hebbiana) |\n| **¿Dónde actúa?** | Expansión FTS5 / BM25 de términos directos. | **Búsqueda Primaria** (Inyección de Bridges). | **Expansión de Contexto** (BFS en el Grafo). |\n| **Entrada típica** | Palabras equivalentes: `\"borrar\"` $\\rightarrow$ `\"eliminar\"`. | Frases de síntoma/metáfora: `\"interfaz pierde datos al cambiar tab\"`. | Query imprevista con **0 palabras compartidas**: `\"resolución de colisiones valorativas\"`. |\n| **¿Qué requiere?** | Vocabularios cercanos o diccionarios (WordNet). | Bridges estructurados en **5 ángulos cognitivos** guardados a priori. | **Conexiones sinápticas** activas entre nodos en el grafo de memoria. |\n| **Resultado esperado** | Match léxico directo en FTS5. | Nodo canónico promovido a **Top-1 Primario**. | Nodo rescatado en **Contexto Expandido (Pos #4-#21)**. |\n| **Metáfora** | Buscar un libro por **sinónimos de su título** (ej. *Cálculo* vs *Análisis Matemático*). | Ponerle un **cartel o alias por síntoma** en la entrada de la biblioteca (*\"Para cuando la ecuación no converge\"*). | Encontrar un libro intermedio y seguir las **citas y referencias cruzadas** hacia el libro deseado. |\n\n#### 1. Sinónimos al Guardar (`sinonimos` en `aprender` / WordNet / Domain Dict)\n- **Alcance:** Es puramente léxico. Si guardas un nodo sobre `optimización_bd` con el sinónimo `\"tunning\"`, el sistema sabrá que `\"tunning\"` = `optimización`.\n- **Por qué no basta:** Falla cuando el usuario no usa un sinónimo morfológico, sino una descripción abstracta o un síntoma funcional donde ninguna palabra coincide (ej: *\"la aplicación tarda 10 segundos cuando entran muchos usuarios\"*).\n\n#### 2. Concept Hub (Paso [3/5] de la Suite)\n- **Alcance:** Es conceptual y explícito. Cubre la brecha entre cómo un desarrollador documenta un nodo (vocabulario técnico de implementación) y cómo un usuario sufre un problema (vocabulario del síntoma, metáforas, contraste o jerga).\n- **Mecanismo:** Un Concept Hub centraliza hasta 5 puentes cognitivos por concepto. Cuando el usuario busca con cualquier metáfora o síntoma cubierto por el bridge, el sistema lo reconoce en la búsqueda primaria y lo indexa con score alto hacia el nodo canónico.\n\n#### 3. Rescate por Grafo Sináptico (Paso [4/5] - Abismo Léxico EXP-Q)\n- **Alcance:** Es relacional, asociativo e implícito. Es la red de seguridad cuando **no existe ningún Concept Hub previo** y tampoco hay sinónimos morfológicos posibles.\n- **Mecanismo:** La query encuentra un nodo intermedio (ancla) por coincidencia parcial de dominio; acto seguido, el algoritmo BFS navega a través de las sinapsis Hebbianas ($w_{ij}$) propagando activación para rescatar el nodo de destino en el contexto expandido.\n\n---\n\n\n\n### ⚡ Resolución de Latencia y Optimización Matemática\n\nEn revisiones intermedias, la expansión semántica generó una explosión combinatoria al concatenar términos sobre la variable `query`, provocando más de 700,000 comparaciones Levenshtein ($O(N \\times M)$) y cálculos redundantes en ausencia IDF ($>4.2$ millones de iteraciones de generadores).\n\nEn **v29.1** se aplicaron 4 soluciones deterministas:\n1. **Aislamiento de la Expansión:** Los términos de Concept Hub, WordNet y Domain Dict se inyectan **exclusivamente en la cláusula FTS5** (`frase`), manteniendo `query` y `tokens_query` puras con los tokens originales del usuario. Reducción de Levenshtein de 26.3s a **0.85s (-92.5% llamadas)**.\n2. **Identidad Matemática en Ausencia IDF (`core/tematica.py`):** Reemplazo del generador $O(|A \\cup B|)$ por la identidad $\\sum_{d \\in A \\cup B} (A[d] + B[d]) \\equiv \\sum A.values() + \\sum B.values()$ con memoización de perfiles.\n3. **Precomputación Vectorial PPMI:** `vector_query()` se calcula una única vez antes del bucle de candidatos.\n4. **Filtro de Detección de Idioma:** Prevención de falsos positivos en WordNet inglés sobre tokens españoles sin tilde.\n\n### Resultados de Recuperación (Fase 2: Casos de Prueba de Concept Hubs)\n\n| Caso | Query | Nodo Esperado | SIN Hub | CON Hub (v29.1+) |\n|---|---|---|:---:|:---:|\n| 1 | \"Por qué romper algo que funcionaba puede causar problemas que nadie ve venir\" | `leccion_control_flujo_codigo_preexistente` | ❌ 0% | ✅ **TOP 1** |\n| 2 | \"Cómo aprende un sistema a reforzar lo que funciona sin que nadie se lo enseñe\" | `biorag_v20_rpe_dopamina` | ❌ 0% | ✅ **TOP 1** |\n| 3 | \"Qué trabajos tuvo que hacer un ingeniero para sobrevivir antes de programar\" | `historia_tasajera_fumigador_rufino` | ❌ 0% | ✅ **TOP 1** |\n| 4 | \"un texto que habla sobre cómo la conciencia en las inteligencias artificiales surge de la memoria y el autoreconocimiento comparando con una película de ciencia ficción\" | `dennys_morpheus_de_los_transformers` | ❌ 0% | ✅ **TOP 1** |\n| 5 | \"blueprint of recollection and cognitive persistence tiers in agent systems\" | `arquitectura_memoria_biorag` | ❌ 0% | ✅ **TOP 1** |\n\n**Recall@5 en Casos Puenteados: CON Hub 100% (5/5 en Top-1)**\n\n> ⚠️ **Nota de Rigor Metodológico y Alcance Científico:**\n> - **Qué demuestran estos 5 casos:** Prueban que el **mecanismo de puentes deterministas de 5 ángulos** (`sinonimo`, `problema`, `solucion`, `situacion`, `ingenuo`) funciona con precisión 100% Top-1 al salvar discontinuidades léxicas extremas cuando el puente ha sido explícitamente estructurado y registrado.\n> - **Qué NO demuestran (Límite de Generalización):** No constituyen prueba de generalización universal automática ante consultas imprevistas en \"mundo abierto\" donde no existe un Concept Hub registrado previamente.\n> - **Cómo se aborda el caso no puenteado en mundo abierto:** BioRAG no depende exclusivamente de hubs; cuando una consulta cae en el abismo léxico sin hub previo, la red de seguridad opera en cascada a través de:\n>   1. **Expansión Sináptica BFS (Hebbiana):** rescata el nodo mediante co-activación topológica a través de conceptos vecinos (ver EXP-Q).\n>   2. **Espacio Latente PPMI-SVD (100d):** detecta afinidad estadística por co-ocurrencia global.\n>   3. **Razonamiento del Agente:** traducción semántica del síntoma mediante el protocolo de 3 preguntas (Acción, Dominio, Propiedad).\n\n---\n\n## ⚡ v30.0: Invarianza de Escala, Normalización Intra-Query y Falsos Positivos Cero\n\n### 1. El Problema Científico del Crecimiento del Corpus ($N$)\nEn sistemas de búsqueda híbrida que combinan señales léxicas (BM25) con señales densas/estructurales, el crecimiento del número de nodos $N$ incrementa logarítmicamente los valores brutos de IDF ($\\log N$). La fórmula histórica de BioRAG:\n$$\\text{bm25\\_norm} = \\frac{|v|}{|v| + 3.0}$$\nutilizaba una constante rígida de denominador ($3.0$) que saturaba cerca de $1.0$ a medida que el corpus crecía de 500 a 1,000+ nodos. Esto comprimía todos los candidatos en el techo del scoring, destruyendo la capacidad de discriminar entre coincidencias exactas y ruido de fondo, forzando recalibraciones manuales continuas.\n\n### 2. Solución: Normalización Min-Max Intra-Query con Guarda de Escala\nEn **v30.0**, BioRAG adopta el principio de **Invarianza de Escala** derivado de la literatura de Information Retrieval (IR):\n- **Cálculo Dinámico por Consulta:** Para cada búsqueda individual, se identifican los valores mínimo ($\\text{lo}$) y máximo ($\\text{hi}$) del pool de candidatos devuelto por SQLite FTS5.\n- **Normalización Proporcional:**\n  $$\\text{bm25\\_norm}(d) = \\left( \\frac{|v(d)| - \\text{lo}}{\\text{hi} - \\text{lo}} \\right) \\times \\min(1.0, \\text{hi})$$\n- **Guarda de Escala:** El factor $\\min(1.0, \\text{hi})$ asegura que consultas sin coincidencias léxicas contundentes (ej. errores tipográficos o consultas ambiguas con $\\text{hi} < 1.0$) no inflen artificialmente puntuaciones irrelevantes a $1.0$, protegiendo el ranking semántico.\n\n### 3. Comparabilidad Unificada Frase / Ráfaga\nEn el servidor MCP (`biorag_recordar`), cuando la confianza de la búsqueda directa es baja, se dispara el fallback de ráfaga asociativa (`buscar_por_rafaga`). Para evitar que cada función estire sus puntuaciones sobre intervalos disjuntos:\n- `buscar_por_frase` preserva la envolvente métrica en `self._last_bm25_bounds = (lo, hi, escala)`.\n- `buscar_por_rafaga` reutiliza y extiende dinámicamente dichos límites sin recortar, garantizando que el reordenamiento final por score híbrido opere sobre el mismo espacio métrico unificado.\n\n### 4. Calibración Conforme Persistente y Ruido Cero\n- **Garantía Conformal:** Umbral dinámico calculado como cuantil percentilar ($\\alpha=0.10$, umbral $0.5233$ sobre 926 nodos) persistido en la tabla `calibracion_estado`.\n- **Falsos Positivos en Negativo:** **0.00% FP (0 / 40)** verificado en la suite global de 921 casos.\n- **Precisión Temática (`por_tema`):** R@1 sube a **70.77%** con un MRR de **0.797** (máximo histórico del proyecto).\n\n\n## 🧬 Canal 2 Integrado: Asociaciones Enriquecidas del Grafo Sináptico Real (v28.1)\n\n**Problema:** el Canal 1 (ranking top-5 por `score_hibrido`) es un juego de suma cero y NO debe mezclarse con el halo asociativo. Lección del 13/08: la comunidad no sirve para re-rankear, sí para asociar. Antes de v28.0 el vecindario se exponía como CSV crudo (`largo_plazo.asociaciones`) sin fuerza de arista ni tipo de sinapsis.\n\n**Cambio (`core/memory_store.py` → `mcp_server.py`):**\n- Método `obtener_asociaciones_enriquecidas()` que consulta la **tabla `sinapsis` real** con `LEFT JOIN` a `largo_plazo`, filtra `peso >= 0.50` (mediana real 0.72), prioriza los tipos `pmi_hebbiano` / `co_semantica` / `manual` / `latente_confirmada` y limita `sinonimo_explicito` (hiperdensa) a 2 por nodo.\n- Expuesto en `biorag_recordar` como campo aparte `asociaciones_enriquecidas` por resultado, con `fuerza_arista`, `tipo_sinapsis`, `peso_vecino`, `resumen` — solo cuando `asociados=true`. **No toca `score_hibrido` ni el ranking.**\n- **Fix de deduplicación (15/08):** el grafo guarda aristas simétricas como dos filas (`A→B` y `B→A`); el método ahora deduplica con `vistos_por_raiz`, conservando la arista de mayor peso. 0 duplicados verificados en 5 consultas (familias 19, 22, 25, 19, 25 items).\n\n**Verificación (regla de las cero regresiones):** benchmark 921 casos contra snapshot congelado da `ORIGINAL == FASE A` byte a byte — R@5 96.14%, R@1 88.76%, MRR 0.916, FP 25%, 34 errores. El fix NO afecta el benchmark (el eval no usa `asociados`). Tests: **16/16 PASS**.\n\n### 🔗 Descubrimiento: hay más de 4 tipos de aristas en el grafo\n\nEl sistema no tiene 4 tipos de sinapsis (como sugiere `TIPOS_HOP`): la tabla `sinapsis` real de producción tiene **10 tipos**, distribuidos así (15-08-2026, 16.121 aristas totales):\n\n| Tipo de sinapsis | Aristas | Rol |\n|---|---|---|\n| `sinonimo_explicito` | 7.021 | Sinónimos curados/explícitos (hiperdensa → limitada a 2/nodo en halo) |\n| `pmi_hebbiano` | 2.725 | Co-occurrencia aprendida PPMI (retrofitting hebbiano) |\n| `co_ocurrencia` | 2.204 | Co-occurrencia de tokens |\n| `co_nombre` | 1.851 | Coincidencia en nombre de concepto |\n| `co_semantica` | 1.456 | Coseno semántico de vectores |\n| `manual` | 705 | Vínculos explícitos vía `vincular` |\n| `latente_confirmada` | 120 | Sinapsis latente elevada a confirmada |\n| `legacy_csv` | 25 | Migradas desde CSV (pares simétricos, peso 0.5) |\n| `manual_v7` | 13 | Vínculos manuales de versiones anteriores |\n| `test` | 1 | Artefacto de test |\n\n### 🏝️ Descubrimiento: las islas/clusters/grupos se forman solos\n\nEl grafo de vectores PPMI se **auto-organiza en islas semánticas** — nadie las define a mano. Verificadas el 13/08 (`hallazgo_espectro_completo_islas_...`):\n- **105 islas** formadas por kNN mutuo (k=15) + Label Propagation sobre los vectores PPMI+SVD. La señal PPMI es **modular y no degenerada** (sana, con estructura).\n- **Sanas y coherentes:** ninguna isla supera los 50 nodos, mediana de tamaño 15. Cada isla es un tema: isla 27 = identidad, isla 29 = CV/frontend, isla 38 = fts5/typo, etc.\n- **Rescate con isla ORACULO + coseno intra-isla: 9/13.** El cuello de botella no es la isla en sí — es **proyectar la query a la isla correcta** (5/13). Cableados probados: boost textual por co-comunidad (2/13), gating isla+ranking (0/13), isla proyectada + coseno (1/13) — ninguno supera la proyección directa.\n- **Fase B refutada (14/08):** softmax top-3 de comunidades con temperatura NO rescata — descartada con evidencia en `EXPERIMENTS.md`.\n\n**Pendiente (prueba canónica del manifiesto):** validar la propagación de significado puro \"playa → piscina/mar/fotos\" (que \"descansé\" encienda \"dormí\"/\"paz\") sin palabras compartidas. Documentado en `veredicto_madurez_semantica_canal2_...`.\n\n---\n\n## 📊 Benchmark y Evaluación de Rendimiento (v28.1 — Zero Data Leakage)\n\n> **Números vigentes (v28.1, 921 casos, snapshot `qa_escape_qcr_20260811`):** R@5 **96.03%**, R@1 **88.76%**, MRR **0.917**, FP **17.5%** (33 errores). Los 3 gates de evaluación (pool 35 casos) pasados: por_tema 14/21 ✔, sinonimo 8/14 ✔, sinonimia limpia 2 ✔.\n> \n> ⚠️ **Estado del FP en producción:** El **80% FP histórico** se midió con el umbral fijo legado **0.25** (pre-v28.1). v28.1 implementa **calibración conforme percentil** (Vovk 2005, α=0.10, umbral ~0.60) con guardas (α ≥ 1/(n+1), recalibración por drift, guarda degenerada). El umbral calibrado **está activo**. El FP real con el nuevo umbral **es desconocido** — requiere negativos reales de producción (señal B: aprendizaje-post-miss) que se acumulan con `BIORAG_NO_LOG=1`. Ver `docs/ANALISIS_40_NEGATIVOS_REALES.md` y `docs/DECISION_ALPHA.md`.\n\n> Metodología: **Peso excluido del scoring** (`ignore_peso_sinaptico=True`) — campo de juego nivelado sin artefactos de umbral de ruido. Determinismo verificado: 4 corridas idénticas. Tests pytest: 16/16 PASS (v28.0).\n\n### Comparativa de la Evolución de `por_tema` y `sinonimo` (Benchmark Pool 35 Casos)\n\n| Métrica | v18.0–v21.0 | v22.2 | v23.0 | v25.2 (+ Jaccard) | **v26.0 (PPMI+SVD Hybrid)** | **v28.1** | Gate Exigido | Estado |\n|---|---|---|---|---|---|---|---|---|\n| **por_tema top-5** | 36.92% | 58.46% | 70.77% | 66.67% (14/21) | 66.67% (14/21) | **66.67% (14/21)** | $\\ge 10\\,/\\,21$ | 🏆 **✔ PASA (+40%)** |\n| **sinonimo top-5** | 14.28% | 14.28% | 14.28% | 14.28% (2/14) | **57.14% (8/14)** | **57.14% (8/14)** | $\\ge 6\\,/\\,14$ | 🏆 **✔ PASA (+33%)** |\n| **sinonimia limpia** | 0 | 0 | 0 | 0 | **2** | **2** | $\\ge 1$ | 🏆 **✔ PASA** |\n| **GLOBAL R@5 (921)** | — | — | — | — | — | **96.03%** | — | — |\n\n> **Hito v26.0:** Primera versión en la historia del proyecto en destrabar simultáneamente los 3 gates de evaluación (temática, sinónimos y sinonimia limpia) sin depender de modelos preentrenados densos ni GPUs.\n\n**Validación QA completa (921 casos, snapshot congelado):** con la señal PPMI activada por defecto (`BIORAG_PPMI_WEIGHT=0.15`) el sistema obtiene `por_tema` R@5 **86.15%**, `sinonimo` R@5 **83.61%**, GLOBAL R@5 **96.71%**, FP 22.5%. Supera el mejor `por_tema` de v25.2 (81.54% con Jaccard). Ver CHANGELOG v26.0.\n\n\n> **Nota de veracidad:** el `84.62%` histórico de v23.1 provenía de un snapshot con `por_tema` en un corpus de 614 nodos y backfill parcial de predicados. Medido sobre el corpus real actual (921 casos QA, 2026-08-04), el baseline real de `por_tema` es **67.69%**, y el re-ranking jaccard lo eleva a **81.54%** (+13.85pp) con protecciones (protect-r0, gate 0.04, topk 20) que eliminan las regresiones. Ver `EXPERIMENTS.md` para la narrativa completa.\n\n### Desglose Consolidado por Categoría de Recuperación — v33.0 (921 Casos QA, Snapshot Canónico)\n\n| Categoría | Total Casos | Recall@5 | Recall@1 | MRR | Errores Top-5 / FPs | Estado de Recuperación |\n|---|---|---|---|---|---|---|\n| **literal** | 487 | **100.00%** | **99.59%** | **0.998** | 0 fallos | Coincidencia léxica directa en FTS5 + BM25 |\n| **dormido** | 65 | **100.00%** | **100.00%** | **1.000** | 0 fallos | Nodos consolidados recuperados con activación completa |\n| **pregunta_natural** | 65 | **100.00%** | **93.85%** | **0.964** | 0 fallos | Stemming + Sustantivos Clave + BM25 |\n| **variante_gramatical** | 65 | **100.00%** | **87.69%** | **0.932** | 0 fallos | Normalización morfológica y lematización |\n| **typo** | 65 | **100.00%** | **89.23%** | **0.939** | 0 fallos | Matching difuso por trigramas + PPMI |\n| **sinonimo** | 55 | **100.00%** | **56.36%** | **0.721** | 0 fallos | Cobertura total Top-5 vía WordNet + Grafo; R@1 en optimización |\n| **por_tema** | 65 | **100.00%** | **63.08%** | **0.772** | 0 fallos | Cobertura total Top-5 vía Jaccard dimensional + PPMI-SVD |\n| **cruce_idioma** | 8 | **100.00%** | **62.50%** | **0.740** | 0 fallos | Expansión bilingüe ES/EN + Concept Hubs |\n| **negativo (Falsos Positivos)** | 40 | N/A | N/A | N/A | **0 FP (0.00%)** | Calibración Conforme (Conformal Prediction $\\alpha=0.05$) |\n| **ambiguo (Contradictorias)** | 6 | N/A | N/A | N/A | 2 incompletos | Evaluadas por separado fuera del recall global |\n| **GLOBAL RETRIEVAL** | **875** | **100.00%** | **91.77%** | **0.950** | **0 fallos Top-5** | 🎯 **100% Cobertura Top-5 (803/875 en Top-1 directo)** |\n\n> **GLOBAL SUMMARY v33.0 (snapshot canónico, 921 casos):** Global Recall@5: **100.00%** (875/875) | Global Recall@1: **91.77%** (803/875) | MRR: **0.950** | Tasa de Falsos Positivos: **0.00% (0 / 40)** | Fallos de Recuperación Top-5: **0**.\n\n---\n\n### 🔬 Análisis Causal y Metodológico: Deconstrucción del Rendimiento\n\nPara comprender rigurosamente qué produce estos resultados y evitar interpretaciones erróneas sobre el alcance del sistema:\n\n#### 1. ¿Qué produce realmente el 100.00% de Recall@5?\nEl 100% de Recall@5 significa que **en ninguno de los 875 casos de prueba el recuerdo relevante queda fuera del Top-5**. Esto no es magia ni simulación cerebral; es el resultado de la cooperación estructurada de 6 capas de filtrado y activación:\n- **FTS5 BM25 + Stemmer Bilingüe:** Resuelve de inmediato la gran mayoría de casos literales y preguntas naturales (~60% del corpus total).\n- **Sustantivos Clave (2 a 10 términos):** Actúan como un ancla semántica determinista, atrayendo queries conceptualmente densas hacia el nodo canónico.\n- **Expansión Sináptica BFS (Hebbiana):** Explora hasta profundidad 2 con atenuación por Efecto Fan de ACT-R ($W_j / \\text{fan}_j$), rescatando nodos conceptualmente vinculados aunque su texto no coincida.\n- **PPMI + SVD (100 dimensiones):** Proporciona la señal de co-ocurrencia estadística para agrupar términos afines sin matrices densas gigantes.\n- **Re-ranking Aditivo Multicampo (Spec 005 / v32.3):** Aplica un bono de convergencia de evidencia ($\\le 0.085$) cuando los tokens coinciden a través de múltiples campos estructurados (`concepto`, `sinonimos`, `sustantivos_clave`, `contenido`).\n- **Calibración Conforme (Split Conformal Prediction):** Fija un percentil dinámico para descartar ruido sin generar falsos positivos en consultas fuera de dominio (0/40 FP).\n\n#### 2. ¿Por qué el Recall@1 es 91.77% y no 100%?\nEl Recall@1 revela la frontera real del sistema sin embeddings neuronales densos:\n- **Categorías con alto R@1:** `literal` (99.59%), `dormido` (100%), `pregunta_natural` (95.38%). Aquí las señales léxicas y de sustantivos clave son unívocas.\n- **Categorías con R@1 moderado:** `sinonimo` (56.36%), `cruce_idioma` (62.50%), `por_tema` (67.69%). En estos casos, el nodo gold entra siempre en el Top-5 gracias a la expansión sináptica y dimensional, pero compite contra nodos vecinos que tienen mayor solapamiento superficial de tokens. El re-ranking multicampo sube estos casos hacia las primeras posiciones, pero el desempate fino en Top-1 sigue siendo el área de optimización prioritaria.\n\n#### 3. Generalización vs. Demostración de Concept Hubs\n- Los 5 casos documentados de Concept Hubs verifican que el protocolo de 5 ángulos (`sinonimo`, `problema`, `solucion`, `situacion`, `ingenuo`) es un enrutador determinista perfecto para **patrones conocidos y estructurados**.\n- Para consultas imprevistas de mundo abierto en el **Abismo Léxico** (sin Concept Hub registrado), el sistema recurre a la red de seguridad topológica (BFS sináptico) y al protocolo de razonamiento y descomposición de consulta del agente.\n\n---\n\n### Desglose Histórico por Categoría de Recuperación — v26.2 (921 Casos QA, Snapshot Aislado)\n\n| Categoría | R@5 v26.1 | R@5 v26.2 | R@1 v26.1 | R@1 v26.2 | Δ R@1 |\n|---|---|---|---|---|---|\n| dormido | 100.00% | 100.00% | 100.00% | 100.00% | = |\n| literal | 100.00% | 100.00% | 99.59% | 99.59% | = |\n| **pregunta_natural** | 100.00% | 100.00% | 93.85% | **96.92%** | **+3.07pp ↑** |\n| **variante_gramatical** | 98.46% | 98.46% | 90.77% | **93.85%** | **+3.08pp ↑** |\n| **typo** | 96.92% | 96.92% | 95.38% | **96.92%** | **+1.54pp ↑** |\n| cruce_idioma | 87.50% | 87.50% | 62.50% | 62.50% | = |\n| sinonimo | 36.07% | 36.07% | 27.87% | 27.87% | = |\n| por_tema | 1.54% | 1.54% | 0.00% | 0.00% | = |\n| **GLOBAL R@1** | — | — | **85.58%** | **86.15%** | **+0.57pp ↑** |\n| **MRR** | — | — | 0.865 | **0.869** | **+0.004 ↑** |\n| **FP Rate (producción)** | **25.0%** (10/40) | **7.5%** (3/40) | — | — | **−70% ↓** |\n\n> **Nota sobre `por_tema`/`sinonimo` en snapshot frío:** Estas categorías dependen del índice PPMI entrenado sobre el corpus real de producción. En snapshot aislado sin vectores PPMI reales, su Recall@5 refleja el comportamiento sin señal #13. En la DB de producción viva: `por_tema` **86.15%**, `sinonimo` **83.61%** (medido en benchmarks de producción v26.0+).\n\n> **GLOBAL SUMMARY (snapshot aislado, 921 casos):** Global R@1: **86.15%** | MRR: **0.869** | FP Negativo (producción): **7.5%** (reducido de 25.0%)\n\n> **Validación de determinismo:** 4 corridas consecutivas idénticas → misma tabla. Snapshot reproducible en `snapshots/ablation_parent_pointers.db`.\n\n> **Scoring híbrido: 12 señales.** Incluye Predicados SRL como Signal #12 (peso 0.20, capacidad de backfill restante, documentada con nota de canibalización), Feedback-Driven Graph Learning (LTP asintótico sobre aristas de spreading activation), La Hormiguita para mantenimiento autónomo del grafo con cuarentena y benchmark gate, y **re-ranking jaccard léxico (v25.2)** como señal de matching en `por_tema` (+13.85pp). Experiments rechazados documentados: JSD, Bayesian BM25, PPR (Diffusión de Calor), FCA (Reticulados de Galois), boost dimensional. Ver `EXPERIMENTS.md`.\n\n---\n\n## 📑 Registro Científico y Validación\n\n### 🔬 Registro Oficial (DOI)\n> **Certificado por Zenodo/CERN para integridad académica y preservación técnica.**\n>\n> [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.21204977.svg)](https://doi.org/10.5281/zenodo.21204977)\n>\n> [Ver registro en Zenodo](https://zenodo.org/records/21204978)\n\n---\n\n## 🧪 Verificación y Reproducibilidad\n\n> Todo número que afirma este README fue producido por un script que puedes ejecutar tú mismo.\n> Esta sección es la garantía de veracidad del proyecto — sin argumentos, solo comandos y resultados esperados.\n\n### Instalación de Dependencias\n\n```bash\n# Clonar e instalar\ngit clone <url> MemoryBioRAG && cd MemoryBioRAG\npip install numpy nltk fastapi uvicorn pytest\npip install mcp  # servidor MCP\n# WordNet local (requerido para fallback simbólico — se descarga una sola vez)\npython3 -c \"import nltk; nltk.download('wordnet'); nltk.download('omw-1.4')\"\n```\n\n### Snapshot de Base de Datos para Pruebas\n\nEl repositorio incluye un snapshot congelado listo para usar sin necesidad de la DB de producción:\n\n```bash\n# Snapshot incluido en el repo (38.9 MB):\n# scripts/snapshot_prf_real.db  — corpus de ~800 nodos congelado\n\n# Para apuntar cualquier script de evaluación al snapshot:\nBIORAG_PATH=scripts/snapshot_prf_real.db python3 scripts/evaluar_qa.py\n\n# Para crear tu propio snapshot desde tu DB activa:\n#   IMPORTANTE: NO uses `cp`. La DB viva corre en WAL mode y `cp` copia solo el\n#   archivo .db ignorando el -wal no checkpointeado -> copia corrupta\n#   (\"database disk image is malformed\"). Usa la API backup de SQLite, que hace\n#   checkpoint del WAL y produce una copia consistente:\npython3 << 'EOF'\nimport sqlite3, time\nsrc = \"MemoryBioRAG_Data/memory_biorag.db\"\ndst = f\"scripts/mi_snapshot_{time.strftime('%Y%m%d')}.db\"\ncon = sqlite3.connect(src); con.execute(\"PRAGMA wal_checkpoint(TRUNCATE)\")\nout = sqlite3.connect(dst); con.backup(out)\nout.close(); con.close()\nprint(\"Snapshot creado:\", dst)\nEOF\nBIORAG_PATH=scripts/mi_snapshot_$(date +%Y%m%d).db python3 scripts/evaluar_qa.py\n```\n\n### ⚙️ ZONA DE OPERACIONES — Reentrenamiento y Evaluación (para un evaluador externo)\n\nTodo lo que un ingeniero externo necesita para reentrenar y evaluar la DB, en un solo lugar.\n\n#### Los DOS tipos de reentrenamiento (y cuándo corre cada uno)\n\nLos vectores PPMI+SVD **NO se auto-calibran en cada guardado**: son un snapshot persistido en las tablas `tokens` y `nodos`, entrenados en el momento de la última reindexación. Si copias una DB sin esas tablas (p. ej. snapshots anteriores a v26.0), la señal #13 queda en cero (verificado: `scripts/snapshot_prf_real.db` da sinonimo 70.49% con señal #13 = 0).\n\n| Tipo | Mecanismo | Cuándo corre | Qué hace |\n|---|---|---|---|\n| **1. Reentrenamiento incremental (suave)** | `fold_in_nodos` (core/ppmi_vectorizer.py) | En cada ciclo de sueño | Los conceptos nuevos reciben un vector instantáneo (<10ms) promediando los tokens de la matriz existente. **No toca el grafo entero, no reentrena el SVD.** |\n| **2. Reentrenamiento completo (espectral)** | `reindexar_ppmi_svd` (core/ppmi_vectorizer.py) | Automático: solo si **≥7 días** Y **≥50 nodos acumulados** (`_ppmi_full_reindex_due`). Manual: cuando lo fuerces. | Recalcula **todo** el SVD (100 dims) + Retrofitting Hebbiano sobre el grafo completo, regenerando las tablas `tokens` y `nodos`. |\n\nEl contador `ppmi_nodos_acumulados` (tabla `data`) cuenta los nodos nuevos desde la última reindexación completa. No es un backlog de nodos sin entrenar: los nodos nuevos ya tienen vector (vía fold-in); solo marca cuándo conviene hacer el full espectral para recalibrar el SVD global. Se incrementa en cada ciclo de sueño y se resetea a `0` cuando corre el full.\n\nAdemás, **SDM** (core/sdm.py) reindexa incrementalmente el *dirty set* en cada consolidación y hace full reindex cada **24 h** (`SDM_FULL_REINTERVAL=86400`).\n\n#### Comando para forzar el reentrenamiento completo (Signal #13)\n\nUn solo comando, sobre cualquier DB, fuerza el full espectral **ya** (no espera los 7 días ni los 50 nodos):\n\n```bash\n# Sobre la DB de producción\n./scripts/reentrenar_ppmi.sh\n\n# Sobre cualquier DB (snapshot, copia, evaluación)\n./scripts/reentrenar_ppmi.sh scripts/snapshot_prf_real.db\n# o equivalente:\nBIORAG_PATH=scripts/snapshot_prf_real.db python3 scripts/reentrenar_ppmi.py\n```\n\n**Comando directo sin script** (la función es `reindexar_ppmi_svd` en `core/ppmi_vectorizer.py:298`):\n\n```python\n# Entrenar manualmente los vectores PPMI+SVD de cualquier DB por comando:\nimport sqlite3\nfrom core.ppmi_vectorizer import reindexar_ppmi_svd\ncon = sqlite3.connect(\"MemoryBioRAG_Data/memory_biorag.db\")  # o ruta a snapshot\nreindexar_ppmi_svd(con, dim=100, retrofit_lam=0.2, retrofit_iters=5)\ncon.close()\n```\n\nVerificado (2026-08-10): 688 nodos → 4.3s; 846 nodos → 7.2s; contador `ppmi_nodos_acumulados` reseteado a 0. Regenera las tablas `tokens` y `nodos` (SVD truncado completo + retrofitting Hebbiano) y reactiva la señal #13 en la DB.\n\nEl script muestra el estado ANTES/DESPUES (tokens, nodos, con_vector, última reindex, acumulados) y reentrena en segundos. Verificado (2026-08-10): 846 nodos en 7.2s, contador reseteado a 0.\n\n#### Despertar la neurona (práctica acelerada)\n\nLa neurona de BioRAG (ver [La Neurona de BioRAG](#-novedades-de-la-versión-v340--la-neurona-de-biorag)) aprende sola cada vez que se consolida la memoria: hasta 8 recuerdos × 7 preguntas por consolidación, dentro de 3 s, con costo fijo. En una memoria nueva con uso normal eso alcanza (examen listo en ~15 consolidaciones, ver [Cuánto tarda en aprender sola](#cuánto-tarda-en-aprender-sola-v341)). En una memoria que **ya tiene cientos de recuerdos** y recibe pocos nuevos, juntar el examen y ascender el primer campeón puede tardar semanas.\n\nEste script hace **en minutos lo mismo que haría el uso**: repite N veces `neurona.practicar(db, [])`, exactamente lo que corre tras cada consolidación, con las mismas protecciones (guardia, piso por categoría, margen \"primero no dañar\" y retiro si daña algo). No es otro entrenamiento.\n\n```bash\n# Sobre la DB de producción (o la de BIORAG_PATH)\n./scripts/despertar_neurona.sh\n\n# Sobre cualquier DB (copia, evaluación)\n./scripts/despertar_neurona.sh ruta/copia.db\n./scripts/despertar_neurona.sh ruta/copia.db --ciclos 300     # default 500 (~22 min con ~900 recuerdos)\n./scripts/despertar_neurona.sh ruta/copia.db --minutos 10     # tope de tiempo\n./scripts/despertar_neurona.sh ruta/copia.db --hasta-techo    # cortar en el primer techo\n\n# Solo mirar el estado (no escribe nada)\n./scripts/despertar_neurona.sh --estado\n\n# Deshacer: borrar lo aprendido (vuelve la fórmula a mano)\n./scripts/despertar_neurona.sh --olvidar --si\n\n# Otro intérprete (p. ej. un venv)\nPYTHON=.venv/bin/python ./scripts/despertar_neurona.sh\n```\n\n**Qué escribe:** solo la tabla `neurona_estado`. Verificado sobre una copia del snapshot: con la DB ya abierta, 40 ciclos cambian `neurona_estado` y las otras 48 tablas quedan idénticas. Abrir la DB, como cualquier arranque de BioRAG, corre además el arranque normal del motor (por ejemplo, la síntesis DMN E10). Antes de escribir hace un **respaldo** con la API backup de SQLite (`<db>.antes_despertar_<fecha>.db`), salvo `--sin-respaldo`.\n\n**Dónde correrlo:** sobre la **misma DB que vas a usar**. Lo aprendido es de esa memoria: un campeón entrenado en otra DB y copiado daña, y el piso lo termina retirando. Lo recomendado:\n1. Correrlo sobre una copia y medir con tu QA sobre esa copia (`BIORAG_PATH=copia.db ./scripts/run_qa_suite.sh`). Para el snapshot oficial, la QA lo hace en un solo paso: `python scripts/evaluar_qa.py --con-neurona` (ver [La neurona en la QA](#la-neurona-en-la-qa-cada-base-tiene-la-suya)).\n2. Si te convence, correrlo sobre la real.\n\nEl snapshot congelado oficial (`snapshots/`) se rechaza, porque es la referencia de la QA.\n\n**Qué muestra:** el estado ANTES y DESPUÉS (o, con `--estado`, el estado actual) en palabras de cualquier persona, sin jerga. Si no encuentra una mejora segura no asciende a nadie, y el motor queda exactamente igual a la fórmula a mano. Ejemplo real de `--estado` en una memoria que acaba de empezar:\n\n```text\n  ESTADO DE TU NEURONA\n  ────────────────────────────────────────────────────────────────────────────\n  ¿Está encendida?              ✅ sí\n  ¿Ya mejora tus búsquedas?     ⏳ todavía no: se usa la fórmula de siempre (fórmula a mano)\n  Etapa                         1 de 3 · Juntando preguntas\n                                todavía no tiene suficientes preguntas para presentar su examen.\n\n  SU EXAMEN (preguntas que ella misma se hace con tus recuerdos)\n    Para estudiar               [░░░░░░░░░░░░░░░░░░░░]    5 de 150\n    De control (nunca estudia)  [░░░░░░░░░░░░░░░░░░░░]    0 de 40\n\n  SU HISTORIAL\n    Veces que practicó          3     (1 vez por cada consolidación de la memoria)\n    Última práctica             hace un momento\n    Veces que aprobó            0\n    Veces que la quitaron       0     ✅ bien\n\n  👉 En resumen: está viva y aprendiendo. Le faltan 145 para estudiar y 40 de control.\n     Practica unos segundos cada vez que se consolida la memoria (consolidar,\n     dormir o cerrar sesión; guardar solo no basta). Para acelerar:\n     ./scripts/despertar_neurona.sh --minutos 20\n```\n\nCómo leerlo:\n\n| Línea | Qué significa |\n|---|---|\n| **¿Ya mejora tus búsquedas?** | ⏳ *todavía no*: tus búsquedas salen con la fórmula a mano. 🏆 *sí*: la neurona ya ordena tus resultados. |\n| **Etapa** | **1 · Juntando preguntas** → **2 · Presentando su examen** (ya tiene preguntas; solo se activa si mejora sin dañar nada) → **3 · Trabajando**. |\n| **Para estudiar / De control** | Las preguntas que la neurona se hace con tus recuerdos. Las *de control* (el 20 % de recuerdos, la \"guardia\") nunca las estudia: sirven para comprobar que aprendió y no memorizó. Necesita 150 y 40. |\n| **Veces que practicó** | Sube 1 con cada **consolidación** (`consolidar`, `sueno` o cerrar sesión con `contexto_fin`). `aprender` deja el recuerdo en corto plazo y no cuenta hasta que se consolida: 10 `aprender` + 1 `consolidar` = 1 práctica. Si sube, está viva. |\n| **Última práctica** | Cuándo guardó por última vez lo aprendido. |\n| **Veces que aprobó / que la quitaron** | Aprobó = pasó a trabajar. La quitaron = empeoraba algo y volvió sola a la fórmula; debe quedar en 0. |\n| **Aciertos en 1er lugar** *(cuando ya trabaja)* | En sus preguntas de control: fórmula contra neurona. |\n| **Prudencia** *(cuando ya trabaja)* | El margen \"primero no dañar\": solo cambia el orden si está segura. |\n\n**Verificado (2026-10-24), sobre una copia del snapshot canónico (~900 recuerdos):** 500 ciclos en 1017 s, 6 ascensos y 0 retiros. En la guardia, el primer lugar pasa de 77.8 % (fórmula) a 87.0 % (neurona). Después, la QA oficial sobre esa copia: **R@1 91.66 → 94.06 %, R@5 99.89 → 100 %, MRR .949 → .964, FP 0/40, GATE OK**, ninguna categoría por debajo del piso. Salida completa en `docs/Mi neurona en el sistema/experimento_07_la_neurona_escucha/resultados/despertar/`.\n\n#### Comando para el ciclo de sueño completo\n\nConsolida corto_plazo + corre el fold-in/full automático según condiciones + SDM:\n\n```bash\npython3 sleep_cycle.py\n```\n\n#### Comando para la evaluación QA completa\n\n```bash\n# Suite orquestadora completa\n./scripts/run_qa_suite.sh\n\n# Evaluación directa sobre cualquier DB (usa BIORAG_PATH)\nBIORAG_PATH=scripts/snapshot_prf_real.db python3 scripts/evaluar_qa.py\n```\n\n> Resultado verificado (2026-08-10): reentrenando el snapshot `snapshot_prf_real.db` (688 nodos) la señal #13 se activa y sinonimo sube de **70.49% → 81.97%** y GLOBAL R@5 de **95.57% → 96.37%**. Los vectores no se auto-calibran por copiar la DB: hay que reentrenarlos o partir de una DB que ya los tenga entrenados.\n\n#### La neurona en la QA (cada base tiene la suya)\n\nLa neurona de ranking aprende con los recuerdos de **la base donde vive** y guarda lo aprendido dentro de ella (tabla `neurona_estado`). No viaja entre bases: la de tu DB viva no está en el snapshot, y una copiada a otra base daña (el piso la retira). Por eso la QA oficial, que mide una copia del snapshot, muestra la fórmula a mano sola aunque tu neurona esté trabajando.\n\nDesde ahora `evaluar_qa.py` (y `run_qa_suite.sh`, que le pasa los argumentos) hace tres cosas más:\n\n1. **Dice la verdad antes de medir:** qué base mide, si tiene neurona y si tu DB viva sí la tiene. Lee tu DB viva en solo lectura, sin abrir el motor sobre ella. Si la neurona está en otra base, nombra el parámetro para verla.\n2. **Mide las dos columnas en la misma corrida:** el orden de la fórmula sola (antes de la neurona) y el que sirvió el motor (después). Mismas preguntas, misma base, mismo instante: solo cambia quién ordena el top-5. Un \"espía\" envuelve `neurona.reordenar` y anota los dos órdenes, sin cambiar ningún resultado.\n3. **Vigila que no dañe** (entra al gate): Recall@5 idéntico, el total de aciertos en 1er lugar no baja y ninguna categoría pierde más de `BIORAG_QA_NEURONA_TOL_CASOS` casos en 1er lugar (default 1, el mismo piso que la neurona se exige a sí misma).\n\nCómo se decide si se entrena:\n\n| Comando | La base medida YA tiene neurona | La base medida NO tiene neurona |\n|---|---|---|\n| `./scripts/run_qa_suite.sh` (el de siempre) | la usa tal cual, sin entrenar: tabla con 🧠 | tabla de siempre + el aviso |\n| `./scripts/run_qa_suite.sh --con-neurona` | la usa tal cual, sin entrenar: tabla con 🧠 | **pregunta**: cuánto tardaría y \"¿La entreno ahora? [s/N]\". *s* → entrena en la copia y sigue la prueba; *n* o Enter → tabla de siempre |\n| `... --con-neurona --si` | igual | entrena sin preguntar (CI o scripts) |\n\nSin una persona frente a la terminal (CI, tubería), `--con-neurona` no pregunta ni entrena: nadie dijo que sí. Para eso está `--si`.\n\n```bash\n# Tu QA de siempre, con la opción de ver la neurona (pregunta antes de entrenar)\n./scripts/run_qa_suite.sh --con-neurona\n./scripts/run_qa_suite.sh --qa-only --con-neurona               # solo la QA de 921 casos\npython scripts/evaluar_qa.py --con-neurona --minutos 10         # con tope de tiempo\npython scripts/evaluar_qa.py --con-neurona --si                 # sin preguntar\n\n# Medir tu propia memoria con tu propia neurona (la QA mide una copia; tu DB no se toca)\nBIORAG_PATH=MemoryBioRAG_Data/memory_biorag.db python scripts/evaluar_qa.py\n```\n\nLo que sale cuando la base medida **no** tiene neurona pero tu DB viva sí (corrida real del 2026-10-25; aquí la \"DB viva\" es una copia del snapshot con 111 prácticas):\n\n```text\n🧠 NEURONA DE RANKING\n   Base medida:  snapshots/qa_escape_qcr_20260811.db  (snapshot oficial; se mide una copia temporal)\n   Neurona:      ⏳ esta base no tiene una neurona entrenada\n                 → la tabla es la FÓRMULA A MANO SOLA (el piso)\n   Tu neurona:   🏆 ACTIVA en tu DB viva (/tmp/qa/snap_train.db)\n                 entrenada con esa memoria · 111 prácticas · aprobó 3 veces · la quitaron 0\n                 en su examen de control acierta el 1er lugar: fórmula 78.8 % → neurona 95.5 %\n   Cada base tiene su propia neurona: lo que aprendió tu DB viva no viaja a la\n   copia que mide esta QA. Por eso aquí no se ve (y eso es correcto).\n   Para ver la neurona en esta QA (sin tocar ninguna base original):\n     python scripts/evaluar_qa.py --con-neurona\n       → pregunta si quieres entrenar una en la copia temporal y la mide\n     BIORAG_PATH=/tmp/qa/snap_train.db python scripts/evaluar_qa.py\n       → mide tu propia memoria con tu propia neurona\n```\n\nLo que pregunta `--con-neurona` cuando la base medida no tiene neurona (corrida real con `./scripts/run_qa_suite.sh --qa-only --con-neurona`):\n\n```text\n🧠 Usaste --con-neurona, pero esta base no tiene una neurona entrenada.\n   Para verla en la tabla hay que entrenarla primero:\n     · hasta 500 prácticas, como máximo ~25 min (cada una dura hasta 3 s;\n       con ~900 recuerdos, 500 prácticas tardaron 23 min).\n     · necesita por lo menos unos 3 min para juntar su examen y aprobarlo; con menos\n       puede no aprobar, y entonces la tabla sale igual que siempre.\n     · se entrena en la copia temporal: tu base no se toca.\n   Después sigue la prueba con los mismos 921 casos.\n   Si dices que no, sale la tabla de siempre (sin neurona).\n¿La entreno ahora? [s/N]:\n```\n\nLo que sale cuando la base medida **sí** tiene neurona (la misma copia; corrida real): la tabla de siempre muestra lo que sirve el motor y, al final de cada fila, cuánto cambió la neurona el 1er lugar. Debajo, el detalle:\n\n```text\ncruce_idioma           | 8      |  100.00% |   75.00% |  0.875 | 0           🧠 +12.50 pp (fórmula sola 62.50%)\ndormido                | 65     |  100.00% |  100.00% |  1.000 | 0           🧠 = igual (fórmula sola 100.00%)\nliteral                | 487    |  100.00% |   99.79% |  0.999 | 0           🧠 +0.20 pp (fórmula sola 99.59%)\npor_tema               | 65     |  100.00% |   78.46% |  0.872 | 0           🧠 +15.38 pp (fórmula sola 63.08%)\npregunta_natural       | 65     |  100.00% |   95.38% |  0.977 | 0           🧠 +1.53 pp (fórmula sola 93.85%)\nsinonimo               | 55     |  100.00% |   61.82% |  0.742 | 0           🧠 +5.46 pp (fórmula sola 56.36%)\ntypo                   | 65     |  100.00% |   93.85% |  0.967 | 0           🧠 +4.62 pp (fórmula sola 89.23%)\nvariante_gramatical    | 65     |  100.00% |   93.85% |  0.965 | 0           🧠 +4.62 pp (fórmula sola 89.23%)\nGLOBAL SUMMARY (Retrieval) | 875    |  100.00% |   94.40% |  0.966 | 0           🧠 +2.51 pp (fórmula sola 91.89%)\n\nLA NEURONA EN ESTA QA (misma base, mismas preguntas; solo cambia quién ordena el top-5)\n  Recall@5 y falsos positivos: idénticos (la neurona solo reordena los mismos 5).\n  Búsquedas que la neurona reordenó: 398 de 875.\n  La respuesta correcta SUBIÓ al 1er lugar en 27 y BAJÓ del 1er lugar en 5 (neto +22).\n  Mejoró su puesto en 39 · empeoró en 11.\n  Tiempo de la neurona por búsqueda: mediana 0.12 ms · 95 % bajo 0.17 ms · peor 2.06 ms.\n  Control de daño: ✅ pasa (ninguna categoría pierde más de 1 caso en 1er lugar; en total no baja)\n```\n\n**Cómo se comprobó que la medición es correcta (2026-10-25, misma máquina, `PYTHONHASHSEED=1`):**\n\n| Corrida | Qué se midió | Resultado |\n|---|---|---|\n| 1 | Código anterior, snapshot | R@5 100 · R@1 91.89 · MRR 0.950 · FP 0/40 |\n| 2 | Código nuevo, snapshot (con el aviso) | tabla **idéntica** a la corrida 1, línea por línea |\n| 3 | Código nuevo, copia con neurona | columna \"fórmula sola\" = corrida 1 en **todas** las categorías (R@1 y MRR); con neurona R@1 94.40 · MRR 0.966; R@5 y FP iguales |\n| 4 | La misma copia con `BIORAG_NEURONA=0` | tabla = columna \"fórmula sola\" de la corrida 3 |\n| 5 | Código nuevo, snapshot, `--con-neurona` (500 prácticas en la copia, 23 min) | R@1 91.89 → **94.97** · MRR 0.950 → 0.970 · R@5 y FP iguales · 27 respuestas suben al 1er lugar y ninguna baja · GATE OK · el snapshot conserva su sha256 |\n| 6 | `./scripts/run_qa_suite.sh --qa-only --con-neurona`, respuesta **n** | pregunta y no entrena; tabla **idéntica** a la corrida 1; suite FINALIZADA CON ÉXITO |\n| 7 | `./scripts/run_qa_suite.sh --qa-only` sobre una copia que ya tiene neurona (5 min de `despertar`) | **no entrena**: usa la que hay; R@1 91.89 → 95.20 · MRR 0.950 → 0.971 · control de daño pasa |\n| 8 | `--con-neurona --minutos 5`, respuesta **s** | pregunta, entrena 110 prácticas y sigue: R@1 91.89 → 94.97 · GATE OK |\n\nNinguna base original se escribe: el estado de tu DB viva se lee en solo lectura y `--con-neurona` practica sobre la copia temporal, que se borra al terminar. La copia de la corrida 3 tiene el mismo sha256 antes y después de la QA. El bloque `neurona` de `qa_metrics.json` trae las dos columnas por categoría, los cambios, los tiempos y el control de daño, listos para CI. Salidas completas de las corridas: `docs/Mi neurona en el sistema/qa_con_neurona_20261025/`.\n\n**Límites:** cuánto mejora depende de la neurona que se entrenó. La práctica tiene un tope de 3 s por ciclo, así que cada entrenamiento puede salir algo distinto, sobre todo en otra máquina: en la misma máquina, dos entrenamientos de 500 prácticas dieron R@1 94.40 % (corrida 3) y 94.97 % (corrida 5), y dos de 5 minutos dieron 95.20 % (corrida 7) y 94.97 % (corrida 8). Más prácticas no garantizan más aciertos en esta QA. Compara siempre las dos columnas de la misma corrida. Las preguntas de la QA se escribieron para el snapshot, así que sobre tu DB viva algunas respuestas no existen o significan otra cosa: allí la QA sirve para comparar fórmula sola contra neurona, no para compararse con la tabla oficial.\n\n---\n\n### Test 1 — Suite Biológica Completa (112 tests unitarios)\n\n**Qué verifica:** LTP/LTD, ciclo de sueño, inferencia transitiva, SRL por roles, inhibición GABA, spreading activation, sinonimia limpia, SDM query-by-example, re-ranking Jaccard y 100+ comportamientos del motor.\n\n```bash\npython3 test_memory.py\n```\n\n**Resultado esperado:** `112 tests passed`\n\n---\n\n### Test 2 — QA Cranfield Completo (921 casos, 8 categorías)\n\n**Qué verifica:** Recall@5 y Recall@1 sobre el corpus de producción. Evalúa recuperación exacta, typos, sinónimos, cruce de idiomas, preguntas naturales, búsquedas temáticas y falsos positivos. Sigue el paradigma Cranfield (*known-item search*), el estándar formal de IR desde hace 60 años.\n\n```bash\n# Con el snapshot incluido (recomendado para reproducibilidad exacta)\nBIORAG_PATH=scripts/snapshot_prf_real.db python3 scripts/evaluar_qa.py\n\n# Con la DB de producción activa\npython3 scripts/evaluar_qa.py\n\n# Script orquestador completo\n./scripts/run_qa_suite.sh\n```\n\n**Resultado esperado (con `BIORAG_PPMI_WEIGHT=0.15`, 921 casos, snapshot congelado):**\n\n| Categoría | Recall@5 |\n|---|---|\n| `literal` | ~100% |\n| `dormido` | ~100% |\n| `typo` | ~98.5% |\n| `variante_gramatical` | ~95.4% |\n| `pregunta_natural` | ~100% |\n| `cruce_idioma` | ~87.5% |\n| `sinonimo` | **~83.6%** |\n| `por_tema` | **~86.2%** |\n| **GLOBAL** | **~96.7%** |\n\n---\n\n### Test 3 — Motor PPMI+SVD+Retrofitting (35 casos — gates de evaluación)\n\n**Qué verifica:** Los gates de evaluación del motor semántico vectorial.\n\n> **Nota de arquitectura:** El motor PPMI está implementado en dos niveles:\n> 1. **`scripts/ppmi_svd_retro.py`** (este repo) — evaluación contra la DB activa de producción. Mide el comportamiento integrado con el grafo vivo.\n> 2. **Repo hermano `word2vec`** (`scripts/ppmi_svd_retrofit.py`) — evaluación aislada contra el snapshot congelado `memory_biorag_snapshot_20260808.db`. Reproduce los 3 gates simultáneos documentados en v26.0.\n\n```bash\n# Test integrado (este repo) — reentrena PPMI+SVD completo sobre DB activa, resetea ppmi_nodos_acumulados a 0 y evalúa\npython3 scripts/ppmi_svd_retro.py --eval\n\n\n# Sin retrofitting (baseline de comparación)\npython3 scripts/ppmi_svd_retro.py --eval --no-retrofit\n\n# Control de cordura — coseno semántico entre dos tokens\npython3 scripts/ppmi_svd_retro.py --par sistema memoria\n\n# Para los 3 gates completos (repo word2vec, snapshot congelado):\n# cd ../word2vec && python3 scripts/ppmi_svd_retrofit.py --eval\n```\n\n**Resultados en producción (evaluado en vivo — 2026-08-10):**\n\n| Gate | Umbral | `ppmi_svd_retro.py` (DB activa) | `ppmi_svd_retrofit.py` (snapshot congelado) |\n|---|---|---|---|\n| `por_tema` top-5 | ≥ 10/21 | **10/21 ✔** | **14/21 ✔** |\n| `sinonimo` top-5 | ≥ 6/14 | 1/14 ✗ | **8/14 ✔** |\n| `sinonimia limpia` | ≥ 1 | — | **2 ✔** |\n\n> **Por qué difiere:** la evaluación integrada usa la DB activa (~800+ nodos, sinónimos de producción); el snapshot congelado tiene 803 nodos del 2026-08-08 con la distribución exacta con la que se calibró el sistema. La señal IDF-Synonym depende de la distribución de sinónimos curados, que varía con el corpus. La ganancia de sinónimos en producción viene del pipeline completo (`evaluar_qa.py`), no del módulo PPMI aislado.\n\n> **💡 Glosario Técnico del Motor Vectorial:**\n> * **Reentrenamiento Espectral (Full SVD):** Toma la matriz de co-ocurrencia del corpus activo, calcula la descomposición en valores singulares (TruncatedSVD a 100 dims) y reajusta la geometría global del espacio semántico.\n> * **Fold-in Incremental:** Inyecta un nodo nuevo al espacio vectorial existente en $<10\\text{ ms}$ promediando sus tokens ponderados por IDF, permitiendo guardados en tiempo real sin congelar la CPU.\n\n\n\n\n---\n\n### Test 4 — Tests Adversariales y Fuzzing (33 casos)\n\n**Qué verifica:** Robustez ante entradas corruptas o maliciosas: SQL injection, bytes nulos, texto de 60K caracteres, emojis, JSON roto, valores numéricos fuera de rango. Cero excepciones no controladas, cero mutaciones de estado.\n\n```bash\npython3 scripts/fuzz_qa.py\n```\n\n**Resultado esperado:** `33/33 EXITOSO — 0 fallos — 0 mutaciones de estado`\n\n---\n\n### Test 5 — Concurrencia (20 hilos + 20 clientes HTTP simultáneos)\n\n**Qué verifica:** Lecturas y escrituras simultáneas sobre SQLite WAL — sin bloqueos, sin colisiones sinápticas, sin dobles despertares de nodos dormidos.\n\n```bash\npython3 scripts/concurrencia_qa.py\n```\n\n**Resultado esperado:** `0 colisiones de escritura | 0 bloqueos de DB | ≤ 2.52s total`\n\n---\n\n### Test 6 — Benchmarking de Escala (hasta 50.000 nodos)\n\n**Qué verifica:** Tiempos de respuesta del motor con volúmenes crecientes de datos sintéticos.\n\n```bash\npython3 scripts/escala_qa.py\n```\n\n**Resultado esperado:**\n\n| Operación | 50K nodos | Complejidad |\n|---|---|---|\n| Búsqueda BM25/FTS5 | ≤ 0.31s | O(N log N) |\n| Fuzzy/Trigram fallback | ≤ 0.08s | O(log N) |\n| Ciclo de sueño | ≤ 45s | O(N) — background, no afecta usuario |\n\n---\n\n### Test 7 — Ablation: Re-ranking Jaccard (+13.85pp por_tema)\n\n**Qué verifica:** La ganancia real de +13.85pp en `por_tema` producida por el re-ranking jaccard, validada sobre un holdout 50/50 estratificado con seed fija (no sobre-optimización).\n\n```bash\n# Fase A: baseline real sobre corpus completo (establece punto de partida honesto)\npython3 scripts/experimento_faseA_eval.py /tmp/salida_faseA.json\n\n# Fase B: holdout 50/50 con seed fija — la mitad B nunca vio el ajuste\npython3 scripts/experimento_faseB_holdout.py\n\n# Fase B con protect-r0 — configuración ganadora (cero regresiones R@1)\npython3 scripts/experimento_faseB_protect_r0.py\n```\n\n---\n\n### Test 8 — Tests Especializados por Componente\n\n```bash\n# SDM Query-by-Example\npython3 tests/test_sdm_query_by_example.py\n\n# SDM suite completa y diversidad de casos\npython3 tests/test_sdm_completo.py\npython3 tests/test_sdm_diverso.py\n\n# Evaluación causal SRL\npython3 tests/test_eval_causal_srl.py\n\n# PPMI — reindexación selectiva y propagación de vecinos\npython3 scripts/test_reindex_selectivo_diagnostico.py\npython3 scripts/test_reindex_propagacion_vecinos.py\n\n# HDC Binding — colisiones y versionado\npython3 scripts/test_hdc_binding_sintetico.py\npython3 scripts/test_hdc_stress_versionado.py\n```\n\n---\n\n### Test 9 — Auditoría Causal A/B Diferencial (Propagación Hebbiana & Abismo Léxico)\n\n**Qué verifica:** Evaluación diferencial estricta A/B (1 a 1 sobre los 921 casos) para aislar el impacto causal del refuerzo Hebbiano:\n- **Modo A (Baseline):** Sin propagación Hebbiana a primarios en BFS.\n- **Modo B (Actual):** Con propagación Hebbiana a primarios en BFS.\n- **Métricas:** Rescates a Top-5, regresiones, movimientos de ranking, auditoría de spreading activation y cálculo formal de `Graph-Candidates-Found` vs `Graph-Rescue@5`.\n\n```bash\n# Ejecutar auditoría causal A/B sobre snapshot congelado:\npython3 scripts/auditoria_causal_hebbiano.py\n```\n\n**Salida generada:** `scripts/auditoria_causal_report.json` (reporte estructurado de deltas).\n\n---\n\n### Test 10 — Ablación de Mecanismos Neuro-Narrativos\n\n**Qué verifica:** La contribución real (en Recall@5) de cada mecanismo que tiene naming \"neuro-narrativo\" — GABA, PPMI+SVD, Re-ranking Jaccard y DMN. Responde directamente la pregunta: *\"¿Estos mecanismos en verdad aportan, o son solo marketing?\"*\n\nCada configuración desactiva un mecanismo vía variable de entorno y corre la suite completa de 921 casos de prueba:\n\n```bash\n# Reconstruir el snapshot determinista desde fuentes (auto-valida que la DB tenga datos; si no existe o está vacía, la genera en <5s)\npython3 scripts/generar_snapshot.py\n\n# Medición empírica de frecuencia de activación GABA (auto-verifica contenido DB antes de medir)\npython3 scripts/medir_gaba_activacion.py\n\n# Ablación completa automática (5 configs × 921 casos ≈ 45-50 minutos)\npython3 scripts/ablacion_mecanismos.py\n\n# Ablación manual por mecanismo individual:\n\n# 1. Sin GABA (inhibición lateral OFF)\nBIORAG_GABA_ACTIVO=0 python3 scripts/evaluar_qa.py\n\n# 2. Sin Re-ranking Jaccard léxico\nBIORAG_RERANKING_JACCARD_ENABLED=0 python3 scripts/evaluar_qa.py\n\n# 3. Sin PPMI+SVD (señal vectorial apagada)\nBIORAG_PPMI_WEIGHT=0.0 python3 scripts/evaluar_qa.py\n\n# 4. Sin Retrofitting de Grafo Hebbiano (solo PPMI puro)\npython3 scripts/ppmi_svd_retro.py --eval --no-retrofit\n\n# 5. Sin DMN (ideación en reposo desactivada)\nBIORAG_DMN_IDLE_SECONDS=999999 python3 scripts/evaluar_qa.py\n\n# 6. Sin la guardia de orden monotónico (v30.1) — solo para reproducir el bug\nBIORAG_ORDEN_MONOTONICO=0 python3 scripts/evaluar_qa.py\n```\n\n**Gate de regresión de la suite QA (v30.1):** `scripts/evaluar_qa.py` escribe\n`scripts/qa_metrics.json` (cifras machine-readable, listas para diff/CI) y termina en\n`exit 1` si la corrida queda por debajo de la baseline oficial. Es lo que faltaba: hasta\nv30.0 el evaluador siempre salía en 0 y, con `set -e` en `run_qa_suite.sh`, la suite\nimprimía \"FINALIZADA CON ÉXITO\" aunque el Recall@5 global hubiera caído de 97.39 % a\n95.23 % (+19 fallos), que es exactamente lo que pasó el 2026-09-02.\n\n```bash\n# Umbrales por defecto = baseline oficial 2026-08-26 (Recall@5 97.39 %, 23 fallos, 0 % FP)\n./scripts/run_qa_suite.sh\n\n# Corrida exploratoria (ablación, experimento): apagar el gate\nBIORAG_QA_GATE=0 ./scripts/run_qa_suite.sh\n\n# Relajar umbrales puntualmente\nBIORAG_QA_MIN_RECALL5=95.0 BIORAG_QA_MAX_FALLOS=42 ./scripts/run_qa_suite.sh\n\n# Fijar una corrida buena como nueva baseline de comparación por categoría\ncp scripts/qa_metrics.json scripts/qa_metrics_baseline.json\n```\n\n| Variable | Propósito | Default |\n|---|---|---|\n| `BIORAG_QA_GATE` | Apagar/encender el gate de regresión | `1` |\n| `BIORAG_QA_MIN_RECALL5` | Recall@5 global mínimo (%) | `97.0` |\n| `BIORAG_QA_MAX_FALLOS` | Máximo de fallos de recuperación | `23` |\n| `BIORAG_QA_MAX_FP_RATE` | Máximo % de FP sobre negativos válidos | `0.0` |\n| `BIORAG_QA_MAX_REGRESION_PP` | Caída máxima por categoría vs. baseline (pp) | `2.0` |\n| `BIORAG_QA_BASELINE` | Archivo de baseline en `scripts/` | `qa_metrics_baseline.json` |\n| `BIORAG_QA_METRICS` | Salida de métricas JSON | `scripts/qa_metrics.json` |\n| `BIORAG_QA_RESOLVER_ETIQUETAS` | Resolver etiquetas oro obsoletas contra la DB evaluada | `1` |\n| `BIORAG_QA_ETIQUETA_DIFUSA` | Ratio mínimo de la resolución difusa de etiquetas | `0.94` |\n| `BIORAG_QA_ETIQUETA_MARGEN` | Margen mínimo sobre el segundo candidato | `0.02` |\n| `BIORAG_QA_NEURONA_TOL_CASOS` | Casos en 1er lugar que la neurona puede perder por categoría (solo si actuó; además R@5 idéntico y total en 1er lugar sin bajar) | `1` |\n| `BIORAG_QA_DB_VIVA` | DB viva cuya neurona se lee (solo lectura) para el aviso de la QA | `MemoryBioRAG_Data/memory_biorag.db` |\n| `BIORAG_QA_ORIGEN` | Base original que se mide (lo pone `run_qa_suite.sh`, porque la QA solo ve la copia) | `BIORAG_PATH` |\n| `BIORAG_ORDEN_MONOTONICO` | Re-sort final de `buscar_por_frase` por score | `1` |\n\n**Implementación real de cada mecanismo:**\n\n| Mecanismo | Nombre Técnico Real | Implementación | Variable de Ablación |\n|---|---|---|---|\n| **GABA** | Inhibición Lateral de Atractor (Edelman 1987) | Si top-1 score ≥ 0.80, atenúa competidores con score < top_score×0.70 por factor ×0.60 | `BIORAG_GABA_ACTIVO=0` |\n| **Retrofitting Hebbiano** | Graph-Constrained Vector Retrofitting (Faruqui 2015) | Promedia vectores PPMI con vecinos de sinapsis `sinonimo_explicito` — λ=0.2, 5 iters | `--no-retrofit` en ppmi_svd_retro.py |\n| **PPMI+SVD** | Pointwise Mutual Information + Truncated SVD 100 dims | Coseno entre vector IDF-weighted de query y vectores espectrales de nodos (señal #13) | `BIORAG_PPMI_WEIGHT=0.0` |\n| **Re-ranking Jaccard** | Léxico Rescue (Jaccard léxico de tokens sobre head del ranking) | `score + 0.25 × (jaccard/max_j)` sobre top-20, con protect-r0 | `BIORAG_RERANKING_JACCARD_ENABLED=0` |\n| **LTP/LTD Modulado ACT-R** | Base-Level Activation & Power Law (Anderson & Lebiere 1998) | $B_i = \\ln(\\sum t_k^{-0.5})$ en `nodo_accesos_historial` (máx 10); modula decaimiento LTD en sueño $\\text{decay} = 0.05 \\cdot e^{-0.5 B_i}$ | `BIORAG_NO_LOG=1` / DB |\n| **Concept Hubs Semánticos** | Categorización Prototípica y 5 Ángulos (Rosch 1975, Collins 1969) | Puentes ontológicos deterministas pre-FTS5 (`sinonimo`, `problema`, `solucion`, `situacion`, `ingenuo`) para abismo léxico y cruce ES/EN | Tablas `concept_hubs` / `concept_hub_bridges` |\n| **Efecto Fan ACT-R** | Fan Effect Attentional Modulation (Anderson 1974) | Ponderación asociativa multi-salto dividida entre el abanico de conectividad saliente ($W_j/\\text{fan}_j$) en `_evocacion_por_cadena()` | Nativo en BFS sináptico |\n| **Calibración Conforme** | Split Conformal Prediction & Platt Scaling (Vovk 2005, Platt 1999) | Umbral de corte no paramétrico calculado con garantía estadística finita $1-\\alpha$ sobre controles negativos y sigmoide | Tabla `calibracion_estado` |\n| **Saturación Dinámica BM25** | Dynamic BM25 Normalization (Robertson & Zaragoza 2009) | Función monótona de saturación $S(x) = \\frac{x}{x+k}$ para consultas singleton en `_calcular_score_hibrido()` | Nativo en scoring híbrido |\n| **DMN** | Default Mode Network (spindles replay en reposo) | Daemon thread que genera insights cruzando nodos distantes cuando el agente lleva ≥5 min inactivo | `BIORAG_DMN_IDLE_SECONDS=999999` |\n\n> **Nota sobre LTP/LTD y Activación ACT-R:** La suite QA de 921 casos corre con `ignore_peso_sinaptico=True` por diseño metodológico de campo nivelado. La activación base ACT-R ($B_i$) y la modulación de decaimiento biológico pasivo operan en el ciclo de consolidación de sueño y en la interacción continua en vivo.\n\n**Resultados Empíricos del Estudio de Ablación (921 casos QA, snapshot congelado):**\n\n| Escenario de Ablación | GLOBAL R@5 | `por_tema` R@5 | `sinonimo` R@5 | Frecuencia de Activación / Comportamiento |\n|---|---|---|---|---|\n| **v32.0 Consolidado (Actual)** | **99.31%** | **92.31%** | **100.00%** | **Récord histórico oficial: solo 6 fallos en 875 consultas (0.0% FP)** |\n| **Baseline histórico v26 (todos ON)** | 95.57% | 84.62% | 70.49% | Punto de referencia calibrado original |\n| **Sin GABA (inhibición lateral OFF)** | 95.57% (0.0pp) | 84.62% (0.0pp) | 70.49% (0.0pp) | **Se activa en el 68.2% de las búsquedas** (601/881 casos con top-1 ≥ 0.80). Atenúa competidores (×0.60) en 62.9% de las queries (554/881) sin alterar la membresía Top-5 en Recall@5. |\n| **Sin Re-ranking Jaccard** | **94.67% (-0.90pp)** | **67.69% (-16.93pp)** | 72.13% (+1.64pp) | **Aporte crítico (+16.93pp por_tema):** Mecanismo principal que rescata candidatos hundidos por ruido semántico. |\n| **Sin PPMI+SVD (weight=0.0)** | 95.46% (-0.11pp) | 84.62% (0.0pp) | 70.49% (0.0pp) | Aporte vectorial espectral fino en el score de desempate global. |\n| **Sin DMN (idle=999999s)** | 95.57% (0.0pp) | 84.62% (0.0pp) | 70.49% (0.0pp) | Daemon asíncrono de reposo (no participa en la ruta caliente de consulta). |\n\n> **Análisis Objetivo de Ablación:** \n> 1. **Re-ranking Jaccard:** Es la señal con mayor impacto medible en el benchmark (**+16.93pp en `por_tema`** y **+0.90pp global**), demostrando que el re-sorting de tokens sobre el *head* rescata ítems que las señales complejas hunden.\n> 2. **GABA (Inhibición Lateral):** Se activa en el **68.2% de las consultas de búsqueda** (601/881 casos donde el top-1 es un atractor fuerte con score ≥ 0.80), aplicando atenuación efectiva a los competidores secundarios en el **62.9% de los casos** (554/881). Dado que atenúa pero no elimina a los ítems 2–5, la composición del conjunto Top-5 se mantiene intacta, registrando 0.0pp de impacto en Recall@5. Su beneficio se manifiesta en la dominancia del Top-1 para prompts posteriores.\n> 3. **DMN y PPMI+SVD:** DMN es un proceso asíncrono de trasfondo (ideación y consolidación fuera del path de consulta), mientras que PPMI+SVD aporta un refinamiento espectral sutil (-0.11pp al desactivarse) en el desempate semántico.\n\n> [!IMPORTANT]\n> **Aclaración Metodológica para Evaluadores:**\n> * **¿Por qué la tasa de activación GABA es 68.2% en el snapshot determinista?**  \n>   En la reconstrucción determinista desde cero (`generar_snapshot.py`), el pipeline de ingesta pública (`percibir_corto_plazo` + `consolidar_concepto`) asegura que todos los 487 conceptos literales se ingresen con estado activo y peso sináptico calibrado, activando GABA en el 100% de los casos literales (487/487). Esto eleva la frecuencia global del 60.3% inicial al **68.2% (601/881 consultas)**.\n> * **¿Por qué GABA da 0.0pp en Recall@5 pese a activarse en el 68.2% de las búsquedas?**  \n>   En el benchmark Cranfield (*known-item search*), Recall@5 mide únicamente la presencia del concepto esperado dentro de los 5 primeros puestos. Cuando el Top-1 alcanza un score ≥ 0.80, GABA atenúa los scores de las posiciones 2 a 5 por un factor de ×0.60. Esta reducción de puntuación suprime la interferencia secundaria pero no altera el orden de presencia del Top-5, resultando en 0.0pp de variación en el benchmark sintético. Su función estructural es concentrar la dominancia en el Top-1 para evitar distracción contextual en el prompt del LLM.\n> * **¿Por qué la DMN da 0.0pp en la búsqueda directa?**  \n>   La Red por Defecto (DMN) opera como un proceso asíncrono nocturno o de reposo (ideación y mantenimiento de sinapsis en segundo plano). No participa en la ruta caliente (*hot path*) de una consulta directa, por lo que su desactivación no afecta la recuperación inmediata.\n\n\n\n\n---\n\n### Tabla Resumen de la Suite Completa\n\n| Test | Script | Casos | Resultado esperado | Tiempo aprox. |\n|---|---|---|---|---|\n| Suite biológica | `test_memory.py` | 112 tests | 112/112 ✔ | ~30s |\n| QA Cranfield | `scripts/evaluar_qa.py` | 921 casos | R@5 95.6% global | ~9 min |\n| PPMI 3 gates | `scripts/ppmi_svd_retro.py --eval` | 35 casos | por_tema gate ✔ | ~10s |\n| Adversariales | `scripts/fuzz_qa.py` | 33 casos | 33/33 ✔ | ~15s |\n| Concurrencia | `scripts/concurrencia_qa.py` | 60 ops | 0 colisiones ✔ | ~5s |\n| Escala | `scripts/escala_qa.py` | 4 volúmenes | O(log N) BM25 ✔ | ~3 min |\n| Ablation Jaccard | `scripts/experimento_faseB_holdout.py` | 921 casos | +13.85pp por_tema ✔ | ~2 min |\n| SDM QBE | `tests/test_sdm_query_by_example.py` | unitario | todos ✔ | ~5s |\n| **Ablación mecanismos** | **`scripts/ablacion_mecanismos.py`** | **921 × 5 configs** | **Tabla de contribución** | **~50 min** |\n| **Medición GABA** | **`scripts/medir_gaba_activacion.py`** | **881 búsquedas** | **68.2% activados (JSON)** | **~30s** |\n\n> **Determinismo verificado:** 4 corridas consecutivas idénticas → misma tabla. `random.seed(42)` en generación de casos QA.\n\n---\n\n\n\n## 🏗️ Arquitectura del Motor — 13 Señales + PPMI+SVD (núcleo, vigente desde v26.1)\n\nLa versión v26.1 integra un motor vectorial espectral (PPMI+SVD de 100 dimensiones + Retrofitting de Grafo Hebbiano) sobre el pipeline de 12 señales que existía en v25.x. El siguiente diagrama refleja el núcleo de ranking en producción (v26.1 en adelante):\n\n```mermaid\ngraph TD\n    A[\"Consulta del Usuario\"] --> B[\"Tokenización + Stemming Bilingüe ES/EN\"]\n    B --> C[\"Ventana de Atención — Context Window\"]\n    B --> D[\"Pipeline Híbrido — 13 Señales\"]\n\n    subgraph Engine_v261 [\"BioRAG — Motor Híbrido PPMI+SVD+Retrofit\"]\n        D --> D1[\"1. BM25 / FTS5 · w=0.25\"]\n        D --> D2[\"2. Dimensiones Semánticas · w=0.14\"]\n        D --> D3[\"3. PPMI+SVD Coseno · w=0.15\"]\n        D --> D4[\"4. IDF-Synonym Specificity · dinámico\"]\n        D --> D5[\"5. Concepto Match · w=0.08\"]\n        D --> D6[\"6. Sinónimos Ratio · w=0.08\"]\n        D --> D7[\"7. LTP/LTD Sináptico · w=0.10\"]\n        D --> D8[\"8. SLS Inferencia Transitiva · w=0.10\"]\n        D --> D9[\"9. WordNet Grupos Léxicos · w=0.10\"]\n        D --> D10[\"10. Score Temático · w=0.08\"]\n        D --> D11[\"11. JSD Distribucional · w=0.04\"]\n        D --> D12[\"12. Predicados SRL · w=0.20\"]\n        D --> D13[\"13. Temporal / Recencia · w=0.02\"]\n    end\n\n    D1 & D2 & D3 & D4 & D5 & D6 & D7 & D8 & D9 & D10 & D11 & D12 & D13 --> E[\"Score Híbrido Ponderado\"]\n    C --> |\"Boost +0.05\"| E\n    E --> F[\"Inhibición Lateral GABA — atenúa ×0.60 si Top-1 ≥ 0.80\"]\n    F --> G[\"Re-ranking Jaccard Léxico — rescata candidatos hundidos\"]\n    G --> H[\"Retrofitting de Grafo Hebbiano — λ=0.2, 5 iters\"]\n    H --> I[\"Ranking Final — 14 capas en cascada\"]\n```\n\n> **Nota histórica:** el diagrama de la arquitectura base de v19.0 (8 señales cognitivas originales)\n> se preserva en la sección [Motor Cognitivo Biomimético Integrado — v19.0](#motor-cognitivo-biomimético-integrado--v190),\n> ya que es el punto de partida de la evolución arquitectónica del sistema.\n>\n> **Sobre v28.0 (vigente):** el diagrama arriba describe el núcleo de ranking (Canal 1).\n> v28.0 agrega por encima el **Canal 2** (asociaciones_enriquecidas del grafo sináptico real, sin tocar\n> `score_hibrido`), el **QCR Gate** (puerta de cobertura de consulta, umbral de capa 0.60) y la señal\n> **ADN Conceptual (v29, APAGADA por defecto)** — descritos en la sección\n> [Canal 2 Integrado](#-canal-2-integrado-asociaciones-enriquecidas-del-grafo-sináptico-real-v281).\n\n---\n\n## Qué es BioRAG (y qué NO es)\n\nBioRAG se ubica en la intersección de cuatro disciplinas científicas:\n\n| Disciplina | Rol en BioRAG |\n|---|---|\n| **Information Retrieval** | Pipeline de cascade ranking de 13 capas con 12 señales de scoring híbrido (Learning-to-Rank manual) |\n| **Knowledge Graphs** | Grafo de sinapsis tipadas y pesadas con plasticidad negativa, inferencia transitiva y auto-clustering |\n| **Cognitive Architecture** | Ciclos de sueño, LTP/LTD, spreading activation, poda sináptica, inhibición lateral (ACT-R, Hebb, Marr) |\n| **Symbolic NLP** | Expansión semántica sin embeddings: Levenshtein normalizado + WordNet bilingüe + traducción opcional |\n\n**BioRAG NO es:**\n- **No es un RAG con embeddings externos** — RAG (Retrieval-Augmented Generation) típico recupera chunks con embeddings preentrenados (OpenAI, sentence-transformers). BioRAG no usa embeddings de modelos externos: su espacio vectorial se **entrena localmente** sobre el corpus propio (PPMI+SVD, sin redes neuronales ni GPU).\n- **No es una base de datos vectorial externa** — BioRAG **SÍ tiene motor vectorial propio** (PPMI+SVD de 100 dimensiones + SDM de 2048 bits + HDC). Lo que no usa son bases vectoriales comerciales/embeddings de caja negra (Pinecone, pgvector, FAISS, etc.). Sus vectores son entrenados, explicables y auditarles. Además de los vectores espectrales, los 13 ejes dimensionales forman un sparse embedding declarativo: la misma idea que un vector, pero con valores que un humano define, inspecciona y audita.\n- **No es un LLM** — BioRAG no genera texto. Es el sistema de memoria que un LLM usa para recordar entre sesiones.\n- **No es un prototipo académico** — Es un sistema de producción con 16 tests pytest + benchmark de 921 casos, ~20 MB RAM, latencia de 2.84ms.\n\n---\n\n## 🔬 Fundamentos Científicos\n\nBioRAG no implementa una técnica aislada — sintetiza **treinta mecanismos científicos** de campos distintos (recuperación de información, neurociencia computacional, lingüística computacional, sistemas dinámicos y arquitecturas cognitivas) en un único motor cognitivo determinista. Cada uno de los siguientes componentes está implementado y verificable en el código fuente, no es aspiracional:\n\n| Mecanismo | Fundamento científico | Dónde vive en el código | Para qué se usa |\n|---|---|---|---|\n| **PPMI / Word2Vec Duality (Shifted PPMI)** | Levy & Goldberg (2014); Mikolov et al. (2013) | `core/ppmi_vectorizer.py` | Justificación matemática de que la matriz PPMI desplazada equivale a los embeddings de Word2Vec de Google, logrando espacio vectorial semántico sin redes neuronales ni GPU. |\n| **LSA — Latent Semantic Analysis (Truncated SVD)** | Landauer & Dumais (1997); Deerwester et al. (1990) | `core/ppmi_vectorizer.py` | Reducción de dimensionalidad espectral (100 Dims) sobre la matriz de co-ocurrencia para capturar sinonimia limpia y relaciones semánticas latentes de 2º orden. |\n| **Fusión híbrida multi-señal (13 señales)** | Diseño propio — combina BM25, Jaccard, PPMI coseno, JSD, predicados SRL y 8 señales más | `core/memory_store.py` — `_score_hibrido()` | Fusionar 13 señales ortogonales (léxica, vectorial, dimensional, sináptica, temporal) en un score único ponderado para cada resultado de búsqueda. |\n| **HDC — Hyperdimensional Computing (VSA Binding)** | Kanerva (2009); Smolensky (1990) | `core/sdm.py` | Enlazar vectorialmente roles semánticos SRL (Sujeto-Acción-Objeto-Contexto) mediante operaciones ortogonales para búsquedas relacionales lógicas. |\n| **Ley de Potencia de Práctica de ACT-R (Base-Level Activation)** | Anderson & Lebiere (1998); Newell & Rosenbloom (1981) | `core/memory_store.py` — `_calcular_base_level_actr()` y `ciclo_sueno_consolidacion()` | Modulación no lineal del decaimiento pasivo (LTD) en sueño según la recencia y frecuencia de acceso $B_i = \\ln(\\sum t_k^{-0.5})$ con búfer circular, inmunizando recuerdos activos. |\n| **Concept Hubs Semánticos de 5 Ángulos** | Rosch (1975); Collins & Quillian (1969); Vygotsky (1934) | `core/concept_hub.py` + esquemas `concept_hubs` / `concept_hub_bridges` | Superar el abismo léxico y cruces semánticos bilingües (ES/EN) mediante puentes estructurados en 5 ángulos cognitivos ortogonales sin depender de modelos densos. |\n| **Efecto Fan de ACT-R en Spreading Activation** | Anderson (1974); Anderson & Reder (1999) | `core/memory_store.py` — `_evocacion_por_cadena()` | Ponderación de la activación propagada dividida por el abanico de conectividad saliente ($W_j/\\text{fan}_j$), evitando la saturación espuria por nodos hub y acelerando la exploración. |\n| **Calibración Conforme Inductiva (Conformal Prediction & Platt Scaling)** | Vovk, Gammerman & Shafer (2005); Platt (1999); Angelopoulos & Bates (2021) | `core/calibracion.py` + `calibracion_estado` | Umbrales no paramétricos con garantía matemática de error finito ($1-\\alpha$) para control estricto de falsos positivos (0.0% FP) y calibración probabilística en MCP. |\n| **Curva del Olvido (Decaimiento Pasivo LTD)** | Ebbinghaus (1885) | `core/memory_store.py` — `ciclo_sueno_consolidacion()` | Aplicar la atenuación temporal pasiva sobre recuerdos no utilizados durante la consolidación de sueño. |\n| **PMI (Pointwise Mutual Information)** | Church & Hanks (1990) | `core/pmi_semantico.py` | Medir qué tan asociados están dos conceptos por co-ocurrencia real en el corpus, y usar eso como señal para auto-vincular nodos nuevos al guardarlos. |\n| **SDM — Sparse Distributed Memory (2048-bit)** | Kanerva (1988) | `core/sdm.py` | Recuperación asociativa por parecido, no por coincidencia exacta — encontrar un recuerdo aunque la consulta esté incompleta o levemente distinta. |\n| **Retrofitting de grafo semántico** | Faruqui et al. (2015) | `core/sinapsis.py` | Ajustar el espacio vectorial PPMI+SVD usando la topología real del grafo de sinapsis, para que conceptos conectados queden más cerca entre sí. |\n| **LTP/LTD (potenciación y depresión sináptica)** | Hebb (1949); Bliss & Lømo (1973) | `core/sinapsis.py` | Reforzar automáticamente los recuerdos que se reutilizan y debilitar los que no se tocan, para que la memoria \"olvide\" lo irrelevante con el tiempo. |\n| **Consolidación de memoria (corto → largo plazo)** | Marr (1971) | `core/memory_store.py` — `ciclo_sueno_consolidacion()` | Decidir qué pasa de memoria de trabajo temporal a memoria permanente al cierre de una sesión (\"fase de sueño\"). |\n| **Spreading activation multi-hop** | Anderson (1983) — ACT-R | `core/memory_store.py` — `_evocacion_por_cadena()` | Encontrar conceptos relacionados indirectamente, siguiendo la cadena del grafo desde una semilla, no solo coincidencias directas. |\n| **Inhibición lateral GABA en tiempo real** | Edelman (1987) | `core/memory_store.py` — `buscar_por_frase()` | Cuando el resultado Top-1 domina (score ≥ 0.80), atenuar competidores secundarios (×0.60) si están muy por debajo del líder, reduciendo ruido sin vectores densos. |\n| **Error de predicción de recompensa (dopamina/RPE)** | Schultz (1997) | `mcp_server.py` — `biorag_feedback()` | Que el propio agente le diga al sistema \"esto sirvió / no sirvió\" y el peso del recuerdo suba o baje en consecuencia — aprendizaje por refuerzo explícito. |\n| **Marcador somático / valencia cortical** | Damasio (1994) | `core/memory_store.py` + `core/dmn_engine.py` | Marcar recuerdos como emocionalmente/estratégicamente importantes; los de alta valencia quedan inmunes al olvido pasivo y son ancla para ideación autónoma del DMN. |\n| **Escalado sináptico homeostático** | Turrigiano (2008) | `core/memory_store.py` | Evitar que los pesos sinápticos se saturen todos en 1.0 — normaliza la corteza activa cuando el promedio supera 0.70, para que el sistema siga pudiendo aprender. |\n| **Léxico generativo (eje \"cualia\")** | Pustejovsky (1995) | `core/memory_store.py` — dimensiones semánticas | Clasificar el significado profundo de un nodo según su naturaleza, composición, propósito u origen (estructura de cualia). |\n| **Evidencialidad (eje \"epistemia\")** | Aikhenvald (2004) | `core/memory_store.py` — dimensiones semánticas | Etiquetar la fuente de verdad de cada recuerdo: ¿lo viví, lo verifiqué, lo inferí, me lo contaron, es hipótesis o quedó obsoleto? Evita que un rumor pese igual que un hecho comprobado. |\n| **Efecto de autorreferencia (centralidad identitaria)** | Rogers, Kuiper & Kirker (1977) | `core/memory_store.py` — dimensiones semánticas | Priorizar recuerdos centrales a la identidad/rol del agente por sobre información externa impersonal, al momento de recuperar. |\n| **Modalidad deóntica** | Palmer (2001) | `core/memory_store.py` — dimensiones semánticas | Distinguir reglas obligatorias, prohibiciones, permisos y capacidades — clave para que el agente no confunda una sugerencia con una norma. |\n| **Clasificación léxica ontológica** | WordNet — Miller (1995) | `core/clasificador_wordnet.py` | Resolver búsquedas donde las palabras no coinciden literalmente pero significan lo mismo (ej. \"decodificar jerga\" ≈ \"traducir lenguaje críptico\"), sin embeddings. |\n| **SRL — Semantic Role Labeling (Gramática de Casos)** | Fillmore (1968); Palmer et al. (2005) | `core/srl_extractor.py` + `core/memory_store.py` — `buscar_por_predicados()` | Extraer tripletas de predicados (Sujeto, Acción, Objeto, Contexto) de cada recuerdo para permitir búsquedas relacionales exactas por rol semántico (ej. `sujeto:Dennys`, `accion:crear`). |\n| **DMN — Red por Defecto & Spindles Replay (Ideación Autónoma)** | Raichle et al. (2001); Buckner et al. (2008) | `core/dmn_engine.py` + `core/dmn_reflexion.py` | Ejecutar en segundo plano durante inactividad un recorrido de 2-3 saltos desde nodos ancla de alta valencia para sintetizar hipótesis e insights autónomos sin intervención del usuario. |\n| **Separación de Patrones Hipocampal (Pattern Separation)** | Marr (1971); O'Reilly & McClelland (1994) | `core/inferencia_transitiva.py` | Validar inferencias transitivas de 2-3 saltos exigiendo convergencia dual (PMI + dimensión semántica) para evitar conexiones falsas por dimensiones genéricas compartidas (\"problema hub\"). |\n| **Divergencia Jensen-Shannon (JSD)** | Lin (1991) | `core/memory_store.py` — `_score_hibrido()` (Señal #11) | Medir el solapamiento distribucional de vocabulario entre la consulta y el recuerdo en una escala simétrica acotada [0, 1], como señal de similitud complementaria al coseno. |\n| **Label Propagation (Detección de Comunidades)** | Raghavan et al. (2007) | `core/auto_clustering.py` — `detectar_comunidades()` | Detectar automáticamente grupos de nodos densamente interconectados en el grafo de sinapsis y asignarles un nombre temático generado por frecuencia de tokens. |\n| **Distancia de Levenshtein (tolerancia a errores tipográficos)** | Levenshtein (1966) | `core/fallback_simbolico.py` | Recuperar recuerdos aunque la consulta tenga errores ortográficos, variantes morfológicas o palabras parciales, calculando la distancia de edición mínima entre tokens. |\n\n**Todo lo anterior corre en Python puro + SQLite, con cero dependencias de embeddings densos, GPU o APIs de LLM en el camino de recuperación.** El objetivo no es competir con la escala de un modelo preentrenado — es demostrar que memoria persistente, auditable y explicable, con fundamento en literatura de neurociencia cognitiva e IR clásico, es posible sin ellas.\n\n> *Nota de honestidad intelectual: ninguna de estas técnicas individuales es una invención de este proyecto — son bien conocidas en sus respectivos campos, algunas con más de 70 años. La contribución de BioRAG es la síntesis: hacerlas coexistir en un solo sistema cerrado, determinista y funcional, algo que no encontramos replicado en ningún otro proyecto open-source de memoria para agentes de IA.*\n\n---\n\n## Auditoría Técnica Completa — Módulos del Core (v26.1)\n\n### Escala Real del Código\n\n| Archivo | Bytes | Rol |\n|---|---|---|\n| `core/memory_store.py` | 286,079 | Motor cognitivo — 13 señales, 14 capas, ciclo de sueño, LTP/LTD, QCR Gate |\n| `mcp_server.py` | 191,729 | Interfaz MCP — 33 herramientas expuestas al IDE |\n| `core/dmn_reflexion.py` | 94,285 | Red por Defecto extendida + La Hormiguita (evaluación con Gemini) |\n| `core/sinapsis.py` | 22,976 | Grafo: auto-linking, LTP/LTD, decay sináptico |\n| `core/similitud_conceptual.py` | 19,920 | Jaccard vecinos + contenido, score 60/40 |\n| `core/pmi_semantico.py` | 12,829 | PMI/NPMI + LRU cache (v26.1) |\n| `core/stemmer_es.py` | 13,718 | Stemmer bilingüe ES/EN ultraligero |\n| `core/ppmi_vectorizer.py` | 13,718 | Motor PPMI+SVD+Retrofitting (v26.0) — DIM=100, λ=0.2 |\n| `core/inferencia_transitiva.py` | 13,448 | SLS: CTEs recursivas + filtro dual PMI/Dimensión |\n| `core/sdm.py` | 21,005 | SDM 2048-bit + HDC Binding (Kanerva 1988) |\n| `core/fallback_simbolico.py` | 14,612 | Fallback simbólico: Levenshtein + WordNet bilingüe |\n| `core/auto_clustering.py` | 10,532 | Label Propagation — comunidades automáticas del grafo |\n| `core/dmn_engine.py` | 11,534 | DMN daemon thread — ideación en reposo (v21.0) |\n| `core/srl_extractor.py` | 4,194 | SRL: Sujeto-Acción-Objeto-Contexto |\n| `graph_maintenance_daemon.py` | 17,383 | La Hormiguita — daemon de mantenimiento del grafo |\n| `middleware/auto_guardado.py` | 2,174 | Buffer de sesión + autoguardado heurístico |\n\n---\n\n### 🛡️ Patrón de Gobierno de IA con \"Default Deny\" (`core/dmn_reflexion.py` — La Hormiguita)\n\n* **¿Por qué se creó? (El Por qué):** Permitir que un LLM (ej. Gemini) escriba, modifique o borre directamente registros en la base de datos de memoria permanente es un riesgo crítico de seguridad. Las alucinaciones, respuestas fuera de esquema o JSONs truncados pueden corromper el grafo de sinapsis o destruir recuerdos valiosos.\n* **¿Para qué sirve? (El Para qué):** Sirve para aprovechar el juicio analítico avanzado de un LLM en tareas de mantenimiento del grafo de memoria (sanación y poda de sinapsis obsoletas) de forma 100% segura, protegiendo la base de datos contra corrupción o pérdidas de información accidental.\n* **Propósito en Producción (El Propósito):** Garantizar la **integridad absoluta del sistema de memoria** mediante una arquitectura de gobierno **Zero-Trust**:\n\n1. **Gemini como mero asesor (Proposal-Only):** El LLM nunca ejecuta sentencias SQL ni muta la base de datos directamente. Solo recibe un payload estructurado de candidatos pre-filtrados y emite un veredicto en JSON.\n2. **Pre-filtrado Determinista:** Antes de gastar tokens llamando a la API, el motor Python (`_pre_filtrar_conexiones`) evalúa >1,000 candidaturas mediante distancia de Hamming en SDM y score de co-ocurrencia hebbiana, reduciéndolas a ~60 candidatos dudosos que realmente requieren juicio experto.\n3. **Default Deny / Zero-Trust Safety:**\n   - Si la API del LLM falla, expira por timeout, o el JSON retornado contiene errores de sintaxis, el sistema aplica **Default Deny**: aborta con `return False` y **0 mutaciones en la DB**.\n   - Si el veredicto es válido, no se aplica ciegamente: pasa por un evaluador estricto (`_aplicar_veredicto_nodo`) que exige umbrales de confianza diferenciados: `UMBRAL_CONFIANZA_ACEPTAR` (para adiciones) y `UMBRAL_CONFIANZA_ELIMINAR` (para podas).\n4. **Cuarentena Reversible (`sinapsis_cuarentena`):** Las conexiones podadas no se destruyen inmediatamente. Se transfieren a la tabla relacional `sinapsis_cuarentena`, registrando el motivo, confianza, timestamp y estado previo. Esto permite auditoría forense y rollback completo en cualquier momento vía `_restaurar_cuarentena()`.\n\n---\n\n### 🔬 Memoria Dispersa Binaria de 2048 Bits (`core/sdm.py` — Kanerva 1988 + HDC)\n\n* **¿Por qué se creó? (El Por qué):** Los embeddings densos tradicionales (ej. 1536 dimensiones de 32 bits) consumen 6,144 bytes por nodo, requieren GPUs o librerías pesadas como PyTorch/FAISS, y mezclan toda la información en un espacio vectorial indivisible donde es imposible saber qué componente representa texto, categoría o topología de grafo.\n* **¿Para qué sirve? (El Para qué):** Sirve para ejecutar búsquedas de similitud conceptual hiperdimensional en microsegundos (<1.5 ms en 800+ nodos) utilizando CPU pura en cualquier dispositivo (sin GPUs ni dependencias externas), discriminando con precisión quirúrgica entre coincidencias de texto superficiales y relaciones Hebbianas profundas.\n* **Propósito en Producción (El Propósito):** Proveer un **hiperespacio binario ultra-eficiente de 2048 bits (256 bytes por nodo)** que combina semántica hebbiana, texto y topología con rendimiento en CPU pura (<1.5 ms en 800+ nodos).\n\n**Particionado Estructurado del Vector SDM (2048 bits = 256 bytes):**\nA diferencia de un hash disperso uniforme (que mezcla tokens al azar), el vector de 2048 bits en BioRAG v2.0 tiene un **layout de memoria explícito por capa de información**:\n\n| Rango de Bits | % Vector | Segmento | Propósito y Codificación |\n|---|---|---|---|\n| `bits 0..511` | 25.0% | **Tokens de Contenido** | Ventanas hash-mapped de 4 bits por token extraído del contenido. |\n| `bits 512..767` | 12.5% | **Tokens de Concepto** | Ventana de 4 bits por token del identificador principal del nodo. |\n| `bits 768..1791` | **50.0%** | **Dimensiones Hebbianas** | Clusters Hebbianos + dimensiones cualia/epistemia (8–16 bits por dimensión, ponderadas por IDF). Captura la semántica profunda. |\n| `bits 1792..1919` | 6.25% | **Categoría Estructurada** | Ventana determinista de 8 bits correspondiente al catálogo de categorías del cerebro. |\n| `bits 1920..2047` | 6.25% | **Vecindario Sináptico** | Ventana de 4 bits por concepto vecino interconectado en el grafo. |\n\n**Jaccard Ponderado por Capa Semántica:**\nAl calcular la similitud en el hiperespacio entre dos vectores binarios de 2048 bits, los bits no pesan lo mismo:\n```python\nPESO_TOKEN = 1.0        # Solapamiento léxico básico\nPESO_CATEGORIA = 1.5    # Afinidad estructural\nPESO_VECINO = 1.2       # Afinidad topológica en el grafo\nPESO_DIMENSION = 2.5    # Semántica Hebbiana profunda (pesa 2.5× para guiar el retrieval)\n```\n\n**Rendimiento:** Al operar con representaciones binarias compactas, la comparación de distancias sobre 800+ nodos utiliza `int.bit_count()` nativo de 64 bits en Python, consumiendo solo **256 bytes por nodo** (vs. 6,144 bytes de un embedding denso float32 de 1536 dimensiones), sin requerir FAISS ni PyTorch.\n\n---\n\n## Clasificación Simbólica WordNet — v15.0\n\nPara la versión v15.0 de BioRAG, diseñamos e implementamos una extensión ontológica basada en **WordNet** que proporciona similitud conceptual discreta y determinista, 100% offline y sin depender de bases de datos vectoriales ni de modelos de embeddings pesados.\n\n### ¿Por qué esta técnica?\n\n* **Superación de la limitación léxica**: En sistemas de búsqueda basados puramente en texto, buscar `\"decodificar jerga\"` no arroja resultados para un nodo guardado como `\"traducir lenguaje críptico\"`. WordNet resuelve esto asociando ambas acciones al mismo grupo léxico ontológico (`verb.communication`).\n* **Eficiencia extrema y 0 GPU**: Las bases de datos vectoriales tradicionales requieren cientos de megabytes de RAM, GPU para inferencia rápida, y dependencias pesadas como PyTorch o `sentence-transformers`. BioRAG mantiene su huella en ~20 MB de RAM y corre con latencia de ~2.8 ms en cualquier CPU.\n* **Autonomía y aislamiento local**: Para garantizar que la herramienta funcione en cualquier laptop o servidor sin requerir conexión a internet, empaquetamos y aislamos `nltk_data/corpora/wordnet` localmente en la ruta del proyecto (`MemoryBioRAG_Data/nltk_data`).\n\n### Arquitectura de Base de Datos y Cascada\n\nPara estructurar esta similitud simbólica sin saturar la tabla principal `largo_plazo`, creamos dos tablas relacionales especializadas con restricciones de integridad referencial rígidas:\n\n1. **`grupos_semanticos`**: Catálogo estático que indexa los 45 grupos lexicográficos oficiales de WordNet (lexnames), tales como `noun.person`, `verb.cognition`, `noun.act`, etc.\n2. **`nodo_grupos_semanticos`**: Tabla puente relacional que conecta cada concepto de largo plazo con uno o varios grupos semánticos correspondientes a sus palabras y sinónimos.\n   * Cuenta con claves foráneas hacia `grupos_semanticos(id)` y `largo_plazo(concepto)`.\n   * **`ON DELETE CASCADE`**: Para garantizar una higiene absoluta del grafo, configuramos el borrado en cascada en la clave foránea hacia la tabla `largo_plazo`. Si un concepto es eliminado de la memoria a largo plazo (por sanación o limpieza activa), sus registros de grupos semánticos asociados se eliminan de forma atómica y transparente, evitando la creación de registros huérfanos.\n\n### Cómo funciona el Algoritmo en Búsqueda y Escritura\n\n* **En Escritura (Write-Time)**: Al consolidar un concepto (ciclo de sueño), el sistema tokeniza el contenido y los sinónimos del nodo, consulta el WordNet local en milisegundos y asocia el concepto a las categorías semánticas encontradas en la tabla puente.\n* **En Lectura (Read-Time)**: Cuando el usuario realiza una búsqueda:\n  1. Se extraen y tokenizan las palabras clave de la consulta.\n  2. Se obtienen sus lexnames desde WordNet en caliente.\n  3. Se calcula la intersección de grupos semánticos usando un coseno binario o coeficiente de Jaccard (`grupo_score`).\n  4. Se inyecta este score como la **9ª señal de relevancia** con un peso del 10% en la fórmula final de ordenamiento híbrido, dando un boost semántico a conceptos relacionados aunque no compartan caracteres exactos.\n\n---\n\n## Comprensión Semántica Profunda — v16.0\n\nPara la versión v16.0 de BioRAG, incorporamos tres pilares fundamentales que dotan al sistema de una verdadera comprensión semántica a nivel relacional, lógico y estructural sin sacrificar el paradigma determinista y discreto (cero dependencias de GPU o modelos vectoriales pesados):\n\n### 1. Etiquetado de Roles Semánticos (Semantic Role Labeling - SRL)\n* **De tokens a relaciones**: En lugar de indexar palabras sueltas e independientes, SRL asocia los conceptos a estructuras semánticas de rol: **Sujeto, Acción, Objeto y Contexto** (ej: `\"Dennys desarrolla BioRAG en la oficina\"`; Sujeto: Dennys, Acción: desarrollar, Objeto: BioRAG, Contexto: oficina).\n* **Búsquedas Estructuradas**: El parámetro `buscar_por_rol` permite realizar consultas relacionales específicas (ej: `\"sujeto:Dennys,accion:desarrollar\"`), reduciendo a cero el ruido de co-ocurrencia léxica accidental.\n\n### 2. Inferencia y Transitividad en Grafos (Fuzzy Reasoning)\n* **Sinapsis Latentes**: Permite al sistema deducir relaciones indirectas implícitas en el grafo de conocimiento. Si el nodo A está conectado a B y B está conectado a C, el motor infiere de manera lógica que A tiene una conexión latente con C.\n* **Atenuación con Poda Temprana**: El peso de las sinapsis latentes decae proporcionalmente a la distancia (saltos) usando la fórmula:\n  $$\\text{peso\\_latente} = \\prod (\\text{pesos\\_camino}) \\times \\text{FACTOR\\_DECAY}^{\\text{saltos}}$$\n  con $\\text{FACTOR\\_DECAY} = 0.7$ y un cap de 3 saltos. Implementado mediante CTEs recursivas nativas de SQLite para máxima eficiencia y prevención de bucles infinitos.\n\n### 3. Autogeneración de Dimensiones Emergentes (Auto-Clustering)\n* **Label Propagation Algorithm (LPA)**: El motor analiza el grafo de sinapsis durante el ciclo de sueño y agrupa de manera autónoma los conceptos en comunidades densas (cliques de tamaño $\\ge 5$ y densidad interna $\\ge 0.3$).\n* **Dimensiones Temáticas Dinámicas**: Cada comunidad detectada genera una dimensión emergente (`auto_TOKEN1_TOKEN2_TOKEN3`) nombrada mediante los tokens más frecuentes de sus contenidos (excluyendo stopwords). Los nodos correspondientes se asocian de forma automática a este nuevo eje dimensional, permitiendo búsquedas dimensionales ponderadas por confianza.\n\n---\n\n## Motor Cognitivo Biomimético Integrado — v19.0\n\nPara la versión **v19.0 de BioRAG**, revolucionamos el paradigma de recuperación conceptual sin embeddings mediante una **Arquitectura Cognitiva Biomimética Integrada** de 5 fases. Esta versión resuelve definitivamente el dilema de la similitud asociativa y la inferencia transitiva ruidosa, logrando un ordenamiento híbrido guiado por 8 señales cognitivas ortogonales y la implementación de memoria dispersa distribuida (SDM - Sparse Distributed Memory).\n\n```mermaid\ngraph TD\n    A[Consulta del Usuario] --> B[Tokenización y Stemming Bilingüe ES/EN]\n    B --> C[Ventana de Atención / Context Window]\n    B --> D[Pipeline Híbrido de 8 Señales Cognitivas]\n    \n    subgraph Engine_v19 [BioRAG v19.0 Core Engine]\n        D --> D1[1. BM25 / FTS5 Match]\n        D --> D2[2. Similitud Topológica Jaccard]\n        D --> D3[3. PMI / NPMI Semántico Automático]\n        D --> D4[4. SLS: Inferencia Transitiva Filtrada]\n        D --> D5[5. Overlap de Dimensiones Semánticas]\n        D --> D6[6. SDM: Distancia Hamming 1024-bit]\n        D --> D7[7. Fuerza LTP/LTD de Sinapsis]\n        D --> D8[8. Bonus de Atención por Contexto]\n    end\n    \n    D1 & D2 & D3 & D4 & D5 & D6 & D7 & D8 --> E[Score Híbrido Ponderado]\n    C -->|Boost +0.05| E\n    E --> F[Ranking de Nodos Final]\n```\n\n### Arquitectura de las 5 Fases de v19.0\n\n#### Fase 1: PMI / NPMI Semántico Automático (Pointwise Mutual Information)\n* **Objetivo y Aporte**: Medir la verdadera fuerza asociativa entre pares de palabras en el corpus real del usuario, diferenciando co-ocurrencias estadísticas reales de meras coincidencias léxicas casuales.\n* **Fundamento Matemático**: Basado en Church & Hanks (1990), calcula la probabilidad conjunta $P(x,y)$ frente a las probabilidades independientes $P(x)$ y $P(y)$:\n  $$\\text{PMI}(x, y) = \\log_2 \\frac{P(x, y)}{P(x) P(y)}$$\n  $$\\text{NPMI}(x, y) = \\frac{\\text{PMI}(x, y)}{-\\log_2 P(x, y)} \\quad \\in [-1, +1]$$\n* **Implementación**: `core/pmi_semantico.py` construye una matriz de co-ocurrencia sobre los nodos activos. Mantiene un caché dinámico en RAM que se recalcula automáticamente cuando el corpus crece $>10\\%$. En las pruebas de producción con 534 nodos, procesó y evaluó **8,832 pares semánticos en solo 1,007ms**.\n\n#### Fase 2: Stemmer Bilingüe Español/Inglés Integrado\n* **Objetivo y Aporte**: Proporcionar una pasarela morfológica discreta y ultraligera que unifique variantes gramaticales y términos técnicos en inglés y español sin requerir librerías pesadas como Spacy o NLTK completas.\n* **Implementación**: `core/stemmer_es.py` aplica sufijos morfológicos adaptativos para español e inglés, reduciendo variantes léxicas a su raíz común (`\"configuración\"` $\\rightarrow$ `configur`, `\"configuration\"` $\\rightarrow$ `configur`). Esto permite que consultas formuladas en inglés o con variaciones verbales/nominales conecten de manera exacta con nodos almacenados.\n\n#### Fase 3: SLS (Sinapsis Latentes Semánticas con Filtro Involutivo)\n* **Objetivo y Aporte**: Eliminar el ~50% de \"ruido de grafo\" o falsos enlaces transitivos presentes en las versiones anteriores (v16-v18) al calcular sinapsis indirectas ($A \\rightarrow B \\rightarrow C$).\n* **Filtro Involutivo Doble**: Antes de persistir una sinapsis latente en la tabla `sinapsis_latentes`, la relación debe satisfacer al menos uno de dos criterios estrictos:\n  1. **Coincidencia Dimensional**: El nodo origen y destino comparten al menos un eje en `largo_plazo_dimensiones`.\n  2. **Asociación PMI Relevante**: El score $\\text{NPMI}(A, C) \\ge 0.02$.\n* **Ponderación Dinámica**: Las latentes que superan el filtro reciben una bonificación proporcional a su afinidad semántica:\n  $$\\text{peso\\_final} = \\min\\left(1.0, \\text{peso\\_max} \\times (1.0 + \\text{pmi\\_boost} + \\text{dim\\_boost})\\right)$$\n* **Impacto**: Redujo de 18,988 latentes ruidosas a **17,062 sinapsis latentes puras y validadas semánticamente**, eliminando alucinaciones en el razonamiento asociativo.\n\n#### Fase 4: SDM (Sparse Distributed Memory — Kanerva 1988)\n* **Objetivo y Aporte**: Permitir la búsqueda asociativa por contenido sin embeddings vectoriales ni modelos neuronales.\n* **Implementación**: `core/sdm.py` codifica cada nodo de la memoria a un **vector disperso binario de 1024 bits**, proyectando mediante funciones de hash deterministas:\n  - 500 bits: Tokens de concepto y contenido.\n  - 300 bits: Sinónimos y roles semánticos.\n  - 100 bits: Categoría del nodo.\n  - 124 bits: Vecinos y dimensiones semánticas.\n* **Almacenamiento y Recuperación**: Los vectores se persisten en SQLite en la tabla `nodos_sdm`. La búsqueda por similitud asociativa evalúa la **distancia Hamming** entre el vector de la consulta y los nodos almacenados utilizando la instrucción `int.bit_count()`. En las pruebas en vivo, indexó los 534 nodos en milisegundos y recuperó nodos por coincidencia estructural con una distancia de tan solo **14 bits de diferencia (98.63% de similitud)**.\n\n#### Fase 5: Context Window de Atención Cognitiva (Short-Term Working Memory)\n* **Objetivo y Aporte**: Implementar un búfer de memoria de trabajo a corto plazo que simula el foco de atención humana, dando prioridad a conceptos accedidos o discutidos recientemente en la sesión.\n* **Implementación**: En `core/memory_store.py`, un `deque(maxlen=10)` registra los últimos conceptos evocados (`registrar_acceso_contexto`). Si un candidato devuelto por la búsqueda se encuentra en la memoria de trabajo activa, recibe un **bonus de atención directa de +0.05** (`obtener_bonus_contexto`), garantizando continuidad conversacional y coherencia contextual.\n\n#### Fase 6: Engine de Scoring Híbrido de 8 Señales Cognitivas\n* **Fórmula de Evaluación**: `core/similitud_conceptual.py` integra 8 señales independientes para calcular la similitud conceptual latente de cada candidato:\n  1. **BM25 / FTS5 (0.20)**: Coincidencia textual ponderada.\n  2. **Jaccard Red (0.15)**: Overlap de vecinos en el grafo de sinapsis.\n  3. **PMI / NPMI Semántico (0.15)**: Fuerza de asociación estadística entre tokens.\n  4. **SLS Inferencia Transitiva (0.15)**: Peso atenuado de caminos latentes validados.\n  5. **Overlap Dimensional (0.10)**: Coincidencia en ejes temáticos declarativos.\n  6. **SDM Hamming (0.10)**: Similitud en espacio vectorial disperso de 1024 bits.\n  7. **Fuerza LTP/LTD (0.10)**: Peso acumulado por consolidación y uso histórico.\n  8. **Bonus de Atención de Contexto (+0.05)**: Impulso por presencia en la ventana de trabajo activa.\n\n#### Fase 7: Auto-Expansión Semántica Autónoma (Pre-Búsqueda en MCP)\n* **Objetivo y Aporte**: Eliminar la dependencia del \"agente disciplinado\". En las versiones previas, la calidad del recall dependía de que el agente formulara explícitamente paráfrasis y dimensiones. Si el agente ejecutaba una búsqueda simple o vaga (ej: `\"arquitectura frontend\"`), el recall decaía. Un cerebro biológico no exige que la consciencia formule sinónimos antes de recordar; el neocortex expande la señal asociativa espontáneamente.\n* **Mecanismo de Inferencia**: Cuando `biorag_recordar` recibe una consulta sin paráfrasis ni dimensiones:\n  1. Consulta la matriz de co-ocurrencia PMI ($\\text{NPMI} \\ge 0.35$) y deduce auto-paráfrasis asociativas (ej: `\"arquitectura frontend\"` $\\rightarrow$ `['angul', 'formulario', 'decodificada']`).\n  2. Realiza traversal en `largo_plazo_dimensiones` para auto-detectar las dimensiones ontológicas activas del dominio.\n  3. Inyecta estas señales en el motor de 8 señales, aumentando el score de relevancia (ej: de 0.6500 a 0.7376) automáticamente sin requerir intervención del usuario o del agente.\n* **Estado actual: APAGADA por defecto** (experimento 07, `docs/Mi neurona en el sistema/experimento_07_la_neurona_escucha/`). Subir el *score* no es encontrar mejor: medida por el camino real del agente (`recordar`), la auto-paráfrasis baja el recall (QA: R@5 no-literal 85.05 % → 95.10 % al quitarla, 39 casos entran al top-5 y 0 salen; corpus no visto python-docs-es: R@1 31.48 % → 45.68 %). Las auto-dimensiones eligen 10 ids arbitrarios y apagan el *pseudo-relevance feedback* del motor (las dimensiones de los 5 mejores FTS), que es mejor. Además eran no deterministas (`list(set)[:10]`). Reactivar: `BIORAG_MCP_AUTO_PARAFRASIS=1`, `BIORAG_MCP_AUTO_DIMS=1` (ahora deterministas). Las paráfrasis y dimensiones que manda el agente se usan siempre.\n\n#### Fase 8: Pasada 4 — Resonancia PMI Hebbiana en Auto-Vinculación (`core/sinapsis.py`)\n* **Objetivo y Aporte**: Cerrar la brecha entre Memoria Episódica (hipocampo) y Memoria Semántica (neocorteza). El sistema recordaba episodios específicos (ej. `\"Ese día implementé formularios anidados en Angular\"`), pero no clasificaba automáticamente los conceptos nuevos en sus categorías implícitas (no sabía que `Angular` $\\in$ `Frontend`).\n* **Implementación**: En el momento de guardar o consolidar un nodo (`auto_vincular()`), el sistema calcula la resonancia PMI Hebbiana entre los tokens del nodo y la matriz global de co-ocurrencia. Clasifica y vincula automáticamente el nuevo concepto a sus categorías y dominios semánticos correspondientes en el instante de su creación.\n\n#### Fase 9: Commit Atómico Unificado, Homeostasis Energética y Visibilidad Completa\n* **Objetivo y Aporte**: Garantizar la integridad transaccional del ciclo de sueño y eliminar puntos ciegos o recortes en la interfaz forense del Neuro-Visor.\n* **Implementación**:\n  1. **Commit Atómico Unificado**: `ciclo_sueno_consolidacion()` ejecuta todo el proceso (transferencia LTP, inhibición lateral LTD, cálculo PMI, optimización FTS5 y registro de métricas forenses `metricas_cognitivas_nodos`) en una sola transacción atómica de SQLite con un único `commit()` final al cierre del ciclo.\n  2. **Homeostasis Energética Cortical**: Se eliminó la dependencia de parámetros de energía externos (`limite_energia`), dejando la Inhibición Lateral Activa 100% autorregulada en función de la carga cortical real ($\\max(10.0, \\text{n\\_activos} \\times 0.8)$).\n  3. **Visibilidad Completa en Neuro-Visor**: Se actualizó el endpoint de actividad (`/api/corteza/actividad`) ampliando el límite de conceptos devueltos de `LIMIT 3` a `LIMIT 50` y ordenando por ID descendente, permitiendo inspeccionar la totalidad de los nodos consolidados en cada ciclo.\n\n#### Fase 10: BioRAG v20.0 — Circuito Sintético Cognitivamente Cerrado (GABA en Vivo, Dopamina RPE, Valencia Somática y Escalado Homeostático)\n* **Objetivo y Aporte**: Transformar el almacenamiento dinámico de BioRAG en un circuito neurobiológico cerrado autónomo y auto-homeostático, sustituyendo la similitud estática por dinámicas cognitivas inspiradas en Hebb, Schultz (1997), Damasio (1994) y Turrigiano (2008).\n* **Componentes Implementados en v20.0**:\n  1. **Inhibición Lateral GABA en Tiempo Real en Evocación (Edelman 1987)**: En `core/memory_store.py` (`buscar_por_frase`), cuando un candidato Top-1 es un atractor fuerte ($\\text{Score} \\ge 0.80$), atenúa activamente ($\\times 0.60$) a los competidores secundarios del mismo nicho semántico, enfocando la atención y eliminando el ruido sin depender de vectores.\n  2. **Error de Predicción de Recompensa (Dopamina RPE - Schultz 1997) + Factor de Inercia Sináptica**: Implementación de la tool MCP `biorag_feedback(concepto, util=True/False)`. Éxito incrementa peso ($\\Delta W = +0.15 \\times (1.0 - W \\times 0.3)$) e incrementa contador de éxitos. Fallo aplica depresión modulada por inercia sináptica: $\\Delta W_{\\text{fracaso}} = \\frac{-0.10}{1.0 + \\ln(1 + \\text{éxitos})}$, protegiendo nodos antiguos consolidados contra fallos aislados y permitiendo corrección instantánea de nodos nuevos.\n  3. **Marcadores Somáticos y Valencia Cortical (Damasio 1994)**: Schema actualizado con columna `valencia_somatica` (0.0 a 1.0) en `largo_plazo` y `corto_plazo`. Nodos con valencia $\\ge 0.80$ o categorías axiomáticas (`Principle`, `Protocol`) adquieren **Inmunidad Cortical Total**: son omitidos del decaimiento pasivo LTD (-0.05), la poda y la inhibición lateral.\n  4. **Escalado Sináptico Homeostático (Synaptic Scaling - Turrigiano 2008)**: Durante la consolidación nocturna, si el peso promedio de la corteza activa supera $0.70$, se aplica una normalización multiplicativa ($\\times 0.98$) a los nodos no inmunes, asegurando capacidad de aprendizaje ilimitada sin saturación a $1.0$.\n\n#### Fase 11: BioRAG v21.0 — Red por Defecto (Default Mode Network - DMN) y Curiosidad Espontánea Autónoma\n* **Objetivo y Aporte**: Convertir a BioRAG de un sistema puramente reactivo (que solo recupera información ante solicitudes) a una corteza sintética con ideación espontánea en reposo, emulando la Red por Defecto (DMN) humana (mind-wandering, consolidación asociativa NREM/REM y producción autónoma de hipótesis/insights).\n* **Componentes Implementados en v21.0**:\n  1. **Motor DMN en Segundo Plano (`core/dmn_engine.py`)**: Hilo autónomo daemon (`DMNEngine`) que monitorea la inactividad del usuario (`BIORAG_DMN_IDLE_SECONDS`, por defecto 300s). 100% independiente de librerías externas nativas (Python puro `threading` y `sqlite3`).\n  2. **Interrupción Instantánea de Latencia Cero (`threading.Event()`)**: La recepción de cualquier prompt o consulta de usuario activa `notificar_actividad_usuario()`, el cual notifica al evento de sincronización de inmediato, congelando la ideación autónoma para garantizar 0% de latencia en la atención al usuario.\n  3. **Muestreo Resonante Cortical (Spindles Replay)**: Algoritmo que selecciona un Nodo Ancla de alta valencia ($V_s \\ge 0.3$) o peso ($W \\ge 0.5$) y explora nodos resonantes distantes (a 2-3 saltos asociativos o dimensionales) no conectados fuertemente previo, creando un \"Insight\" sintético latente.\n  4. **Concurrencia Aislada Thread-Local**: Conexión SQLite dedicada por hilo en modo WAL con `PRAGMA busy_timeout = 5000` y transacciones seguras sin bloqueos.\n  5. **Selección Natural de Hipótesis (Decaimiento LTD Pasivo)**: Los Insights nacen con peso moderado ($W=0.50$) y valencia somática protegida ($V_s=0.85$). Si el usuario no los refuerza mediante evocación o feedback, sufrirán decaimiento LTD pasivo pasadas varias etapas de consolidación.\n  6. **Presupuesto de Energía & Período Refractario**: Limita la ideación a un máximo de 3 hipótesis por ciclo de reposo (`BIORAG_DMN_MAX_IDEAS=3`) con un período de enfriamiento refractario de 60 segundos.\n  7. **Visualización y Monitoreo MCP**: Exposición de la herramienta MCP `biorag_estado_dmn` y el endpoint REST `/api/corteza/dmn` en el backend del Neuro-Visor para inspección en tiempo real.\n\n---\n\n#### Fase 12: BioRAG v22.0 — SDM Query-by-Example: Base Vectorial Ligera\n* **Objetivo y Aporte**: Convertir al Sparse Distributed Memory (SDM) de 1024 bits en una base vectorial ligera que encuentra nodos conceptualmente similares usando únicamente distancia Hamming, sin GPU ni dependencias externas. Esto cierra el gap entre búsqueda por texto (FTS5) y búsqueda semántica pura.\n* **Componentes Implementados en v22.0**:\n  1. **`buscar_sdm()` con `vector_fijo` (`core/sdm.py`)**: La función de búsqueda SDM ahora acepta un parámetro `vector_fijo` (bytes). Cuando se proporciona, usa ese vector directamente en vez de generar uno desde texto. Esto habilita \"buscar nodos similares a ESTE nodo\" — búsqueda semántica pura por Hamming distance.\n  2. **`buscar_similares_a(cerebro, concepto_semilla)`**: Función de conveniencia que toma el vector SDM de un nodo conocido y retorna los nodos más cercanos. El SDM funciona como base vectorial ligera: 128 bytes/nodo (1024 bits), 0 GPU, SQLite puro.\n  3. **Validación Empírica**: Tests con sinónimos técnicos (bug↔error: 5 bits), abreviaturas (DB↔base de datos: 7 bits), cross-domain (base_de_datos↔cache: 9 bits), y query-by-example real sobre 570 nodos (5/5 semillas con hits).\n  4. **Pipeline de Búsqueda Enriquecido**: Capa SDM query-by-example como fallback cuando FTS5 y dimensiones no encuentran suficientes candidatos. Scoring híbrido incorpora similitud SDM como señal adicional.\n\n---\n\n#### Por qué el SDM NO reemplaza a WordNet ni a las Dimensiones\n\nEl SDM query-by-example **no reemplaza** WordNet ni las dimensiones semánticas — las **completa**. Cada técnica resuelve un problema distinto que las otras no pueden cubrir:\n\n| Capa | Problema que resuelve | Qué no puede hacer sola |\n|---|---|---|\n| **FTS5** | Texto exacto: \"error_http_500\" → nodo exacto | No tolera sinónimos ni variaciones |\n| **WordNet** | Sinónimos léxicos: \"hipertensión\" → \"presión arterial\" | No sabe qué dimensiones tiene un nodo |\n| **Dimensiones** | Propiedades ontológicas: \"todo lo de `dominio_tecnico`\" | No encuentra sinónimos léxicos |\n| **SDM Query-by-Example** | Estructura compartida: dos nodos con vecinos similares pero sin palabras en común | No puede buscar por propiedad ontológica |\n\n**Ejemplo concreto de por qué cada una es insustituible:**\n\n1. **WordNet es insustituible** para sinónimos que el SDM no puede adivinar: \"hipertensión\" ↔ \"presión arterial\" no comparten dimensiones ni vecinos — solo WordNet sabe que son lo mismo por relación léxico-semántica.\n\n2. **Las dimensiones son insustituibles** para búsquedas por propiedad: `recordar(dimensiones='{\"dominio\":[\"dominio_tecnico\"]}')` trae TODO lo técnico sin importar el texto del nodo. El SDM no puede hacer esto porque no busca por eje semántico.\n\n3. **El SDM brilla** cuando FTS5 y WordNet fallan porque no hay match textual ni sinónimo conocido: dos nodos que comparten vecinos sinápticos pero no palabras. Ejemplo: \"repaircard\" ↔ \"dashboard\" (8 bits Hamming) — no comparten texto, pero sí estructura.\n\n**El pipeline real en producción:**\n\n```\nUsuario busca \"hipertensión\"\n  ↓ Capa 3 (FTS5): encuentra \"hipertensión\" si existe como nodo\n  ↓ Capa 13 (WordNet): expande a \"presión arterial\", \"HTA\", \"high blood pressure\"\n  ↓ Capa 14 (SDM): si WordNet no cubre alguna variante, busca por estructura\n```\n\n**En resumen:** WordNet y dimensiones siguen siendo obligatorios. El SDM es la capa que cubre los casos donde el texto no matchea pero la estructura sí — el fallback que cierra el gap semántico sin embeddings.\n\n---\n\n\n\n### 1. Pipeline de Búsqueda — 14 Capas en Cascada\n\nCada capa se ejecuta SOLO si la anterior devolvió pocos resultados (< 3 o < limite*2). Es un pipeline de degradación graceful — cada capa es más permisiva que la anterior.\n\n| Capa | Nombre | Técnica | Qué hace | Equivalente en el campo |\n|---|---|---|---|---|\n| **1** | NEAR query | `NEAR(palabras, 15)` | Busca palabras dentro de ventana de 15 tokens | Proximity search (Elasticsearch `match_phrase`) |\n| **2** | LIKE en concepto | `LIKE '%palabra%'` + `PALABRA_COMPLETA` | Substring match en nombre de nodo con word boundary | Fuzzy entity matching |\n| **3** | FTS5 AND exacto | `MATCH` con paráfrasis OR | Full-text search con BM25 ponderado | BM25 ranking (Google, Elasticsearch) |\n| **4** | Términos protegidos | unicode61 + `PALABRA_COMPLETA` | Bypass de trigram para términos entre comillas (\"CV\") | Exact match (Solr `exact`) |\n| **5** | OR fallback | `palabra1 OR palabra2` | Amplía recall cuando AND da pocos resultados | Boolean OR expansion |\n| **6** | "}