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 推理芯片" 的问题,与一条标题为 "在 SSD 层上跑 2.6T 模型" 的记忆没有任何共同词——但它们其实是同一个主题。词面搜索一无所获;智能体凭空作答。这里的解法不是嵌入,而是识别:整个索引(每行一条记忆,写成一条识别触发器)放进一个提示词里,由一个小模型指出哪些与问题相关。约 ~500 条记忆的索引大约 6k token——只占现代上下文窗口的百分之几,而且它位于前缀缓存中。

什么都写会毒化存储。 智能体断言了一件事;一个天真的蒸馏器把断言记成事实;下一个智能体把它当作真相读回来,并以更高的置信度复述。这个循环是自我强化的,提示词指令拦不住它——要测量,不要假设。因此写入路径由确定性的 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 typepinned_types

完整保留

新鲜

fresh_days 内变动过

完整保留

触发器

其他一切

压缩到约 ~trigger_tokens

触发器行由 scribe 模型撰写,并缓存在一个以描述预算为键的账本中,因此稳态下的重新编织不花任何代价。没有模型可达时,织机改为机械裁剪——记忆系统不能因为一块 GPU 宕机就变空白。

年龄不是 mtime。 cp -r、一次恢复或一次检出会重置所有时间戳,整个索引变成"新鲜",什么都不裁剪,机制就悄悄关掉了自己。所以织机优先采用写在记忆内部的日期,并且不信任任何与存储中五分之一条目共享同一个日历日的 mtime。

它去哪里,以及为什么那是一个缓存决策

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

前缀缓存从第一个改变的字节起就失效了——在一台本地服务器上实测:一个相同的 4,029-token 前导从 0.68 s 重新计价到 0.14 s,在末尾追加保持 0.14 s,而在前面加一个词就赔掉整个缓存(0.66 s)。人设通常带一个时钟,所以它每分钟都在变;地图是提示词里最大的块,一天只变几次。大而稳定的东西放在会滴答作响的东西前面。

因此这个块本身不包含日期、时钟、计数器——而 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,或 shell 钩子里的 kura prefill

MCP instructions 字段在规范里是 MAY,而且 9,000-token 的索引无论如何也过不了 2KB 上限,所以这个项目不假装不是这样。


模式:不止一个 kura

一份同时服务"帮我构建这个"和"帮我把这个想清楚"的记忆,两件事都做不好:帮你调试的回忆,在讨论下一步该做什么的对话里就是噪音。所以存储是一个目录,模式映射到存储。

[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 而无需变更预设。工具:kura_recallkura_readkura_doctorkura_listkura_usekura_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_effortthinking_effortenable_thinking),所以全部都发送——未知的会被模板忽略,而默认深度思考的模型可能把整个预算花在推理上然后什么都不返回。还有,章程文本被逐字节放在每个角色提示词的头部,所以在慢速本地模型上,三个角色共享一个缓存前缀,而不是付三次预填充。

如果思考者(thinker)宕机,召回(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 接口

路由

作用

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>/… 路径前缀。没有身份验证:请绑定到回环地址,或在前面加一层代理。


动手修改前值得一读的设计说明

  • docs/DESIGN.md — 为什么识别优于搜索、门控带来了什么,以及每个机制背后的失败教训。

  • docs/OPERATING.md — 常驻运行、调度器和退出码、备份、需要关注什么。

  • docs/TRUST.md — 存储边界的含义与边界之外、写入策略,以及两个容易忽略的边界(共享日志、共享模型)。在部署私有存储之前请先阅读。

有些决定看起来很奇怪,直到你遇到它们所阻止的问题:

  • 先预留,再消费。 蒸馏器在读取一段日志之前,会在锁下认领它,水印只会向前推进。两个蒸馏器各自写回自己的快照,结果互相覆盖进度,把同一片水喝了十几遍。

  • 水印是每个适配器各自的单位。 追加式转录用字节偏移量,会被重写的归档用序列号(对重新压缩过的文件使用字节偏移量是自欺欺人)。

  • 回声抑制。 存储中已经存在的引用不是新素材——那是存储通过工具结果读回自身。没有这一点,记忆系统会永远重新发现并重新记录自己的内容。

  • 最后一道门是模型,而不是人。 如果每份草稿都必须由人来批准,系统就悄悄地把那个人变成了瓶颈,草稿会永远堆积。循环中不能有任何需要那个不总在场的人参与的环节。

  • kura distill run 在没有事情可做时退出码为 2。 调度器必须能区分"做了工作"和"什么都没找到",否则看门狗会在空队列上空转,饿死那些需要空闲时间的步骤。

用测量代替宣称

两个问题用一个数字来回答,这是不对的。

小了多少? store_ratio = 记忆和索引中的 token 数 / 实际消费的原始日志 token 数。丢失了什么? 这是另一个度量,一个只保留百分之一记忆的存储在第一个问题上得分很高,但毫无用处。

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

第二个不是 bug。在没有填充内容的材料上,蒸馏并不会压缩——每条记忆都增加了它的原因如何应用,存储最终会比转录稿稍大。这个比率是语料库的属性,而不是这个工具的属性,这就是为什么这里没有一个大标题数字,也是为什么命令会报告它计算了什么。

保留率是无模型评分的:每个植入的事实都带有一个标记,该标记必须出现在召回返回的内容中,因此该分数可以在别人的机器上复现。干扰项则相反——如果存储保留了一个标记为 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 会话归档

模型端点

任何以 OpenAI 格式响应 POST <url>/chat/completions 的服务

"兼容 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 的编写方式相同——每个用例都是一次逃逸尝试,而不是快乐路径——因为它守护的是一个真实存在的漏洞:存储曾经会为任何你能拼出路径的文件作答。端到端测试在真实套接字上针对脚本化的模型服务器运行完整的 蒸馏→排空 周期。

许可证

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