Reemplacé las bases de datos vectoriales con el patrón de agente de memoria de Google para mis notas en Obsidian

Esto empezó porque mi asistente de Obsidiana seguía teniendo amnesia. No quería defender a Pinecone o Redis solo para que Claude pudiera recordar que Alice aprobó el presupuesto del tercer trimestre la semana pasada. Resulta que, con más de 200.000 ventanas de contexto, es posible que no necesites nada de eso.

Quiero compartir un nuevo mecanismo que comencé a ejecutar. Es un sistema basado en SQLite y razonamiento LLM directo, sin bases de datos vectoriales ni canalización de integración. La búsqueda vectorial era principalmente una solución para ventanas de contexto pequeñas y evitaba que las indicaciones se confundieran. Con los tamaños de contexto modernos, a menudo puedes omitir eso y dejar que el modelo lea tus recuerdos directamente.

La configuración

Tomo notas detalladas, tanto en mi vida personal como en el trabajo. Solía ​​garabatear en cuadernos que se perdían o se quedaban atascados en un estante y nunca más se hacía referencia a ellos. Hace unos años, me mudé a Obsidian para todo y ha sido fantástico. El año pasado, comencé a conectar genAI a mis notas. Hoy ejecuto Claude Code (para mis notas personales) y Kiro-CLI (para mis notas de trabajo). Puedo hacer preguntas, pedirles que hagan resúmenes de liderazgo, realizar un seguimiento de mis objetivos y redactar mis informes. Pero siempre ha tenido un gran talón de Aquiles: la memoria. Cuando pregunto sobre una reunión, utiliza un MCP de obsidiana para buscar en mi bóveda. Lleva mucho tiempo, es propenso a errores y necesito que mejore.

La solución obvia es una base de datos vectorial. Incrusta los recuerdos. Almacene los vectores. Realice una búsqueda de similitud en el momento de la consulta. Funciona. Pero también significa una pila de Redis, una cuenta de Pinecone o una instancia de Chroma que se ejecuta localmente, además de una API integrada y un código de canalización para unirlo todo. Para una herramienta personal, eso es mucho y existe un riesgo real de que no funcione exactamente como lo necesito. Necesito preguntar qué pasó el '1 de febrero de 2026' o 'resumir la última reunión que tuve con esta persona', cosas con las que las incrustaciones y RAG no son buenas.

Luego me encontré con el agente siempre en memoria de Google https://github.com/GoogleCloudPlatform/generative-ai/tree/main/gemini/agents/always-on-memory-agent. La idea es bastante simple: no hagas ninguna búsqueda de similitudes; simplemente proporcione al LLM sus recuerdos recientes directamente y déjelo razonar sobre ellos.

Quería saber si eso se mantiene en AWS Bedrock con Claude Haiku 4.5. Así que lo construí (junto con Claude Code, por supuesto) y agregué algunas características adicionales.

Visita mi repositorio de GitHub, ¡pero asegúrate de volver!

https://github.com/ccrngd1/ProtoGensis/tree/main/memory-agent-bedrock

Una idea que cambia las matemáticas

Los modelos más antiguos alcanzaron un máximo de tokens de 4K u 8K. No podías incluir más que unos pocos documentos en un mensaje. Las incrustaciones le permiten recuperar los documentos relevantes sin cargar todo. Eso era realmente necesario. Haiku 4.5 ofrece una ventana de contexto de 250k, entonces, ¿qué podemos hacer con eso?

Una memoria estructurada (resumen, entidades, temas, puntuación de importancia) ejecuta alrededor de 300 tokens. Lo que significa que podemos obtener unos 650 recuerdos antes de que llegue al techo. En la práctica, es un poco menos ya que las indicaciones y consultas del sistema también consumen tokens, pero para un asistente personal que rastrea reuniones, notas y conversaciones, son meses de contexto.

Sin incrustaciones, sin índices vectoriales, sin similitud de cosenos.

El LLM razona directamente sobre la semántica, y en eso es mejor que la similitud del coseno.

La Arquitectura

El orquestador no es un servicio independiente. Es una clase de Python dentro del proceso FastAPI que coordina los tres agentes.

El trabajo de IngestAgent es simple: toma texto sin formato y pregúntale a Haiku qué vale la pena recordar. Extrae un resumen, entidades (nombres, lugares, cosas), temas y una puntuación de importancia de 0 a 1. Ese paquete va a la tabla "recuerdos".

