前言¶
DSH(DeepSeek Harness)的运行方式决定了上下文会持续膨胀:每一步的工具结果都会留在会话里,之后每一步都要重新发一遍。真正吃掉上下文的不是所有结果,而是少数几个特别大、之后再没人读第二次的结果。dsh-recall 就是针对这个点做的插件:大的 tool result 头尾留在对话里,中间部分搬到本地 SQLite,模型用 recall("<id>") 把需要的片段取回来。
它由 sh-222 维护,分类在社区目录里是「记忆」,当前星标为 1(目录页同时显示 1 forks),属于个人维护的小项目,不是 DeepSeek 官方出品。下面介绍它的机制、安装方式和配置项。
这是什么¶
一句话定位:一个 context-reclaim 插件。它不删不改工具产出,只把超出阈值的结果中间段落移出对话,换成一个短的对象 ID 和一行说明,原全文仍保存在会话日志和本地存储里。
它对已有做法的区别:DSH 自带的 spill 策略和 15% 压缩只保留头部,尾部的检索提示和摘要会被丢掉;dsh-recall 头尾都留,并支持模型按字符区间或关键字把内容取回。它不是全文压缩,也不是删掉旧消息。
核心功能¶
触发条件:在下一次工具调用时,凡是「比最新 keep 条数更老」且「长度超过 thresholdChars」的 tool result,会被重写成 头部 + [recall] 说明 + 尾部;中间部分按 md5(session:seq:chars)[:8] 命名存为对象,例如 #4d3661d2。模型在对话里看到的是这样的占位:
XXXX… (600 chars of head) …
[recall] read call_A — 4000 chars total. The head and tail are above and below; the middle 3160
chars are stored as object #4d3661d2. Fetch any part with recall("4d3661d2") (optionally
recall("4d3661d2", start, count)), or search it with recall("4d3661d2", query).
XXXX… (240 chars of tail, or the whole stock spill notice when there is one)
配套能力:
- 尾部整段保留,尤其是 stock spill notice(
Full formatted result stored at: …)连同左括号一起保留,模型不会丢失定位线索。 - 可选 token 预算闸门(
budgetTokens),只在当前 surface 真的超过预算时才搬,避免短会话被提前压缩。 recall工具支持三种调用:取前 8 000 字符、按字符区间取(单次上限 24 000)、按关键字搜索返回最多 20 行命中行及其字符偏移。偏移的单位与说明里引用的一致,可直接回填到区间调用。- 存储是一个 SQLite 文件,payload 做 deflate 压缩并以原文 SHA-256 作键,同一份输出读两次只存一份;用
PRAGMA application_id拒绝外来数据库文件。 - 版本保留策略存在:
maxAgeDays和maxBytes按最旧优先淘汰。 - 记账用的是 DSH 自己的 token meter,而不是
chars / 4估算。 - 重写走 DSH 自带的
compaction/prune+surfaceOp: {op:'replace'}路径,持久化日志里保留原文,会话恢复后重放结果一致。 - 存储按
(session, seq)判断是否已搬过,因此工具只是打印[recall]字样不会让真正的搬运被跳过。 - tool result 里的图片块不受影响的只有文本被缩短。
- composer 下方有一个 strip,显示搬出量、留在上下文外的 token 数、recall 命中次数和磁盘占用。需要 web profile(
dsh.client.platform: web)。
根据 README 中对 8 504 条实测 tool result 的统计:超过 6 KB 的结果占 4.6%,而这些结果占了全部 tool result 字符量的 40.7%,其中 76% 之后再也没有被打开过。作者把上限说得比较直白:tool result 大约占上下文的 46%,即使全部搬走,上限也只在上下文的 18% 左右。缓存读取计价接近输入价的路线上才有 20–25% 的实际收益,缓存便宜的一方路线上只有个位数。
安装与启用¶
Web / TUI profile 下用官方命令安装:
dsh plugin --profile web add github:sh-222/dsh-recall
装完需要重启 harness,插件只在启动时加载。
Electron 的 desktop profile 按设计不支持 CLI 管理,需要手动改 ~/.dsh/profiles/desktop/package.json,加入依赖和 bundle 行,然后安装:
{
"dependencies": { "dsh-recall": "github:sh-222/dsh-recall" },
"dsh": { "profile": { "bundles": ["…", "dsh-recall"] } }
}
cd ~/.dsh/profiles/desktop && pnpm install --ignore-scripts
升级和卸载:
dsh plugin --profile web remove dsh-recall # 或直接改掉 bundle 行再 pnpm install
卸载不会动 ~/.local/share/dsh-recall/ 里的存储,不需要时自己删掉即可。
运行环境要求:dsh >= 0.2.0-0(package.json 声明);对象存储需要 Node >= 22.5(使用标准库 node:sqlite);strip 需要 web profile,宿主侧对 profile 无要求。Node 较旧时插件仍能加载、recall 工具仍能应答,只是报告存储不可用。
配置¶
以下配置项来自插件源码中的 Config 校验定义,加载时逐个校验,出现未知配置键直接报错而不是静默忽略:
| 配置项 | 默认值 | 说明 |
|---|---|---|
thresholdChars |
2500 | 超过该长度的 tool result 才被搬走 |
headChars |
600 | 保留在对话开头的字符数 |
tailChars |
240 | 保留在对话结尾的字符数 |
keep |
2 | 最新的 N 条 tool result 始终不处理 |
budgetTokens |
0 | 只在 surface 超过该 token 预算时搬;0 表示只看阈值 |
spillNoticeChars |
600 | 为完整保留 stock spill notice 额外占用的尾部字符上限;0 表示关闭 |
maxAgeDays |
0 | 超过该天数的对象删除;0 表示永久保留 |
maxBytes |
0 | 超出该字节数时按最旧优先淘汰;0 表示不设上限 |
enabled |
true | 置 false 只关掉搬移动作,recall(id) 仍可用 |
stateDir |
~/.local/share/dsh-recall |
对象存储所在目录 |
校验规则里有一条很实用:headChars + tailChars 必须小于 thresholdChars,否则搬完比原文还长,插件直接拒绝启动。源码注释说明 2 500/600 这个默认值是实测拐点——阈值 6 000 只覆盖 23.5% 的 tool result 体量,2 500 覆盖 53.0%。
典型用法¶
安装并重启后,插件在后台自动工作,不需要额外命令。模型拿回内容靠三种 recall 调用:
recall("4d3661d2") # 取前 8 000 字符
recall("4d3661d2", 8000, 1600) # 取 8000 起的 1600 个字符,单次上限 24 000
recall("4d3661d2", query: "FATAL") # 返回最多 20 行命中行及其字符偏移
搜索命中的偏移可以直接作为区间调用的起点,两者用的是同一个 Unicode 码点单位,代理对不会被切开。
想确认这套交换值不值得,看 composer 下方的 strip:搬出次数、留在上下文外的 token 数、recall 命中次数和磁盘占用。如果 recall 命中次数长期为零,说明搬出去的东西没人取,此时可以把 thresholdChars 调高或给 budgetTokens 设一个更紧的值。
适用场景与注意¶
适合:长会话、工具输出量大、按 token 计价或缓存读取成本不低的场景;希望保留完整会话日志、不删除任何原始输出的团队。
需要注意:
- 插件以当前
dsh进程权限运行,可以直接读写本地目录。安装前先读一遍源码,确认index.js、store.js、client.js与cordis.patch.yml的内容符合预期,并查看仓库的许可证。 - 这是一个个人维护项目,星标和 fork 数都很低,升级和问题响应取决于维护者;仓库版本(
package.json中 0.5.0)与本地 dsh 版本的匹配情况要自己验证。 - 存储不可用时插件不会让 profile 挂掉,只会记一行日志并让
recall报告存储不可用,这属于降级行为而非故障。 - strip 需 web profile;其他 profile 上插件仍能搬动和取回,只是没有那个状态条。
- 淘汰策略(
maxAgeDays/maxBytes)一旦生效,指向已删除对象的指针会失效,这是任何容量上限的固有代价,所以默认一律不删。 - 收益有上限,大约是上下文 18% 的量级;在缓存便宜的路线上,优先减少新增输入和步数更划算。
链接¶
- 目录页:https://www.skillhub.cn/plugins/sh-222/dsh-recall
- GitHub:https://github.com/sh-222/dsh-recall
社区目录是独立站点,与 DeepSeek / 幻方没有官方从属关系;dsh-recall 也只是社区插件,不是官方应用商店上架产品。