# dsh-context-branch [English](README.md) | 中文 一个基于 [dsh-nested-followups](https://github.com/sluminositys/dsh-nested-followups) 二次开发的 [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) 会话树插件,强调上下文分支与步骤级信息保留。 它为网页界面增加会话树。你可以针对此前的任意一条回答提问,新的问题会作为 一条独立分支展开,而不是被追加到主对话的末尾。同时,树视图详情面板会保留 工具调用、工具结果和推理过程等完整内容块。 [![许可证: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE) [![Node.js](https://img.shields.io/badge/Node.js-%3E%3D22.19-brightgreen.svg)](https://nodejs.org) [![DeepSeek Harness](https://img.shields.io/badge/DeepSeek%20Harness-0.1.0--rc.7-orange.svg)](https://github.com/deepseek-ai/deepseek-harness) ## 为什么有这个分支版本 本插件基于开源项目 [dsh-nested-followups](https://github.com/sluminositys/dsh-nested-followups) (DeepSeek Harness 的会话树插件)二次开发而来。 ### 我们对 DeepSeek 新对话冷启动的观察 每次新开一个 DeepSeek 对话,模型通常不会直接开始干活。它一般会: 1. 先理解任务和环境; 2. 展开对项目文件的探索; 3. 等它觉得自己对项目有了一定的了解,才会开始动手。 如果我们频繁开新对话,这段冷启动探索的 token 基本是多余的: - 如果不开新对话,这部分开销本来不需要重复发生; - 一旦开了新对话,这段冷启动内容又几乎无法命中 DeepSeek 的 Prompt Cache。 所以,“频繁开新对话来省 token”这个做法,会被冷启动成本严重制约。 假设冷启动大约占用上下文的 **5%**,按 DeepSeek 的输入 / 缓存命中价格比例计算: ```text sqrt(3元 / 0.1元 × 2) × 5% ≈ 38.73% ``` 也就是说,只有当旧对话的长度超过约 **38.73%** 时,开新对话或压缩上下文在经济上才划算。这正好对应大家常说的“对话到一半左右就该开新对话或压缩”——它不只是注意力机制下的最优解,也是经济上的最优解。 ### 这个插件做了什么 我们允许用户**在一轮对话的任意一步进行分叉**。 也就是说,你不必等整轮跑完,也不必修到会话末尾,而是可以在模型**刚完成项目探索、还没有开始实际工作**的那个节点,直接开出一条新分支。 这样做的好处是: - 新分支从“探索完成、尚未动手”的状态继续; - 分支之前的冷启动探索不需要重复发送; - 后续上下文也不会再携带已经不需要的探索过程。 对比 dsh 默认的“从会话第一轮结束才分叉”,本插件还能省掉第一轮中**具体完成用户任务的那部分内容**。在每轮有数十步甚至上百步工具调用的真实任务里,这种节约非常可观。 ### 数学推理:为什么总任务量越大越省 当总任务量固定时,线性对话每一轮都要携带前面所有历史,因此输入成本包含 **O(N²)** 项;而本插件的分支从共享根出发,重复的基础上下文只按缓存价计费,总成本近似 **O(N)**。 ```text T = 总任务量 B = 基础上下文 q = 每轮新增提问 token 数 o = 每轮输出 token 数 N = T / (q + o) ``` 代入 `T = 2,000,000`、`B = 10,000`、`q = 200`、`o = 500`: | 模式 | 总成本 | |---|---:| | 线性累积到 100% | ≈ 293,194,264 | | 80% 线性 + 20% 新会话 | ≈ 195,528,550 | | 本插件分支模式 | ≈ 6,294,714 | 对比结果: - 比线性累积省约 **97.85%** - 比 20% 新会话混合省约 **96.78%** 核心逻辑一句话: > 固定总任务量后,线性模式的 O(N²) 输入膨胀被放大,而插件把它降成了近似 O(N)。 ### 我们在基础插件上做了什么改动 1. **保留 text,新增 blocks** - `text` 继续用于文本选区、锚点、搜索和分支边界; - 新增 `blocks` 用于完整展示每一步的内容。 2. **不再丢弃非文本内容** - 后端从“只提取 text 块”改为把 `tool-call`、`tool-result`、`reasoning` 等完整内容块都传给前端。 3. **树视图详情面板可查看完整过程** - 点击节点后,不仅能看到用户提问和最终 AI 总结; - 还能看到中间的工具调用、工具结果、推理过程等。 4. **协议与类型同步升级** - 更新了 `MessageNodeView`、zod schema、客户端渲染和样式; - 兼容旧数据:没有 `blocks` 时自动回退到原来的 `text` 展示。 ## 要解决的问题 DeepSeek Harness 的对话是线性的。当你正在完成一项工程任务,想问清楚早先某条回答 里的一个概念时,只有两个都不理想的选择:在主对话里直接问,这会把无关内容混进任务 上下文,并且此后每次请求都会继续携带它;或者新开一个对话,这又会丢掉那些让这个 问题有意义的上下文。 本插件提供了第三种选择:把追问变成一条分支。分支只继承到你所提问的那条回答为止的 对话内容,不包含其他任何东西,主对话也完全不会看到分支里发生的事。 ## 功能 - **把当前对话显示为树。** 每条用户消息和助手回答各自成为一张卡片。主对话向下延伸, 分支向右生长。 - **可以无限层追问。** 分支里的回答可以再次被追问,嵌套层数没有限制。 - **真正的上下文隔离。** 每条分支都是用官方的会话分叉机制创建的独立会话,而不是靠 提示词要求模型"忽略某些内容"。 - **分支只读。** 分支可以读取工作区,但不能修改,因此追问绝不会干扰主对话里正在 进行的工作。 - **大树可渐进折叠。** 每个锚点可收成一枚点位,点开后每条分支一枚胶囊,再逐级 展开,深层树也能保持可读。折叠中的分支照常生成,并显示活动标记。 - **不改动原有界面。** 原生的对话视图、侧边栏和消息渲染都保持不变,分支也不会出现在 会话列表中。 - **复用模型服务已缓存的上下文。** 分支发出的请求前缀与主对话一致,因此无需重新 读取继承来的历史。 ## 环境要求 | 项目 | 版本 | | --- | --- | | DeepSeek Harness | `0.1.0-rc.7`(未经修改) | | Node.js | 22.19 及以上 | | 包管理器 | pnpm | ## 安装 ```sh dsh plugin --profile web add dsh-context-branch ``` 如果 DeepSeek Harness 的网页服务已在运行,请在安装后重启。
改用源码安装 ```sh git clone https://github.com/yummy4727/dsh-context-branch.git cd dsh-context-branch pnpm install pnpm run check dsh plugin --profile web add . ```
卸载插件: ```sh dsh plugin --profile web remove dsh-context-branch ``` 卸载只会移除插件的界面和服务,不会修改你的主对话,也不会删除分支的历史记录。 ## 使用方法 照常打开一个对话,然后点击对话顶栏的 **Tree View**。在 **Chat** 和 **Tree View** 之间切换只改变对话的显示方式,不会复制或转换任何数据。 进入树视图后: 1. 把鼠标移到一条已完成的助手回答上,点击 **Ask follow-up**; 2. 输入你的问题。它会作为一张卡片出现在所提问回答的右侧,回复在其下方生成; 3. 想在这条分支里继续对话,就在该分支最新的回答上点击 **Continue this branch**; 想再隔离一层上下文,则再次点击 **Ask follow-up**; 4. 点击任意卡片可以阅读完整消息。节点较多时,可以使用搜索、聚焦、折叠、缩略图和 缩放控件来浏览。 想继续主任务时,随时切回 **Chat**。 ### 折叠较大的会话树 首次打开一棵树,看到的就是最小形态:主对话加上每条有分支的回答旁的一枚点位。 点击 **⊕** 后出现每条分支各自的胶囊,再点击某枚胶囊才恢复该分支的消息卡片。打开 点位永远从胶囊列表开始,每一层都由你亲自选择展开哪条;其余胶囊保持折叠。想把卡片 组收回胶囊,把鼠标移到虚线框内侧底边,点击浮现的上箭头阴影区;点击 **⊖** 可把整组 收回点位。 按住 Alt 点击 **⊕** 或胶囊,可以一次深度展开其全部后代;工具栏的 **一键全收** 会把 所有一级锚点组收成点位。重启后按当前会话恢复布局。折叠状态下,蓝色脉冲表示后代仍在 生成,红色表示其中有失败;搜索命中折叠内容时会自动逐级展开祖先链并定位消息。 ### 两个动作,两种含义 这两个动作的区别不只体现在外观上,也体现在数据结构中: | 动作 | 生长方向 | 效果 | | --- | --- | --- | | **Ask follow-up** | 向右 | 新建一条分支,继承到所选回答为止的对话内容 | | **Continue this branch** | 向下 | 在当前分支中追加下一轮对话 | **Ask follow-up** 绝不会向已有分支追加内容,**Continue this branch** 也绝不会新建 分支。后者只出现在某条分支最新的已完成回答上,主对话中不会出现。 ### 删除分支 删除一条分支时,它下面的所有分支也会一并删除。确认对话框会说明将要删除多少条分支 和多少条消息。主对话和同级的其他分支不受影响。 ## 工作原理 ### 分支隔离 每条分支都是一个真实的 DeepSeek Harness 会话,从其上级对话的一个完整回合处创建。 以从回答 A2 创建的分支为例,它继承从对话开头到 A2 为止的全部内容;主对话在此之后 产生的内容不会进入这条分支,分支里的内容也不会进入主对话。从同一条回答创建的多条 分支之间同样互不可见。 分支会被标记为子代理来源的会话。这样既能让它们不出现在会话列表中,又完整保留了各自 的历史记录——分支从属于它的主对话,而不是变成一个需要你单独管理的条目。 ### 只读执行 分支以只读方式运行:可以查看工作区,但不能改变其中的任何内容。 这一限制是在工具真正执行时生效的,而不是通过对模型隐藏工具来实现。允许使用的工具 如下: `read`、`read_image`、`glob`、`grep`、`lsp`、`session_*` 系列查询工具、`job_list`、 `job_output`、`terminal_list`、`terminal_read`、`list_agents` 和 `get_goal`。 其余一律拒绝,包括插件不认识的任何工具,因此新增的工具不会因为遗漏而被放行。代码 模式的 `run_code` 仍然可用,因为程序调用的每个工具都会被逐个检查,其中的写入操作会 被逐个拒绝。 放行读取是有意为之:追问常常就是"这个文件是做什么的"。而写入不能放行,因为分支与 主对话使用同一个工作目录,否则分支可能在任务进行过程中修改文件。 ### 复用模型服务的缓存 插件不会改写分支发出的请求。分支会沿用其上级会话所使用的配置组合,并且完全不改动 工具定义、提示词段落和呈现格式。因此,分支请求的开头部分与主对话在分叉点处的请求 逐字节一致。 这直接关系到响应速度。模型服务会缓存请求的前缀,而工具定义正好位于请求的最前面。 哪怕只移除一条工具定义,开头的字节就变了,整段缓存随之失效,模型服务必须重新读完 继承来的全部对话,才能给出回答的第一个字。限制可见工具列表实现起来更简单,但会白白 放弃这份收益,而且并不会更安全——隐藏一个工具和拒绝执行它,拦下的是同一次调用。 ## 与 DeepSeek Harness 0.1.0-rc.7 的兼容性 当前版本面向未经修改的 `@deepseek-ai/dsh` `0.1.0-rc.7`。以下两项限制源于该版本对 子代理来源会话的处理方式。 **分支无法在原生对话界面中续聊。** DeepSeek Harness 用子代理来源标记来判断一个会话 归哪个组件所有,并会拒绝从原生对话界面向这类会话发送消息。原生界面可以显示分支的 历史,但无法向其中追加内容。因此当前版本没有提供"在对话中打开分支"的功能,阅读和 续聊都在树视图中完成。 **分支可能出现在子代理菜单中。** 由于插件不会安装子代理描述符,对话内置的子代理菜单 可能会把分支显示为不可用的条目。这些条目无法选中,键盘浏览时会被跳过,也不计入活跃 子代理的数量,菜单和对话本身都能正常使用。分支仍然不会出现在侧边栏的会话列表里。 插件中预留了对一项尚未发布的 DeepSeek Harness 能力的检测,该能力将允许在原生对话 界面中续聊分支。检测要求对应版本同时提供该能力并明确声明支持投递用户消息,以免某个 只实现了一半的版本悄悄启用可写界面。在此之前,该功能保持关闭。 ## 开发 ```sh pnpm install pnpm run check ``` `pnpm run check` 会依次执行代码检查、宿主端与浏览器端的类型检查、测试、生产构建和 发布包校验。 | 命令 | 用途 | | --- | --- | | `pnpm run lint` | 静态检查 | | `pnpm run typecheck` | 类型检查 | | `pnpm test` | 单元测试与集成测试 | | `pnpm run build` | 生产构建 | | `pnpm run check` | 以上全部,外加发布包校验 | ## 参与贡献 欢迎提交问题反馈和合并请求。提交合并请求前,请先运行 `pnpm run check`。 ## 项目状态 当前实现已在未经修改的 DeepSeek Harness `0.1.0-rc.7` 上验证通过。DeepSeek Harness 仍处于开发者预览阶段,即使本软件包声明的兼容范围更宽,后续版本仍可能需要相应更新。 ## 许可证 [MIT](LICENSE)