dsh-btw
为 DeepSeek Harness 复刻的 /btw 旁路提问插件。
个人很喜欢 Claude Code 里的
/btw指令,于是为 DeepSeek Harness(DSH)做了一个复刻插件。
/btw 用来在不打断主 Agent 的情况下快速问一个与当前上下文有关的问题。问题由独立的一次性模型请求回答,结果显示在输入框上方的临时面板中;它不会成为主对话消息,也不会写入 DSH 的普通会话目录。
本项目是非官方社区插件,与 Anthropic、Claude Code 或 DeepSeek AI 没有关联。
/btw 是做什么的?
/btw 可以理解成在主会话旁边“顺手问一句”。当 Agent 正在执行一个较长任务时,你经常会临时想确认一个细节:刚才提到的配置文件叫什么、某段已经读过的代码为什么这样写、之前决定采用哪个方案。直接发送普通消息会改变主会话的走向并增加后续上下文,而另开会话又会失去当前任务已经积累的信息。
BTW 解决的正是这个夹缝问题:它读取截至提交时父会话可安全复用的上下文,独立生成一次回答,然后把结果放进可关闭的临时面板。主 Agent 继续原来的任务,问题和回答也不会进入普通会话历史。
它的核心价值是:
- 不打断主任务:主 Agent 正在思考或调用工具时仍可提问,BTW 在旁路独立运行。
- 不污染主上下文:临时问题不会改变主任务方向,也不会占用后续对话历史。
- 不用重新交代背景:它知道父会话已经讨论过的代码、结论和决策。
- 快速确认细节:适合答案已经存在于当前上下文中的短问题。
典型用法:
/btw 刚才说的配置文件叫什么?
/btw 为什么这里选择队列而不是直接执行?
/btw 我们最终决定使用哪种缓存策略?
/btw 这个报错和刚才修改的代码有关吗?
什么时候不该用 /btw?
| 需求 | 应该选择 |
|---|---|
| 询问当前会话已经知道的内容,只需要一次快速回答 | /btw |
| 需要读取尚未看过的文件、执行命令或联网搜索 | 普通消息或 subagent |
| 需要连续追问、共同讨论或改变实现方案 | 普通消息 |
| 需要真正修改文件或执行操作 | 普通消息或带工具的 subagent |
BTW 没有工具权限,也没有后续轮次。它不是免费的本地查询,而是额外的一次模型请求;插件会尽可能复用父请求的共享 prompt/cache 前缀,以降低重复上下文的成本。
这一交互定位参考了 Claude Code 官方 /btw 文档,DSH 版本的底层实现、私有 sidechain 和缓存兼容层均为本项目独立实现。
功能
| 能力 | 行为 |
|---|---|
| 主任务并行 | 主 Agent 忙碌时也可以提交 /btw,主任务不会被暂停 |
| 共享上下文 | 复用父会话当前的模型配置、系统提示词、工具 schema 和安全消息前缀 |
| 一次性回答 | 只进行一轮模型生成,不执行工具,也不会继续追问 |
| 会话隔离 | 不向主 SessionStore 写入用户消息、回答或隐藏 Session |
| 临时界面 | Markdown 回答显示在 composer 上方,不遮挡输入框 |
| 隐私存档 | 可选择私有 JSONL、仅内存或完全不保存三种 sidechain 模式 |
| 缓存保护 | Anthropic/pi-ai 路由把缓存边界移回共享前缀;其他路由交给 provider 管理 |
| 可取消 | 关闭面板会中止请求,并支持独立超时控制 |
使用效果
先在主会话发送至少一条消息,然后在输入框中输入:
/btw 这个错误最可能是什么原因?
DSH 会立即释放输入框,主 Agent 可以继续工作。BTW 回答完成后会显示在输入框正上方。
↑/↓:滚动长回答Enter/Space/Esc:关闭回答- 请求尚未完成时点击
Cancel:中止本次 BTW
面板关闭后,BTW 不会出现在主对话历史、会话侧栏或普通 Session log 中。
实现架构
flowchart LR
A["Composer: /btw question"] --> B["Client InputTrigger"]
B --> C["Session-scoped BtwController"]
C -->|"trusted-host RPC"| D["Host BTW Service"]
D --> E["Context Snapshot"]
E --> F["Tool-free One-shot LLM"]
D --> G["Private Sidechain Kernel"]
F --> H["Composer Dock Panel"]
1. Client 输入拦截
Client 通过 DSH 的 input-trigger 管线认领 /btw。提交后不会走普通消息发送流程,因此问题不会进入主会话事件流。每个父 Session 都有独立的 BtwController,负责请求状态、取消和 UI 生命周期。
2. Trusted Host RPC
浏览器通过受信 Host RPC 把 sessionId、requestId 和问题发送给 Host。Host 只接受结构合法、仍有 live Agent 的会话请求,并把浏览器取消信号与插件超时合并。
3. 上下文快照
Host 从父 Agent 中快照:
- 当前 provider、model、reasoning、sampling 等请求配置
- 当前 system prompt
- 当前 tool schemas
- 最长的工具调用平衡消息前缀
- 正在处理的当前用户消息(如果已经进入父 Session)
工具调用平衡算法不会留下“只有 tool call、没有 tool result”的残缺历史。
4. 一次性模型调用
BTW 使用独立的 sidechainId 发起一次生成。工具 schema 会保留,以尽量维持与父请求相同的 prompt/cache 前缀,但插件不会注册工具执行器,也不会进行第二轮模型调用。如果模型只返回工具调用,界面会显示可读的降级说明。
5. 隐藏 sidechain
BTW 不创建普通 DSH Session。可选的审计记录由插件自己的 sidechain kernel 管理:
private-jsonl:默认模式,写入$DSH_HOME/btw-sidechains/v1memory:只保留在当前进程内存中none:完全不保存
private-jsonl 目录权限为 0700,文件权限为 0600,并按天数和最大数量自动清理。记录中包含问题、上下文快照和回答;共享机器上请根据隐私要求选择合适模式。
6. skipCacheWrite 等价处理
DSH rc.6 没有公开的 skipCacheWrite 平台开关。本插件对 pi-ai 的 Anthropic Messages 路由使用每次请求独立的 adapter,并通过 onPayload 把一次性 BTW 尾部上的 cache_control 标记移动到最后一个共享前缀块,避免为不会复用的尾部写缓存。
原生 DeepSeek 自动服务端缓存没有对应的 wire-level 关闭参数,因此保持 provider-managed,不伪造客户端能力。
安装
环境要求
- Node.js 22.19+(或 DSH 当前支持的更高版本)
- pnpm / Corepack
- DeepSeek Harness
0.1.0-rc.6 - 已经可以正常启动的
webprofile
DeepSeek Harness 仍处于 developer preview。本插件固定依赖 rc.6 的 API;DSH 升级后如果出现不兼容,请先查看“兼容性”一节。
方法一:直接从 GitHub 安装
dsh plugin --profile web add github:iyllyt/dsh-btw
dsh --profile web --dump-config
dsh --profile web
如果没有全局 dsh 命令,可以把上面的 dsh 替换为 npx @deepseek-ai/dsh。
仓库提交了预构建的 lib/,因此从 GitHub 安装时不需要授权执行 prepare 构建脚本。生产环境建议锁定 commit:
dsh plugin --profile web add github:iyllyt/dsh-btw#<commit-sha>
方法二:从本地源码安装
git clone https://github.com/iyllyt/dsh-btw.git
cd dsh-btw
corepack enable
pnpm install
pnpm test
pnpm run typecheck
pnpm run build
cd ..
dsh plugin --profile web add ./dsh-btw
dsh --profile web --dump-config
dsh --profile web
--dump-config 输出中应当出现 # == dsh-btw 配置层和 id: btw 插件行。
卸载
dsh plugin --profile web remove dsh-btw
卸载插件不会自动删除已经写入 $DSH_HOME/btw-sidechains/v1 的私有审计记录。
配置
bundle 自带以下默认配置:
- insert:
- id: btw
name: dsh-btw
config:
timeoutMs: 120000
sidechain:
mode: private-jsonl
retentionDays: 7
maxSessions: 500
如需覆盖默认值,在 $DSH_HOME/profiles/web/cordis.patch.yml 中追加同一个 id: btw 的完整插件行。例如完全禁用 BTW 存档:
- insert:
- id: btw
name: dsh-btw
config:
timeoutMs: 120000
sidechain:
mode: none
retentionDays: 7
maxSessions: 500
可配置项:
| 字段 | 默认值 | 说明 |
|---|---|---|
timeoutMs |
120000 |
Host 侧单次 BTW 超时,单位毫秒 |
sidechain.mode |
private-jsonl |
private-jsonl、memory 或 none |
sidechain.root |
$DSH_HOME/btw-sidechains/v1 |
私有 JSONL 的自定义绝对路径 |
sidechain.retentionDays |
7 |
文件保留天数 |
sidechain.maxSessions |
500 |
最多保留的 sidechain 数量 |
DSH 的 patch 会替换同一插件行的整个 config,而不是递归合并,所以覆盖时请写出希望保留的全部配置字段。
源码结构
src/
├── client/
│ ├── input-source.ts # /btw 命令识别与输入管线接管
│ ├── controller.ts # 每个 Session 的异步状态与取消
│ ├── overlay.tsx # Dock 面板、Markdown 与键盘交互
│ └── index.tsx # Client 插件注册
├── host/
│ ├── service.ts # trusted RPC、校验、超时与编排
│ ├── context-snapshot.ts # 父会话请求头与平衡上下文快照
│ ├── one-shot.ts # 无工具的一次性模型生成
│ ├── cache-boundary.ts # Anthropic 缓存边界迁移
│ ├── btw-pi-ai-adapter.ts # rc.6 pi-ai 请求级 adapter
│ └── sidechain-kernel.ts # 隐藏 JSONL / memory / none 存储
└── shared/
└── protocol.ts # Client/Host RPC 协议与运行时校验
Host 和 Client 是同一个 DSH bundle 的两个运行面:cordis.patch.yml 加载 Host 插件,package.json 的 dsh.client 元数据加载浏览器端插件。
开发
pnpm install
pnpm test
pnpm run typecheck
pnpm run build
当前测试覆盖上下文平衡、缓存边界、输入接管、一次性生成、sidechain 隔离以及 Controller 生命周期。
兼容性
- 当前版本:
0.2.0 - 目标 DSH:
0.1.0-rc.6 - Node.js:
>=22 - pi-ai 缓存桥接会在预期的 rc.6 adapter internals 改变时 fail closed;其他 provider 仍走 DSH 公共
LlmRuntime.prepareCall()路径
No comments yet. Be the first to write one.