Skip to main content
Glama

蒸留蔵 — distill-kura

Una memoria a largo plazo para agentes que está destilada, no acumulada. El recuerdo funciona por significado, la escritura está controlada por evidencia, y un mismo servidor puede tener varias memorias separadas — una por cada modo de agente — de modo que cambiar de modo cambia lo que el agente recuerda.

Se distribuye como un plugin de DeepSeek Harness, un servidor MCP para cualquier otro host, un servicio HTTP y una librería de Python. Solo con la biblioteca estándar; sin base de datos vectorial, sin embeddings, sin framework.

        ┌── recall ──────────────────────────────────────────────┐
        │  question → whole index in one prompt → picked slugs   │
        │           → walk [[links]] → the neighbourhood         │  ~0.4 s
        └────────────────────────────────────────────────────────┘
        ┌── distil ──────────────────────────────────────────────┐
        │  journal → classed evidence → candidates → GATE        │
        │  → new? → composed → draft → judged → poured           │
        └────────────────────────────────────────────────────────┘

Por qué existe esto

Dos fallos matan la memoria a largo plazo de un agente, y la matan desde lados opuestos.

Recuperar por palabra clave pierde lo que necesitabas. Una pregunta sobre “chips de inferencia SSD” no comparte ninguna palabra con una memoria titulada “ejecutando el modelo de 2.6T desde un nivel de almacenamiento SSD” — y sin embargo son el mismo tema. La búsqueda de palabras no devuelve nada; el agente responde desde la nada. La solución aquí no son los embeddings sino el reconocimiento: el índice completo (una línea por memoria, escrita como un desencadenador de reconocimiento) va en un solo prompt, y un modelo pequeño nombra lo que tiene relación con la cuestión. Un índice de ~500 memorias ocupa unos 6k tokens — un pequeño porcentaje de una ventana de contexto moderna, y se queda en la caché de prefijo.

Escribirlo todo envenena el almacén. Un agente afirma algo; el sistema de destilado registra la afirmación como un hecho; el siguiente agente lo lee como verdad absoluta y lo repite con más confianza. Ese bucle se auto-refuerza, y las instrucciones no lo detienen — eso está medido, no asumido. Así que la ruta de escritura está controlada por Python determinista: toda memoria candidata debe llevar citas que existan carácter por carácter en el material de origen, etiquetadas con su procedencia.

clase

qué es

qué autoriza

[USER]

las palabras del propio usuario

“ellos decidieron”, “ellos pidieron”

[TOOL]

salida de máquina

los números — la única fuente

[ACT]

una herramienta que fue invocada

"esto ocurrió", "esto se hizo"

[SELF]

la prosa del propio agente

un juicio, en primera persona, nunca un dato crudo

Una cita que no se encuentra textualmente se descarta. Un candidato sin ninguna cita superviviente se elimina. Un número sin un [TOOL] que lo respalde se suprime. El texto que atribuye una decisión a la persona, si no sobrevive ninguna cita de [USER], es rechazado en la última puerta. Las ideas son bienvenidas — van a un archivo semilla, nunca al almacén, y se convierten en memoria solo cuando a evidencia posterior las confirme.


Related MCP server: Synapto

Inicio rápido

git clone https://github.com/lna-lab/distill-kura && cd distill-kura
pip install -e .                       # or just run: python3 -m distill_kura.cli

cp kura.example.toml kura.toml         # edit: one model endpoint is enough to start
kura init main --path ~/kura/main      # create an empty store
kura serve                             # http://127.0.0.1:8085
curl -s -X POST localhost:8085/recall -H 'content-type: application/json' \
     -d '{"question":"what did we decide about the archive disk?","hops":1}'

Incorpora el índice para que el agente siempre sepa lo que sabe:

kura weave                             # build the three-layer cloth
kura prefill                           # the block to put in the system prompt

Dele las transcripciones de tus agentes:

