Memoria — El sistema de conocimiento persistente de Theus
Theus nunca parte de cero: indexa tu proyecto en tu máquina, construye un grafo de conocimiento vivo, recuerda lo aprendido entre sesiones y poda el contexto con inteligencia. El resultado son dos cosas concretas: el conocimiento del proyecto no se pierde y cada turno gasta menos tokens.
La propuesta
Un agente de código sin memoria re-explora el repositorio en cada sesión: vuelve a leer los mismos archivos, vuelve a descubrir la misma arquitectura y te cobra esos tokens una y otra vez. El sistema de memoria de Theus ataca ese problema en seis capas que trabajan juntas — todas locales, todas con kill-switch y todas fail-open: si una capa falla, el turno sigue exactamente igual, sin error visible.
| Capa | Qué hace | Kill-switch |
|---|---|---|
| Índice local + grafo | Indexa archivos y símbolos, construye el grafo del proyecto y lo mantiene vivo con un watcher. | THEUS_NO_PROJECT_INTELLIGENCE=1 |
| Recall híbrido | Recupera contexto por keywords + IDF + traversal del grafo, con re-rank semántico opcional. | THEUS_NO_EMBEDDINGS=1 (solo capa semántica) |
| Memoria episódica | Persiste los resúmenes de compactación y de sesión como episodios recuperables. | THEUS_NO_EPISODES=1 |
| Puente memdir ⇄ grafo | Sincroniza MEMORY.md y los topic files con los nodos de memoria del grafo, en ambas direcciones. | — |
| Microcompact | Poda tool results antiguos del contexto cuando la caché de prompt del servidor ya expiró. | THEUS_NO_MICROCOMPACT=1 |
| Inyección de contexto | Adjunta el resultado del recall a cada turno y a cada subagente, para no re-leer código. | THEUS_NO_KNOWLEDGE_INJECT=1 |
Índice local del proyecto
Al arrancar dentro de un proyecto, el servicio de inteligencia local indexa los archivos fuente, extrae símbolos (funciones, clases, tipos), construye el grafo de conocimiento, detecta esquemas de base de datos (Prisma/SQL) y persiste todo bajo ~/.theus/knowledge/. Sobre ese índice se levanta un dashboard en 127.0.0.1 con la visualización neural del grafo:
/dashboard /api/status /api/graph /api/database /api/schema /api/health
Watcher en tiempo real
El índice no es una foto: un watcher recursivo (fs.watch nativo — FSEvents en macOS, inotify en Linux) lo mantiene vivo. Cada archivo cambiado se re-indexa con un debounce propio de 400 ms; el guardado a disco se agrupa aparte (~2 s) para no reescribir el JSON en cada tecla. Si cambian más de 40 archivos en una ventana de 2 s (checkout de rama, formateo masivo), el watcher descarta los deltas pendientes y hace un re-scan completo — preservando siempre los nodos de memoria. Los cambios en schema.prisma o archivos .sql disparan además el re-descubrimiento del esquema de base de datos.
| Variable | Uso |
|---|---|
THEUS_NO_PROJECT_INTELLIGENCE=1 | No levanta índice, API local ni dashboard. |
THEUS_NO_OPEN_DASHBOARD=1 | Levanta la API local sin abrir el navegador. |
PROJECT_API_PORT | Puerto de la API local del dashboard. |
INDEX_MAX_FILES | Límite de archivos indexados (por defecto 2500). |
INDEX_MAX_FILE_BYTES | Tamaño máximo por archivo indexado (por defecto 1 MB). |
Recall híbrido
Cuando el agente (o tú) pregunta algo, el recall combina tres señales sobre el grafo:
- Keywords ponderadas por IDF — los tokens raros del repo pesan más que las palabras omnipresentes; incluye plegado barato singular/plural (
toolmatcheatools) sin un stemmer que rompería identificadores. - Traversal del grafo — desde los nodos semilla se recorren las relaciones
imports,contains,references,definesyderived-frompara traer el contexto estructuralmente conectado. - Embedding local — un hash-embedding local actúa solo como desempate cuando ya hay señal de keywords (peso 0.2 por defecto); no puede «inventar» hits por sí solo.
El índice de scoring se memoiza por instancia del grafo y solo se reconstruye cuando el grafo mutó — sin eso, cada consulta recalcularía todo el corpus (~850 ms con ~20k nodos) y bloquearía cada turno.
Capa semántica (embeddings reales)
El recall léxico falla en consultas conceptuales donde la pregunta no comparte tokens con el código («cómo se registran las herramientas del agente»). La capa semántica re-rankea los candidatos con embeddings reales de un endpoint OpenAI-compatible /embeddings. La configuración se resuelve en cascada:
- Variables de entorno
THEUS_EMBEDDINGS_URL/THEUS_EMBEDDINGS_KEY/THEUS_EMBEDDINGS_MODEL— cualquier proveedor, gana siempre. - Auto-config con la suscripción: tras
theus login, la capa se activa sola contra el gateway con el modelotheus-embed. No hay nada que configurar. - Sin ninguna de las dos → la capa queda apagada: cero latencia añadida, cero red.
# apagar la capa semántica incondicionalmente
THEUS_NO_EMBEDDINGS=1 theus
Memoria episódica
Antes, el resumen que produce una compactación de contexto vivía solo en el transcript: al cerrar la sesión se perdía para siempre. Ahora cada resumen (de compactación o de cierre de sesión) se persiste como episodio en dos lugares:
- Un markdown legible en
<memdir>/episodios/<fecha>-<slug>.md, con frontmatter (fecha, tipo, archivos tocados) y el texto íntegro. - Un nodo
memoryen el grafo de conocimiento (texto acotado a 1500 caracteres) — recuperable vía recall en sesiones futuras.
Así, la próxima sesión puede preguntar «¿qué hicimos con el login?» y recuperar el episodio sin que nadie lo haya guardado a mano. THEUS_NO_EPISODES=1 desactiva toda la persistencia de episodios.
Puente memdir ⇄ grafo
Theus tiene dos memorias con formatos distintos: el memdir (MEMORY.md + topic files bajo ~/.theus/projects/<root>/memory/, prosa legible por humanos) y los nodos memory del grafo (recuperables estructuralmente). El puente las mantiene sincronizadas en ambas direcciones:
- memdir → grafo: cada entrada del índice
MEMORY.mdse upserta como nodomemorycon una key determinista (hash del texto normalizado), así los re-syncs son idempotentes y no duplican nodos. - grafo → memdir: una memoria guardada en el grafo se escribe como topic file con su puntero en
MEMORY.md, respetando el formato y los caps del memdir. Los topic files que escribe el puente llevanorigen: grafoen el frontmatter para no re-importarse en eco.
Microcompact — poda inteligente de contexto
El historial de la conversación se reenvía íntegro en cada turno: sin poda, un tool result de 25K tokens se re-cobra en todos los turnos siguientes. El microcompact por tiempo detecta cuándo la caché de prompt del servidor ya expiró con certeza (más de 60 minutos desde el último mensaje del asistente) y, antes de la llamada al modelo, limpia los tool results antiguos — conservando siempre los 5 más recientes. Como la caché ya estaba vencida, nunca fuerza un cache-miss que no iba a ocurrir de todos modos.
| Variable | Uso |
|---|---|
THEUS_NO_MICROCOMPACT=1 | Desactiva la poda por completo. |
THEUS_MICROCOMPACT_GAP_MINUTES | Umbral de inactividad en minutos (por defecto 60). |
THEUS_MICROCOMPACT_KEEP_RECENT | Tool results recientes que nunca se tocan (por defecto 5; se recomienda ≥ 5). |
Inyección de contexto
Antes de cada turno, Theus consulta el grafo con tu petición y, si el recall aporta señal real (score mínimo 0.35), adjunta un bloque compacto de contexto (máximo 40 líneas) como recordatorio de sistema — el modelo llega sabiendo dónde está el código relevante sin tener que re-leerlo. Todo bajo un presupuesto de tiempo duro (150 ms; +800 ms extra solo si la capa semántica está configurada): si el recall no llega a tiempo, se descarta y el turno sigue sin retraso.
Subagentes con memoria
Un subagente arranca sin el contexto que el padre ya pagó, así que normalmente re-exploraría el repo desde cero. Theus hace un recall con la descripción de la tarea y se lo inyecta al arrancar: ~500 tokens que ahorran decenas de lecturas. Los forks de contexto se saltan la inyección — ya heredan el contexto del padre, incluida su inyección. THEUS_NO_KNOWLEDGE_INJECT=1 apaga toda la inyección (turnos y subagentes).
Datos y privacidad
Todo el sistema de memoria es local. El único componente que toca la red es la capa semántica opcional, y solo si la configuras o inicias sesión con la suscripción.
| Ruta | Qué contiene |
|---|---|
~/.theus/knowledge/ | El índice y el grafo de conocimiento persistidos por proyecto, y el caché de embeddings. |
~/.theus/projects/<root>/memory/ | El memdir del proyecto: MEMORY.md, topic files y episodios/. |