by lna-lab
蒸留蔵 — distilled long-term memory for agents: recall by meaning, writing gated by evidence, one kura per agent mode. Ships as a DeepSeek Harness plugin and an MCP server.
# Add to your Claude Code skills
git clone https://github.com/lna-lab/distill-kuradistill-kura is an open-source ai agents skill for AI coding assistants such as Claude Code, Codex CLI, and ChatGPT, built by lna-lab. 蒸留蔵 — distilled long-term memory for agents: recall by meaning, writing gated by evidence, one kura per agent mode. Ships as a DeepSeek Harness plugin and an MCP server. It has 50 GitHub stars.
distill-kura's catalog security scan is still queued. You can run an instant dependency and prompt-injection check now with the "Scan for vulnerabilities" button above.
Clone the repository with "git clone https://github.com/lna-lab/distill-kura" and add it to your Claude Code skills directory (see the Installation section above).
distill-kura is primarily written in Python. It is open-source under lna-lab on GitHub, so you can review or fork the full source.
Yes. SkillsLLM lists many other AI Agents skills you can browse and compare side by side. Open the AI Agents category from the badge at the top of this page, or use the Related Skills and comparison links further down to weigh distill-kura against similar tools.
No comments yet. Be the first to share your thoughts!
Unlocks once the catalog security scan passes (runs nightly).
⚠️ Third-Party Software Notice
This skill is third-party open-source software developed and hosted independently on GitHub. SkillsLLM is an informational directory and does not control or maintain the underlying repository.
Any security checks, ratings, or warnings displayed by SkillsLLM are automated and limited in scope. They do not constitute a security certification or guarantee that the software is safe, error-free, or free from malicious code, vulnerabilities, compromised dependencies, or prompt-injection risks.
Review the source code, permissions, dependencies, and configuration before installing or running any third-party skill. Use is at your own risk. To the maximum extent permitted by applicable law, SkillsLLM is not liable for losses arising from third-party software.
The deep catalog scan for this skill is still queued. Run an instant dependency check now instead.
A long-term memory for agents that is distilled, not accumulated. Recall works by meaning, writing is gated by evidence, and one server can hold several separate memories — one per agent mode — so switching mode switches what the agent remembers.
Ships as a DeepSeek Harness plugin, an MCP server for any other host, an HTTP service, and a Python library. Standard library only; no vector database, no embeddings, no framework.
┌── recall ──────────────────────────────────────────────┐
│ question → names a memory? → deterministic hit ~2 ms│
│ else → 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 │
└────────────────────────────────────────────────────────┘
Two failures kill an agent's long-term memory, and they kill it from opposite sides.
Retrieval by keyword misses the thing you needed. A question about "SSD inference chips" shares no word with a memory titled "running the 2.6T model off an SSD tier" — yet they are the same subject. Word search returns nothing; the agent answers from nowhere. The fix here is not embeddings but recognition: the entire index (one line per memory, written as a recognition trigger) goes into one prompt, and a small model names what bears on the question. An index of ~500 memories is around 6k tokens — a few percent of a modern context window, and it sits in the prefix cache.
Writing everything poisons the store. An agent asserts something; a naive distiller records the assertion as a fact; the next agent reads it back as ground truth and repeats it with more confidence. That loop is self-reinforcing, and prompt instructions do not stop it — measured, not assumed. So the write path is gated by deterministic Python: every candidate memory must carry quotes that exist character-for-character in the raw material, tagged with where they came from.
| class | what it is | what it licenses |
|---|---|---|
[USER] |
the human's own words | "they decided", "they asked" |
[TOOL] |
machine output | numbers — the only source |
[ACT] |
a tool that was invoked | "this was done" |
[SELF] |
the agent's own prose | a judgement, in the first person, never a bare fact |
A quote that is not found verbatim is discarded. A candidate with no surviving quote is
thrown away. A number with no [TOOL] behind it is stripped. Text crediting the human
with a decision, when no [USER] quote survived, is refused at the last gate. Ideas are
welcome — they go to a seed file, never to the store, and graduate only when later
evidence confirms them.
The recall above is the right tool for a question that shares no words with its memory. It is the wrong tool for a question that names what it wants — and in a working session, most questions do. A blind 40-question benchmark on a live 317-memory store split exactly along that line: the direct questions needed no intelligence at all, and everything else needed all of it.
So before the thinker runs, a deterministic recognizer gets one look. The design is transposed from the n-gram embedding table inside Qwen3.8-Flash-Next — many hash heads voting over one table, behind a gate — onto the index:
Blind-tested — the examiner wrote the 40 questions from the index alone, never seeing the implementation — against the live store over HTTP:
| question type | tier zero | thinker tier |
|---|---|---|
| direct (14) | 14/14, median 2.3 ms | 14/14, ~900 ms |
| paraphrase (10) | silent → falls through | 10/10 |
| semantic bridge (10) | silent → falls through | 10/10 |
| not in the store (6) | 6/6 refused | 0/6 refused |
| wrong answers, whole set | 0 | — |
What that buys:
how: "fastpath",
fastpath_verdict, fastpath_ms — so a slow answer is never a mystery.Configured under [fastpath] (enabled, on by default; gate), per-store
overridable like everything else. The row it will never win: a question that
shares no surface with its memory. That is the thinker's job, and the gate exists
to hand it over rather than guess.
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}'
Wear the index, so the agent always knows what is known:
kura weave # build the three-layer cloth
kura prefill # the block to put in the system prompt
Feed it your agent transcripts:
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
Nothing enters the store until drain (or a hand-run pour). Drafts carry their
evidence in an HTML comment, so you can always see why a memory exists.
Recall-by-tool answers "what do you know about X?" — but only once the agent has decided to ask. It never answers the question the agent does not think to ask: is there anything here at all? An agent that cannot see the map does not know what it is missing, so it guesses, and a confident guess about your household is precisely the failure this project exists to prevent.
So the index is also worn: a standing block in the system prompt, on every turn.
kura weave # re-weave the index into the three-layer cloth
kura prefill # print the block a host should inject
A blind A/B test — 20 questions, fat index vs slimmed index, scored without knowing which was which — settled the shape:
| band | fat | slim |
|---|---|---|
| overall | 9 | 11 |
| recent events | 4 | 1 |
| doctrine | 1 | 4 |
| cross-domain leaps | 1 | 4 |
The doctrine lines were byte-identical in both indexes, and the slim index still won that band: a lighter surround makes the standing lines work better. Detail is not the source of insight. It earns its place only where things are still moving.
| layer | rule | line |
|---|---|---|
| pinned | frontmatter type in pinned_types |
kept in full |
| fresh | changed within fresh_days |
kept in full |
| trigger | everything else | compressed to ~trigger_tokens |
Trigger lines are written by the scribe model and cached in a ledger keyed on the
description and the budget, so a re-weave in the steady state costs nothing. With no
model reachable the loom trims mechanically instead — a memory system must not go blank
because a GPU is down.
Age is not mtime. cp -r, a restore or a checkout resets every timestamp, the whole
index turns "fresh", nothing is trimmed, and the mechanism has silently switched itself
off. So the loom prefers a date written inside the memory, and distrusts any mtime
that a fifth of the store shares with one calendar day.
- id: kura
name: distill-kura
config: { store: eq, promptOrder: -50 } # before the persona
A prefix cache is lost from the first changed byte onward — measured on one local server: an identical 4,029-token preamble reprices from 0.68 s to 0.14 s, appending at the end stays 0.14 s, and one word added at the front costs the whole cache (0.66 s). The persona commonly carries a clock, so it changes every minute; the map is the largest block in the prompt and changes a few times a day. The big stable thing goes in front of the thing that ticks.
The block itself therefore contains no date, no clock, no counter — and build()
refuses a header that d