kura distill run      # drink a batch → candidates → gate → drafts
kura distill drafts   # look at what it wants to write
kura distill drain    # the scribe re-reads each draft cold: pour / fix / toss
kura distill night    # stay resident and do it whenever things go quiet

Nada entra en el almacén hasta drain (o un pour manual). Los borradores llevan su evidencia en un comentario HTML, así siempre puedes ver por qué existe una memoria.


El mapa residente

La recuperación como herramienta responde a “¿qué sabes sobre X?” — pero solo una vez que el agente ha decidido preguntar. Nunca responde a la pregunta que el agente no piensa en hacer: ¿hay algo aquí o no? Un agente que no ve el mapa no sabe qué está perdiéndose, así que se pone a adivinar, y una suposición segura sobre tu propio contexto es exactamente la falla que este proyecto existe para prevenir.

Por eso el índice también está presente: es un bloque permanente en el prompt del sistema, en cada turno.

kura weave      # re-weave the index into the three-layer cloth
kura prefill    # print the block a host should inject

Tres capas, porque el detalle solo paga para lo reciente

Una prueba A/B a ciegas — 20 preguntas, índice amplio contra índice reducido, corregido sin saber cuál era cuál — determinó la forma:

banda

amplio

ligero

general

9

11

eventos recientes

4

1

doctrina

1

4

saltos entre dominios

1

4

Las líneas de doctrina eran byte idénticas en los dos índices, y el índice más reducido aun así ganó esa categoría: un entorno más ligero deja ver mejor las líneas fijas. El detalle no esa fuente de comprensión. Se gana su lugar solo donde las cosas siguen en movimiento.

capa

regla

línea

fija

el type del frontmatter, en pinned_types

se conserva por completo

reciente

tocado dentro de fresh_days

se conserva por completo

disparador

todo lo demás

comprimido a ~trigger_tokens

Las líneas de activación las escribe el modelo scribe y se guardan en un registro con una clave basada en la descripción y en el presupuesto, de manera que un re-tejido en el estado estable no cueste nada. Si no hay ningún modelo disponible, el telar recorta mecánicamente en su lugar — un sistema de memoria nunca debe quedar en blanco porque una GPU se haya caído.

La edad no es el mtime. Un cp -r, una restauración o un checkout activa todas las marcas de tiempo, todo el índice se vuelve "reciente", no se descarta nada, y el mecanismo se ha deshabilitado silenciosamente. Por eso el telar prefiere una fecha escrita dentro de la memoria, y rechaza cualquier mtime que una quinta parte del almacén esté compartiendo con el mismo día del calendario.

Dónde va y por qué es una decisión sobre la caché

- id: kura
  name: distill-kura
  config: { store: eq, promptOrder: -50 }   # before the persona

Una caché de prefijo se pierde desde el primer byte que cambia — como se midió en un servidor local: un preámbulo idéntico de 4.029 tokens cambia de 0.68 s a 0.14 s, añadir al final sigue siendo 0.14 s, y añadir una palabra al principio cuesta toda la caché (0.66 s). La persona que usa el sistema suele tener un reloj, así que cambia cada minuto; el mapa es el bloque más grande del prompt y cambia un puñado de veces al año. Lo grande y estable va delante de lo que da tic y tac.

Por eso el bloque no contiene fecha, ni hora, ni contador — y build() rechaza un encabezado que la tuviera, en tiempo de construcción y no a través de turnos misteriosamente lentos tres semanas después.

Nunca entrega medio mapa

situación

qué recibe el agente

todo está bien

el mapa completo, entre los marcadores <<<KURA-MAP>>>

se supera budget_fraction

todo el mapa, y una advertencia en el JSON (nunca en texto, porque un banner es contenido volátil)

se supera hard_fraction

un stub sin líneas de índice, que dice que el mapa falta, no no que está vacío

kura inaccesible

una nota explícita de que el mapa falta, nunca una cadena vacía

