Construyendo un motor de memoria para convertir notas diarias en conocimiento propio

Construyendo un motor de memoria para convertir notas diarias en conocimiento propio

"Lo que más olvidamos no son los hechos, sino lo que logramos superar. Este feature existe para recordárnoslo."

Las apps de journaling prometen ayudarte a reflexionar. La mayoría te da una caja de texto y espera que tú hagas el trabajo cognitivo. Yo estoy construyendo algo diferente: un motor que detecta patrones en tus entradas del pasado, los conecta con tu presente, y te recuerda cosas que ya superaste pero olvidaste que pudiste superar.

Esto es parte de Deeditt, una app de journaling con memoria. Y este post es sobre cómo funciona técnicamente, qué estoy aprendiendo, y por qué creo que vale la pena construirlo, porque de qué sirve acumular datos y conocimiento si no te ayuda a vivir mejor?

El problema que quiero resolver

Imagina que llevas meses escribiendo en un diario. Cada entrada es una instantánea de tu estado mental: cómo te sentiste, qué te preocupó, qué pasó. La información está ahí, guardada en texto.

Ahora imagina que estás pasando por algo difícil hoy. Ansiedad, sobrecarga de trabajo, un patrón que se repite. En algún rincón de tu historial, hay evidencia de que ya pasaste por algo parecido y lo superaste. Pero no lo recuerdas. El diario no te lo dice.

El objetivo no es almacenar más información. Es convertir lo que ya escribiste en algo accionable: "Tú has estado aquí antes. Y saliste."

Eso es lo que estoy construyendo. No una búsqueda de texto, no un resumen semanal genérico. Un sistema que entiende estructura en el tiempo: qué pasa el mismo día de años distintos, qué patrones emocionales se repiten, qué causas llevan siempre a los mismos efectos.

Cómo está estructurado el sistema

Arquitectura — tres capas

  • Entradas diarias — Texto libre del usuario, escrito cada día ↓ pipeline de señales
  • Señales estructuradas — Emoción, narrativa, temas, causa/efecto, embedding semántico ↓ descubrimiento de patrones
  • Patrones y conexiones — Similitudes semánticas, arcos emocionales, patrones temporales, cause & effect recurrente ↓ ensamblado
  • Storylines + reminders — Narrativas de memoria y recordatorios inteligentes para el usuario

Cada entrada pasa por un pipeline que la transforma en señales: qué emoción domina, en qué punto de la narrativa está (apertura de un tema, conflicto, resolución), qué temas toca, qué causas y efectos menciona. Luego se genera un embedding semántico de 256 dimensiones para comparar significados, no solo palabras.

Con esas señales se construye un grafo de conexiones entre entradas. Una conexión puede ser:

  • thought_evolution — Una idea del pasado que maduró en el presente
  • emotional_arc — Una preocupación abierta que luego se resolvió
  • cause_effect — Un patrón recurrente: X siempre lleva a Y
  • temporal_pattern — El mismo estado emocional en el mismo mes o día de la semana
  • semantic_similarity — Dos entradas que comparten el mismo espacio semántico aunque usen palabras distintas

Cada tipo de conexión tiene su propio algoritmo de detección. Las conexiones semánticas usan similitud coseno sobre vectores de embedding. Las de arco emocional requieren superposición temática entre una preocupación abierta y una resolución posterior. Para evitar saturar al usuario con conexiones redundantes, el sistema aplica supresión de fan-out: si una entrada antigua generaría múltiples conexiones hacia entradas recientes dentro de la misma ventana de tiempo, solo sobrevive la más fuerte.

No todas las conexiones descubiertas llegan al usuario. El sistema opera con dos umbrales separados: uno para escribir una conexión al grafo interno, y uno más alto para surfacearla. La capa de presentación solo ve las conexiones que superaron el segundo umbral.

