Volver al blog
El panel de Huygens: a la izquierda el diario por fechas, en el centro el grafo con las propuestas del día —nodos marcados como nuevos o editados, con sus cambios de estado y sus bloques—, y a la derecha los informes narrativos que el agente redactó sobre cada corrección.

Huygens: un grafo en SurrealDB como memoria para agentes

LaboratorioAgentesSurrealDBGrafo de conocimientoMCP

Un agente de programación no recuerda nada de ayer. Cada sesión empieza de cero: le vuelves a explicar el proyecto, las decisiones que ya tomasteis y por qué aquello se descartó. La respuesta obvia es darle memoria, y la pregunta interesante es qué forma tiene esa memoria.

Huygens fue mi respuesta durante dos meses: un servidor MCP sobre un grafo tipado en SurrealDB, para que Claude Code, Codex o el agente de turno pudieran consultar y escribir en la misma memoria. Hoy está congelado. Este post cuenta por qué lo construí así, qué aprendí de SurrealDB por el camino, y cuál fue el dato que me hizo pararlo — que no tiene nada que ver con la tecnología.

Por qué un grafo, y por qué SurrealDB

Arrancó con MongoDB y un ORM encima, dando por supuesto que el dominio era documental: notas con embeddings y búsqueda híbrida. Durante el diseño del modelo apareció lo que reordenó el proyecto: esto no es un almacén de documentos, es una topología. Que una nota apoye a otra, la refute o quede bloqueada por ella no es un metadato del documento: es la información. Y en Mongo eso vive como un array de referencias sin schema, con las reglas sostenidas a mano en el código.

El pivote fue a SurrealDB, y lo elegí sobre los sospechosos habituales por una razón concreta en cada caso. Neo4j es la base de grafos canónica, pero nació antes de que los vectores importaran y los trae de aparato añadido. FalkorDB va rápido y apunta a IA, pero el motor no obliga a que una relación sea de un tipo concreto. Memgraph resolvía el problema con una licencia y un peso de empresa que no venían a cuento para un usuario. SurrealDB daba las tres cosas en el mismo esquema: relaciones tipadas que valida el motor, vectores nativos e historial de cambios.

El dominio es topológico; sostener los invariantes en código era frágil.
ADR-0005SurrealDB sobre MongoDB + Prisma

Lo que SurrealDB hace bien, y lo que cuesta

Lo mejor, con diferencia, es que las relaciones tienen esquema. Declaras que una relación va de este tipo a este otro y el motor rechaza lo demás. En una memoria que escribe un agente eso no es comodidad: es la diferencia entre un grafo consultable y un montón de aristas inventadas que parecen plausibles. Es exactamente la misma lección que me llevé del vocabulario cerrado en los corpus de conocimiento, solo que aquí la impone la base de datos en vez de un validador propio.

Lo que cuesta se paga en tres monedas. La primera es madurez: el ecosistema es joven y el driver cambió de forma incompatible entre versiones mayores mientras el proyecto estaba vivo. La segunda es que su lenguaje de consulta es de nicho, y eso importa más de lo normal aquí — los modelos conocen SQL y Cypher mucho mejor, así que el propio agente que iba a usar la memoria escribía peor contra ella. Lo mitigué encapsulando todo detrás de herramientas del MCP, que es lo que acabé haciendo de todos modos.

La tercera fue la que no vi venir. Dos de las capacidades por las que había elegido el motor —consultar el pasado y leer el registro de cambios— dependen del almacenamiento que tengas debajo, y con el que traía por defecto simplemente no funcionaban: el registro salía vacío siempre. Había un documento de decisión entero implementado contra una función que no existía en esa configuración. Se arregló migrando a otro almacenamiento con versionado explícito, pero la lección es anterior: con un motor joven, la ficha de características es del motor, no de tu despliegue.

Las dos ideas que sí merecieron la pena

La primera es separar la evidencia de la interpretación. Lo que se dijo literalmente entra crudo en una bandeja de entrada y se queda ahí, intacto. La síntesis sobre eso —lo que yo entendí, lo que decidí— es otra cosa distinta, con su propio sitio, y apunta de vuelta a la captura de la que salió. Cuando el que escribe es un agente, esa frontera es lo único que te deja distinguir después lo que pasó de lo que el modelo interpretó que pasaba.

La segunda es que el agente no escribe en el grafo: propone. Toda mutación pasa por una propuesta visible que alguien aprueba antes de tocar nada, y el grafo guarda de qué propuesta salió cada arista. Eso permitió una comprobación de la que sigo orgulloso: el grafo vivo tiene que ser exactamente el resultado de reproducir el registro de decisiones desde cero, y hay una prueba que lo verifica campo a campo.

  1. captura durante el día
  2. bandeja de entradacrudo, intacto
  3. sesión deliberada de procesamiento
  4. informe narrativo aprobado
  5. propuesta visible de mutaciones
  6. commit al grafo
invariante: grafo vivo == fold(registro de decisiones)

El dato que lo paró todo

