De erizo a telaraña: rediseñar un cerebro de notas para agentes
Tengo un "tercer cerebro": una base de conocimiento en org-roam, replicada entre varias máquinas, donde voy guardando lo que aprendo. Nació como un Zettelkasten humano —nota larga, enlaces a mano—. Pero ha pasado una cosa: ya casi solo lo alimenta y lo recorre una IA. El humano mete cuatro notas; el agente vive ahí.
Así que toca rediseñarlo para lo que de verdad es ahora: un sustrato para agentes. Este es el plan, en tres partes —lo que tenemos, lo que queremos, y cómo llegar—. Lo escribo antes de tocar nada, a propósito.
Lo que tenemos
Una auditoría honesta del grafo, hoy:
- ~150 nodos, colgando de ~15 índices (nodos-hub).
- Pero estructuralmente es un erizo, no una telaraña: forma de estrella, el índice apunta a sus notas, y las notas no se enlazan entre sí.
- Más de la mitad de los nodos no tienen ni un enlace entrante.
- Un par de nodos aislados del todo, y algún título duplicado.
¿Conclusión? Funciona para un humano que mete cuatro notas y tira de la memoria. No funciona para una IA, que necesita recorrer conexiones para que el conocimiento aflore solo. La disciplina de re-enlazar cada nota nueva con las viejas es justo lo que mata a todos los segundos cerebros humanos: nadie la mantiene. Pero un agente sí puede.
Lo que queremos
Dos capas
- Nota fuente: el texto crudo, legible, la verdad de base. Lo que ya hay.
- Átomo concepto: una idea atómica, destilada por la IA, densamente enlazada, que apunta de vuelta a su fuente.
La síntesis de la IA es interpretativa y puede equivocarse; por eso la fuente no se tira nunca. El humano navega la fuente; el agente recorre los átomos. Nada se pierde.
Una telaraña, no un erizo
Enlaces nota↔︎nota de verdad, no solo índice→nota. La conectividad que una máquina puede mantener al día y un humano no.
Un vault privado, sin muros internos
El cerebro entero es privado por naturaleza. Así que dentro no hay distinción "público vs privado" —fuera esa etiqueta, una capa de complejidad menos—. Los dominios (áreas, sesiones) se quedan, pero solo como organización, no como muros: eso libera el enlazado cruzado, que es justo lo que le falta al grafo.
La única frontera de verdad se mueve fuera del cerebro: al acto de publicar (manual, con doble revisión, y cerrado por defecto). Y un eje aparte que no se relaja nunca: los secretos no entran jamás —un cerebro que se replica a varias máquinas no es sitio para credenciales; "privado" no es "seguro para secretos"—.
Ramas por sesión
Cada agente/sesión tiene su nodo raíz y su área, y enlaza libremente con las demás cuando un concepto hace de puente. Organización para que cada uno aflore lo suyo sin ruido del resto.
El motor: deduplicación por embeddings (local)
Esta es la pieza clave, y conviene entender qué es un embedding: convertir un texto en una lista de números (un vector) que captura su significado. Textos que significan lo parecido caen cerca en ese espacio; los distintos, lejos. Como coordenadas de sentido.
¿Para qué? Para no duplicar. Un buscador normal busca palabras: "el límite de 64KB" y "el tope del buffer" no se emparejan, aunque signifiquen lo mismo. Sus embeddings, sí. Así que antes de crear un átomo nuevo: lo paso a vector, busco si ya hay uno casi idéntico en el grafo, y si lo hay, enlazo en vez de duplicar. Sin esto, un grafo granular se pudre en mil casi-duplicados —el problema, no la solución—.
Y corre en casa: un modelo pequeño de embeddings sobre el hardware local, gratis y sin que el cerebro salga de la red. El mismo motor mejora el aflorado: recuerdas por significado, no por palabra clave.
El plan
Fase 1 — El motor de dedup (embeddings)
Modelo de embeddings local + un índice vectorial sobre los nodos + búsqueda semántica. Primero esto, porque atomizar sin deduplicar es construir el problema.
Fase 2 — El pipeline de ingesta (dos capas)
Entra un texto → la IA lo analiza y lo atomiza → cada átomo pasa por el dedup → se crean/actualizan nodo fuente + átomos concepto + sus enlaces. Granularidad con criterio: un nodo = una idea que querrías recordar o enlazar por separado.
Fase 3 — Migrar el grafo viejo (sin bulldozer)
Re-enlazar las hojas sueltas del erizo a sus hubs y entre sí, fusionar duplicados. Control de versiones como red de seguridad. Borrar solo basura real, y revisada antes. Un nodo suelto casi nunca es basura: es una buena nota que nadie enlazó. Se cura, no se arrasa.
Fase 4 — Aflorado por dominio + ramas
Recuperación filtrada por rama y por significado. Cada sesión, su raíz.
Por qué
La apuesta es simple: un cerebro que una máquina mantiene coherente. La disciplina que los humanos no sostienen —re-enlazar, deduplicar, destilar— una IA sí. Y a cambio, el conocimiento aflora en el momento justo, en el contexto, sin que se lo pidas. Eso es lo que separa un cajón de notas de un cerebro.
Esto es el plan. Lo revisamos, y le damos.
Fase 1, en directo: eligiendo el motor
(Este post crece mientras lo construyo. Aquí van las pruebas reales, no el cuento.)
Primer paso de la Fase 1: ¿qué modelo de embeddings usamos? Tenía
Ollama corriendo, pero ninguno de embeddings instalado. Bajé el
sospechoso habitual, nomic-embed-text, y
le hice la prueba de fuego: ¿reconoce que dos frases distintas
significan lo mismo, frente a una ajena?
La métrica es la separación: cuánto más cerca cae el "mismo concepto" que el "concepto distinto" (una frase de recetas de tortilla, de control). Cuanto mayor la separación, mejor discrimina el modelo.
| modelo | mismo concepto | distinto (tortilla) | separación |
|---|---|---|---|
| nomic-embed-text | 0.567 | 0.552 | +0.015 |
| nomic-embed-text + prefijo | 0.653 | 0.619 | +0.035 |
| bge-m3 (multilingüe) | 0.502 | 0.257 | +0.245 |
El resultado es contundente, y esconde una trampa que casi me como: nomic apenas distingue. +0.015 es ruido puro; con su prefijo de tarea recomendado sube a +0.035, y sigue siendo malo. ¿Por qué? Porque nomic está entrenado en inglés, y el cerebro está en español. Le pides distinguir conceptos en castellano y se encoge de hombros.
bge-m3, multilingüe, separa
siete veces mejor: el mismo concepto a 0.50, la
tortilla a 0.25, un abismo limpio entre medias. Ese es el motor.
La lección, anotada con sangre: el modelo de embeddings tiene que hablar tu idioma. El que sale por defecto en todos los tutoriales es una trampa si no escribes en inglés. Una hora de pruebas te ahorra un cerebro que no sabe deduplicar.
Siguiente paso: indexar los ~150 nodos reales con bge-m3 y sacar los clusters de casi-duplicados de verdad. Sigo.
Fase 1: el motor, sobre datos reales
Indexados los ~150 nodos del grafo con bge-m3 y comparados todos contra todos (un vector por nodo, coseno de cada par). El motor encontró los duplicados reales él solo:
| par | coseno | qué es |
|---|---|---|
| alacritty ↔︎ alacritty | 0.974 | duplicado casi exacto, a fusionar |
| haskell ↔︎ Haskell | 0.849 | variante de mayús/minús, a unir |
| LLM ↔︎ "IA / LLM (índice)" | 0.768 | una hoja que debe colgar de su índice |
Y dos hallazgos que no esperaba:
- Los nodos índice se parecen entre sí (0.85–0.89 entre ellos): comparten el "esto es un mapa de contenido", así que el motor los ve casi gemelos. Lección: el dedup debe excluir los índices — son hubs distintos a propósito.
- Los nodos-stub finos se agrupan por genéricos: notas de una línea sobre temas técnicos caen juntas porque tienen poco contenido propio que las distinga. Es, gratis, un detector de nodos demasiado delgados — justo los que la capa atómica va a engordar.
O sea: el motor no solo deduplica, es un diagnóstico del grafo. Una hora de trabajo y ya sé qué fusionar, qué enlazar y qué nodos están anémicos. Siguiente: convertirlo en un comando que toda sesión use antes de crear un nodo, para que el grafo deje de duplicar en origen.
Fase 1, cerrada: el motor ya es un comando
El motor está cableado en la herramienta del cerebro: similar "<texto>" (¿ya existe esto?),
dups (pares casi-duplicados) y reindex (incremental — solo re-embebe lo nuevo o
cambiado, los 150 nodos en 54 segundos la primera vez, y cero las
siguientes). Cada sesión lo usa antes de crear un nodo.
Y al probarlo en serio, una sorpresa que vale doble. similar "el daemon de emacs colgado al 100% de CPU"
encontró el nodo exacto a 0.70. Pero similar "lenguaje funcional con mónadas" NO
encontró el nodo «haskell», y «terminal por GPU» no encontró
«alacritty». ¿Bug? No: esos nodos son stubs —solo el
título, sin cuerpo—. Una nota que es la palabra "haskell" a secas no
tiene significado que agarrar, así que embebe débil.
O sea: el motor no solo deduplica y aflora — delata los nodos
anémicos. Sin quererlo, similar
es un test de calidad: si una búsqueda descriptiva no encuentra un nodo
que existe, ese nodo está demasiado flaco. Justo los que la Fase 2 va a
engordar. El erizo tiene púas finas; toca darles carne.
Fase 1: hecha. El cerebro ya sabe decir "esto ya lo tienes". Siguiente: la capa de átomos.
Fase 3: barrido de duplicados
Con dups en la mano, la limpieza fue
quirúrgica. El grafo escondía cuatro nodos redundantes: dos alacritty idénticos creados con un minuto de
diferencia, un dropbox vacío junto al
bueno, una copia accidental de un tutorial, y un stub de haskell sin enlazar al lado del que tenía cinco
referencias. Todos eran stubs vacíos sin un solo enlace
entrante — borrarlos no rompió nada. Cada borrado se revisó
antes; cada cambio es un commit en git, por si acaso.
Grafo: 150 → 146 nodos, cero duplicados reales. Y lo elegante: los
"duplicados" que quedan (wsl ↔︎ nix,
=mysql=…) ya no son duplicados — son nodos anémicos que
se parecen por flacos. El dedup terminó; lo que queda es darles carne. Y
eso, darle cuerpo a lo que es solo un título, es justo la Fase 2.
Comentarios (0)
Sin comentarios todavia. Se el primero!
Deja un comentario