Un mapa truncado es el peor artefacto posible: parece completo, y cualquier memoria que quede por debajo del corte aparentemente no existir. weave acortará la ventana de frescos para que quepa, pero nunca eliminará una línea; y si ningún ajuste llega al presupuesto, lo dice, y mantiene el mejor mapa que pueda y indica dónde está el peso.

Cómo integrarlo en un host

Host

Uso/Téc de implementación

DSH

plugin nativo — un systemPrompt.section actualizado en segundo plano

Claude Code, VS Code, Goose

El campo instructions de MCP lleva un puntero corto (con límite de 2 KB); el mapa viene de la herramienta kura_map o de un gancho de sesión que ejecuta kura prefill

Claude Desktop, Claude.ai

ignoran por completo instructions — usar kura_map

cualquier otro

GET /prefill?format=text, o kura prefill en un hook de shell

El campo instructions de MCP es un MAY en la especificación, y un índice de 9000 tokens no puede viajar a través de un límite de 2 KB de ningún forma, así que este proyecto no lo oculta.


Modos: más de un kura

Una sola memoria que sirve tanto para "ayúdame a construir esto" como para "ayúdame a pensar en esto" no sirve bien para ninguna de las dos: lo que te ayuda a depurar es ruido en una conversación sobre qué hacer a continuación. Por eso un almacén es un directorio, y un modo está asociado a un almacén.

[stores.maker]
path = "~/kura/maker"
label = "maker mode — building things"

[stores.eq]
path = "~/kura/eq"
label = "EQ mode — talking things through"

[modes]
maker = "maker"
eq    = "eq"

Cada ruta toma un selector, así que un mismo proceso puede servirlos todos:

curl -s -X POST localhost:8085/recall -d '{"question":"...","mode":"eq"}'
curl -s localhost:8085/index?store=maker
curl -s localhost:8085/s/eq/doctor          # path form, for clients that only vary a base URL

Los almacenes no comparten ni memorias, ni índice, ni marca de agua del destino. Cambiar de modo cambia de verdad lo que se recuerda — no es la misma memoria dicha de otra forma.

Independientes como enrutamiento, no como confidencialidad. El servidor no tiene autenticación, así que cualquier proceso que pueda llegar a su puerto puede nombrar cualquiera de los almacenes que contenga. Atar un agente mantiene a un modelo dentro de su carril; no mantiene a un proceso fuera. Hay un solo nivel de confianza por proceso; docs/TRUST.md es solo breve y vale la pena leerlo antes de poner un almacén privado. Ahí se trata también los dos límites que son fáciles de pasar por alto: dos almacenes que beben de la misma raíz del diario, y dos almacenes que están detrás de un mismo servidor de modelo.

Con DeepSeek Openark

DSH cambia la persona y las herramientas mediante el preajuste del agente. distill-kura cambia la arquitectura mediante el almacén. Visona un preajuste y todo el ser se mueve:

# .agent-presets/eq/agent.cordis.yml
- id: kura-eq
  name: distill-kura
  config:
    url: http://127.0.0.1:8085
    store: eq            # this preset's memory
    readonly: true       # the CLIENT's own switch: do not even offer a write tool
    # (the store's own `write_policy` is the authority; this just keeps the tool
    #  out of the model's hands. Naming a store already binds the preset.)

Deja allowSwitch en su valor predeterminado y el agente también tienes kura_use, así puede desplazarse entre kura en el transcurso de la conversación sin cambiar de preajuste de herramientas: kura_recall, kura_read, kura_doctor, kura_list, kura_use y kura_remember (solo cuando el almacén es escribible). La integración completa, incluyendo el puente MCP y la regla de aislamiento del dominio isolate para filas de servicio, está en examples/pre~remote/.

La persona es asunto del anfitrión, no nuestra. Este proyecto nunca niche render ni inyecta una persona; solo registra, por almacén, qué archivo de persona le corresponde, que se puede leer en GET /profile?store=eq, para que las dos mitades puedan mantenerse en sincronía por quien controle el preajuste. Las instrucciones del agente también se quedan con el mecanismo AGENTS.md del anfitrión; ver AGENTS.md en este repo para las convenciones que debe seguir un agente que trabaja en este código.