Llevo alrededor de un año escribiendo mis memorias diarias en Deeditt, y el sistema de memoria ha estado activo durante los últimos 6 meses, pero la detección de patrones apenas logré terminarlo hace unos pocos días. Y a partir de este punto todo deja de ser teoría y finalmente probaré si el sistema realmente puede enseñarme algo útil sobre mí mismo. Y claro, muchos errores y ajustes por hacer, pero el primer paso era construir la infraestructura para que eso sea posible.

El stack tecnológico

El pipeline de análisis está en Python. La capa de servicio y las APIs están en Go. Los datos viven en PostgreSQL.

Arquitectura de almacenamiento

Una decisión de diseño que vale la pena explicar: los embeddings y las señales derivadas no viven en PostgreSQL. Viven en archivos SQLite por usuario. Solo los resultados que superan el umbral de surfacing se sincronizan a la base de datos principal.

Esto elimina la dependencia de pgvector y mantiene las operaciones de embedding completamente aisladas por usuario. El costo es coordinación extra en el pipeline; el beneficio es que la base de datos central solo almacena resultados curados, no todo el trabajo intermedio.

Embeddings y búsqueda

Los embeddings se generan con Cohere (256 dimensiones, modelo especializado para búsqueda semántica) y se almacenan como blobs binarios en SQLite. Para comparación vectorial se usa similitud coseno sobre los vectores en memoria. Cohere para este fin es bastante rápido y económico, y el tamaño de los vectores es lo suficientemente pequeño como para mantenerlos en memoria sin problemas.

El problema de comparar todos los embeddings contra todos es O(n²). Para evitarlo, el sistema usa un índice FTS5 (full-text search) como paso de pre-filtrado: antes de hacer cualquier cálculo vectorial, FTS5 reduce el universo de candidatos a un subconjunto pequeño. En la práctica, esto reduce el número de comparaciones de embeddings en uno o dos órdenes de magnitud, pero aún es algo que planeo optimizar más adelante, estoy en la parte de exploración.

La inspiración cognitiva: técnicas de memoria humana

Lo interesante de este proyecto es que no es solo ingeniería de software. El diseño del sistema tiene influencia directa de cómo funciona la memoria humana. Analicé un conjunto de técnicas de memoria y pattern discovery, y clasifiqué cuáles ya están en el sistema, cuáles vale la pena implementar, y cuáles no aplican.

Técnica Estado Nota
Pattern recognition Activo El núcleo del sistema entero
Association / linking Activo Conexiones explícitas entre entradas
Chunking Parcial Señales y storylines son chunks, falta el nivel episodio
Pattern completion Parcial Un cue presente puede recuperar entradas antiguas relacionadas
Pattern separation Parcial Embeddings ayudan, pero falta separación explícita
Spaced retrieval Por construir Alto valor: resuperficiar lecciones pasadas a intervalos crecientes
Memory palaces No aplica Técnica espacial humana, sin análogo directo en software
MVPA / DiMViGI No aplica Técnicas de neuroimagen, fuera del alcance del producto

La que más me interesa implementar próximamente es el spaced retrieval: volver a mostrar al usuario una lección de su pasado en intervalos de 1 semana, 1 mes, 3 meses. No como notificación genérica, sino como: "Hace tres meses, saliste de algo parecido a lo que estás viviendo ahora."

Cómo el sistema detecta patrones temporales

El descubrimiento de patrones trabaja sobre un registro de features extraídas de cada entrada: emociones, temas, marcadores de arco narrativo. Para cada combinación de feature y valor, el sistema prueba si sus apariciones en el tiempo forman alguna de estas estructuras:

  • Intervalo fijo — El mismo estado cada 7, 14, 30, 60, 90, 180 o 365 días, con una ventana de tolerancia adaptativa según el intervalo
  • Mensual — El mismo feature aparece concentrado en ciertos meses del año
  • Semanal — El mismo feature aparece preferentemente en ciertos días de la semana
  • Estacional — Agrupación por primavera/verano/otoño/invierno a través de múltiples años
  • Anual — El mismo cluster de fechas se repite año tras año

