前言¶
在智能体对话里,历史随着轮次增长,迟早需要压缩。DeepSeek Harness 内置了 /compact,但这个命令在慢链路或上游不稳定时有两个实际问题:agent 忙时它直接拒绝,你这次想压缩的请求就丢了;它执行期间 composer 处于锁定状态,如果 summarizer 卡住不流式返回,你没有按钮可按、也没有地方可输入。
下面介绍的插件就是对着这两个问题做的:imrcpan/dsh-compact-enhanced,维护者 imrcpan,分类「工作流」。它把 /compact 请求改成立即返回、在 agent 忙时排队、后台执行的方式,并提供 /compact-cancel 取消,同时在斜杠菜单里用一行状态提示告诉你现在发生了什么。
这是什么¶
一个 DSH(DeepSeek Harness)插件,DSH 本身的理念是「一切皆插件」,能力通过插件拼装。这插件针对的是「手动触发上下文压缩」这一段工作流,不改动模型、不改动提示词,只改命令的调度与可观测性。
解决的问题:
- 内置
/compact在 agent 非空闲时拒绝请求,请求会丢失在正在运行的 turn 中间。 - 内置
/compact执行期间命令行 composer 不可用,卡住时无法中断。 - 完成后没有方便的方式告诉用户「到底压了多少、压缩是否成功」。
命令与行为¶
插件提供两个命令,都不接受参数:
| 命令 | 行为 |
|---|---|
/compact |
agent 空闲时立即开始;否则排队,等到当前 turn 结束再开始 |
/compact-cancel |
丢弃排队中的请求、中止正在运行的 summarizer 请求,或取消由其它命令模块启动的压缩 |
两个命令都不接受参数。当已有请求在排队或运行中时,/compact 会拒绝第二个请求,并在报错中指出用哪个命令取消。
取消这一行什么时候落定,取决于 abort 是否到达 summarizer:
Compaction cancelled.—— 运行已结束。Cancellation requested; the summarizer has not stopped yet.—— 在cancelTimeoutMs内 summarizer 没有响应 abort,请求仍在。
会话面板里看到什么¶
请求自己的生命周期行会立刻出现:
Compaction started in the background.
Compaction queued; it starts when the current turn ends.
因为插件把请求命令的 id 作为 sourceCommandId 传给压缩引擎,压缩事件会挂到这一行上,摘要落地后这一行会升级成标准的压缩卡片。尝试结束后,插件会为同一个 id 再补一条 command/done,记录结果:
Compacted 42 history items (~12,345 tokens).
No compactable history yet.
Compaction cancelled.
以及 busy、changed、summary、commit、persistence 这几类已文档化的失败文案。
那条额外的 command/done 是刻意加的:命令服务在 handler 返回后就会关闭请求(这正是 composer 能解锁的原因),所以想在原来那一行上报后台结果,只能为同一个 id 再走一次生命周期事件,客户端会把它折叠到已有行里。
斜杠菜单里的实时状态¶
/compact 在斜杠菜单里不是一句固定文案,而是一行状态,会随状态变化,并在有时附加当前会话的 token 估算:
| 状态 | 菜单行文本 |
|---|---|
| 空闲 | Ready · 12.3k token |
| 在等当前 turn 结束 | Queued · 12.3k token |
| summarizer 正在运行 | Compacting · 12.3k token |
| 拿不到 token 估算 | Ready / Queued / Compacting,不带 token 部分 |
token 标签用一位小数加固定 k 单位(Claude Code 风格):3.9k token、12.0k token、0.9k token。估算为零或拿不到 token 计量时整段省略,所以不会看到 0.0k token。
/compact-cancel 那一行同步状态:Cancel the queued compaction、Cancel the running compaction,或 No queued or running compaction to cancel。
实现上,插件在命令目录这一层替换了 commands.list() 里的 compact 描述符,并去掉内置定义 id,避免客户端把描述重新本地化回通用文案;状态每次变化时重新发布 commands/change,下一次打开菜单会重新拉取。代价是菜单行显示的是原始命令名,丢掉了内置的本地化标签和图标——这是为换取实时状态做的取舍。
安装与启用¶
包本身是一个 DSH bundle:package.json 声明了 dsh.bundle.patch,所以标准安装会同时挂载命令。
dsh plugin --profile web add github:imrcpan/dsh-compact-enhanced
装完要重启 profile,因为组合关系是在启动时读取的。随包发布的 cordis.patch.yml 会禁用内置的 command-compact 行并插入插件行,这就腾出了 /compact 这个名字。
从本地 checkout 安装时,要作为 bundle 来装,好让它的 manifest 生效:
dsh plugin --profile web add /absolute/path/to/dsh-compact-enhanced
file: 依赖也可以被接受,但那样不会应用 bundle 层,需要手工把 example/cordis.patch.yml 里的那一行抄进 profile 的 cordis.patch.yml。
典型用法¶
在对话里直接使用即可:
# 触发一次压缩,立即返回
/compact
# 如果排队中或正在运行,想取消
/compact-cancel
运行环境要求:
- DeepSeek Harness
0.2.1-rc.5(其它 release 可能不同;package.json里engines写的是dsh >=0.2.1-rc.2) - Node.js
>=20.3,插件用到了AbortSignal.any
配置项¶
配置是可选的,bundle 层不提供任何配置。
| key | 默认值 | 含义 |
|---|---|---|
retryDelayMs |
250 |
一次启动与刚好新开的 turn 撞车后,等多久重试 |
maxQueueAttempts |
40 |
重试多少次后把排队失败写进行里 |
cancelTimeoutMs |
2000 |
取消命令等待被 abort 的运行落定的时长,超时后报告请求仍在 |
实现上的几点¶
- 命令注册表不允许同一名重复全局注册,因此 bundle 层禁用内置
command-compact行,插件用自己的/compact注册到这个名字。如果 profile 仍然挂载了内置行,插件会捕获重复注册并打 warning,保留/compact-cancel,把内置命令留在原处。 - 压缩入口没有办法从 profile 根访问到:agent preset 把它挂在隔离的 per-agent 组里,实际
agent.ctx也不暴露它。插件因此用commands.find解析 agent 能用的那个compact定义(通常是 preset 挂载的那个,其 handler 闭包持有隔离服务),该 handler 以插件自己的AbortSignal在后台运行。找不到其它定义时就退回自己 context 可达的引擎;两者都不可达时,命令会报告压缩不可用,而不是静默地什么都不做。 - agent preset 自己注册了 scoped
/compact,会遮蔽全局替换。插件在命令服务这一层处理/compact这一行,所以排队逻辑作用在目录解析出的那个定义上;插件卸载时会还原原来的execute方法。 - 取消就是 abort 插件交给被委托 handler(或直接交给引擎)的 signal,因此能作用到 summarizer 请求。
- 一次启动如果与新开的 turn 抢跑,会在
retryDelayMs后重试,最多maxQueueAttempts次,之后才写失败。排队状态按 agent 划分且只在内存里。 - agent 忙时什么都不跑:队列等 agent 空闲,然后在 maintenance 里启动,位置正是内置命令本来会跑的地方。
适用场景与注意¶
适合:在慢链路或上游不稳定的环境下工作的 DSH 智能体开发者;需要手动压缩历史、并且希望压缩期间不阻塞对话输入的人;希望取消操作不必等 summarizer 自然结束的人。
需要注意:
- 这个插件以当前 dsh 进程的权限运行,能力边界就是 profile 里已有的命令与服务,安装前应检查源码与许可证(MIT)。
- 没有客户端按钮。取消只能靠
/compact-cancel,有效是因为/compact在 summarizer 启动前就返回了。 - 队列在内存里。host 重启会结束 turn 并丢掉排队请求,之后要重新
/compact。 - 自动压力压缩没有被改动,只包装了手动命令。排队的请求即使自动压缩已经做过一轮,仍会执行一次压缩,因为插件不知道下一次空闲检查会发现什么。
- 取消报告是即时的,但最终这一行要等后台结果落定。
结尾¶
这个插件的价值很清楚:/compact 从「一次会被拒、无法中断、结果不可见的请求」变成「立即返回、可排队、可取消、状态可见的后台任务」。代码量不大,排队与取消状态按 agent 保存在内存中,配置只有三个数值项。如果你的使用场景是长会话加不稳定上游,值得一试。
目录页:https://www.skillhub.cn/plugins/imrcpan/dsh-compact-enhanced
仓库:https://github.com/imrcpan/dsh-compact-enhanced