ConsolidateAgent se ejecuta con programación inteligente: al inicio si existe algún recuerdo, cuando se alcanza un umbral (más de 5 recuerdos de forma predeterminada) y diariamente como paso forzado. Cuando se activa, agrupa recuerdos no consolidados y le pide a Haiku que encuentre conexiones transversales y genere ideas. Los resultados aterrizan en una tabla de "consolidaciones". El sistema rastrea la última marca de tiempo de consolidación para garantizar un procesamiento regular incluso con poca acumulación de memoria.

QueryAgent lee recuerdos recientes además de información de consolidación en un solo mensaje y devuelve una respuesta sintetizada con ID de citas. Esa es toda la ruta de consulta.

Lo que realmente se almacena

Cuando se ingiere un texto como "Me reuní con Alice hoy. Se aprueba el presupuesto del tercer trimestre, 2,4 millones de dólares", el sistema no se limita a volcar esa cadena sin formato en la base de datos. En cambio, IngestAgent se lo envía a Haiku y le pregunta: "¿Qué es importante aquí?"

El LLM extrae metadatos estructurados:

{ "id": "a3f1c9d2-…", "summary": "Alice confirmó la aprobación del presupuesto del tercer trimestre por valor de 2,4 millones de dólares", "entities": ["Alice", "Presupuesto del tercer trimestre"], "topics": ["finanzas", "reuniones"], "importancia": 0,82, "fuente": "notas", "marca de tiempo": "2026-03-27T14:23:15.123456+00:00", "consolidado": 0 }

La tabla de recuerdos contiene estos registros individuales. A ~300 tokens por memoria cuando se formatea en un mensaje (incluidos los metadatos), el límite teórico es de alrededor de 650 memorias en la ventana de contexto de 200K de Haiku. Intencionalmente configuré el valor predeterminado en 50 recuerdos recientes, por lo que estoy muy por debajo de ese límite.

Cuando se ejecuta ConsolidateAgent, no solo resume los recuerdos. Razona sobre ellos. Encuentra patrones, establece conexiones y genera ideas sobre lo que significan los recuerdos juntos. Esa información se almacena como registros separados en la tabla de consolidaciones:

{ "id": "3c765a26-…", "memory_ids": ["a3f1c9d2-…", "b7e4f8a1-…", "c9d2e5b3-…"], "connections": "Las tres reuniones con Alice mencionaron preocupaciones presupuestarias…", "insights": "La supervisión del presupuesto parece ser una prioridad recurrente…", "timestamp": "2026-03-27T14:28:00.000000+00:00" }

Cuando realiza una consulta, el sistema carga las memorias sin procesar *y* los conocimientos de consolidación en el mismo mensaje. El LLM analiza ambas capas a la vez, incluidos hechos recientes y patrones sintetizados. Así es como se obtienen respuestas como "Alice ha planteado preocupaciones presupuestarias en tres reuniones distintas [memoria:a3f1c9d2, memoria:b7e4f8a1] y el patrón sugiere que se trata de una alta prioridad [consolidación:3c765a26]".

Este diseño de dos tablas es toda la capa de persistencia. Un único archivo SQLite. Sin Redis. Sin piña. Sin canalización de incrustación. Solo registros estructurados sobre los que un LLM puede razonar directamente.

Qué hace realmente el agente de consolidación

La mayoría de los sistemas de memoria son puramente de recuperación. Almacenan, buscan y devuelven texto similar. El agente consolidador funciona de manera diferente; Lee un lote de recuerdos no consolidados y pregunta: "¿Qué los conecta?", "¿Qué tienen en común?", "¿Cómo se relacionan?".

Esas ideas se escriben como un registro de consolidaciones separado. Cuando consultas, obtienes tanto los recuerdos en bruto como los conocimientos sintetizados. El agente no sólo está recordando. Es razonamiento.

La analogía del cerebro dormido de la implementación original de Google parece bastante precisa. Durante el tiempo de inactividad, el sistema procesa en lugar de simplemente esperar. Esto es algo con lo que a menudo lucho cuando construyo agentes: ¿cómo puedo hacerlos más autónomos para que puedan trabajar cuando yo no lo hago? Y este es un buen uso de ese "tiempo de inactividad".

Para una herramienta personal, esto es importante. “Has tenido tres reuniones con Alice este mes y todas mencionaron preocupaciones presupuestarias” es más útil que tres retiros individuales.

El diseño original utilizaba un umbral simple para la consolidación: esperaba 5 recuerdos antes de consolidarse. Eso funciona para uso activo. Pero si sólo ingiere esporádicamente, una nota aquí, una imagen allá, puede que espere días antes de alcanzar el umbral. Mientras tanto, esos recuerdos permanecen sin procesar y las consultas no se benefician del reconocimiento de patrones del agente de consolidación.

