Skip to main content
Glama

蒸留蔵 — distill-kura

Долговременная память для агентов, которая дистиллируется, а не накапливается. Воспоминание работает по смыслу, запись ограничена доказательствами, и один сервер может хранить несколько отдельных воспоминаний — по одному на режим агента — так что переключение режима меняет то, что агент помнит.

Поставляется как плагин DeepSeek Harness, MCP-сервер для любого другого хоста, HTTP-сервис и Python-библиотека. Только стандартная библиотека; никакой векторной базы данных, никаких эмбеддингов, никакого фреймворка.

        ┌── 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           │
        └────────────────────────────────────────────────────────┘

Зачем это существует

Два сбоя убивают долговременную память агента, и они убивают её с противоположных сторон.

Поиск по ключевым словам упускает то, что было нужно. Вопрос о «чипах для инференса SSD» не имеет ни одного общего слова с воспоминанием под названием «запуск модели 2.6T с SSD-уровня» — и всё же это одна и та же тема. Поиск по словам ничего не возвращает; агент отвечает из ниоткуда. Решение здесь — не эмбеддинги, а распознавание: весь индекс (одна строка на воспоминание, записанная как триггер распознавания) помещается в один промпт, и небольшая модель называет то, что относится к вопросу. Индекс из ~500 воспоминаний — это около 6k токенов — несколько процентов современного контекстного окна, и он лежит в кэше префикса.

Запись всего подряд отравляет хранилище. Агент делает утверждение; наивный дистиллятор записывает утверждение как факт; следующий агент читает его как истину и повторяет с большей уверенностью. Этот цикл самоподкрепляющийся, и инструкции в промпте его не останавливают — это измерено, а не предположено. Поэтому путь записи ограничен детерминированным Python: каждая кандидатная память должна содержать цитаты, которые существуют символ-в-символ в исходном материале, с указанием, откуда они взялись.

класс

что это

что это разрешает

[USER]

собственные слова человека

«они решили», «они попросили»

[TOOL]

вывод машины

числа — единственный источник

[ACT]

инструмент, который был вызван

«это было сделано»

[SELF]

собственная проза агента

суждение от первого лица, никогда не голый факт

Цитата, не найденная дословно, отбрасывается. Кандидат без выжившей цитаты выбрасывается. Число без подкрепляющего [TOOL] удаляется. Текст, приписывающий человеку решение, когда ни одна цитата [USER] не выжила, отклоняется на последнем рубеже. Идеи приветствуются — они идут в файл семян, никогда в хранилище, и получают статус только когда позднейшие доказательства их подтверждают.


Related MCP server: Synapto

Быстрый старт

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}'

Носите индекс, чтобы агент всегда знал, что известно:

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

Скармливайте ему транскрипты вашего агента:

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

Ничто не попадает в хранилище, пока не выполнен drain (или запущенный вручную pour). Черновики несут свои доказательства в HTML-комментарии, так что вы всегда можете видеть, почему существует память.


Карта резидента

Вызов-по-инструменту отвечает на вопрос «что ты знаешь об X?» — но только после того, как агент решил спросить. Он никогда не отвечает на вопрос, который агент не догадывается задать: есть ли здесь вообще что-нибудь? Агент, который не видит карту, не знает, чего ему не хватает, поэтому он догадывается, а уверенная догадка о вашем хозяйстве — это именно тот сбой, который этот проект призван предотвратить.

Поэтому индекс также носится: постоянный блок в системном промпте, на каждом ходу.

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

Три слоя, потому что детализация окупается только для недавнего

Слепой A/B-тест — 20 вопросов, полный индекс против урезанного, оценка без знания того, какой был какой — определил форму:

диапазон

полный

урезанный

в целом

9

11

недавние события

4

1

доктрина

1

4

кросс-доменные скачки

1

4

Строки доктрины были побайтово идентичны в обоих индексах, и урезанный индекс всё равно выиграл этот диапазон: более лёгкое окружение заставляет постоянные строки работать лучше. Детализация — не источник понимания. Она оправдывает своё место только там, где вещи ещё движутся.

слой

правило

строка

закреплённый

frontmatter type в pinned_types

сохраняется полностью

свежий

изменён в пределах fresh_days

сохраняется полностью

триггер

всё остальное

сжимается до ~trigger_tokens

Строки триггеров пишутся моделью scribe и кэшируются в журнале с ключом по описанию и бюджету, так что повторное плетение в стабильном состоянии ничего не стоит. Если модель недоступна, ткацкий станок обрезает механически — система памяти не должна глохнуть из-за того, что GPU лежит.