Una noche de junio, el volumen que contenía toda la memoria de Huygens se borró y se recreó vacío. Notas, bloques, capturas, propuestas, aristas: todo. Un mes después, el contenedor del proyecto ni siquiera estaba levantado en mi máquina de trabajo.

Lo relevante no es la pérdida. Es que no me enteré. Ningún proyecto se paró por no poder consultar el grafo, ningún compromiso se rompió, ninguna decisión se tomó peor. Un sistema que se llamaba a sí mismo «memoria de confianza» perdió el cien por cien de su contenido y el coste percibido fue cero.

Eso dice más que cualquier crítica de arquitectura: el bucle diario nunca llegó a cerrarse. Huygens jamás se convirtió en el sitio donde miro antes de decidir. Y eso no lo arregla ningún motor.

La auditoría, con números

Con esa sospecha encima conté el sistema en vez de opinar sobre él. Para una memoria personal de un solo usuario había casi doce mil líneas de producción entre servidor y panel, treinta y seis herramientas expuestas al agente y quince tablas. Eso es superficie de producto con equipo detrás, no de utilidad personal.

~12.000líneas de producción
36herramientas MCP
15tablas, 6 de ellas relaciones
1usuario

Y al mirar dónde estaba ese volumen apareció el diagnóstico real: no era un sistema, eran tres pegados. Una memoria de conocimiento, un sistema de productividad completo encima —tareas del día, vencimientos, aplazamientos, estados— y encima de eso una capa que enseñaba a usar el método, con reglas para no dar la lata. Cada capa estaba bien hecha. El error fue de orden: construí la tercera sobre la segunda sobre la primera sin que ninguna tuviera todavía uso diario demostrado.

El código lo confirmaba sin piedad. Los tests de las capas de productividad son sólidos y su contenido es inventado: ni un solo caso que imite un día real. La pieza que la propia documentación señalaba como el cuello de botella —partir un proyecto en la siguiente acción concreta— ni siquiera era una herramienta ejecutable, solo un texto servido al agente, sin una prueba. En cambio la capa de memoria sí tenía huellas de uso real: setecientas y pico aristas heredadas que hubo que recuperar y volver a trazar.

Y la tentación de huir hacia Markdown

Por esas fechas circulaba el argumento de que todo esto sobra y que basta con una wiki de ficheros Markdown que el modelo navega solo. Es una crítica buena y la comparto en parte: una base de vectores que redescubre el mundo en cada pregunta, en vez de acumular conocimiento ya compilado, es un mal negocio.

Pero irse a Markdown en ese momento habría sido el mismo error mirándose al espejo: elegir arquitectura antes de tener evidencia del hábito que esa arquitectura debe sostener. Huygens apostó a que el hábito llegaría con la herramienta y no llegó. Migrar por fe deja, tres meses después, un repositorio de Markdown tan muerto como la base de datos, solo que más barato de haber construido. Más barato ya es un argumento a favor; no es lo mismo que haber resuelto el problema.

Qué hice y qué sobrevive

No reescribir nada. Congelar Huygens —que costaba cero, porque la base ya estaba vacía y nunca llegó al clúster— y montar en paralelo la versión mínima en ficheros, operada por el agente con lectura y escritura pelada. Con una condición: el criterio de salida escrito antes de empezar, no a los treinta días. El código, congelado tal cual, sigue público en el repositorio de Huygens.

Si al mes hay páginas vivas que consulto antes de decidir, el hábito existe, Markdown basta y Huygens se archiva sin drama. Si al mes la wiki está tan muerta como quedó la base de datos, entonces el problema nunca fue la arquitectura, y la pregunta que toca no es de ingeniería.

Cuatro cosas viajan pase lo que pase, y ninguna necesita motor: separar evidencia de interpretación; que nada se consolide sin que alguien lo apruebe, aunque el muro sea un pull request en vez de software; capturar sin ceremonia y procesar deliberadamente; y que toda página compilada pueda señalar de qué captura salió.

Lo que me llevo

Sobre SurrealDB, que para un dominio de relaciones tipadas cumple lo que promete, y que su precio no es el rendimiento sino la juventud: cuenta con que la ficha de características del motor no describa tu despliegue, y con que tu propio agente escriba peor en un lenguaje que apenas ha visto.

Y sobre memoria de agentes, algo que aplica igual a un cliente que a mí: la parte difícil no es el almacenamiento. Es que exista un momento del día en el que alguien mire ahí antes de decidir. Yo construí treinta y seis herramientas para un bucle que no estaba cerrado, y la prueba de que no lo estaba es que perdí toda la memoria y no me di cuenta. Ahora, cuando alguien me pide una base de conocimiento para sus agentes, la primera pregunta ya no es qué motor: es enseñarme dónde está hoy ese conocimiento y quién lo mira.

Zetesis-Labs/Huygens

MCP de memoria personal: grafo en SurrealDB, worker autónomo en Agno y errores tipados de punta a punta entre TypeScript y Python.

TypeScript

Más del laboratorio