Entonces, decidí agregar dos factores desencadenantes más. Cuando se inicia el servidor, busca recuerdos no consolidados de la sesión anterior y los procesa inmediatamente. Sin esperas. Y en un temporizador diario (configurable), fuerza una pasada de consolidación si hay algo en espera, independientemente de si se ha alcanzado el umbral de 5 memorias. Por lo tanto, incluso una sola nota por semana se consolida en 24 horas.

El modo original basado en umbrales todavía se ejecuta para uso activo. Pero ahora hay una red de seguridad debajo. Si estás ingiriendo activamente, el umbral lo detecta. Si no es así, el pase diario sí lo es. Y al reiniciar, nada se pierde.

Vigilancia de archivos y detección de cambios

Tengo una bóveda de obsidiana con cientos de notas y no quiero ingerirlas manualmente. Quiero señalar al observador la bóveda y dejar que él se encargue del resto. Eso es exactamente lo que hace esto.

Al iniciar, el observador escanea el directorio e ingiere todo lo que no ha visto antes. Ejecuta dos modos en segundo plano: un escaneo rápido cada 60 segundos busca archivos nuevos (rápido, sin cálculo de hash, simplemente “¿está esta ruta en la base de datos?”), y un escaneo completo cada 30 minutos, calcula hashes SHA256 y los compara con los valores almacenados. Si un archivo ha cambiado, el sistema elimina los recuerdos antiguos, limpia las consolidaciones que hacen referencia a ellos, vuelve a ingerir la nueva versión y actualiza el registro de seguimiento. Sin duplicados. Sin datos obsoletos.

Para los flujos de trabajo de notas personales, el observador cubre lo que cabría esperar:

Archivos de texto (.txt, .md, .json, .csv, .log, .yaml, .yml) Imágenes (.png, .jpg, .jpeg, .gif, .webp), analizadas mediante las capacidades de visión de Claude Haiku. PDF (.pdf), texto extraído mediante PyPDF2.

El escaneo recursivo y las exclusiones de directorios son configurables. Edite una nota en Obsidian y, en 30 minutos, la memoria del agente refleja el cambio.

¿Por qué no hay base de datos vectorial?

Si necesita incrustaciones para sus notas personales se reduce a dos cosas: cuántas notas tiene y cómo desea buscarlas.

La búsqueda vectorial es realmente necesaria cuando tienes millones de documentos y no puedes colocar los relevantes en su contexto. Es una optimización de recuperación para problemas a gran escala.

A escala personal, estás trabajando con cientos de recuerdos, no con millones. Vector significa que está ejecutando una canalización de incorporación, pagando por las llamadas a la API, administrando el índice e implementando una búsqueda de similitudes para resolver un problema que una ventana de contexto de 200K ya resuelve.

Así es como pienso sobre las compensaciones:

Complejidad
Exactitud
Escala

No podría justificar tener que configurar y mantener una base de datos vectorial, ni siquiera FAISS para las pocas notas que genero.

Además de eso, este nuevo método me brinda mayor precisión en la forma en que necesito buscar mis notas.

Verlo en acción

Así es como se ve realmente su uso. La configuración se maneja a través de un archivo .env con valores predeterminados razonables. Puede copiar el ejemplo directamente y comenzar a usarlo (suponiendo que ya haya ejecutado aws configure en su máquina).

cp .env.ejemplo .env

Luego, inicie el servidor con el observador de archivos activo.

./scripts/run-with-watcher.sh

CURL el punto final /ingest para probar una ingesta de muestra. Esta es una opción, sólo para demostrar cómo funciona. Puede omitir esto si está configurando en un caso de uso real.

-H "Tipo de contenido: aplicación/json" -d '{"text": "Me reuní con Alice hoy. Se aprobó el presupuesto del tercer trimestre, 2,4 millones de dólares.", "source": "notas"}'

La respuesta se verá así

{ "id": "a3f1c9d2-…", "summary": "Alice confirmó la aprobación del presupuesto del tercer trimestre por valor de 2,4 millones de dólares.", "entities": ["Alice", "Presupuesto del tercer trimestre"], "topics": ["finanzas", "reuniones"], "importancia": 0,82, "fuente": "notas" }

Para consultarlo más tarde, CURL el punto final de la consulta con

query?q=¿Qué+dijo+Alicia+sobre+el+presupuesto+

O utilice la CLI:

python cli.py ingest "París es la capital de Francia". –fuente wikipedia python consulta cli.py "¿Qué sabes sobre Francia?" python cli.py consolidar # activar manualmente estado de python cli.py # ver recuento de memoria, estado de consolidación

