前言¶
在 DeepSeek Harness(DSH)里长聊时,某一轮的回答不满意,常见做法是复制用户提问再发一遍,或点动作条上的「分支」另开会话。前者模型仍能看到上一轮自己的回答,容易沿旧思路改稿;后者会切断同一条会话里的后续上下文。若目标是留在当前会话、只重做某一 turn,需要先把该轮旧回答从送给模型的消息列表里拿掉,再把该轮用户提问重新提交。
下面介绍社区插件 dsh-session-retry(维护者 linanbuan):在每条助手消息动作条上增加「重新回答」,先登记可见面改写、再在下一步请求打开步骤时补写,使模型重答时看不到自己在该轮的上一次输出;界面与日志里的历史消息不动。分类为工作流;仓库 linanbuan/dsh-session-retry,当前版本 1.0.0,许可证 MIT。DSH 社区目录 skillhub.cn 为独立站点,与 DeepSeek / 幻方无官方从属关系。
这是什么¶
dsh-session-retry 是一个标准 DSH bundle:宿主半边注册 API、在 agent/request 钩子里补写可见面替换;浏览器半边向 conversation.chat.assistant-actions 插槽注册动作条按钮(行 id:session-retry,order: 20,落在复制与分支之间)。浏览器半边只依赖 react,不 require Harness 客户端包;宿主半边注入 connection 与 agents。
核心功能¶
界面上做什么¶
动作条与原生按钮一致:方形图标、悬停提示、随该轮动作条显隐。客户端按会话拉一次锚点表,每条动作条用 messageId 查表——命中可点,未命中置灰并说明原因。
点击后先调用改写 API,成功才用 session.prompt([{ type: 'text', text }], 'queue') 重新提交提问(与 ui-conversation 的 conversation.send 同路径)。两步都成功才提示「已重新回答(旧回答已退出上下文…)」;改写失败只转述原因、不提交,避免旧回答仍在上下文时重复提问。
宿主 API¶
在共享 /api 通道上注册两条路由(只读 / 改写):
POST /api/session-retry/context { sessionId }
→ { ok, sessionId, latestTurn, cached, anchors: [...] }
POST /api/session-retry/regenerate { sessionId, messageId }
→ { ok, turn, promptSeq, runs, shadowed, kept, eventSeqs, deferred: true }
/context 扫会话日志,把「动作条挂在哪条助手消息」与「该轮用户提问」配对,结果按会话缓存约 1.5 秒。/regenerate 仅针对活会话(内存中的 sessions.get(id)),用与 /context 相同的 retryTargets 规则现场重算锚点,并登记影子计划(deferred: true),不在点击当场写可见面。
旧回答如何「消失」¶
改写的是可见面(surface),不是删日志:会话记录、界面历史、标题与统计保留;送给模型的列表由 dsh-session 的 surface 折叠与 deriveMessages() 决定。
机制是在下一步骤打开时,通过 system/message 事件携带 surfaceOp: { op: 'replace', startSeq, endSeq } 与完整的 sourceEventSeqs,用空 system 节点占位(空 user 消息会真发出去,故不用)。因 V4 要求步骤事件的 turn/step 必须等于当前打开的那一对,空闲时点按钮当场写入会破坏日志;因此 /regenerate 只登记计划,在 agent/request 钩子里用当时打开的 turn/step 补写——早于本轮提问的可见面节点保留,本轮产物按 shadowPlan() 影掉并分段替换。
判定规则(与界面一致)¶
动作条挂在每轮最后一个产出文本的步骤里最后一条 assistant/message 上。该轮若无产出文本的步骤,界面本就不渲染动作条。该轮提问取 turn/start 之后、锚点之前、source.kind === 'user' 的消息;注入上下文、压缩检查点、带 surfaceOp 的替换不计入提问。同一轮内多句用户话(steering / 排队)会合并成一段重发,提示语会说明「合并 N 条提问」。非文字块(图片、附件)只计数、不重发。
刻意取舍¶
| 行为 | 说明 |
|---|---|
| 影掉而非删除 | 日志事件不可删;要完整另起历史用「分支」 |
| 重答更早轮次 | 影子范围从该轮提问到可见面末尾,其后轮次也退出上下文 |
| 仅活会话 | 未打开返回 NOT_LIVE;回答中返回 RUNNING,需先停止 |
| 依赖降级 | 缺 sessionQuery 时 /context 为 UNAVAILABLE;缺 sessions 时 /regenerate 同理 |
安装与启用¶
先安装 DSH 桌面端(deepseek.com/download)。任选一种方式安装,<spec> 可为仓库地址、GitHub 源码 zip、本机目录绝对路径或将来可能的 npm 包名。
方式一(推荐):侧栏 插件 → 添加插件,填入例如:
https://github.com/linanbuan/dsh-session-retry
确认信息后 安装,再 立即启用。无需命令行、不必手改 profile。
方式二:在会话里把仓库地址或本机目录交给 Agent,由其调用 plugin_manager(action: install_bundle)。
方式三:已安装 dsh CLI 时:
dsh plugin --profile <profile 名> add <spec>
参数以 dsh plugin --help 为准。
方式四(排障):在 ~/.dsh/profiles/<profile 名> 的 package.json 中登记 dependencies 与 dsh.profile.bundles;WSL 下 Linux 符号链接 Windows 侧 Electron 可能无法解析,宜在 Windows 侧安装或使用目录副本。
装上之后:改 client.js 可在插件页 停用再启用 热挂载,浏览器半边随 /plugins/events 更新,无需刷新页面。改 index.js 或 package.json 须重启应用(宿主 ESM 缓存不会随热挂载刷新)。Node 要求 >=20;开发验证环境为 DSH 0.1.7-rc.2(Windows 桌面端)。
典型用法¶
- 在 DSH 中打开一场会话,等到某轮助手回答结束(该轮最后一条带文本的助手消息出现动作条)。
- 若对该轮回答不满意,点该条助手消息动作条上的 重新回答(位于复制与分支之间)。
- 若会话正在生成,先停止再点;成功后会按该轮用户提问(或多句合并后的文本)重新排队请求,旧回答不再进入模型上下文。
- 若需保留「含旧回答」的完整时间线作留证,请改用 分支 另开会话,勿在本插件改写的同一会话里当作唯一证据链。
插件不联网、无遥测;以当前 DSH 进程权限读会话日志与内存对象。安装前应自行查看源码与 MIT 许可证。它依赖 surface 与步骤事件等内部契约,非稳定 API,Harness 升级后可能出现置灰或改写失败,此时插件只转述原因、不提交,设计上避免写坏日志。0.3.0 之前曾在空闲时当场写步骤事件导致日志损坏,1.0.0 起改为 deferred 补写;旧版请升级。官方插件管理器目前不支持自动更新,升级需卸载后重装,宿主改动后需重启应用。
开发与测试¶
仓库内零依赖单元测试:
node test/retry-targets.mjs
node test/client-contract.mjs
或 npm test(等同上述两条)。
结尾¶
dsh-session-retry 把「同一会话内重做某一 turn」拆成可见面影子改写 + 重发用户提问两步,并与动作条锚点、多句提问合并、非文字内容跳过等行为对齐界面规则。适合在 DSH 工作流里快速迭代单轮回答、又不愿分支丢后续上下文的场景。