dsh-context-reclaim
工具结果进历史之前就回收,前缀缓存永不失效。
> ⚠️ 只支持 DSH 1.5(>=0.1.5-rc.2 <0.2.0)。DSH 出新版本会跟进更新。
> 🟢 冲突概率极低。 本插件占 5 个服务端接缝、0 个客户端槽位 —— 实测与本机 44 个已装插件里 19 个同赛道插件共存无冲突,且不占任何 chain 型槽位(唯一会让插件互相顶掉的类型)。详见下方兼容性。
English · 简体中文
这是什么
DSH 会话的上下文不是被对话撑爆的,是被工具结果撑爆的:一次 read 几百行、一次 pwsh 一大片日志、一份系统提示每轮原样重复。这些内容一旦进了历史,就只能靠压缩器事后动刀——而压缩要重写已提交的历史,会把服务端前缀缓存整段打掉,越压越贵。
dsh-context-reclaim 换了个位置:在工具结果被 append 进历史之前就处理掉,并且从不改写已经提交的内容。所以前缀缓存永远不会因为本插件而失效。
它挂在 5 个接缝上:
| 接缝 | 做什么 | 默认 |
|---|---|---|
tools/post-execute |
工具结果一产生就净化 + 按内容类型有界化,截断处插入取回指针 | 开 |
system-prompt/assemble |
折叠工具定义面、稳定前缀段顺序、截断超长描述行 | 开 |
tools/pre-execute |
重复编辑拦截(同一个文件同一段内容改第二次直接拦下) | 开 |
session/event |
摘要完整性审计(压缩摘要缺段时报警) | 开 |
llm/stream |
每请求一行成本归因,打出 route / prompt / 缓存命中 / 输出 | 开 |
1. 工具结果净化与有界化
这是核心。每次工具调用返回时,插件先做无损净化,再做有界化。
无损净化
折叠 ANSI 转义序列、去掉行尾空白、统一 CR。这一步是纯净化,不丢任何信息。
只有在内容同时满足两个门槛时才动手:长度 ≥ 32 字符,且净化后缩减比例 ≥ 0.2%。小结果根本不碰。
关掉它:config.noise = false 或环境变量 DSH_RECLAIM_NOISE=0。
按内容类型有界化
不同类型的工具结果,容忍度完全不同。插件把工具分成三类:
| 类别 | 覆盖的工具 | 头 / 尾保留 | 单行上限 | 总字符上限 |
|---|---|---|---|---|
| 只读 | read grep glob codegraph_* lsp_* |
80 行 / 12 行 | 512 | 160 |
| 有副作用 | pwsh bash docker_* job_output |
40 行 / 40 行 | 1024 | 256 |
| 保真 | web_fetch web_search knowledge_* read_image pilot_snapshot |
原样不动 | — | — |
认不出来的工具按保真处理——宁可不省,也不误伤。
被截断的地方会插入一个取回指针,告诉你原文还有多少、可以用什么方式取回,而不是无声无息地丢掉。
有界化的触发门槛是「能省下超过 5%」;省得不多就不动。
关掉它:config.bounded = false 或环境变量 DSH_RECLAIM_BOUNDED=0。
> 关于超大结果:超过 config.maxInlineBytes(默认 50000)的内容会交给 spill 机制落盘。这需要你的 profile 里装了 spill 插件——没有装的话,这个阈值不生效,超限内容原样通过。
保真白名单
三类工具的名单都可以在配置里整表替换:
- id: reclaim
config:
readOnlyTools: ['read', 'grep', 'glob'] # 整表替换,不是追加
sideEffectTools: ['pwsh', 'bash']
faithfulTools: ['web_fetch', 'web_search']
注意这是整表替换,不是逐字段合并——只写 headLines 会丢掉字符上限。要么整张表写全,要么别写。
2. 工具定义面折叠
每个会话一开始,DSH 会把所有可用工具的完整定义(名字、描述、参数 schema)塞进系统提示。装了几十个插件之后,这一块能占到很大比例,而其中大部分工具在这一轮里根本不会被用到。
插件默认折叠这部分:保留最常用的若干个完整定义,其余压成一行清单(只有名字 + 一句话描述,没有参数 schema)。
被折叠的工具照样能调。 一行清单只是把 schema 从提示里拿掉了 —— 工具本身仍然注册在宿主上,所以模型依然可以用 search_tools 找到它、用 invoke_tool 直接调用,或用 load_plugin 让它重新常驻。没有 load_tools 这个工具,配对的是 search_tools + invoke_tool。如果你更希望每个 schema 都留在提示里,就关掉折叠(见下一行)。
关掉它:config.foldTools = false 或环境变量 DSH_RECLAIM_TOOL_FOLD=0。
3. 前缀稳定化
服务端前缀缓存只在前缀逐字节不变时才命中。而系统提示里天生带一堆每轮都在变的东西:token 计数、时间戳、会话序号。
插件默认抹平这些易变数字,并重排段落顺序让稳定的段排在前面、易变的段排在后面。每次重排会在日志里打一行 [reclaim/section-order],告诉你哪几段被移动了。
4. 超长描述行截断
系统提示里偶尔会有单行特别长的描述(某些插件写的长注释)。超过阈值(config 里没有暴露,环境变量 DSH_RECLAIM_PROMPT_LINE_MAX,默认 120 字符)的行会被截断,并在日志打出被截断的行号。
5. 重复编辑拦截
同一个文件、同一段内容,如果模型连续两次发起完全一样的修改,第二次会被拦下并返回说明——这种调用几乎必然是无意义的重复,白白烧一次往返。
日志行:[reclaim/dedup-deny]。
关掉它:环境变量 DSH_RECLAIM_DEDUP_DENY=0。
6. 摘要完整性审计
压缩器生成的摘要如果被输出预算截断,会静默变短——你不会收到任何提示,只是下一轮开始上下文就缺了一块。
插件在每次压缩后审计摘要是否包含应有的结构段,缺了就报警:
[reclaim/summary-audit] ⚠️ 摘要缺 3/8 段
它只报警,不改摘要。真正防止摘要被截断的是 dsh-aux-degrade,见下面的联动章节。
7. 成本归因
每个请求结束打一行:
[reclaim/usage] route=aux prompt=12043 hit=11890 miss=153 out=204 purpose=compaction
hit / miss 是前缀缓存的命中与未命中字符数,purpose 是这次调用的用途(如果有)。缓存被打破时会另外打一行 [reclaim/cache-break]。
这个日志是排查「为什么我的缓存命中率不高」最快的入口。
8. 接管「可写请求」——不需要给 dsh-llm 打补丁
这一段就是「装完插件就不必再改文件」的原因。
原版 @deepseek-ai/dsh@0.1.5-rc.2 里,主线路的对话请求在进入 @deepseek-ai/dsh-llm 之前就已经被宿主深度冻结。它的入口长这样:
stream(options) { return this.streamWithRegistration(options) }
streamWithRegistration(options, prepared) {
return this.ctx.waterfall(this, "llm/stream", options, () => this.adapterStream(options, prepared))
}
瀑布的 next 是忽略参数的闭包,适配器读的是外层那个 options。所以挂在 llm/stream 上的插件只能看这个请求,改不了它——往冻结对象上写会抛错,把新对象传给 next(...) 则完全没有效果。桌面版 DSH 一直随包带一个 dsh-llm/lib/index.js 的文件补丁,全部内容就六行:把冻结请求浅拷贝一份、连 messages 数组一起拷、用 markAgentLoopRequest 重新打标记,再把副本交给瀑布。
1.0.0 起,本插件在插件层做了同一件事。 它在加载时把 ctx.llm.streamWithRegistration 包一层:
- 请求冻结 → 浅拷贝 + 拷
messages数组 + 逐个拷消息对象,再用宿主自己那份dsh-llm模块里的markAgentLoopRequest重新打标记,副本才进llm/stream; - 请求没冻结(宿主已经打过补丁)→ 原样透传,逐字节不变,行为零变化;
- 出现任何意外 → 记一条日志后原样透传。本插件绝不会把一个能用的会话弄坏。
因此,一个补丁都没打的原版运行环境,不碰任何文件就能拿到可写请求。 在一个完全零补丁的真实运行时上(dsh --profile headless,真实请求,[reclaim/wire] 行)实测 A/B:
# 接管开启(默认)
[reclaim/wire] 入口探测:messages=5 条 · options 冻结=false · messages 冻结=false · route=deepseek-official|deepseek-flash|main|ba8a6419
# 接管关闭(DSH_RECLAIM_WRITABLE_REQUEST=0)
[reclaim/wire] 入口探测:messages=5 条 · options 冻结=true · messages 冻结=true · route=deepseek-official|deepseek-flash|main|d7d22e75
用 DSH_RECLAIM_WRITABLE_REQUEST=0 可以关掉它。启动日志会把实际情况说清楚:
[reclaim/writable] 已接管 llm.streamWithRegistration:冻结的主请求先浅拷贝再进 llm/stream ⇒ 不再需要给官方 dsh-llm 打文件补丁(可用 dsh-llm 副本 2 份)
它怎么找到「宿主自己那一份」模块
markAgentLoopRequest 往一个 WeakSet 里写,而这个集合只存在于某一个模块实例内部;Node 对每一个不同的模块 URL 都会造一份独立实例。用别的实例去标记等于白标:别的插件在那个集合里查不到你的对象。所以本插件:
- 从
process.argv推导候选位置(真正的…/@deepseek-ai/dsh/lib/bin.js——桌面外壳里它在argv[2],不是argv[1]),再用createRequire从宿主入口解析一次,再补上$DSH_HOME/profiles{/*}/node_modules这两条软链接拼法; - 逐个 import,并对着当前这个真实请求做往返自检:第一个
isAgentLoopRequest(options)返回true的,就是宿主自己那份,用它; - 一个都不匹配时,打一次警告,然后仍然用第一份接管。这条限制如实说: 这种情况下上下文改写照常生效,但
@deepseek-ai/dsh-session-title可能不再自动给会话起标题,因为它查的是同一个WeakSet。该现象在某种 npm 安装布局上出现过,桌面端布局上没有。DSH 内部除它之外没有别的消费者。
如果检测到 invariants 服务存在(dsh-agent-loop 的「循环请求必须冻结」断言),本插件会自动跳过接管——否则会话会被断言中断。现有 1.5 的任何 profile 都没有加载它,但真出现了我们宁可让路,也不弄断你的会话。
启动时的「官方补丁体检」——只报告,绝不动手
插件加载时会扫一遍它能定位到的官方补丁目标,然后打且只打一行:
[reclaim/patch-check] 官方补丁体检(只读,本插件不会自动改官方文件):7 个目标里未检测到 7 条 —— dsh-llm 可写请求、agent-loop 快照回收 / 系统提示收敛、…
它刻意做成只读。本插件不是补丁器:改动你 DSH 安装目录里的文件,是你的决定,不该是「装了个插件」的副作用。判据是启发式(找补丁脚本在文件旁边留下的 *.orig 备份),所以说的是「未检测到」而不是「没打」;一旦推导不出你的安装布局,它就完全闭嘴。用 DSH_RECLAIM_PATCH_CHECK=0 可以关掉。
真有想打又没打上的,别去找脚本——把这件事交给你的 agent。 本插件包里的 tutorial-install-and-official-patches.zh-CN.md(英文版 tutorial-install-and-official-patches.md)有一段可以直接整段复制的提示词,写清了每条补丁改哪个文件、验收标准是什么;你的 agent 读完就在你自己的机器上打。推荐走这条路,因为你的 agent 看得到你真实的安装目录,而写在插件里的硬编码脚本看不到。
> **补丁库:官方 10 条补丁已单独开源 → ** 补丁本体、自适应应用器、五种打法、一键回退都在里面。node apply.mjs --check 是只读体检,--apply 自动备份并逐个 node --check,--revert 一键还原。
其余官方补丁:插件能替代什么、不能替代什么
如实写明,因为装上这四个插件并不等于其余文件补丁就不需要了:
| 官方 1.5 文件补丁 | 改的哪里 | 能否用插件替代 |
|---|---|---|
@deepseek-ai/dsh-llm — 可写请求 |
lib/index.js |
✅ 能——本插件已经做了,见上 |
@deepseek-ai/dsh-client-ui-sidebar — 页脚槽位加 flex-wrap |
lib/lib-client.js |
✅ 能(客户端插件注入 CSS 即可);本项目未随包提供 |
@deepseek-ai/dsh-agent-loop — 运行时快照回收、系统提示收敛 |
lib/index.js |
❌ 不能——要改 agent-loop 私有的投影类与提交点 |
@deepseek-ai/dsh-llm-pi-ai — splitSystemPrompt 收集开头全部 system |
lib/index.js |
❌ 不能,而且没有上面那条 agent-loop 补丁时它没有意义 |
@deepseek-ai/dsh-compaction-tool-result-pruner — 结果剪枝 + 承重守卫 |
lib/index.js |
❌ 不能——宿主直接 new 那个导出的类,没有注册接缝 |
@deepseek-ai/dsh-client-ui-renderer — conversation.chat.turnTail 链式选举 |
lib/client.js |
❌ 不能——必须改官方客户端 bundle 本身 |
@deepseek-ai/dsh-session-format-v1-to-v2 — 修 turn/start |
lib/index.js |
❌ 不能——跑在会话恢复路径内部 |
@deepseek-ai/dsh-session-persistence-jsonl — 同上,持久化侧 |
lib/worker.cjs |
❌ 不能——跑在 worker 线程里 |
@earendil-works/pi-ai — max_tokens 下界 |
dist/api/simple-options.js |
❌ 不能——第三方 dist,其 ESM 命名空间只读 |
一句话总结:这四个插件原本必须依赖的那一条补丁已经不需要了;剩下几条改的是 DSH 自己的机器行为,属于可选增强。真要打:补丁本体已开源成补丁库 (10 条补丁 + 自适应应用器 + 五种打法 + 一键回退);「要不要打、每条是什么、怎么让你自己的 agent 去打」见本仓库内的 tutorial-install-and-official-patches.zh-CN.md。
9. 官方补丁提醒:装好就弹一次,指到哪条、怎么打
本插件受益的官方补丁,由本插件自己在界面上弹一次窗告诉你 —— 不靠别的插件代言,也不会去替别的插件弹。
- 哪条补丁:列出补丁 id、改哪个包的哪个文件、以及本插件为什么需要它(写清楚「必须」还是「建议」);
- 怎么打(三种,任选一种):A 点弹窗里的「复制提示词」,把整段发给你自己的 agent(它先跑只读体检、再逐条打、hunk 对不上就停下问你);B 用补丁库的脚本
node apply.mjs --check/node apply.mjs --apply --only;C 手动cd <被改包> && git apply -p1 <库>/patches//change.diff; - 补丁库:(10 条自包含补丁 + 自适应应用器 + 五种打法);
- 检测系统:按目标文件旁的
*.orig备份 + 内容哈希(对补丁库清单里的pristine/patched)判断打没打;两者都不确定时它明说「无法判定」,绝不猜「已打」; - 打完就不再弹。弹窗里可以「稍后再说」(只关这一次)或「不再提醒」(永久,且按补丁 id 记 —— 将来新增的补丁仍然会提醒你)。
同一个弹窗还会列出其余官方补丁(不绑定本插件,按你遇到的症状决定打不打),保证一条都不漏。
配置
配置文件是 profile 的 cordis.patch.yml:
- id: reclaim
config:
enabled: true
noise: true
bounded: true
stripLines: false
noOpGuardRatio: 0.05
maxInlineBytes: 50000
attribution: true
trimSections: false
foldTools: true
activePlugins: []
| 键 | 类型 | 默认 | 作用 |
|---|---|---|---|
enabled |
boolean | true |
总开关(!== false 即视为开) |
noise |
boolean | true |
无损净化(ANSI / 行尾空白 / CR) |
bounded |
boolean | true |
按内容类型有界化 + 取回指针 |
stripLines |
boolean | false |
剥离行号前缀 |
noOpGuardRatio |
number | 0.05 |
有界化「省得够多才动手」的门槛,5% |
maxInlineBytes |
number | 50000 |
超过这个字节数交给 spill(需装 spill 插件) |
readOnlyTools |
string[] | 见上表 | 只读类工具名单,整表替换 |
sideEffectTools |
string[] | 见上表 | 有副作用类工具名单,整表替换 |
faithfulTools |
string[] | 见上表 | 保真类工具名单,整表替换 |
attribution |
boolean | true |
打 [reclaim/usage] 归因行 |
trimSections |
boolean | false |
段落收纳(注意:实际闸门是环境变量 DSH_RECLAIM_SECTION_TRIM,见下) |
foldTools |
boolean | true |
折叠工具定义面 |
activePlugins |
string[] | [] |
参与段归属计算的插件名单 |
> ⚠️ trimSections 这个配置项的取值不会真正开门——代码里的闸门只认环境变量 DSH_RECLAIM_SECTION_TRIM,而且还需要 activePlugins 非空。这一条在旧文档里说法有误,这里如实写出。
常用环境变量
完整的环境变量有 40 多个,覆盖每一处内部阈值。按功能分组,最常用的这些:
| 变量 | 默认 | 作用 |
|---|---|---|
DSH_RECLAIM_NOISE |
1 |
设 0 关无损净化 |
DSH_RECLAIM_BOUNDED |
1 |
设 0 关有界化 |
DSH_RECLAIM_STRIP_LINES |
0 |
设 1 剥离行号 |
DSH_RECLAIM_TOOL_FOLD |
1 |
设 0 关工具定义面折叠 |
DSH_RECLAIM_PROMPT_LINE_MAX |
120 |
系统提示单行截断阈值 |
DSH_RECLAIM_DEDUP_DENY |
1 |
设 0 关重复编辑拦截 |
DSH_RECLAIM_SECTION_TRIM |
0 |
设 1 开段落收纳(配合 activePlugins) |
DSH_RECLAIM_STABILIZE |
1 |
设 0 关前缀稳定化 |
DSH_RECLAIM_WRITABLE_REQUEST |
1 |
设 0 停止接管 llm.streamWithRegistration(见第 8 节);关掉它之后才需要官方 dsh-llm 补丁 |
DSH_RECLAIM_PATCH_CHECK |
1 |
设 0 关闭启动时那行「官方补丁体检」提示 |
DSH_RECLAIM_LOG |
1 |
设 0 关本插件的日志输出 |
DSH_RECLAIM_DUMP_SYSTEM |
0 |
设 1 把组装好的系统提示转储到临时目录,用于排查 |
其余阈值类变量(采样行数、预算观测、污染检测窗口、归档上限、推理剥离等)在源码顶部有逐条注释。
📚 教程
| 文档 | 讲什么 |
|---|---|
| 安装与官方补丁(中文) | 怎么装(两条实测路线),官方补丁要不要打、打哪几条、怎么让你自己的 agent 去打 补丁本体已开源成补丁库: |
| Install & official patches (English) | the same, in English the patches themselves ship as a library: |
🔗 同系列
四个插件各自独立、可单装,但配在一起最省。另外三个:
dsh-aux-degrade— 辅助调用降级:压缩摘要 / 会话标题不再被高思考档烧光预算dsh-capability-composer— 能力组合:把常驻工具面收成几个组合,按需切换dsh-composer-launcher— 「+」启动器:一个面板统管组合 / 插件 / MCP,并引导装扩展
> 全部只支持 DSH 1.5,DSH 出新版本会跟进更新。需要打官方补丁的插件装好后会自己弹窗说清楚,一条不漏。
安装
从 GitHub(推荐,已在全新 DSH_HOME 上实测)
dsh plugin --profile web add github:xiamubumu/dsh-context-reclaim
一条命令装好;DSH 会自己把它登记进 profile 的 dsh.profile.bundles,不需要手改配置。
从本地 tarball(离线机器)
去 Releases 下载 dsh-context-reclaim-1.1.0.tgz,然后:
dsh plugin --profile web add /path/to/dsh-context-reclaim-1.1.0.tgz
从 DSH 插件市场 / npm(暂时还不行)
市场投稿(awesome-dsh-plugin 的 PR)还没提交,npm 上也还没发布 ⇒ 现在这两种方式都装不到。
收录 / 发布之后,市场里直接点安装、或者 dsh plugin --profile web add dsh-context-reclaim 才成立。
> ⚠️ 已知问题:不要拿 Release 资产的 tarball URL 直接装。
> dsh plugin --profile web add https://github.com/.../releases/download/v1.1.0/....tgz 在 DSH 自带的 pnpm 10.34.5 上会失败:
> ERR_PNPM_MISSING_TARBALL_INTEGRITY(pnpm#14351,上游在 pnpm ≥ 12.5.1 才修好)。
> 连装第二个插件时必然失败 —— 请用上面两条路(github: 简写,或先下载 tarball 再用本地路径)。
装完需要重启 DSH——宿主侧插件只在 DSH 启动时装载。
本插件没有任何运行时依赖,peerDependencies 只声明官方的 @deepseek-ai/dsh-tools。安装不会往你的 profile 里塞任何第三方包。
兼容性
共存实测:会不会和别的插件打架(2026-10-15 实测)
本插件挂了 5 个服务端接缝:llm/stream、tools/pre-execute、tools/post-execute、system-prompt/assemble、session/event —— 是四个自研插件里最多的一个。
本机 44 个已装插件里,19 个与本插件共享至少一个接缝,其中 18 个是协作式的(每个 handler 都调用 next() 往下传)。本插件与它们同机共存,包括共享 3 个接缝的 dsh-vision-router —— 实测无冲突。
原因:DSH 的事件接缝是中间件瀑布。只要各方都调 next()、只做增量修改,就是叠加而不是抢占。
客户端槽位:本插件一个都不占 —— 它是纯服务端插件,没有前端。
已知会撞的三种情况(如实)
- 对方在同一个服务端接缝上做整体重建(把 system prompt / 工具结果整个重写),而不是增量修改;
- 对方占用同一个
chain型槽位(本插件不占任何槽位,所以只有对方受这条影响); - 双方要求打同一个官方文件的补丁 —— 这一条会被补丁库的
apply.mjs --check明确报conflict,不会静默出错。
已实测共存:dsh-context-reclaim + billion-context@0.1.175 同机同 profile 无冲突 —— 前者走 Cordis 事件总线,后者走 agent 引导层(bootstrap-hook / native loader,自带 6 种 agent 的打包产物),两者没有共享接缝。
💡 搭配建议:再加一个「上下文压缩」插件(不是本项目的插件)
本插件负责的是「别让请求越长越大、别让前缀抖动」:工具结果净化、按类型有界化、工具定义面折叠、描述行截断、重复编辑拦截、摘要完整性审计、成本归因,以及把请求变成可写(第 8 节)。
它不负责把长历史压成摘要。所以推荐你再装一个上下文压缩 / 历史摘要类插件 —— 两者互补:压缩插件在对话变长时把历史收拢,本插件保证压缩前后前缀稳定、被压缩的原料(工具结果)本身不膨胀。前缀越稳,缓存越容易命中,也就越省钱。
实测记录(本机,2026-10-14,仅供参考)
| 指标 | 实测值 |
|---|---|
| 长会话上下文规模 | 涨不到 200K |
| 前缀缓存命中 | 约 98% |
| 成本(最佳情况) | 约半小时 不到 ¥3(人民币,按当时的模型单价与用量;峰值,不是常态) |
> ⚠️ 这组数字是「长时间连续运行时」的最佳/峰值表现 —— 不是常态,不是承诺,不是上限,也不是报价。
> 实际使用中很多情况会让它达不到:前缀被破坏(中途换模型 / 系统提示变化 / 上下文顺序改变 / 有插件往前面塞新内容);有新内容持续加进来(新技能目录、新 MCP 工具、超长文档、频繁贴大段材料);压缩时机不合适或工具结果超预期膨胀;单轮任务切换、频繁新开会话、工具面变化;模型单价、缓存策略或服务方变更。一切以你机器上的真实运行为准。
搭配对象由你自己选。 我们实测时用的那款是第三方插件 billion-context —— 它不是本项目的插件,与本项目没有任何关联(不是依赖、不是合作、不随本插件安装),这里只是如实记录「我们拿它一起跑过」。你要是用别家的压缩插件,同样可以配。
本插件只为自己承诺:前缀稳定化、工具结果有界化、压缩前的请求可写。压缩效果与最终成本由你实际使用的模型、缓存策略、对话内容决定。
🔗 与其它插件的联动
🔗 与 dsh-aux-degrade(上下文治理两件套)—— 请务必一起安装
dsh-context-reclaim 与 dsh-aux-degrade 是同一套「上下文治理」方案的两半。强烈建议同时安装,这是本插件最重要的联动关系。
① 它们共享同一个路由口径。 两个插件都用同一个判据区分「主对话调用」与「辅助调用」:
tools.length > 0 ? 'main' : 'aux'
reclaim 用它给成本归因打 route= 标签,aux-degrade 用它决定要不要给这次调用降思考强度。口径必须一致,否则两个插件对「哪一次调用算辅助调用」的判断会打架,一边省着、一边漏着。
② 它们的分工。 reclaim 管内容(工具结果进历史前的净化与有界化、工具定义面折叠、前缀稳定化);aux-degrade 管调用(把压缩摘要 / 会话标题这类辅助调用的思考强度降到 off,并给压缩指令追加两节保真要求)。
③ 缺了对方会怎样。
| 只装 | 结果 |
|---|---|
| 只有 reclaim | 一切照常工作。但辅助调用仍会烧满思考预算,压缩摘要仍可能被输出顶截断——reclaim 只会在日志里打 [reclaim/summary-audit] ⚠️ 摘要缺 N/8 段 提醒你,它自己不会去改摘要(这是有意的:reclaim 不碰已提交内容) |
| 只有 aux-degrade | 一切照常工作。但工具结果不会被净化与有界化,长结果原样进历史 |
④ 两者互不依赖。 reclaim 源码里没有任何 import / require,aux-degrade 只 import 官方的 @deepseek-ai/schemastery。它们不互相探测、不互相引用——任何一个都能单独安装并正常工作,只是覆盖度不同。这也是为什么它们是两个仓库而不是一个。
⑤ 一个实用细节。 如果你打开 reclaim 的 [reclaim/usage] 归因日志,会看到里面有个 purpose= 字段。这个字段就是用来诊断「哪一类辅助调用没被 aux-degrade 降到 off」的——它是一次只读诊断,不改任何行为。看到 purpose=unknown 或某个没见过的 purpose 时,那通常意味着有一条辅助调用路径绕过了降级判据。
⑥ 一句话。 装了 reclaim 不装 aux-degrade,你会看到问题但修不掉;装了 aux-degrade 不装 reclaim,你的辅助调用省了但工具结果没省。两个一起装,上下文治理才是完整的。
🔗 与 dsh-capability-composer
reclaim 内部维护一张「哪一段系统提示归谁管」的归属表(SECTION_OWNER),其中「可用插件」这一段归 dsh-capability-composer。
也就是说:装插件清单不该由 reclaim 来裁剪——那是能力组合器的职责。两者同时安装时,段的收窄交给组合器执行,reclaim 只在组合器不在场时保留默认行为。
🔗 与 dsh-composer-launcher
无直接耦合。两者可以任意组合安装。
FAQ
它会破坏我的前缀缓存吗?
不会。这是本插件最重要的一条设计约束:它只在内容进入历史之前动手,从不改写已经提交的内容。已提交部分逐字节不变,前缀缓存因此永远不会因为 reclaim 而失效。
它会把我的工具输出改掉吗?
只在超过阈值时截断,并且一定在截断处留下取回指针。净化那一层是无损的(ANSI 转义、行尾空白这类本来就不携带信息的东西)。另外有三类工具被列为「保真」,原样通过,一个字符都不动。
装完要重启吗?
要。宿主侧插件在 DSH 启动时装载。重启一次之后就一直生效。
它会往我的 profile 里装别的东西吗?
不会。本插件零运行时依赖,peerDependencies 只声明官方包,安装过程不会引入任何第三方代码。
我怎么知道它到底省了多少?
看 [reclaim/usage] 日志行的 hit / miss 字段,那是每请求的真实前缀缓存字符数。被截断的结果会在截断处自带说明(省了多少、怎么取回)。
支持网页版 DSH 吗?
支持。本插件是宿主侧插件,只依赖 Cordis 的通用接缝(tools/post-execute、system-prompt/assemble、session/event、llm/stream),不依赖任何 Electron 或桌面专属能力。只要是 DSH,桌面版和网页版都能用。
为什么有两个插件都叫「上下文治理」?
因为它们管的是两层不同的事:一个管内容(结果进历史前怎么处理),一个管调用(辅助调用花多少思考预算)。见上面的联动章节。
仓库结构
dsh-context-reclaim/
├── index.js # 全部实现(单文件,无外部依赖)
├── cordis.patch.yml # DSH 插件清单补丁
├── package.json
├── verify.mjs # 离线自检:node verify.mjs
├── README.md # English
└── README.zh-CN.md # 简体中文
自检
node verify.mjs
离线运行、不写盘。检查清单字段、语法、有没有混进私钥或备份文件、运行时代码里有没有写死的机器路径。
License
MIT © 2026 夏目 (xiamubumu)