Возраст — это не mtime. cp -r, восстановление или checkout сбрасывают все временные метки, весь индекс становится «свежим», ничего не обрезается, и механизм молча отключает сам себя. Поэтому ткацкий станок предпочитает дату, записанную внутри памяти, и не доверяет любому mtime, который пятая часть хранилища делит с одним календарным днём.

Куда это идёт, и почему это решение о кэше

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

Кэш префикса теряется с первого изменённого байта — измерено на одном локальном сервере: идентичный преамбул из 4 029 токенов переоценивается с 0,68 с до 0,14 с, добавление в конец остаётся 0,14 с, а одно слово, добавленное в начало, стоит всего кэша (0,66 с). Персона обычно несёт часы, так что она меняется каждую минуту; карта — это самый большой блок в промпте и меняется несколько раз в день. Большой стабильный блок идёт перед тем, что тикает.

Поэтому сам блок не содержит ни даты, ни часов, ни счётчика — и build() отказывает заголовку, который их содержит, во время сборки, а не через таинственно медленные ходы три недели спустя.

Она никогда не отдаёт полкарты

ситуация

что получает агент

всё в порядке

карту, между маркерами <<<KURA-MAP>>>

свыше budget_fraction

всю карту и предупреждение в JSON (никогда в тексте — баннер — это изменчивое содержимое)

свыше hard_fraction

заглушку без строк индекса, говорящую, что карта отсутствует, а не пуста

kura недоступен

явное примечание, что карта отсутствует, никогда не пустую строку

Усечённая карта — худший из доступных артефактов: она выглядит полной, и каждое воспоминание ниже среза выглядит несуществующим. weave сократит окно свежести, чтобы вписаться, но никогда не отбросит строку — и если ни одна настройка не достигает бюджета, он сообщает об этом, сохраняет лучшую карту и показывает вам, где вес.

Как доставить её в хост

хост

механизм

DSH

нативный плагин — systemPrompt.section, обновляется в фоне

Claude Code, VS Code, Goose

MCP instructions несёт короткий указатель (лимит 2KB); сама карта приходит из инструмента kura_map или хука сессии, запускающего kura prefill

Claude Desktop, claude.ai

игнорируют instructions полностью — используйте kura_map

что угодно ещё

GET /prefill?format=text, или kura prefill в шелл-хуке

Поле MCP instructions — это MAY в спецификации, и индекс на 9 000 токенов не может пройти через лимит 2KB в любом случае, так что этот проект не притворяется иначе.


Режимы: больше одной куры

Одна память, которая служит и «помоги мне это построить», и «помоги мне это обдумать», не служит ни тому, ни другому хорошо: воспоминание, которое помогает вам отлаживать, — это шум в разговоре о том, что делать дальше. Поэтому хранилище — это каталог, а режим сопоставляется с хранилищем.

[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"

Каждый маршрут принимает селектор, так что один процесс обслуживает их все:

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

Хранилища не разделяют воспоминания, индекс и водяной знак дистиллятора. Переключение режима действительно меняет то, что запоминается, — а не ту же память другим голосом.

Независимость как маршрутизация, а не как конфиденциальность. У сервера нет аутентификации, так что любой процесс, который может достичь его порта, может назвать любое хранилище, которое он держит. Привязка агента удерживает модель в её полосе; она не удерживает процесс снаружи. Один уровень доверия на процесс — docs/TRUST.md короткий, и его стоит прочитать, прежде чем в хранилище попадёт приватное. Он также покрывает две границы, которые легко упустить: два хранилища, пьющие из одного корня журнала, и два хранилища за одной конечной точкой модели.

С DeepSeek Harness

DSH переключает персону и инструменты по пресету агента. distill-kura переключает память по хранилищу. Свяжите их, и одно изменение пресета сдвигает всё «я» целиком:

# .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.)

Оставьте allowSwitch по умолчанию, и агент также получит kura_use, так что он сможет перемещаться между курами в середине разговора без смены пресета. Инструменты: kura_recall, kura_read, kura_doctor, kura_list, kura_use и kura_remember (только когда хранилище доступно для записи). Полная схема подключения, включая MCP-мост и правило области isolate для сервисных строк, — в examples/dsh-presets/.

Персона — дело хоста, не наше. Этот проект никогда не рендерит и не внедряет персону; он только записывает, по хранилищу, какой файл персоны принадлежит ему, доступный по GET /profile?store=eq, чтобы две половины можно было держать в шаге тому, кто владеет пресетом. Инструкции агента также остаются с механизмом AGENTS.md хоста — см. AGENTS.md в этом репозитории для соглашений, которым должен следовать агент, работающий над этой кодовой базой.

С любым 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" }
}}}

