Producto
Un solo binario, archivos markdown y un índice derivado
Los hooks capturan la actividad del agente. Markdown en git guarda las páginas; SQLite guarda su índice de búsqueda y registros exclusivos de la base, como sesiones, traspasos y cuentas. Haz copia de ambos. Sigue abajo el recorrido desde la captura hasta el próximo informe.
Un solo binario, un directorio de datos.
SQLite va incluido, libgit2 va vendorizado y el embedder es Rust puro. No hay sidecar, ni servidor de base de datos, ni cola que mantener. Todo lo que sabe está en una carpeta.
<data_dir>/
wiki/Páginas markdown con frontmatter YAML, en un repositorio git. La fuente de verdad.db/memory.sqliteÍndice de búsqueda y registros exclusivos de la base: sesiones, observaciones, traspasos, cuentas y auditoría. Modo WAL.raw/Segmentos JSONL inmutables y saneados de las sesiones gestionadas con ai-memory run.models/El modelo local de embeddings, all-MiniLM-L6-v2, de unos 87 MB y con SHA-256 fijado.logs/Logs con rotación diaria.config.tomlSe lee una vez al arrancar. Cada valor se puede sobrescribir con una variable AI_MEMORY_*.El servidor escucha en 127.0.0.1:49374 por defecto. Hacer una copia de seguridad es ejecutar ai-memory backup, o un git push de la wiki más un rsync de la carpeta.
De un hook al siguiente resumen.
Siete de estos ocho pasos corren sin modelo y sin clave de API. El paso con LLM está apagado hasta que configuras un proveedor.
Hook
Sanear
Escritor único
Observaciones
Observaciones
Página de sesión
LLM opcional
Wiki en git
Resumen
Wiki en git
Hook
HookCero LLM
La CLI del agente dispara un hook de ciclo de vida. Es fire and forget con un presupuesto de 200 ms: los hooks nativos guardan el evento en una cola local y un helper independiente lo entrega. El servidor responde 202, o 429 si está saturado.
SaneadoCero LLM
El router de /hook elimina los secretos y limita los tamaños. Es el único camino del texto no confiable hacia el store.
Escritor únicoCero LLM
El evento saneado entra en una sola cola, que vacía un solo hilo, dueño de la única conexión de escritura.
ObservacionesCero LLM
Los eventos caen en SQLite como un rastro de auditoría operativo. Es una proyección acotada de la sesión, nunca una transcripción completa.
Fin de sesiónCero LLM
Unas reglas, sin modelo, convierten las observaciones en sessions/<id>.md y abren una fila Handoff para el siguiente agente, en una sola transacción.
ConsolidaciónLLM opcional
Con un proveedor configurado, un LLM reescribe el resumen o lo reparte en concepts/, decisions/, gotchas/ y procedures/. Corre desde una cola con reintentos, fuera de la latencia de los hooks.
Commit e índiceCero LLM
Cada escritura de página es atómica (tmp, rename, fsync), se commitea en git y se indexa en la misma transacción de SQLite que su fila.
Consulta y resumenCero LLM
memory_query busca en el índice. En el siguiente SessionStart, el hook trae el traspaso abierto y un resumen estructurado para ese directorio.
Dos capas, una fuente de verdad.
Si los archivos y el índice no coinciden, ganan los archivos.
Escritor
Markdown en git
Fuente de verdad
Índice SQLite
Derivado
Markdown en git
File watcher
Reindexar
Índice SQLite
Los archivos son la verdad
Las páginas son markdown con frontmatter YAML bajo wiki/
/ /. Ábrelas en Obsidian, pásales grep, súbelas a un remoto. El índice de páginas es derivado
Reindex reconstruye páginas, enlaces y búsqueda de texto desde Markdown. No recupera sesiones, observaciones, traspasos, usuarios, claves ni auditoría que solo existen en la base.
Las escrituras son del servidor
Las escrituras normales pasan por la capa de la wiki, que actualiza juntos el archivo, el historial de git y el índice.
Un watcher recoge el resto
Un file watcher detecta las ediciones hechas desde vim u Obsidian. Un diff completo cada 30 segundos recoge los eventos que se le escaparon.
Recuperación: cinco flujos, un ranking.
Consulta
FTS5
Entidades
Grafo
Observaciones
Vectores
RRF k=60
Autoridad
Resultados
Texto completo
SQLite FTS5 sobre los títulos y los cuerpos de las páginas, con las stopwords filtradas en las consultas sin operadores.
Coincidencia de entidades
Un índice léxico de nombres sacados de las entidades y los tags del frontmatter, ponderado por frecuencia inversa de página.
Vecinos en el grafo
Un salto sobre la tabla de enlaces: wikilinks, enlaces markdown, aristas tipadas y enlaces entre proyectos. SQL plano, sin base de datos de grafos.
Observaciones
Texto completo sobre las observaciones en bruto del proyecto. Cada sesión que coincide vota una sola vez, a través de su mejor observación, por su propia página de sesión, así que el detalle que nunca llegó a la página también la encuentra. Activado por defecto desde la 3.0.
Vectores, opcionales
Similitud coseno sobre embeddings del modelo local que corre dentro del proceso. Activados por defecto desde la 2.0, nunca obligatorios, y por fuerza bruta a propósito.
Después de la fusión
- Reciprocal Rank Fusion con k=60 fusiona los flujos por posición, así que ningún flujo necesita calibrar sus puntuaciones.
- Después, un multiplicador de autoridad acotado inclina los empates ajustados hacia reglas, decisiones, procedimientos y gotchas que se mantienen al día. Las páginas episódicas e históricas siguen apareciendo en las búsquedas, y nada se excluye del todo.
- Rerank opcional con LLM: una llamada por consulta sobre hasta 30 títulos y fragmentos. Ante cualquier fallo se conserva el orden local. Todavía no hay un reranker local.
- Si las páginas compiladas no dan ningún resultado, una búsqueda acotada sobre las observaciones en bruto devuelve raw_hits.
- Los filtros se aplican antes del ranking:
kind,tag,tiery un prefijo de ruta acotan todas las vías, así una página poco frecuente no queda desplazada por una común. - Dos señales opcionales parten de cero: una baja la autoridad de una página con feedback de obsoleta o errónea sin resolver; la otra favorece una página que la misma sesión leyó o marcó como útil para una consulta parecida. Una reescritura borra ambas.
- Pasa explain=true para ver las posiciones por flujo, las contribuciones de RRF y el multiplicador de cada resultado.
Japonés, coreano y chino
- Las secuencias de Han, Kana y Hangul sin separadores de palabras necesitan coincidencias por caracteres. Un segundo índice de secuencias de uno y dos caracteres se escribe en la misma transacción que la página.
- Es un índice de SQLite, no un modelo. Buscar en japonés o coreano sigue sin costar ninguna llamada de API ni embeddings.
- El texto latino sigue la vía de siempre, sin cambios. Un almacén que viene de una versión anterior rellena el índice nuevo antes de aceptar tráfico.
Tiempo: as_of
- Pasa una fecha ISO para preguntar qué decía la wiki sobre algo en ese momento.
- Solo registra el tiempo de ingesta: cuándo ai-memory aprendió un dato y cuándo lo reemplazó, nunca cuándo fue cierto en el mundo.
- No reproduce el ranking que habría devuelto una búsqueda en esa fecha.
Aristas tipadas
- El campo
relations:del frontmatter acepta un conjunto cerrado:causes,fixes,contradicts. Una errata no puede crear un tipo nuevo. contradictsalimenta el lint sin LLM y se reporta hasta que alguien lo reconcilia.- En la búsqueda solo sirven para explain: entran en el grafo como enlaces normales y no mueven el ranking, porque el benchmark no dio base para asignarles un peso.
memory_read_pagepuede recorrerlas para listar páginas relacionadas.
Un solo receptor, un solo escritor.
Dos reglas sostienen casi toda la corrección: un traspaso se puede reclamar una sola vez, y solo un hilo escribe en SQLite.
Los traspasos son un protocolo
- Un traspaso es un registro tipado: agente de origen y de destino, proyecto, cwd, resumen, preguntas abiertas, archivos tocados, siguientes pasos.
- Aceptar es un compare and set atómico. Un segundo agente que pregunte no recibe nada.
- El cwd coincide por límites de ruta:
/repocubre/repo/apiy nunca/repo-other. - Un traspaso manual gana al automático. Aceptarlo expira los candidatos automáticos más antiguos en la misma transacción.
- En un servidor compartido, un traspaso pertenece a su dueño salvo que se envíe con
shared=true.
La regla del escritor único, medida
Todas las escrituras pasan por una cola acotada de 1024 hacia un solo hilo del sistema operativo. Las lecturas usan un pool aparte, de solo lectura. Una ráfaga frena a sus productores, y no se pierde ninguna escritura.
- 1 escritor42/s23,9 ms
- 8 escritores295/s3,4 ms
- 32 escritores698/s1,43 ms
- 128 escritores700/s1,43 ms
- El techo ronda las 700 escrituras por segundo, plano de 32 escritores en adelante. A un solo escritor lo limita fsync, no la CPU.
- Medido en un disco local rápido. Un volumen en red o lento dará cifras bastante más bajas.
- El test ataca el store directamente y se salta la entrada HTTP.
- Reprodúcelo con
cargo test -p ai-memory-store --test writer_throughput -- --ignored --nocapture.
Las páginas de sesión viejas se puntúan, y las frías se desalojan, se compactan o se fusionan. Cómo envejece la memoria.
El código, crate por crate.
Diez crates del producto, cada uno con una función y una API tipada, sin dependencias circulares.
| Crate | Responsabilidad |
|---|---|
ai-memory-core | Tipos de dominio, errores, ids. Sin IO. |
ai-memory-store | SQLite, el actor escritor, el pool de lectores, el cálculo del decaimiento. |
ai-memory-wiki | Escrituras atómicas de markdown, el file watcher, git. |
ai-memory-mcp | Transporte MCP y router de herramientas. |
ai-memory-web | Vistas web y API JSON de solo lectura. Las acciones de cuenta y aprobación llaman a rutas autenticadas. |
ai-memory-hooks | Esquemas de payload, el sanitizer, la entrada de /hook. |
ai-memory-llm | Frontera de autenticación con proveedores, traits de LLM y de embedder. |
ai-memory-consolidate | Ingesta, lint, sweep y el pipeline de auto-improve. |
ai-memory-workstream | Adaptadores de solo lectura para transcripciones nativas y lanzamientos. |
ai-memory-cli | El binario ai-memory y sus subcomandos HTTP, que son finos. |
25 herramientas MCP, pocas a propósito
Los hooks hacen la captura rutinaria, así que los agentes rara vez tienen que llamarlas a mano.
Recuperación (7)
memory_querymemory_recentmemory_read_pagememory_read_session_observationsmemory_briefingmemory_explorememory_status
Traspasos (4)
memory_handoff_beginmemory_handoff_listmemory_handoff_acceptmemory_handoff_cancel
Mensajes entre proyectos (4)
memory_message_sendmemory_message_listmemory_message_popmemory_message_cancel
Escritura y mantenimiento (10)
memory_write_pagememory_patch_pagememory_delete_pagememory_restore_pagememory_consolidatememory_auto_improvememory_feedbackmemory_lintmemory_forget_sweepmemory_install_self_routing
ARCHITECTURE.md, con los 16 invariantesDecisiones de diseño y opciones descartadas
Preguntas y respuestas
¿Dónde guarda ai-memory sus datos?
En un solo directorio de datos: un repositorio git de páginas markdown en wiki/, un índice SQLite derivado en db/, segmentos saneados del workstream en raw/, el modelo local de embeddings en models/, y los logs.
¿ai-memory necesita un LLM?
No. La captura, los resúmenes de sesión, los traspasos, la indexación, la búsqueda y el resumen inicial funcionan sin ningún proveedor configurado. La consolidación con LLM, la mejora automática y el rerank son opt-in.
¿Qué pasa si el índice SQLite se pierde o se corrompe?
Reindex reconstruye páginas, enlaces y búsqueda de texto desde Markdown. No recupera sesiones, observaciones, traspasos, usuarios, claves ni auditoría que solo existen en la base.
¿Cómo ordena los resultados la recuperación?
Se fusionan cinco flujos de candidatos: texto completo FTS5, coincidencias de entidades, vecinos del grafo, observaciones y vectores opcionales. Reciprocal Rank Fusion con k=60 los combina, seguido de un ajuste acotado por la autoridad de la fuente. El reranking con LLM es opcional.
¿Cuántas escrituras por segundo aguanta?
El store mide 42 escrituras por segundo con un escritor, 295 con 8, y un techo cercano a 700 de 32 escritores en adelante. Las cifras salen de un disco local rápido, y el test ataca el store directamente, sin la entrada HTTP.
Sigue leyendo
Lee los archivos que escribe.
Instálalo, ejecuta una sesión y abre la carpeta de la wiki en tu editor.
