Memoria persistente local-first para agentes AI. SQLite + FTS5 + HRR vectors + embeddings opcionales. Sin servicios externos, sin GPU, sin API keys.
- ¿Por qué memento?
- Instalación
- Primeros pasos
- Arquitectura
- Características
- Embedding Providers
- MCP Server
- Web Viewer
- Benchmarks
- API
- Contribuir
- Licencia
Los agentes AI necesitan memoria persistente para ser útiles. Pero las opciones existentes implicaban elegir entre:
- Dependencia de APIs externas (Pinecone, OpenAI embeddings) — tu agente deja de funcionar sin internet.
- Infraestructura pesada (Chroma, Qdrant, AgentMemory con iii-engine) — 2GB+ de descarga, runtimes externos, config compleja.
- Archivos JSON artesanales — crecen como plaga, sin búsqueda, sin estructura.
memento es el punto medio: SQLite embedded, sin servidores, sin dependencias obligatorias, sin llamadas externas. Tu información nunca sale de tu máquina.
pip install memento-etch
python -c "from memento import EtchStore; s = EtchStore('memory.db'); print('anda')"
Eso es todo lo que necesitás para arrancar.
# Mínimo: FTS5 + Jaccard (solo stdlib de Python)
pip install memento-etch
# Recomendado: FTS5 + HRR vectors (necesita numpy)
pip install "memento-etch[hrr]"
pip install "memento-etch[embeddings]"
pip install "memento-etch[mcp]"
pip install "memento-etch[all]"Requisitos: Python 3.10-3.12 | Sin GPU | Sin CUDA | Sin runtime externo.
from memento import EtchStore, EtchRetriever
# Crear o abrir la base de datos
store = EtchStore("memory.db")
# Guardar hechos
store.add_fact("Python es un lenguaje interpretado", category="tech")
store.add_fact("SQLite soporta FTS5 para búsqueda de texto completo", category="tech")
store.add_fact("FastAPI está construido sobre Starlette", category="tech")
# Guardar con campos estructurados (v1.0)
store.add_fact(
content="Usar httpx para llamadas HTTP asincrónicas en Python",
what="Decisión técnica",
why="httpx tiene mejor soporte de async/await que requests",
where="src/http_client.py",
learned="httpx funciona con anyio y trio, no solo asyncio",
)
# Buscar
retriever = EtchRetriever(store)
results = retriever.search("búsqueda de texto completo")
for r in results:
print(f"[{r['_score']:.2f}] {r['content']}")
# Búsqueda inteligente con fallback automático (v1.0)
results = retriever.search(
"¿cómo hago requests HTTP en Python?",
mode="auto", # FTS5 → HRR multi-query → embeddings (si están configurados)
limit=5,
)
# Detección automática de proyecto (v1.0)
# Si estás en un repo git, el proyecto se detecta solo del remote origin
store = EtchStore("project.db", project="auto")┌─────────────────────────────────────────────────────┐
│ Tu Agente AI │
├─────────────────────────────────────────────────────┤
│ MCP Server (stdio) │ Python API │
├─────────────────────────────────────────────────────┤
│ EtchRetriever │
│ ┌─────────┬──────────┬───────────┬──────────────┐ │
│ │ FTS5 │ HRR │ Jaccard │ Embeddings │ │
│ │ (exact) │(vectors) │ (n-gram) │ (semántico) │ │
│ └────┴────┴────┴─────┴────┴──────┴──────┴───────┘ │
│ │ │ │
│ Reciprocal Rank Fusion (RRF) │
│ │ │
│ EtchStore — SQLite + FTS5 + triggers automáticos │
└─────────────────────────────────────────────────────┘
Tres capas de búsqueda, sin dependencias externas por defecto:
| Capa | Qué hace | Costo | Dependencia |
|---|---|---|---|
| FTS5 | Búsqueda exacta por palabras clave | ~0.05ms | stdlib |
| HRR | Similaridad semántica holográfica | ~0.8ms | numpy (opt-in) |
| Jaccard | Re-ranking por n-gramas | incluido en HRR | numpy (opt-in) |
| Embeddings | Búsqueda semántica densa | ~185ms | fastembed (opt-in) |
Por defecto usa solo FTS5 + Jaccard. Con pip install memento-etch[hrr] ganás HRR.
Con pip install memento-etch[embeddings] ganás embeddings densos.
Cada nivel es opcional, aditivo, y retrocompatible.
| Feature | Descripción |
|---|---|
| FTS5 | Búsqueda de texto completo con triggers auto-sincronizados |
| HRR vectors | Representaciones holográficas sin modelos, sin GPU |
| Jaccard re-rank | Overlap de n-gramas para ordenar resultados |
| Soft delete | Los hechos no se borran, se ocultan |
| Consolidación activa | LLM decide ante hechos duplicados o contradictorios |
| Entity tracking | N:M entre entidades con tipos y alias |
| Fact relations | compatible, conflicts_with, supersedes |
| Session timeline | Contexto cronológico por sesión |
| Web viewer | SPA en puerto :9120 |
| Trust scoring | Puntuación de confianza que se refuerza con retrievals |
| Topic upsert | Hechos que evolucionan: mismo topic_key, se actualizan |
| Feature | Descripción |
|---|---|
| MCP Server | 9 tools vía stdio (facts, search, timeline, similar, inbox review) |
| Structured facts | Campos what/why/where/learned para memorias disciplinadas |
| Project detection | Detecta el proyecto desde git remote automáticamente |
| Embedding providers | Pluggable: NoopProvider, FastembedProvider, OllamaProvider |
| Search expanded | FTS5 con expansión progresiva (full query → OR → single terms) |
| HRR multi-query | Búsqueda paralela con variaciones semánticas de la query |
| Dynamic RRF | k adaptativo según cantidad de resultados |
| Fallback chain | Modo "auto" que cascada FTS5 → HRR → embeddings |
| SHA-256 dedup | Deduplicación exacta con ventana de 60s |
| Conflict surfacing | Detecta hechos similares al insertar y muestra conflictos |
| Circuit breaker | Protege contra fallos en cadena de LLM externos (3 fallos, 60s cooldown) |
| Auto-eviction | Elimina facts stale (trust < 0.1 o 30 días sin retrieve) |
| Session summaries | Genera resúmenes estructurados de sesiones |
| Progressive disclosure | Search devuelve resumen (200 chars), get_fact_full() da el contenido completo |
Tres modos de búsqueda semántica, plug and play:
# 1. Sin embeddings (FTS5 + HRR, cero overhead)
store = EtchStore("memory.db") # NoopProvider por defecto
# 2. Con fastembed (local, ONNX, sin API key)
# pip install memento-etch[embeddings]
from memento.embedding import FastembedProvider
store = EtchStore("memory.db", embedding_provider=FastembedProvider())
# 3. Con Ollama (si ya tenés Ollama corriendo)
from memento.embedding import OllamaProvider
store = EtchStore("memory.db", embedding_provider=OllamaProvider(
base_url="http://localhost:11434",
model="nomic-embed-text",
))Cada provider se puede usar en cualquier combinación con el MCP server.
Para integrar memento con cualquier agente que soporte MCP (Claude Code, Codex, Gemini CLI, etc.):
pip install "memento-etch[mcp]"
# Con variable de entorno
set MEMENTO_DB_PATH=./memory.db
python -m memento.mcpHerramientas MCP expuestas:
add_factsearch_factsget_factdelete_factget_timelinesearch_similarlist_inboxpromote_factreject_fact
Configuración vía MEMENTO_DB_PATH. Si no está definida, el servidor usa :memory: como default; para uso persistente, seteá una ruta explícita como ./memory.db o ~/.memento/etch.db.
Provenance-aware facts with governed scopes and an inbox review lifecycle. Every fact can carry source identity (source_harness, source_agent, source_kind) and a scope that controls discoverability.
| Scope | Default search | Use case |
|---|---|---|
canonical |
✅ Included | Trusted project memory |
inbox |
❌ Excluded | Untrusted / subagent writes awaiting review |
personal |
❌ Excluded | Private user facts |
ephemeral |
❌ Excluded | Transient data |
Provenance example:
store.add_fact(
"FastMCP retries on timeout",
source_harness="opencode",
source_agent="worker-1",
source_kind="manual",
scope="canonical",
)# List pending inbox facts
inbox = store.list_inbox(project="my-project", limit=20)
# Promote to canonical (becomes searchable)
store.promote_fact(inbox[0]["fact_id"])
# Reject (soft-deleted, hidden from default search)
store.reject_fact(inbox[3]["fact_id"], reason="low quality")No new dependencies. Works with existing add_fact callers — provenance args are optional, scope defaults to canonical. See docs/api/store.md for the full API.
Benchmark integrado para medir recall@k con dataset sintético y juez Gemini:
# Requiere GEMINI_API_KEY
export GEMINI_API_KEY="..."
# Benchmark memento (FTS5 + HRR)
python -m memento.benchmark --verbose
# Benchmark contra baseline JSON (para comparar)
python -m memento.benchmark --provider json-baseline --verbose
# Personalizar dataset
python -m memento.benchmark --n-docs 500 --seed 42 --output results.jsonPara benchmarkear OTRO sistema de memoria contra el mismo benchmark,
implementá MemoryProvider:
from memento.benchmark import MemoryProvider, BenchmarkRunner
class MyMemory(MemoryProvider):
name = "mi-sistema"
def ingest(self, documents): ...
def retrieve(self, query, k=10, user_id=None): ...
runner = BenchmarkRunner(MyMemory())
results = runner.run(verbose=True)
print(f"Accuracy: {results['accuracy']:.1%}")Resultado de referencia (100 docs, 18 queries):
| Provider | Accuracy | Avg retrieve |
|---|---|---|
| memento (FTS5 + HRR) | 94.4% (17/18) | 5.2ms |
| JSON baseline (word overlap) | ~40% | ~0.1ms |
Visualizá toda la memoria de tu agente en un SPA local, sin servidores, sin config.
python -m memento.viewer --db ./memory.db
# http://127.0.0.1:9120Qué ves:
| Feature | Para qué sirve |
|---|---|
| Buscador | Buscá facts por contenido, proyecto, o categoría |
| Timeline | Cronología por sesión — qué pasó y cuándo |
| Relaciones | Facts conectados: compatible, conflicts_with, supersedes |
| Metadata | trust_score, retrieval_count, categoría, proyecto |
| Soft delete | Facts archivados no se pierden, se ocultan |
Combinado con la DB versionable:
# Compartí la misma memoria con tu equipo
git add memory.db
git commit -m "seed data: 500 facts de referencia"
git push
# Otro dev hace pull y abre el viewer
git pull
python -m memento.viewer --db memory.db
# → ve exactamente los mismos facts, relaciones, timelineÚtil para debuggear el estado de un agente, revisar qué facts acumuló, o compartir datasets de prueba con el equipo.
| Modo | Recall | Latencia | Dependencias |
|---|---|---|---|
| FTS5 + HRR (search_expanded + re-score) | 94.4% (17/18) | 5.2ms | numpy |
| Solo FTS5 raw | ~5% | ~0.05ms | stdlib |
| Con embeddings (BGE-small) | ~72% | ~185ms | fastembed + 65MB |
Benchmark reproducible:
set GEMINI_API_KEY=...
pip install "memento-etch[hrr]"
python scripts/run_amb_benchmark.py --n-docs 100 --verbose| Métrica | FTS5 solo | FTS5 + HRR | Embeddings densos |
|---|---|---|---|
| Coverage @100 facts | 39.2% | 69.7% | 72% |
| Latencia por query | ~0.05ms | ~0.8ms | ~185ms |
| Dependencias extra | ninguna | numpy | fastembed + ONNX |
HRR es 200-400x más rápido que embeddings densos con ~97% de su cobertura.
Documentación detallada en docs/api/:
- EtchStore — Core SQLite: CRUD, FTS5, HRR, sesiones, relaciones, consolidación.
- EtchRetriever — Búsqueda híbrida: FTS5 + HRR + Jaccard + embeddings con RRF.
- QueryClassifier — Clasificador rule-based para rutear estrategias de búsqueda.
| Proyecto | Diferenciador |
|---|---|
| memento | Local-first, KISS, SQLite, sin runtime externo, HRR vectors |
| CodeGraph | Code intelligence (tree-sitter + grafo de símbolos), NO es memoria de agente |
| AgentMemory | Memoria full-featured con iii-engine dedicado, más features, más complejidad |
| Engram | Memoria para agentes Go/MCP, sin embeddings, curada por el agente |
git clone https://github.com/Basiliskode/Memento-Memory
cd Memento-Memory
pip install -e ".[dev]"
python -m pytest tests/ -vTodos los PRs son bienvenidos. Usamos conventional commits y TDD estricto.
MIT. Construí algo útil.
memento nació dentro de un agente AI real que necesitaba acordarse de las cosas sin depender de servicios externos. Hoy corre en producción y está probado con miles de facts.
Si estás construyendo un agente que necesite memoria, probalo. Son 30 segundos.
pip install "memento-etch[hrr]" python -c "from memento import EtchStore; s = EtchStore('test.db'); print('anda')"

