Ir al contenido

Sistema de memoria

El sistema de memoria de APX es una capa de cuatro piezas que le da al super-agente contexto durable y entre canales sin ninguna curación manual. Opera en segundo plano en cada turno y se degrada con gracia — si una pieza falla, el resto sigue funcionando.

Pieza 1 — Notas autoescritas (~/.apx/memory.md)

Sección titulada «Pieza 1 — Notas autoescritas (~/.apx/memory.md)»

Cuando el super-agente llama a su herramienta remember, el dato se agrega a ~/.apx/memory.md bajo una entrada con fecha. Este archivo también lo lee directamente el broker (Pieza 4) así que las notas manuales recientes siempre salen a la superficie.

Ventana de terminal
# Desde una conversación: "recordá que la dirección del servidor de staging cambió a 10.0.1.5"
# El agente llama a la herramienta `remember` — esto no lo ejecutás vos.

El archivo se crea al arrancar el daemon si no existe. No se committea al repo — es estado global local de la máquina.

Pieza 2 — Recuperación RAG (búsqueda vectorial)

Sección titulada «Pieza 2 — Recuperación RAG (búsqueda vectorial)»

Cada mensaje que pasa por cualquier canal se indexa de forma asíncrona en un almacén vectorial en ~/.apx/memory.db (SQLite vía better-sqlite3). El indexador corre en segundo plano cada 60 segundos por defecto y embebe los mensajes nuevos desde la posición de su último cursor.

En tiempo de consulta, el broker embebe el mensaje entrante y corre una búsqueda de similitud por coseno para recuperar los top-K fragmentos más relevantes de todo el historial entre canales.

El proveedor de embeddings se configura en config.memory.embeddings y se resuelve mediante el mismo patrón de registro de motores que TTS/STT. Proveedores soportados:

ProveedorNotas
ollamaLocal, preferencia por defecto. Modelo: nomic-embed-text.
openaiEn la nube. Modelo: text-embedding-3-small por defecto.
geminiEn la nube. Usa el endpoint de embeddings de Gemini.
tfFallback offline. Vector TF determinístico por feature-hashing. Siempre disponible.

El modo de selección por defecto es "auto" (router en cadena): APX prueba ollama → gemini → openai → tf en orden y usa el primero disponible. Podés fijar un único proveedor:

{
"memory": {
"embeddings": {
"provider": "openai",
"openai": {
"model": "text-embedding-3-small",
"api_key": "sk-..."
}
}
}
}

Cuando ningún proveedor es alcanzable, APX cae silenciosamente a un vector determinístico de frecuencia de términos por feature-hashing, sin dependencias (256-dim, normalizado L2). La calidad de recuperación es menor que la de un embedding neuronal real pero mantiene el sistema funcional offline. La etiqueta de embedder en cada vector almacenado asegura que la similitud por coseno solo se calcule dentro del mismo espacio de embedder.

Las conversaciones largas hacen crecer la ventana de contexto. Cuando un chat de canal acumula más de 60 turnos conversacionales (configurable), los turnos más viejos más allá de los 40 más recientes se colapsan en un resumen escrito por el LLM (registro type: "compact" en el log JSONL). Los turnos futuros anteponen ese resumen como un turno de sistema [RESUMEN COMPACTADO], manteniendo el contexto acotado mientras se preservan decisiones, asignaciones de tareas y resultados de herramientas.

El resumidor sigue la mecánica de condenser de OpenHands (registrada como condenser: "v2" en la metadata del registro compact):

  • Estado estructurado, no un recuento. El resumen mantiene secciones etiquetadas — USER_CONTEXT, TASK_TRACKING, COMPLETED, PENDING, CURRENT_STATE, más CODE_STATE/TESTS/CHANGES/DEPS/VERSION_CONTROL_STATUS para trabajo de código — así el próximo modelo retoma desde un estado explícito en vez de una narrativa.
  • Encadenado del resumen previo. Cuando un chat se compacta de nuevo, el resumen anterior se le pasa al condenser como primer evento y el nuevo lo subsume — el estado trackeado nunca se pierde en silencio entre compactaciones (el nuevo registro enlaza al anterior vía prev_compact_ts).
  • Turnos iniciales keep_first. Los primeros turnos de una conversación contienen el objetivo original. En la primera compactación se citan textuales dentro del prompt del condenser con la instrucción de preservar ese objetivo en USER_CONTEXT; las compactaciones siguientes lo heredan a través del resumen encadenado.

