dsh-force-compact
September 5, 2026 · View on GitHub
面向 DeepSeek Harness agent 的激进、本地优先上下文压缩。
一个 DSH Cordis 功能插件,让 agent 的工作上下文始终保持精悍:用自建 llama.cpp 以
适中上下文承载 Qwen3.8‑27B,插件负责收缩会话本身——近乎大窗口的体验,无 API 费用、
数据不出本机。
为什么要做
- 本地推理——agent 走标准 DeepSeek 适配器对接一台本地 OpenAI 兼容的 llama.cpp 服务端,无需另行编写适配器。
- 低上下文、高信号——不与小上限较劲,而是直接收缩会话:agent 在短促的请求上 思考,同时靠压缩后的开头段保有深层记忆。
- 只关压缩的思考,其余放行——
disableThinking: true(默认)只关掉 本插件自己的压缩摘要调用 的思考;其他一切模型请求沿用机器原有配置,原样放行。 - 私密免费——不计 token 费用,无数据外流。
它能做什么
两条压缩引擎经由统一门面(resolveCompaction)共存,对调用者透明:
| 引擎 | 启用条件 | 说明 |
|---|---|---|
| 官方 | agent 领域内可解析出 compaction 服务 | 优先;委托 compaction/basic。 |
| 内置 | 自动回落(典型标准预设将服务隔离,解析不到) | 基于 ctx.sessions / ctx.llm.stream / ctx.tokenMeter 的自包含持久事务;复用官方 compaction/* 事件词汇,跨版本回放安全。 |
无需切换——官方可达就走官方,不可达就内置接管。
触发点
- 每次请求门禁(
agent/pre-step)——读取会话预计上下文 token(即界面右下角显示的 数字)。达到autoThresholdTokens时,拒绝本次出站请求并改压缩头部,最近retainLatestTokens逐字保留;低于阈值则请求照常进行。 - 回合结束 / 静止(
agent/status→idle)——agent 静止时(含子代理全部结束), 可选地经compactNow压缩(开关:turnEndForceCompactionEnabled)。 - 手动
/force-compact——对忙/闲 agent 都能生效:空闲立即压缩;繁忙则排队一个 进程内强制标记,在下一个模型步骤消费。命令本身惰性加载——见安装章「命令可用性」。 session/flush——等待型的持久化检查点。
每条路径最终都汇入唯一的「压缩结果落入会话」边界——也正是发送 LiveUI 信令的位置。
判定键用 projectedTokens(provider 锚定,与界面角标同源),故插件永远不会偏离你所见;
触发后循环压缩(2026-09 语义):自动门禁、/force-compact、回合结束两条路径会在同
一次触发内反复压缩,直到 projectedTokens 压回 autoThresholdTokens 以下,或表面
已无可压缩头部(整个表面都不超过 retainLatestTokens 保留预算)为止;单轮"压缩后仍
≥ 阈值"不再被跳过(旧版的 threshold-aware shrink-gate 已移除,它把 provider 压力基线
计入判定,baseline 偏高时会令可压缩会话滞留超阈值)。硬上限
MAX_COMPACTION_ROUNDS=8 兜底,防止 provider 基线异常时无限烧摘要调用。真正"物理防呆"
的拒绝保留不变:摘要必须显著小于被遮蔽区间(post-summary shrink gate,防膨胀)、
小于 MIN_USEFUL_SPAN_TOKENS 的小头部不浪费摘要调用、replay 消息上限、失败冷却、
busy 锁与表面一致性校验。
内置事务的影子价格账单取自与官方引擎相同的 tokenMeter.measure 逐节点单价,故米表
折叠协议结算正确——压缩后右下角计数器下降,而非漂移上升。
思考控制:只作用于压缩
自 2026‑08 语义修订,disableThinking 只管一件事:本插件自己的摘要调用
(engine/builtin.js → engine/summarizer.js → ctx.llm.stream)是否携带
reasoningEffort:'off'。其余一切不受影响:
| 调用位置 | disableThinking: true 时的行为 |
|---|---|
| 内置引擎的摘要调用 | 携带 reasoningEffort:'off' |
| 其他一切模型请求(业务、子代理、工具、其他插件) | 机器 LlmCallConfig 原样不动 |
官方 compaction 服务的调用 | 不经过本插件任何接缝——不受影响 |
目标是 llama.cpp / OpenAI 兼容端点时,适配器吐出的 thinking: { type: 'disabled' }
字段在那里被静默忽略——所以摘要器另外打上 llama.cpp 原生顶层字段
reasoning_effort: "none",门控条件与前一字完全一致。同一 options 对象携带两字段:
| 端点家族 | 读取 | 结果 |
|---|---|---|
| 真·DeepSeek API | reasoningEffort:'off' → thinking:{type:'disabled'} | 关思考 ✅ |
| llama.cpp / OpenAI 兼容 | reasoning_effort:"none"(顶层) | enable_thinking=false ✅ |
每一类端点都宽容无视对方家族的键,故双发无害。字段打在 src/engine/summarizer.js
(紧挨 llm.stream(options) 调用之前),不在 llm/stream 瀑布里——早期草稿曾在那
处尝试注入,实证证明结构性无效(中间层返回值被丢弃;就地改种子会使宿主崩溃);论证全文
见 src/hooks/wire-rewrite.js 模块头部。该钩子现在只承担 LiveUI 水印角色。
业务调用也要关思考?在请求头层面设定你 provider 的 reasoningEffort——插件有意退出
该决定。
可观测性:每次摘要尝试的审计行
每次摘要尝试都会打两行日志(默认 debug: true 即见)——不抓包就能确认作用域裁定与其
wire 字段的持久证据:
[force-compact] <sessionId>: compaction thinking-policy — settings.disableThinking=true → extra.reasoningEffort='off' (this summarization call carries thinking-OFF)
[force-compact] <sessionId>: summarization wire-fields → <provider>/<model>: reasoningEffort='off' + reasoning_effort="none" (llama.cpp-native wire field)
- 第一行(
engine/builtin.js)记录disableThinking在哪里被读出、如何进入调用 选项;关闭时则记录沿用机器默认。 - 第二行(
engine/summarizer.js)记录离开 options 对象那一刻的两个 wire 字段(加上 解析出的 provider/模型);未盖章字段标注(absent…)。
实证背书:对本机 llama.cpp 端点做过对照——基线请求返回非空 reasoning_content
(默认会想),带顶层 reasoning_effort:"none" 的同款请求完全没有——该类端点上该字段
确实在关思考,而未携字段业务调用维持正常思考。
LiveUI 状态
极小的宿主→客户端信道(liveUi 设置字段被实时镜像到浏览器),在回合旁钉一枚徽标:
- 🟥 红色「压缩中」——压缩即将提交前,钉在行进中的回合旁(屏幕文字为中文);
- 🟢 绿色「完成」——压缩结果落定的瞬间点亮;约 3 秒后,一条新鲜的随机工作中 短句接替;
- 🔵 蓝色「工作中」——平时是一条轮换的俏皮短句;
- ⬜ 会话结束「清空」——agent 转入 idle(一轮彻底结束)时推一个空文本 (isImportant):徽章文字抹掉、相位色撤掉,回到官方外观。取代 2026-09 前的 "会话开始时强制重绘随机工作态"。
发送方绝对安全:信令故障绝不误伤真实的压缩事务。
工作原理
agent/request(payload, next) # 每次模型请求
return await next() # 纯透传(思考关闭只作用于本插件
# 自己的摘要调用)
agent/pre-step(payload, next) # 每个模型步骤前
projectedTokens >= autoThresholdTokens?
否 -> next() # 放行
是 -> compactRegion(保留段之前的头部区间, signal)
return { kind: "reject" } # 本步骤不请求模型
agent/status({ agent, status }) # 生命体征过渡
status === "idle" && turnEndForceCompactionEnabled?
-> compactNow(agent, 新signal) # 回合结束压缩
session/flush(session) # 持久化检查点
选取区间 -> 投影消息 -> 预览 + 缩容门禁
-> compaction.compactRegion(start, end, agent, signal)
支撑模块:
src/hooks/guard.js—— 每次请求的门禁:agent/request纯透传(不再批量盖思考章)+pre-step阈值门 + 进程内强制标记(thinkingDisabled仅作遗留谓词保留,热路径不再 调用)。src/hooks/command.js——/force-compact命令(惰性注册)。src/hooks/idle.js—— 回合结束强制压缩。src/hooks/wire-rewrite.js——llm/stream的 LiveUI 水印钩子(不再做任何 wire 操作;缘由见模块头部历史注记)。src/engine/region.js—— 选区(锚定头/尾,含官方工具配对台账)。src/engine/summarizer.js—— 一次性 LLM 摘要器,与官方compaction-basic完全 对齐(三级 target 解析、前缀缓存对齐、purpose:'compaction'标签、fail-closed 终局分类、用量采集)。src/engine/builtin.js—— 内置持久事务(官方compaction/*词汇)。src/engine/checkpoint.js—— 预览 + 缩容门禁 + 委托压缩服务。src/core/projected.js—— provider 锚定的projectedTokens。src/core/ui-signal.js—— LiveUI 信道。
安装
作为安装包(推荐):
# 从 npm(已发布):
npm install @falling-ts/dsh-force-compact
# 从 git:
dsh plugin --profile web add github:falling-ts/dsh-force-compact
# 从本地检出:
dsh plugin --profile web add ./dsh-force-compact
或不安装,直接从本地检出以 --patch 覆盖层挂载:
dsh web --patch dsh-force-compact/cordis.patch.yml
插件加载 ⟺ ~/.dsh/logs/dsh-force-compact.log 新增一行:
[force-compact] debug logging enabled — writing [force-compact] lines to <absolute path>
命令可用性——/force-compact 是惰性加载的
commands 服务随 agent 预设平面到达,晚于插件启动期的 apply——所以注册发生在
第一次受守卫监听器激活时(agent/request / agent/pre-step / agent/status /
session/flush 任一),首次成功后永久闩闭。实际效果:实例(重新)启动后,全新会话的
/ 命令列表在该会话第一次模型请求之前不会出现 /force-compact——先发任意一条消息,
命令即进程级注册,此后所有会话可用。
- 成功:
[force-compact] /force-compact command registered (deferred) commands永久缺席:约 10 分钟后打一条… still UNREGISTERED 10 min …警告,解释 空列表。注册完成前插件其余功能照常——属自愈式降级,不是安装失败。
验证压缩确实发生:
idle compaction (builtin) shadowed N nodes (~M tokens)
builtin compaction OK — replaced span seq[A..B] (N nodes, ~K tokens) with a P-char checkpoint
compaction thinking-policy — settings.disableThinking=true → extra.reasoningEffort='off' (…)
summarization wire-fields → <provider>/<model>: reasoningEffort='off' + reasoning_effort="none" (…)
(最后两行即上文「可观测性」所述的那对审计行。)
设置
$DSH_HOME/settings.yaml,命名空间 falling-ts-force-compact:
| 键 | 类型 | 默认 | 含义 |
|---|---|---|---|
disableThinking | 布尔 | true | 只有关闭时,本插件摘要调用不携带 reasoningEffort:'off';其余请求一律机器默认。 |
autoThresholdTokens | 数字 ≥ 32000 | 32000 | 门禁的预计 token 触发值。越低越激进,常驻请求越小。读取时不低于 32000(低于则抬高)。 |
retainLatestTokens | 正整数 ≥ 8000 | 8000 | 从会话最新条目起保留的最新 token 数,逐字保留;更早内容一次性发往摘要。读取时不低于 8000。同时驱动自动门禁与 /force-compact。 |
turnEndForceCompactionEnabled | 布尔 | true | agent 转入 idle 时压缩。 |
debug | 布尔 | true | 把 [force-compact] 诊断打到插件日志。实时生效(2026-09):开关在每一条导出日志时实时读取,改 settings.yaml 后下一行即生效,无需重启;logFile 路径在安装时固定,改路径需重启进程。 |
logFile | 字符串 | ~/.dsh/logs/dsh-force-compact.log | 诊断输出路径(~ 展开到家目录)。 |
compactionMode | 'realm' | 'global' | 'realm' | 官方服务解析策略(优先级一)。 |
builtinEnabled | 布尔 | true | 内置引擎回落闸门。 |
maxSummaryTokens | 整数(1024–200000) | 1024 | 摘要 LLM maxTokens 上限。 |
示例——激进的本地档:
falling-ts-force-compact:
disableThinking: true
autoThresholdTokens: 40000 # 早压 ⇒ 常驻请求更小
retainLatestTokens: 8000
turnEndForceCompactionEnabled: true
settings 服务缺席时,插件回落到同样的默认值并照常压缩——命名空间可选,永不成为硬
依赖。
低上下文 llama.cpp 调优建议
autoThresholdTokens 保持在所供上下文的明显之内(例如以 80k–128k 舒适上下文供服,
就把阈值设在 40k):常驻请求小而延迟稳,agent 依旧靠压缩头段保有深层记忆。压力以
预计 token(provider 锚定)度量,阈值与你界面上看到的数字可预期对应。
行为说明
- 运行时依赖:
compaction服务(preset 平面agent-presets:compaction-basic), 经ctx.get('compaction')实时读取;不可用时回落内置引擎(或放行请求)。 - 可选依赖:
settings/tokenMeter/commands/llm/agents均经ctx.get(...)读取并守卫;缺任一都优雅降级而非崩溃。 - 每请求读参数: 参数每次模型请求读取,改动下次请求即生效,无需重启。
- 信号:
agent/*瀑布转发当前回合的信号;session/flush检查点与agent/status静止监听器各自新建AbortController。 - 持久化: 持久物为
compaction/*括号事件 + 一条surfaceOp:replace的user/message检查点,跨版本回放安全。 - 客户端半部:
web/client.js新增「强制压缩」设置分区(本地化标签),支持实时改 值(uSES 安全镜像),除一处有意保留的 3 秒定时器(纯表现层,见下)外无任何定时/状态。 - 唯一的有意定时器:
publishDone的 3 秒回落(表现层,见插件 AGENTS.md 例外节); 其余全部纯监听器 + 进程内Map强制标记。
截图

设置页面——强制压缩 分区;上方九个字段全部支持不改重启地实时编辑。

会话页面——LiveUI 信令绘制三种状态(红:压缩中 / 绿:完成 / 蓝:工作中);绿色横幅约 3 秒后淡出,回到随机工作中短句;会话结束(idle)时徽章被清空,回到官方外观。
许可
MIT(见 LICENSE)。