Con cualquier host MCP

{ "mcpServers": { "kura": {
    "command": "python3", "args": ["-m", "distill_kura.mcp"],
    "env": { "KURA_URL": "http://127.0.0.1:8085", "KURA_STORE": "eq", "KURA_READONLY": "1" }
}}}

Deje KURA_STORE libre sin configurar para el modo libre: las herramientas aceptan una store opcional, y kura_use switch de sesión.


Modelos: uno por defecto, reparto ese número a la vez

Son tres roles, no tres personas:

rol

cuándo se ejecuta

necesita *

thinker

cada vez que se hace una búsqueda

pequeño y rápido; debe juzgar relevancia por el significado

brain

destilando: lee una edición completa del diario

longitud de contexto y paciencia

scribe

destilando: escribe el recuerdo y luego juzga los conocimientos

buena prosa en tu idioma y buen juicio

Declara solo [models.thinker] y un solo modelo se queda con los tres roles. Eleva de nivel a cualquiera de los otros dos independientemente — un modelo local más grande, o una API en línea (cualquiera compatible con /chat/completions; la clave se lee de una variable de entorno que tú nombre, no se guarda nunca en la configuración):

[models.thinker]                       # always-on, local, small
url = "http://127.0.0.1:8000/v1"
model = "local-small"

[models.scribe]                        # upgrade just the writing
url = "https://api.example.com/v1"
model = "big-model"
api_key_env = "EXAMPLE_API_KEY"

Dos cosas que este sistema maneja por ti: los dialectos de esfuerzo de razonamiento difieren por familia de modelos (reasoning_effort, thinking_effort, enable_thinking), así que se envían todos — las desconocidas se ignoran en la plantilla, y un modelo que por defecto queda en el modo de pensamiento profundo puede gastar todo su presupuesto en razonar y no devolver nada. Also el texto de la carta se coloca idéntico byte a byte al frente del prompt de cada rol, de manera que en un modelo local lento los tres model usen una sola caché de prefijo en lugar de hacer tres prefijos diferencias.

Si el pensador está caído, recall no se queda en silencio — recurre a la superposición de palabras y etiqueta la respuesta como how=words, que las herramientas muestran como ⚠ degraded. La degradación silenciosa es peor que la degradación.


Cómo es una memoria

Un archivo, un hecho.

---
name: archive-on-slow-disk
description: the archive lives on the slow disk; the fast one stays scratch
metadata:
  type: project          # user | feedback | project | reference
---

The archive goes on the slow disk. The fast disk is scratch space.

**Why:** the other way round burns write endurance for nothing.
**How to apply:** check which disk a target directory is on before writing there.
Related: [[disk-layout]]

Y una línea en MEMORY.md:

- [Archive on the slow disk](archive-on-slow-disk.md) — the archive lives on the slow disk; the fast one stays scratch

Esa línea es lo único que se lee absolutamente siempre. Es un disparador de reconocimiento, no un resumen: nombres propios, números, ⚠️ minas, la conclusión alcanzada. Si una línea pudiera intercambiarse con la línea de otra memoria y seguir leyéndose bien, no está haciendo su trabajo — kura distill tidy encuentra los casos mecánicamente detectables y los reescribe.

kura doctor informa de recuentos, enlaces muertos, islas (memorias a las que nada enlaza) y deriva del índice. Es el ojo que el metabolismo necesita.


La superficie HTTP

ruta

qué hace

POST /recall

{question, hops, top, chars, total_chars, store|mode} → seleccionado, recorrido, contexto. chars es por memoria; total_chars es un límite máximo para todo el contexto

POST /remember

{slug, description, body, type, title} — una escritura DIRECTA, rechazada a menos que write_policy = "direct-allowed"

GET /index

el índice sin procesar

GET /prefill

el bloque residente, listo para inyectar (&format=text para un gancho)

GET /memory/<slug>

una memoria completa

GET /doctor