La compactación corre fuera del camino crítico de respuesta — el turno actual usa el último compact existente; el próximo turno se beneficia del nuevo.

El modelo de compactación y los tamaños de ventana son configurables:

{
"memory": {
"compact_model": "ollama:gemma4:31b-cloud",
"compact_fallback_model": "",
"compact_threshold": 60,
"keep_recent": 40,
"keep_first": 2
}
}

Si tanto compact_model como compact_fallback_model no están disponibles, la compactación se omite silenciosamente — los turnos crudos se preservan y no se bloquea ninguna respuesta.

Antes de cada turno del super-agente (en llamadas sin herramientas como summarize/ask), el broker arma un bloque [MEMORIA RELEVANTE] que se inyecta en el prompt de sistema. El broker:

  1. Lee las últimas 10 entradas de ~/.apx/memory.md (síncrono, siempre rápido).
  2. Compite una consulta RAG contra un presupuesto de 800 ms — si Ollama está lento, el bloque igual se devuelve con lo que sea que haya provisto memory.md.
  3. Deduplica los hits por texto normalizado.
  4. Formatea una lista con viñetas con fecha, canal y un extracto de 160 caracteres por entrada.

El bloque se omite por completo cuando no hay nada útil para mostrar (memory.md vacío y sin hits de RAG).

Además del bloque de memoria, en canales interactivos (no routine) el broker agrega un bloque separado ”# Hilos activos en otros canales” — el turno más reciente de cada otro canal dentro de una ventana de tiempo configurable (por defecto: 6 horas, hasta 3 viñetas). Esto ayuda al agente a notar referencias como “lo de antes en Telegram” sin una coincidencia semántica.

Todas las claves viven bajo config.memory:

ClavePor defectoDescripción
enabledtruePoné false para deshabilitar todo el subsistema RAG.
index_interval_s60Cada cuánto corre el indexador en segundo plano (segundos).
broker_budget_ms800Tiempo máximo que el broker espera por RAG antes de devolver.
rag_top_k5Cantidad de hits de RAG inyectados por turno.
compact_threshold60Turnos antes de que se dispare la compactación.
keep_recent40Turnos que quedan textuales después de la compactación.
keep_first2Turnos iniciales citados textuales en el prompt del condenser (objetivo original).
compact_modelollama:gemma4:31b-cloudModelo primario para la generación de resúmenes.
compact_fallback_model(super_agent.model)Fallback si el primario está caído.
embeddings.provider"auto"auto, ollama, openai, gemini, o tf.
active_threads.enabledtrueMostrar turnos recientes de otros canales.
active_threads.window_hours6Qué tan atrás mirar turnos de otros canales.
active_threads.max_lines3Máximo de viñetas en el bloque de hilos activos.

En qué se diferencia del memory.md por agente

Sección titulada «En qué se diferencia del memory.md por agente»
memory.md por agenteSistema de memoria entre canales
Ubicación~/.apx/projects/<apx_id>/agents/<slug>/memory.md~/.apx/memory.md + ~/.apx/memory.db
¿Committeado?NuncaNunca
AlcanceUn agenteTodos los canales, todos los agentes
Escrito porVos, a manoLa herramienta remember (auto)
RecuperaciónInyección completa en cada llamadaRAG (top-K, puntuado) + últimas 10 notas
CompactaciónNo aplicaProgresiva (Pieza 3)

Usá el archivo por agente para datos estables y curados sobre el rol del agente en el proyecto. El sistema entre canales maneja el contexto dinámico, turno a turno, que abarca superficies.