NGT Memory: Memoria Persistente para LLMs con Grafo Asociativo y Perfil de Usuario
NGT Memory implementa memoria persistente para modelos de lenguaje grandes sin almacenamiento externo. El módulo almacena datos del usuario entre sesiones, recupera contexto relevante y lo inyecta en el prompt. Tres mecanismos de recuperación garantizan precisión: similitud coseno de embeddings, un grafo asociativo hebbiano y consolidación jerárquica.
La similitud coseno compara el embedding de la consulta con los datos almacenados. El grafo vincula conceptos que se activan juntos: mencionar una dieta con una consulta sobre restaurantes recupera el dato relevante. La consolidación promueve datos usados frecuentemente a memoria a largo plazo, desplazando los raramente solicitados.
El tiempo de recuperación es de 2–3 ms en CPU. Los principales retrasos provienen de la API de OpenAI: embeddings ~700 ms, generación ~800–1500 ms.
Configuración y API
El módulo se ejecuta mediante Docker:
git clone https://github.com/ngt-memory/ngt-memory
cd ngt-memory
cp .env.example .env # OPENAI_API_KEY
docker-compose up -d
Código del cliente:
import httpx
client = httpx.Client(base_url="http://localhost:9190")
# Almacenar datos
client.post("/chat", json={
"message": "Soy vegetariano y vivo en Moscú.",
"session_id": "user_42"
})
# Recuperación por sesión
r = client.post("/chat", json={
"message": "¿Qué debería comer?",
"session_id": "user_42"
})
print(r.json()["response"]) # Restaurantes vegetarianos en Moscú
print(r.json()["memories_count"]) # 2
Endpoints: /chat, /store, /retrieve, /session/reset, /health.
Perfil de Usuario Estructurado
El sistema extrae campos de los mensajes: edad, ciudad, dieta, alergias. Los datos se inyectan en el prompt del sistema con prioridad:
[PERFIL DE USUARIO — datos estructurados, máxima prioridad]
- edad: 30
- ciudad: "Moscú"
- dieta: "vegetariano"
[FIN PERFIL DE USUARIO]
[CONTEXTO DE MEMORIA — datos verificados sobre este usuario]
1. [0.91] Soy vegetariano y vivo en Moscú.
[FIN CONTEXTO DE MEMORIA]
Unión de fragmentos: "Soy" + "30" + "años" → "Soy 30 años" → edad=30 (confianza 0.6).
Resolución de conflictos: Evita disminuir la edad sin la frase "Me equivoqué." Modo corrección — 60 segundos.
Filtro de Calidad y Arquitectura
El filtro elimina basura:
- Números puros, caracteres especiales, palabras sueltas
- Menos de 6 letras
- Respuestas del asistente a basura del usuario
Arquitectura:
- POST /chat
- OpenAI text-embedding-3-small (~700 ms)
- Extracción de perfil (regex)
- Recuperación NGT Memory (coseno + grafo, 2–3 ms)
- OpenAI gpt-4.1-nano (~800–1500 ms)
- Filtro → almacenar
Stack: FastAPI, AsyncOpenAI, Pydantic. Almacenamiento en RAM de un solo proceso.
Resultados de Experimentos
Exp 44: Precisión factual con memoria 2.44/3 (+100% vs sin memoria 1.22/3).
Exp 48: Prueba A/B, 94% gana memoria (17/18), puntuación media 0.889 vs 0.056.
Exp 49: 51/54 (94%) en casos límite: entre idiomas, unión, conflictos.
Rendimiento (5000 datos):
| Operación | Rendimiento | p50 |
|-----------|------------|-----|
| store() | 3450/s | 0.29 ms |
| retrieve()| 150/s | 6.3 ms |
End-to-end: memoria 2.5 ms, embeddings 764 ms.
Desafíos de Escalabilidad
- Multi-worker: cada worker tiene su propio SessionStore. Solución:
--workers 1o Redis. - Prompt suave: el modelo ignoraba datos. Reforzado: "Trata cada dato como verdad absoluta."
- Errores de regex:
Soy alérgico→nombre= alérgico. Lista negra 25+ palabras.
Conclusiones Clave
- Combinar coseno + grafo + consolidación logra 94% en pruebas límite.
- Perfil estructurado con prioridad en prompt duplica la precisión.
- Filtro de calidad evita degradación de búsqueda por basura.
- Latencia de memoria 2–3 ms, no es un cuello de botella.
- Código abierto, BSL 1.1, listo para Docker.
— Editorial Team
Aún no hay comentarios.