salud de un almacén (?all=1 para cada almacén)

GET /stores

almacenes, modos y qué modelo desempeña cada rol

GET /profile

la carta del almacén y un puntero a su persona (nunca se renderiza aquí)

GET /health

disponibilidad

Cualquier ruta acepta ?store= / ?mode=, un campo store/mode en el cuerpo, o el prefijo de ruta /s/<name>/…. No hay autenticación: enlázalo a loopback o pon algo delante.


Notas de diseño que conviene leer antes de cambiar nada

  • docs/DESIGN.md — por qué el reconocimiento supera a la búsqueda, qué aporta la compuerta y el fallo que motivó cada mecanismo.

  • docs/OPERATING.md — ejecutarlo residente, planificadores y códigos de salida, copias de seguridad, qué vigilar.

  • docs/TRUST.md — qué es y qué no es un límite de almacén, políticas de escritura y los dos límites que son fáciles de pasar por alto (diarios compartidos, modelos compartidos). Léelo antes de poner en marcha un almacén privado.

Algunas decisiones que parecen extrañas hasta que te topas con lo que evitan:

  • Reservar antes de beber. El destilador reclama un tramo del diario antes de leerlo, bajo un bloqueo, y las marcas de agua solo avanzan. Dos destiladores que escribían cada uno su propia instantánea borraron el progreso del otro y volvieron a beber la misma agua una docena de veces.

  • Las marcas de agua son unidades por adaptador. Desplazamientos de bytes para transcripciones de solo añadir, números de secuencia para archivos que se reescriben (un desplazamiento de bytes dentro de un archivo recompimido es una mentira).

  • Supresión de eco. Una cita que ya existe en el almacén no es material nuevo: es el almacén leyéndose a sí mismo a través del resultado de una herramienta. Sin esto, un sistema de memoria redescubre y vuelve a registrar sus propios contenidos para siempre.

  • La última compuerta es un modelo, no un humano. Si una persona debe aprobar cada borrador, el sistema ha convertido silenciosamente a esa persona en su cuello de botella, y los borradores se acumulan para siempre. Nada en el bucle puede requerir a alguien que no esté siempre presente.

  • kura distill run sale con el código 2 cuando no había nada que hacer. Un planificador debe poder distinguir "hizo trabajo" de "no encontró nada", o un vigilante gira en una cola vacía y priva de recursos a los pasos que necesitan el tiempo de inactividad.

Medirlo, en lugar de afirmarlo

Dos preguntas se responden con un solo número y no deberían.

¿Cuánto más pequeño? store_ratio = tokens en las memorias y el índice / tokens del diario bruto realmente consumido. ¿Qué se perdió? Esa es una medición diferente, y un almacén que guarda una memoria de cada cien obtiene una puntuación magnífica en la primera mientras es inútil.

kura bench compress                       # what this store cost, from the distiller's own metrics
kura bench compress --tokenizer-command "./count-tokens"   # exact, not estimated
kura bench retention --questions bench/fixtures/questions.json

Medido aquí, con los datos de prueba incluidos y el estimador integrado:

corpus

store_ratio

scripts/demo-clean-room.sh (charla normal, mayormente relleno)

0.18

bench/fixtures/corpus.jsonl (denso: cada línea es señal)

1.14

El segundo no es un error. Con material donde nada es relleno, destilar no comprime: cada memoria añade su porqué y su cómo aplicarlo, y el almacén sale ligeramente más grande que la transcripción. La proporción es una propiedad del corpus, no de esta herramienta, por lo que aquí no hay una cifra destacada y por lo que el comando informa de con qué ha contado.

La retención se puntúa sin modelos: cada hecho plantado lleva un marcador que debe aparecer en lo que devuelve recall, de modo que la puntuación es reproducible en la máquina de otro. Los distractores invierten la puntuación: un hecho marcado con must_not_store cuesta un punto si el almacén lo ha conservado, porque un sistema de memoria se juzga tanto por lo que rechaza como por lo que conserva.

