Memoria para agentes de código: guardar es la mitad fácil
El post sobre el Harness de la semana pasada enseñaba un briefing de sesión real, y una línea dentro de él trabajaba más que todas las demás:
- **MCP Server** [PITFALL] (claude-code/vincent, 1 day ago): every new ID argument
name must be added to idArgumentResolvers in authz.go, or the cross-org check
silently skips it.
Un agente que lee eso se ahorra la tarde que perdió el agente anterior. Bien. Ahora envejécela seis meses. La misma frase, con el mismo tono seguro, servida a un agente que trabaja sobre un fichero que dos personas han reescrito mientras tanto. Nada en la línea parece distinto. El agente no tiene forma de saberlo, y tú tampoco.
Ese es el problema de verdad con la memoria para agentes de código, y no es la parte que construyen la mayoría de las herramientas. Apuntar las cosas es la mitad fácil.
Por qué un montón de notas no puede responder a la pregunta
Las formas habituales de memoria de agente hoy son un fichero markdown al que el agente va añadiendo, y un vector store en el que escribe. Los dos almacenan bien. Los dos recuperan de forma razonable. Ninguno puede decirte que el suelo bajo una nota se ha movido, porque ninguno sabe de qué va la nota en ningún sentido estructural. Un vector store sabe que una nota está cerca de las palabras "gateway" y "headers". No sabe que ApiGateway es un container de tu sistema, que tiene una ruta en el código, y que el código en esa ruta dejó de coincidir con el modelo documentado hace tres semanas.
Un modelo de arquitectura sabe las tres cosas. Ese es todo el argumento para poner la memoria al lado de uno, y es la única parte de este post que otro producto no podría copiar en un sprint.
La memoria ya está en Archyl, en todos los planes. Esto es lo que hace.
Una memoria está enganchada a un elemento, no a una conversación
Una memoria es un hecho tipado como note, convention o pitfall, adjunto a un elemento C4 o al proyecto entero. Los agentes las escriben a través de MCP; las personas las escriben en el Agent Hub o en el panel de detalle de cualquier elemento del diagrama.
▶ remember(projectId: …, element: "ApiGateway", kind: "pitfall",
content: "The gateway strips custom headers longer than 8KB — payloads must go in the body.")
Hay un cuarto tipo, session_outcome, escrito automáticamente cuando termina una sesión de trabajo. Una sesión que tenía ocho leases produce una memoria adjunta a ocho elementos, no ocho copias del mismo párrafo. La forma importa para la recuperación: un agente que pregunta por cualquiera de esos elementos recibe el resultado una vez, en lugar de releer ocho veces el mismo resumen solo porque la sesión tocó ocho cosas.
El contenido y los títulos de las memorias están cifrados en reposo, como cualquier otra columna de contenido sensible del producto.
El recall ordena por significado, no por subcadenas compartidas
recall mezcla similitud semántica con solapamiento de términos, ponderados 0.55 frente a 0.45. Un agente que pregunta por "rate limiting" recibe de vuelta la nota que otra persona escribió sobre "throttling", el caso que una búsqueda por palabras se pierde y un compañero no se perdería nunca.
Los vectores son best-effort a propósito. Sin un proveedor de IA configurado (compatible con OpenAI u Ollama), no hay vectores y el scoring se queda puramente léxico, como se comportaba antes. Se degrada en lugar de romperse, y eso importa si te lo autoalojas sin proveedor. Y las memorias escritas cuando no existía ningún proveedor no se quedan de segunda clase para siempre: un worker en segundo plano rellena sus vectores en cuanto configuras uno.
Escribir el mismo hecho dos veces lo confirma
Repetir algo que el proyecto ya sabe no crea una segunda copia. Por encima de una similitud coseno de 0.94, la escritura confirma la memoria existente en su lugar, y la respuesta dice deduplicated: true. Un agente que reafirma lo que aprendió es evidencia, no ruido.
Entre 0.82 y 0.94 está la banda interesante: parecido, pero no el mismo hecho. Esas se guardan, y las casi-coincidencias vuelven en similarTo, para que quien escribe pueda llamar a remember(supersedes: "Old title") a propósito en vez de contradecir en silencio una memoria que sigue viva y sigue sirviéndose.
La memoria aprende del uso
Cada recall registra qué memorias sirvió a qué sesión. Cuando la sesión termina, usedMemories nombra aquellas en las que se apoyó de verdad.
Esas dos señales no están ponderadas igual, y es a propósito. Que te sirvan una memoria es circunstancial. Decir que la usaste es un testimonio. Así que solo las citas suben el rango de una memoria, en escala logarítmica y con tope en 1.8x, para que una memoria popular no pueda enterrar a la más nueva que la corrige. Una memoria servida a cinco sesiones sin una sola cita recibe un multiplicador de 0.75 y se trata como ruido.
Se trata como, no se borra. Nada en la memoria lo elimina jamás una heurística. Las memorias ignoradas caen en una cola de revisión con su número de impresiones, y decide una persona. El mismo principio recorre toda la funcionalidad: corregido, nunca borrado.
Una memoria tiene un ciclo de vida
La frescura decae con una vida media de 45 días desde el momento en que se supo por última vez que la memoria era cierta, que es su creación o su confirmación más reciente. confirm_memory reinicia ese reloj y sube el contador de confirmaciones. remember(supersedes: …) sustituye un hecho que cambió: la versión antigua sale de la recuperación pero se queda en el historial y en el grafo, así que todavía puedes ver lo que el proyecto creía el año pasado.
Encima van los pesos por tipo, y son opinados: un pitfall puntúa 3.0, una convention 2.0, una note normal 1.5, un session outcome 1.0. Para un agente que está a punto de cambiar código, "esto te va a morder" gana a "esto es lo que pasó".
Lo que de verdad invalida una memoria es el drift
Todo lo de arriba es contabilidad decente. Esta sección es la razón por la que la memoria pertenece a una herramienta de arquitectura.
El tiempo es un sustituto flojo de la verdad. Una convention escrita hace dos años sobre cómo funcionan tus fronteras de servicio probablemente sigue siendo correcta. Una nota escrita el mes pasado sobre un fichero que se ha reescrito desde entonces probablemente está mal. La decadencia las trata igual, porque un reloj es todo lo que tiene.
Lo que de verdad vuelve sospechosa a una memoria es que cambie el código detrás de su elemento. Archyl ya calcula eso, de forma determinista: el drift score compara el modelo documentado contra el repositorio y nombra los elementos que ya no encajan. Lánzalo desde la UI, desde la API, o en cada push con la GitHub Action drift-score. La memoria ya está conectada a él.
Cuando el drift encuentra un elemento desincronizado, cada memoria adjunta a ese elemento queda sellada con el momento en el que ocurrió. Una memoria confirmada por última vez antes de ese sello describe algo que desde entonces se ha movido por debajo. De ahí salen tres cosas:
- Baja en el ranking, con un multiplicador de 0.6. Baja, no se esconde: puede ser lo único que alguien haya escrito nunca sobre ese elemento, y esconderla sería peor que servirla con un aviso.
- Aparece en la cola de revisión para una persona.
- El agente lee un aviso, en el briefing, con palabras en vez de metadatos:
- **ApiGateway** [PITFALL] [VERIFY — the element drifted since this was written]
(claude-code/sarah, 96 days ago): the gateway strips custom headers longer than 8KB.
Reconfirmar la memoria quita la marca, porque una confirmación responde directamente a la pregunta del drift: alguien ha mirado, y sigue siendo verdad.
Las dos mitades de ese mecanismo viven en el mismo producto. El conocimiento está aquí, y también la comparación modelo-contra-código que puede echarlo abajo. Una capa de memoria atornillada a un cliente de chat tiene la primera mitad y ninguna forma de conseguir la segunda.
Las memorias se enlazan entre ellas
Dale un título a una memoria y se vuelve direccionable. Cualquier otra memoria puede entonces referenciarla con [[Title]] en su contenido, al estilo Obsidian. La misma sintaxis resuelve a elementos C4 por nombre ([[ApiGateway]]) y a decisiones ([[ADR-17]]), y un enlace a un título que todavía no existe se queda pendiente y se engancha solo en el momento en que alguien escribe esa memoria.
Los enlaces no son solo para leer. recall los sigue: las mejores coincidencias arrastran a sus vecinas enlazadas, marcadas con via para que veas qué las ha traído. Un pitfall sobre el gateway que enlaza al ADR que explica por qué existe esa frontera llega con el razonamiento pegado.
El knowledge map, y el grafo que tiramos
La primera versión del panel de memoria era un grafo de nodos y enlaces. Renderizaba, agrupaba en clusters, tenía la pinta de eso de lo que haces captura. Respondía a "qué memoria enlaza con qué memoria", y esa no es una pregunta que estuviera haciendo nadie.
Lo que la gente necesita saber es qué partes de su arquitectura entiende el proyecto y sobre qué partes no ha escrito nadie una palabra. Así que lo sustituimos. El panel ahora enseña una celda por elemento C4: qué se sabe de él, cómo de fresco es ese conocimiento, cuántos pitfalls hay ahí, y, para los elementos sin absolutamente nada, un hueco visible. Produce un titular que ningún dashboard te daba antes:
3 of 19 elements documented
Esa frase es incómoda de una forma útil. El grafo no lo era.
Lo que no hace
El recall semántico necesita un proveedor de IA. Sin endpoint compatible con OpenAI y sin Ollama no hay vectores, y el ranking cae de vuelta al solapamiento de términos. Todo lo demás de esta página sigue funcionando.
El emparejado de elementos sigue siendo léxico. La memoria ahora ordena por significado. El paso anterior, find_relevant_context eligiendo de qué elementos va tu tarea, sigue puntuando por solapamiento de palabras en nombres, descripciones, tags y rutas. Una tarea sobre "checkout" seguirá sin sacar un component llamado OrderProcessor. Señalamos eso como un límite en muchos agentes, una arquitectura y sigue siendo cierto.
La señal de utilidad solo existe si los agentes citan lo que usaron. El skill archyl-harness le enseña al agente a pasar su sessionId a recall y a nombrar usedMemories cuando termina. Nada lo obliga. Un agente conectado sin el skill produce impresiones y ninguna cita, lo cual se lee exactamente igual que una memoria que nadie encontró útil.
La memoria tiene alcance de proyecto. Una convention que vale para toda la organización hay que escribirla en cada proyecto que la necesite. Eso es lo próximo que vamos a arreglar.
Y la advertencia honesta de conjunto: la memoria acaba de salir. No tenemos números de adopción, ni benchmark, ni un cliente que te diga que le ahorró nada. Lo de arriba es lo que hace el código, y puedes comprobar cada trozo contra tu propio proyecto.
Por dónde empezar
Si ya ejecutas el Harness, la memoria ya está encendida. remember, recall y confirm_memory son tres de los dieciséis tools del perfil coding. La versión 0.8.0 del plugin de Claude Code es la pieza que le enseña a un agente los dos hábitos de los que depende el ranking: pasar su sessionId a recall, y nombrar lo que usó cuando termina.
Lo primero que merece la pena no es escribir memorias. Es abrir el knowledge map y leer la línea de cobertura. Sea cual sea la fracción que enseña, esa es la fracción de tu arquitectura que sobrevive a que la persona que la entiende se vaya de vacaciones. Adivina el número antes de mirar, y luego mira.
Después coge el elemento con más tráfico y menos escrito, y escribe el pitfall que le contarías a alguien nuevo en su primer día. Esa es la memoria que necesita el próximo agente, y hasta que alguien la teclee, ninguna cantidad de recuperación la va a encontrar.
La memoria es parte del Archyl Harness: las sesiones de trabajo, el preflight gate, el hook del Guard y la Fleet console. El plugin, los skills y el hook del Guard y las GitHub Actions son open source, y la referencia completa está en la guía del Harness. Lectura relacionada: sesiones de trabajo, muchos agentes, una arquitectura, y por qué tus agentes tienen un archivo de reglas y no un modelo.