dsh-condense
Token optimizer for DeepSeek Harness — condense your context, keep the essence. 为 DSH 的 agent 上下文减负并汇报每一分节省。
- Stars
- 0
- Language
- TypeScript
- Created
- Aug 19, 2026
- Updated
- Aug 20, 2026
Introduction
✂️ dsh-condense
浓缩上下文,留住精华。 Condense your context. Keep the essence.
面向 DeepSeek Harness 的 token 优化插件 — 自动削减 agent 循环中的低价值内容,同时 保持 prompt 缓存命中。
中文 · English
🧐 为什么需要它
LLM 编程 agent 在很少有帮助的内容上烧 token:
| 浪费场景 | 说明 |
|---|---|
| 📚 整文件读取 | node_modules、lock 文件、vendor 代码灌满上下文 |
| 🐘 超大输出 | 工具输出在整个会话期间留在历史里 |
| 🔁 重复读取 | 同一文件被连续读两遍 |
| 🎯 盲目检索 | 候选 20 个文件时只读 5 个就够 |
| 📝 啰嗦回复 | 一句话能说清时模型写一大段 |
dsh-condense 在每一个点上动手。
💡 核心考量:省 token,同时不破坏缓存命中
💰 API 侧的 prompt 缓存按 前缀匹配 计费:命中部分大幅折扣(约全价的 1/10),未命中的新增内容才是全价。
两种"省 token"的思路,结果截然不同:
| 思路 | 缓存影响 | 结论 |
|---|---|---|
| ❌ 事后削减(摘掉/重写已进历史的内容) | 历史前缀一变,后续请求从变化点起全部 cache miss | 省下的 token 被全价重算抵消,可能比不省更贵 |
| ✅ 入历史前削减(本插件的做法) | 历史从创建起就是浓缩形态,前缀 字节级稳定 | 缓存命中率不受影响 |
模型请求 ──► 内容还没进历史 ──► dsh-condense 削减 ──► 浓缩版进历史 ──► 前缀稳定
🎯 省的是 未命中的全价 token(每次新增的大块输出),保留的是 已命中的折扣 token —— 这正是经济收益最大的位置。
⚙️ 功能一览
| 能力 | 机制 | 时机 |
|---|---|---|
| 🚫 屏蔽低信号读取 | tools/pre-execute 拒绝读取 node_modules/、.git/、dist/、lock 文件、.min.js | 自动 |
🧭 read → smart_read 引导 | 模型全量 read 源码文件时拒绝并提示改用骨架读取;局部读不受影响(Config smartReadRedirect,默认开) | 自动 |
| 🗜️ 压缩超大输出 | tools/post-execute 对 >8 KB 输出做头尾截断 | 自动 |
| ♻️ 重复调用去重 | 相同调用且 输出哈希一致 即返回占位符(无时间窗口,输出一变就返回新内容,绝不误导)。覆盖只读工具 + bash | 自动 |
| 🤫 conciseness 指令 | 系统提示注入"直接回答、不说废话" | 自动 |
🧠 smart_read | tree-sitter 语法级骨架提取 — TS/JS/TSX/JSX/Python/Rust/Go | 按需 |
🔍 trim_context | BM25 候选文件排序(支持 中英文) | 按需 |
📊 condense_stats | 统一节省量统计 + 真实用量计量 + 持久化 | 按需 |
📈 实测效果
所有数字均来自真实运行(详见 EXPERIMENTS.md);token 估算口径 ≈4 字符/token,诚实标注。
| 实验 | 结果 |
|---|---|
🧠 smart_read 单文件节省 | 实现文件 45–74%;纯类型声明(.d.ts)≈0%(骨架≈原文) |
🔗 trim_context → smart_read 链路 | 4 组查询 整体 91%,相关文件全部命中 |
| 🗜️ 输出压缩 | 超大输出保留头尾 8 KB,削减率 77–82% |
| 🚫 屏蔽低信号读取 | 目标文件完全不进上下文(100%) |
| 💾 prompt 缓存影响 | 无 — 所有削减发生在内容进历史之前,前缀保持稳定 |
🏗️ 工作原理
┌────────────────────────────────────────────────┐
│ dsh-condense │
│ │
model ────────►│ tools/pre-execute ──► 屏蔽低信号读取 │
│ │ │
│ tool 执行 (或按需 smart_read / │
│ │ trim_context) │
│ ▼ │
│ tools/post-execute ──► 压缩 + 去重 │
│ │ │
│ 会话历史(已浓缩)── 前缀稳定,缓存命中不损 │
│ │ │
│ 系统提示 section ──► conciseness 指令 │
└────────────────────────────────────────────────┘
两个组成部分,都是纯插件(零 DSH 源码改动):
- 🧩 主插件(标准 Cordis 插件,
dsh plugin add一键安装):事件钩子(tools/pre-execute、tools/post-execute、session/event真实计量)、按需工具、统计持久化。 - 🚀 宿主插件(
host-plugin/):运行在完整 Node 环境,因此能加载 tree-sitter WASM 语法 — 这是语法级符号提取的基础。
宿主插件通过 ctx.loader.create() 引导加载,随主插件自动启动,无需额外配置。
🚀 安装
✅ 已验证 DSH 0.1.0-rc.8(peer 范围:
>=0.1.0-rc.7)。
# 从 npm(推荐,可版本化更新)
dsh plugin add @jxame/dsh-condense
# 或从 GitHub
dsh plugin add github:JxaMe/dsh-condense
安装即完成:包自带 bundle patch(dsh.bundle 声明),dsh plugin add 自动注册为 profile 层,无需手动编辑任何配置。重启 DSH 后宿主插件随启动加载,钩子和工具立即生效。
🔄 更新到新版本:
dsh plugin remove @jxame/dsh-condense && dsh plugin add @jxame/dsh-condense。 🛠️ 开发期改代码可用本地路径dsh plugin add /path/to/dsh-condense(symlink 跟随本地目录,npm run build后重启即生效)。
📍 放置位置
dsh plugin add 默认装入 host 组成(全会话生效)。如需限定单个会话,把插件行放进对应 agent preset 的 plugins: 列表:
| 位置 | 生效范围 |
|---|---|
| 🏠 host 组成(默认) | 所有会话,全 harness 共享一份统计 |
| 🎯 agent preset | 单个会话,每会话独立统计 |
统计(condense_stats)与削减记录持久化在 ~/.dsh/plugins/dsh-condense/stats.json(Windows: %USERPROFILE%\.dsh\plugins\dsh-condense\stats.json),重启不丢。
🛠️ 工具
| 工具 | 说明 |
|---|---|
🧠 smart_read | 源码文件的符号骨架(tree-sitter,TS/JS/Python/Rust/Go)。探索结构时优先于 read。 |
🔍 trim_context | 对候选文件路径按 BM25 与查询排序,返回 top-N 及分数。支持中英文。 |
📊 condense_stats | 查询会话的 token 节省量(按机制细分)+ 真实用量(provider 实测,session/event 持久化事件)+ 持久化位置。 |
⚠️ 已知限制
- 🧠
smart_read对 Python/Rust 的类/结构体成员:方法签名化、字段保留(与 TS/JS 一致);Go 结构体保留字段。 - 🇨🇳 单字符中文查询无结果(bigram 分词器至少需要 2 个字符)。
- 🔄 宿主插件代码更新需重启 DSH 生效(模块缓存)。
- 🚫 DSH 的
pre-execute按设计禁止改写工具参数,因此无法做 quiet-flag 式命令改写;屏蔽读取是等价的替代防护。 - 🖥️ 实时 UI 统计面板未提供:统计的 数据层已完全固化(
condense_stats工具 +stats.json持久化 +condenseStats服务,重启不丢);浏览器端展示层需要 DSH 官方 client 插件构建工具(__ModuleLoader__bundle 格式),当前版本未随发行提供,故未提供面板。统计经condense_stats工具查询。
🏛️ 架构说明
- 🔌 用到的钩子:
tools/pre-execute(deny)、tools/post-execute(替换 content)、session/event(真实用量计量,官方推荐事件溯源方式)、systemPrompt.section()(conciseness + smart_read 引导)、ctx.loader.create()(宿主插件引导)。 - 🧩 为什么是两个插件:标准插件环境无法加载 WASM/import npm 包;宿主插件运行在完整 Node 运行时。这个拆分正是 tree-sitter 可行的原因。
- 🚫 陈旧输出截断有意不重复实现 — DSH 原生自带
toolResultPruner。 - 🚫 不做请求前历史裁剪:把已写进历史的内容摘掉会破坏缓存前缀(省下的 token 被全价重算抵消)。本插件只削减"将进入"的内容,不碰"已在"的历史 — 这是 DSH 缓存计费模型下的最优策略。
🧑💻 开发
# 边界测试:正则提取器在 dsh-src(87 个真实文件)上的翻车率
node bench-edge.js
# 组合链路测试:trim_context → smart_read 的 token 节省
node bench-combo.js
# 本地验证 tree-sitter 提取器(无需 DSH)
node --input-type=module -e "import { extractSymbols } from './host-plugin/dist/index.js'; console.log(await extractSymbols('def f(x): return x', '/tmp/a.py'))"
# 单元测试
npm test
实验数据、方法学与发布素材见 EXPERIMENTS.md。
📜 许可证
MIT — 详见 LICENSE。
🙏 致谢
受 ctxlite(MIT + Commons Clause)启发 — 同类问题,不同架构(DSH 原生插件 vs 外部工具)。BM25 参数与符号提取策略遵循其公开设计;实现为原创。署名义务见 LICENSE 声明。