Оставьте KURA_STORE неустановленным для свободного режима: инструменты принимают необязательный аргумент store, и kura_use переключает для сессии.


Модели: одна по умолчанию, обновляйте роль за раз

Три роли, а не три машины:

роль

когда запускается

требования

thinker

каждый вызов воспоминания

маленькая и быстрая; должна судить о релевантности по смыслу

brain

дистилляция: читает целую партию журнала

длина контекста и терпение

scribe

дистилляция: пишет память, затем оценивает черновики

хорошая проза на вашем языке, суждение

Объявите только [models.thinker], и одна модель делает всё три. Обновите любую из остальных независимо — более крупную локальную модель или онлайн-API (любой OpenAI-совместимый /chat/completions; ключ читается из переменной окружения, которую вы называете, и никогда не хранится в конфиге):

[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"

Две вещи, которые это обрабатывает за вас: диалекты усилия рассуждения различаются по семействам моделей (reasoning_effort, thinking_effort, enable_thinking), поэтому отправляются все из них — неизвестная игнорируется шаблоном, а модель, оставленная на глубоком мышлении по умолчанию, может потратить весь свой бюджет на рассуждение и ничего не вернуть. И текст устава размещается побайтово идентично в начале промпта каждой роли, так что на медленной локальной модели три роли разделяют один кэшированный префикс вместо трёх префиллов.

Если мыслитель недоступен, recall не замолкает — он откатывается к пересечению слов и помечает ответ how=words, который инструменты отображают как ⚠ degraded. Тихая деградация хуже деградации.


Как выглядит память

Один файл — один факт.

---
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]]

И одна строка в MEMORY.md:

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

Эта строка — единственное, что читается каждый раз. Это триггер распознавания, а не сводка: имена собственные, числа, ⚠️ мины, сделанный вывод. Если строку можно поменять местами со строкой другого воспоминания, и она по-прежнему будет читаться нормально, она не выполняет свою работу — kura distill tidy находит механически обнаруживаемые случаи и переписывает их.

kura doctor сообщает о количестве, битых ссылках, островах (воспоминаниях, на которые ничто не ссылается) и дрейфе индекса. Это глаз, необходимый метаболизму.


HTTP-поверхность

route

что делает

POST /recall

{question, hops, top, chars, total_chars, store|mode} → выбрано, пройдено, контекст. chars — на одно воспоминание; total_chars — жёсткий потолок для всего контекста

POST /remember

{slug, description, body, type, title} — ПРЯМАЯ запись, отклоняется, если write_policy = "direct-allowed" не установлено

GET /index

необработанный индекс

GET /prefill

резидентный блок, готовый к внедрению (&format=text для хука)

GET /memory/<slug>

одно воспоминание полностью

GET /doctor

здоровье одного хранилища (?all=1 для всех хранилищ)

GET /stores

хранилища, режимы и какая модель выполняет какую роль

GET /profile

устав хранилища и указатель на его персону (здесь никогда не отображается)

GET /health

живость

Любой маршрут принимает ?store= / ?mode=, поле store/mode в теле или префикс пути /s/<name>/…. Без аутентификации: привяжитесь к loopback или поставьте что-то перед ним.


Заметки по дизайну, которые стоит прочитать перед изменениями

  • docs/DESIGN.md — почему распознавание лучше поиска, что даёт шлюз и какой сбой мотивировал каждый механизм.

  • docs/OPERATING.md — запуск в резидентном режиме, планировщики и коды выхода, резервное копирование, за чем следить.

  • docs/TRUST.md — что такое граница хранилища, а что нет, политики записи и две границы, которые легко упустить (общие журналы, общие модели). Прочтите, прежде чем создавать приватное хранилище.

