前言¶
在 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 工作流裏快速迭代單輪迴答、又不願分支丟後續上下文的場景。