Cada patrón candidato recibe un score de significancia que combina tres dimensiones: qué tan frecuente es, qué tan consistente es el intervalo (baja desviación = alta consistencia), y cuántos años distintos abarca. Solo los patrones que superan un umbral mínimo de significancia se guardan y se usan para generar conexiones.

El sistema de retrieval: encontrar la memoria correcta

Hay un componente que trabaja en paralelo con el descubrimiento de conexiones: un sistema de candidatos de retrieval. Dado un entry fuente, el sistema genera candidatos rankeados usando cuatro modos distintos:

  • similar_experience — Entradas con alta similitud semántica y superposición temática
  • past_resolution — Entradas antiguas con evidencia de resolución de algo que era una preocupación abierta
  • recurring_pattern — Entradas que participan en los mismos patrones temporales descubiertos
  • same_day_recall — Entradas del mismo día de años anteriores, validadas con similitud semántica para evitar matches puramente calendáricos

Lo importante de este diseño es que cada candidato incluye un payload de evidencia estructurado: qué temas coincidieron, qué score semántico tiene, qué patrones lo respaldan, y un desglose del score final. El sistema no solo dice "esto es relevante" — puede justificar por qué.

El gap que aún falta cerrar: no existe todavía un componente propio que, dado el contexto del usuario hoy, seleccione activamente el episodio pasado más útil y lo presente en el momento correcto.

Qué tan cerca está el sistema de su objetivo real

Dimensión Progreso
Almacenar 85%
Encontrar 62%
Conectar 57%
Enseñar / mejorar 32%

El sistema ya detecta y conecta, pero aún no enseña de forma confiable (y no estoy seguro que lo logre en el corto plazo, pues es subjetivo y seguramente terminaré diciendo así me parece bien). El gap más grande está en la capa de retrieval dirigido: dado lo que el usuario vive hoy, ¿cuál es el episodio pasado más relevante para mostrarle? Esa lógica de ranking y selección todavía no existe como componente propio.

La visión que guía las decisiones

Lo que estoy construyendo tiene dos ejes de tiempo:

Una línea horizontal: las entradas del día a día, avanzando hacia adelante. Una línea vertical: el mismo día a través de años distintos, comparando quién eras y quién eres. En la intersección de esas dos líneas vive el conocimiento que la mayoría de las apps de journaling nunca te muestran.

El reto técnico más interesante no es el almacenamiento ni la extracción. Es enseñarle al sistema a detectar que una entrada de hoy y una entrada de hace 8 meses son instancias del mismo patrón no resuelto, aunque las palabras sean completamente distintas.

Para eso se necesita semántica, no solo texto. Emoción, no solo sentimiento. Arcos narrativos, no solo frases. Y tiempo, que es la dimensión que casi ningún sistema de journaling trata con seriedad.

Lo que estoy aprendiendo

Técnicamente: que la parte difícil no es el modelo de ML, es el diseño del grafo de memoria y cómo servirlo. Un ejemplo concreto: evalué usar pgvector para búsqueda de vecinos aproximados, pero un índice FTS5 como pre-filtro resultó más rápido y más barato para este volumen de datos. La búsqueda vectorial bruta no se justifica cuando puedes reducir el espacio de candidatos con texto antes de hacer cualquier álgebra. Que un buen pipeline de señales vale más que un modelo más complejo. Que los tests de comportamiento temporal son los más difíciles de escribir bien.

De producto: que la diferencia entre "detectar un patrón" y "enseñarle algo al usuario" es un abismo que requiere diseño, no solo datos. Que el valor real del sistema no está en acumular conexiones, sino en saber cuándo y cómo mostrar exactamente la conexión correcta.

Y de diseño cognitivo: que los mejores sistemas de memoria humana (spaced repetition, pattern completion) tienen décadas de investigación detrás, y que ignorar eso sería perder ventaja gratis.

Hay mucho camino por delante, mucho que aprender, muchos errores y hasta malas decisiones, pero de este tipo de proyectos extraños salen ideas geniales.

Happy coding.

--

Photo by dianne clifford on Unsplash

Written with StackEdit.

Jack Fiallos

Jack Fiallos

Te gustó este artículo?