Несколько решений, которые выглядят странно, пока не столкнёшься с тем, от чего они защищают:

  • Резервируй, прежде чем пить. Дистиллятор резервирует участок журнала до его чтения, под блокировкой, и водяные знаки движутся только вперёд. Два дистиллятора, каждый из которых записывал свой снимок, стирали прогресс друг друга и по дюжине раз перепивали одну и ту же воду.

  • Водяные знаки — это единицы на адаптер. Байтовые смещения для журналов с добавлением, порядковые номера для архивов, которые перезаписываются (байтовое смещение в пережатом файле — это ложь).

  • Подавление эха. Цитата, которая уже есть в хранилище, — это не новый материал; это хранилище читает само себя через результат инструмента. Без этого система памяти вечно переоткрывает и перезаписывает собственное содержимое.

  • Последний шлюз — это модель, а не человек. Если человек должен утверждать каждый черновик, система тихо делает этого человека своим узким местом, и черновики копятся вечно. Ничто в цикле не должно требовать того, кого нет рядом.

  • kura distill run завершается с кодом 2, когда делать было нечего. Планировщик должен отличать «сделал работу» от «ничего не нашёл», иначе сторожевой пёс крутится в пустой очереди и морит голодом шаги, которым нужно время простоя.

Измеряем, а не заявляем

На два вопроса отвечают одним числом, и не следовало бы.

Насколько меньше? store_ratio = токены в воспоминаниях и индексе / токены реально потреблённого сырого журнала. Что потеряно? Это другое измерение, и хранилище, которое хранит одно воспоминание из ста, прекрасно набирает баллы по первому, оставаясь бесполезным.

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

Измерено здесь, с поставляемыми фикстурами и встроенным оценщиком:

корпус

store_ratio

scripts/demo-clean-room.sh (обычный чат, в основном вода)

0.18

bench/fixtures/corpus.jsonl (плотный: каждая строка — сигнал)

1.14

Второе — не баг. На материале, где нет воды, дистилляция не сжимает — каждое воспоминание добавляет своё зачем и как применить, и хранилище получается чуть больше стенограммы. Коэффициент — свойство корпуса, а не этого инструмента, поэтому здесь нет ключевой цифры и поэтому команда сообщает, с чем она считала.

Удержание оценивается без модели: каждый посаженный факт несёт маркер, который должен появиться в том, что возвращает recall, поэтому оценка воспроизводима на чужой машине. Дистракторы инвертируются: факт, помеченный must_not_store, стоит очко, если хранилище его сохранило, потому что систему памяти судят и по тому, что она отклоняет, и по тому, что сохраняет.

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

Это десять посаженных фактов в синтетической фикстуре, дистиллированных локальной Qwen3.8-27B (NVFP4) в роли мозга и писца с max_items = 8, coverage_passes = 2 и оценённых той же моделью в роли мыслителя. Другая модель даст другой балл: оценка измеряет конвейер-плюс-модель, и фикстура существует, чтобы модель была единственной переменной. Она измеряет, находим ли факт, а не хорошо ли читается ответ, — оценка прозы требует модели, и тогда бенчмарк перестаёт быть воспроизводимым.

kura distill run пишет по одной строке на пакет в _still/metrics.jsonl, откуда берётся сырая сторона. Каноническая сторона считает только те воспоминания, чей манифест свидетельств указывает на записанный пакет — делить всё хранилище на сырьё нескольких пакетов — это число не в ту сторону на порядок, и первая версия этой команды делала именно так. Воспоминания, предшествующие манифестам, помечаются как unattributed, а не молча включаются. Сырая сторона — это всегда оценка дистиллятора на момент питья, поэтому с --tokenizer-command коэффициент помечается как mixed.

С чем это работает

требование

Python

3.11+ (без зависимостей; pip install -e ".[dev]" добавляет только pytest)

Node

20+, только для плагина DSH

zstd

только для чтения архивов сессий DSH

модельная конечная точка

всё, что отвечает на POST <url>/chat/completions в формате OpenAI

«Совместимость с OpenAI» уже, чем «любой провайдер». Собственному API вендора нужен совместимый с OpenAI шлюз перед ним; собственный URL не подойдёт. Строгий сервис также отклоняет неизвестные поля верхнего уровня, поэтому установите dialect = "openai" (или "generic") — по умолчанию "vllm" отправляет chat_template_kwargs, которые нужны локальным серверам, а строгий отвечает 400. Клиент повторяет попытку один раз с простым телом и записывает, почему вызов не удался, а не схлопывает все причины в молчаливый None.

Тесты

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

Шлюз тестируется состязательно: каждый случай — это способ, которым реальная модель пыталась что-то протащить. test_containment.py написан так же — каждый случай это попытка побега, а не счастливый путь, — потому что он закрывает реальную дыру: хранилище отвечало за любой файл, путь к которому можно было произнести. Сквозной тест прогоняет полный цикл distil→drain против скриптованного модельного сервера на реальном сокете.

Лицензия

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