Haciéndolo útil más allá de CURL

curl funciona, pero no vas a rizar tu sistema de memoria a las 2 am cuando tengas una idea, por lo que el proyecto tiene dos rutas de integración.

Habilidad Claude Code / Kiro-CLI. Agregué una habilidad nativa que se activa automáticamente cuando es relevante. Di "recuerda que Alice aprobó el presupuesto del tercer trimestre" y lo almacenará sin que tengas que invocar nada. Pregunte "¿qué dijo Alice sobre el presupuesto?" la próxima semana y comprueba la memoria antes de responder. Maneja la ingesta, consultas, cargas de archivos y verificaciones de estado a través de una conversación natural. Así es como interactúo con el sistema de memoria con mayor frecuencia, ya que tiendo a vivir en CC/Kiro la mayor parte del tiempo.

CLI. Para usuarios de terminales o scripts

python cli.py ingest "París es la capital de Francia". –fuente wikipedia python consulta cli.py "¿Qué sabes sobre Francia?" python cli.py consolidar el estado de python cli.py lista de python cli.py –límite 10

La CLI se comunica con la misma base de datos SQLite, por lo que puede combinar API, CLI y uso de habilidades de manera intercambiable. Ingerir desde un script, consultar desde Claude Code y verificar el estado desde la terminal. Todo llega a la misma tienda.

¿Qué sigue?

La buena noticia es que el sistema funciona y lo estoy usando hoy, pero aquí hay algunas adiciones de las que podría beneficiarse.

Filtrado de consultas ponderadas por importancia. En este momento, el agente de consulta lee los N recuerdos más recientes. Eso significa que los recuerdos antiguos pero importantes pueden desaparecer debido al ruido reciente. Quiero filtrar por puntuación de importancia antes de crear el contexto, pero aún no estoy seguro de qué tan agresivo debo ser. No quiero que un recuerdo de gran importancia de hace dos meses desaparezca sólo porque ingerí un montón de notas de reuniones esta semana.

Filtrado de metadatos. De manera similar, dado que cada recuerdo tiene metadatos asociados, podría usar esos metadatos para filtrar recuerdos que obviamente son incorrectos. Si estoy haciendo preguntas sobre Alice, no necesito ningún recuerdo que sólo involucre a Bob o Charlie. Para mi caso de uso, esto podría basarse en mi jerarquía de notas, ya que mantengo notas alineadas con clientes y/o proyectos específicos.

Eliminar y actualizar puntos finales. La tienda es sólo para agregar en este momento. Eso está bien hasta que ingiere algo mal y necesita arreglarlo. DELETE /memory/{id} es una brecha obvia. Simplemente todavía no lo he necesitado tanto como para construirlo.

Integración MCP. Envolver esto como un servidor MCP permitiría a cualquier cliente compatible con Claude usarlo como memoria persistente. Esa es probablemente la cosa que requiere mayor elevación en esta lista, pero también es la que requiere más trabajo.

Pruébalo

El proyecto está en GitHub como parte de una serie en curso que comencé, donde implemento artículos de investigación, exploro ideas de vanguardia y reutilizo herramientas útiles para Bedrock (https://github.com/ccrngd1/ProtoGensis/tree/main/memory-agent-bedrock).

Es Python sin dependencias exóticas, solo boto3, FastAPI y SQLite.

El modelo predeterminado es `us.anthropic.claude-haiku-4-5-20251001-v1:0` (perfil de inferencia entre regiones de Bedrock), configurable a través de .env.

Una nota sobre seguridad: el servidor no tiene autenticación de forma predeterminada; está diseñado para uso local. Si lo expone en una red, agregue autenticación primero. La base de datos SQLite contendrá todo lo que haya ingerido, así que trátela en consecuencia (chmod 600 Memory.db es un buen comienzo).

Si está creando herramientas personales de IA y está estancado en el problema de la memoria, vale la pena echarle un vistazo a este patrón. Déjame saber si decides probarlo, cómo funciona para ti y en qué proyecto lo estás usando.

Acerca de

Nicholaus Lawson es un arquitecto de soluciones con experiencia en ingeniería de software y AIML. Ha trabajado en muchos sectores verticales, incluidas empresas de automatización industrial, atención médica, servicios financieros y software, desde nuevas empresas hasta grandes empresas.

Este artículo y cualquier opinión expresada por Nicholaus son suyos y no un reflejo de sus empleadores actuales, pasados ​​o futuros ni de ninguno de sus colegas o afiliados.

No dude en conectarse con Nicholaus a través de LinkedIn en https://www.linkedin.com/in/nicholaus-lawson/