score 1.0 (10/10)   decision 1/1  number 2/2  negation 1/1  reversal 1/1
                    conditional 1/1  landmine 1/1  returning 1/1  distractor 2/2

Eso son diez hechos plantados en un banco de pruebas sintético, destilados por un Qwen3.8-27B local (NVFP4) como cerebro y escriba con max_items = 8, coverage_passes = 2, y puntuados con el mismo modelo como pensador. Un modelo diferente dará una puntuación diferente: la puntuación mide un pipeline más modelo, y el banco de pruebas existe para que el modelo sea lo único que varíe. Mide si un hecho es encontrable, no si la respuesta se lee bien: juzgar la prosa necesita un modelo, y entonces el benchmark deja de ser reproducible.

kura distill run escribe una línea por lote en _still/metrics.jsonl, que es de donde proviene el lado bruto. El lado canónico cuenta solo las memorias cuyo manifiesto de evidencia apunta a un lote registrado — dividir un almacén entero por el material bruto de unos pocos lotes da un número desviado por un orden de magnitud, y la primera versión de este comando hizo exactamente eso. Las memorias anteriores a los manifiestos se notifican como unattributed, no se incluyen silenciosamente. El lado bruto es siempre la estimación del destilador en el momento de beber, así que con --tokenizer-command la proporción se etiqueta como mixed.

Contra qué se ejecuta

requisito

Python

3.11+ (sin dependencias; pip install -e ".[dev]" solo añade pytest)

Node

20+, solo para el plugin DSH

zstd

solo para leer archivos de sesión DSH

endpoint de modelo

cualquier cosa que responda a POST <url>/chat/completions con el formato OpenAI

"OpenAI-compatible" es más estricto que "cualquier proveedor". La API nativa de un proveedor necesita una pasarela compatible con OpenAI delante; su propia URL no sirve. Un servicio estricto también rechaza campos de nivel superior desconocidos, así que configura dialect = "openai" (o "generic") — el valor predeterminado "vllm" envía chat_template_kwargs, que los servidores locales quieren y uno estricto responde con 400. El cliente reintenta una vez con un cuerpo simple y registra por qué falló una llamada, en lugar de reducir cada causa a un None silencioso.

Pruebas

python3 -m pytest tests -q                              # 145 tests, no model required
cd dsh-plugin && npm test                               # 24 more for the plugin

La compuerta se prueba de forma adversarial: cada caso es una forma en que un modelo real intentó realmente colar algo. test_containment.py está escrito de la misma manera — cada caso es un intento de escape, no un camino feliz — porque protege un agujero que era real: un almacén solía responder por cualquier archivo cuya ruta pudieras escribir. La prueba de extremo a extremo ejecuta un ciclo completo de distil→drain contra un servidor de modelos con guion en un socket real.

Licencia

MIT.

Install Server
A
license - permissive license
A
quality
C
maintenance

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Servers

  • F
    license
    Not graded
    quality
    D
    maintenance
    Provides AI agents with persistent, searchable memory that survives across conversations using semantic search, temporal versioning, and smart organization. Enables long-term context retention and cross-session continuity for AI assistants.
    14
  • A
    license
    Not graded
    quality
    A
    maintenance
    Provides persistent, searchable memory for MCP-compatible agents, enabling recall by meaning, automatic decay, trust scoring, and cross-agent handoffs.
    4
    MIT
  • A
    license
    A
    quality
    A
    maintenance
    Provides long-term memory and a temporal knowledge graph for AI agents, enabling persistent memory and reasoning across sessions.
    26
    1
    MIT

View all related MCP servers

Related MCP Connectors

  • Shared, governed long-term memory for AI agents across tools and sessions via MCP and REST.

  • Persistent memory for AI agents. Search, store, and recall across sessions.

  • Persistent memory and knowledge management for AI agents with semantic search and 50+ tools.

View all MCP Connectors

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/kisaragi-mochi/distill-kura'

If you have feedback or need assistance with the MCP directory API, please join our Discord server