dsh-per-turn-isolation
让 DSH 的每一轮对话互相独立 —— 每轮只把「本轮输入 + 一段你定的长期设定」交给模型,
之前的历史不再一轮轮堆进上下文。
先花 30 秒搞懂它是什么
如果你还没接触过 DSH,先看这三句话:
| 名词 | 白话解释 |
|---|
| DSH | DeepSeek Harness(npm 包名 @deepseek-ai/dsh),一个跑在你自己电脑上的 AI 工作台,自带网页界面,可以用插件扩展。 |
| 插件 / bundle | 给这个工作台加功能的包。装完重启,界面上就多出对应的按钮或页面。 |
| 本插件 | 一个 DSH 插件。装上后,输入框旁边会多一个「独立」开关,用来控制"每轮对话要不要互相隔离"。 |
一、适用范围(谁该用它)
适合你,如果:
- 你经常在 同一个会话里连续问几十轮,历史越滚越长,模型开始跑偏或答非所问;
- 你希望每一轮都严格遵守同一份规则(人设、代码规范、输出格式、回答语言),不想每次都重复贴一遍;
- 你在意上下文占用和费用,不希望它随轮数一路涨上去;
- 你在做"批量重复劳动"型任务:同一个要求,反复换输入,每轮之间不需要互相影响。
不适合你,如果:
- 任务本身需要模型记住整段对话(例如逐步排查一个 bug、多轮追问细节、边聊边改需求)——这种场景开着它,模型会"失忆",请保持关闭;
- 你只是偶尔问一两句,本来就没有上下文堆积问题。
它不是"清空聊天记录"。会话记录始终完整保留,界面随时能回看;被隐藏的只是「模型能看到的部分」。
二、它能做什么(通俗版)
把「一个会话」想象成一条不断变长的聊天记录:第 1 轮 → 第 2 轮 → 第 3 轮 → ……
默认情况下,每开一轮,模型都会把前面所有轮次重新读一遍。轮数越多,模型要读的内容越多——这就是上下文膨胀。
开启「独立」后,每轮开始前插件会做一件事:
本轮开始
↓
[第1轮 … 第N-1轮] ← 全部折叠成一个占位节点
↓
模型实际看到的内容 =
① 本轮你说的话
② 你设定的长期规则(可选,每轮都注入)
于是:
| 对比项 | 关闭(默认) | 开启「独立」 |
|---|
| 模型看到的上下文 | 本轮 + 之前所有轮 | 本轮 + 长期规则 |
| 界面上能否回看历史 | ✅ 完整可回看 | ✅ 完整可回看 |
| 上下文占用 / 费用随轮数 | 📈 一路增长 | ➡️ 每轮基本恒定 |
| 每轮的规则一致性 | 靠历史里残留的指令 | ✅ 每轮强制注入 |
长期规则是一段你想让模型"永远记住"的话,按会话保存,可以留空。例如:
你是我的代码审查助手。回答只用中文,先给结论再给理由,不要任何客套话。
三、安装
前置条件
| 项 | 要求 |
|---|
| Node.js | 18 或更高 |
| pnpm | npm i -g pnpm |
| DSH | npm i -g @deepseek-ai/dsh,装完能跑起 dsh web |
| 操作系统 | Windows / macOS / Linux 均可(本插件无平台特有代码) |
方式 A:直接从 GitHub 安装(推荐)
dsh plugin --profile web add github:xinshang777/dsh-per-turn-isolation
方式 B:先克隆到本地,再以链接方式安装(想改代码时用)
git clone https://github.com/xinshang777/dsh-per-turn-isolation.git
cd dsh-per-turn-isolation
dsh plugin --profile web add link:.
用 link: 装的好处:改完源码不用重装,重启 dsh web 就生效。
方式 C:只想下载,不想装
打开本仓库页面 → 绿色 Code 按钮 → Download ZIP,解压即可(离线分发、给别人传包都行)。
安装后必须重启
# 停掉正在运行的 dsh web(Ctrl + C),然后
dsh web
💡 装了 dsh-restart-button 的话,
点界面上的重启按钮一步到位,不用回命令行。
装完没看到按钮?检查这一步
dsh plugin add 在安装成功后会自动把插件登记进 profile 的 dsh.profile.bundles(源码里的 reconcile() 逻辑),正常情况下你不需要手动改任何配置。
如果重启后界面没有出现「独立」按钮,打开下面这个文件确认一下:
// ~/.dsh/profiles/web/package.json
{
"dsh": {
"profile": {
"bundles": [
"@deepseek-ai/dsh-base",
"@deepseek-ai/dsh-web-app",
"dsh-per-turn-isolation" // ← 数组里有这一项才算已挂载
]
}
}
}
没有的话手动加进去,重启 dsh web。
四、使用教程
第 1 步:找到开关
打开 dsh web,看输入框发送区左侧。「独立」开关在 「回退」按钮的左边:
flowchart LR
A["输入框区域"] --> B["「独立」开关<br/>(本插件注入)"]
B --> C["「回退」按钮"]
C --> D["发送按钮"]
style B fill:#7C3AED,color:#fff
第 2 步:设置长期规则并开启
- 点击**「独立」**→ 弹出设置对话框;
- 在输入框里写长期规则(可留空);
- 点 「保存并开启」。
对话框再次打开时,如果功能已在运行,里面会显示 「关闭功能」 按钮。
第 3 步:验证是否生效
开启后连续问两轮,比如:
第 1 轮:记住一个数字:42
第 2 轮:我刚才让你记的数字是多少?
开启「独立」时,第 2 轮模型答不出来——因为第 1 轮已经被折叠掉了,
这正说明隔离生效了(而界面上你仍能往上翻到第 1 轮)。
第 4 步:关闭
再点一次「独立」→ 对话框里点 「关闭功能」。
关闭后之后的轮次恢复正常上下文(上一轮会正常保留);
但已被折叠的更早轮次不会回到模型上下文里,这一点请留意。
典型用法:给每一轮套同一个规矩
| 场景 | 长期规则可以这么写 |
|---|
| 代码审查 | 只评审 diff,输出格式固定为:问题 / 原因 / 建议改法。不要复述代码。 |
| 批量改写文案 | 每条输入都按"标题 20 字内 + 正文 80 字内"重写,保持原意,不要加 emoji。 |
| 结构化抽取 | 把输入当作文本抽取字段,只输出 JSON,不要解释。 |
五、实现原理
挂钩点:agent/pre-step
插件实现的是 DSH 的 agent/pre-step 事件,在一轮的第一个步骤(step === 1)拦截:
ctx.on("agent/pre-step", async (payload, next) => {
const { agent, messages, turn, step } = payload ?? {};
const isTurnStart = step === 1 && Array.isArray(messages) && messages.length > 0;
// …
});
折叠:把历史压成一个占位节点
在该轮开始前,插件把「本轮之前的所有可见节点」合成一个 user/message 占位节点,
并用 session.append("user/message", placeholder, …) 写回会话树。
- 写回会话树 → 所以界面上历史依然完整可见;
- 折叠成单节点 → 所以模型看不到更早的内容。
长期规则:走系统提示
通过 ctx.inject(["systemPrompt"], host => contribute(host)) 把长期规则贡献进系统提示,
使它在每一轮都稳定生效,而不是"靠历史里残留的一句指令"。
状态存储
按 sessionId 保存,落盘在 $DSH_HOME 下(默认 ~/.dsh,Windows 为 %USERPROFILE%\.dsh):
// %USERPROFILE%\.dsh\.dsh-per-turn-isolation.json
{
"sessions": {
"<sessionId>": { "enabled": true, "rule": "…" }
}
}
没有记录 = 关闭 + 规则为空,所以新建会话默认是关闭状态。
HTTP 接口(供前端开关使用)
| 方法 | 路径 | 说明 |
|---|
GET | /dsh-per-turn/state?sessionId=… | 读取该会话的 { ok, sessionId, enabled, rule } |
POST | /dsh-per-turn/state | 写入 { sessionId, enabled, rule },返回同上 |
GET | /dsh-per-turn/debug | 查看存储路径、当前会话状态、最近的折叠记录 |
前端注入
client.js 通过 @deepseek-ai/dsh-client-ui-slots 把按钮插进输入框发送区的插槽,
并依赖 @deepseek-ai/dsh-client-connection 等客户端包与宿主通信。
六、常见问题
| 现象 | 原因 / 处理 |
|---|
| 开启后模型"失忆",答不上之前的内容 | 这是设计目标,不是 bug。需要记忆时关掉即可。 |
| 关闭功能后,更早的轮次还是没回来 | 预期行为:已折叠的更早轮次不会回溯恢复;上一轮会正常保留。 |
| 输入框旁边没有「独立」按钮 | ① 确认 ~/.dsh/profiles/web/package.json 的 bundles 里有 dsh-per-turn-isolation;② 完整重启 dsh web;③ 浏览器硬刷新(Ctrl + F5)。 |
| 按钮在,但点了没反应 | 打开 GET /dsh-per-turn/debug 看当前会话状态;确认浏览器控制台无报错。 |
| 换个会话后设置没了 | 规则是按会话保存的,不同会话互不影响;新建会话默认关闭。 |
七、开发与目录
dsh-per-turn-isolation/
├── lib/index.js # 宿主侧:pre-step 折叠逻辑 + 长期规则注入 + HTTP 接口
├── client.js # 浏览器侧:注入「独立」开关按钮与设置对话框
├── cordis.patch.yml # bundle 挂载声明(把插件插进 profile 配置树)
├── icon.svg # 插件图标
├── locale/zh.json, en.json # 中英文文案
└── package.json
本地开发:把仓库 link: 进 profile 后改代码,重启 dsh web 即生效。
License
MIT