dsh-suggest
DeepSeek Harness 插件(动态 + 正式双形态):Agent 每轮回答完成后,在答案下方生成 3 个可点击、可编辑的推荐后续问题。
A DeepSeek Harness (DSH) plugin (dynamic and formal forms) that renders three clickable, editable follow-up question suggestions under every agent answer.
🏠 仓库:https://github.com/shaoeric/dsh-suggest
✨ 功能特性 / Features
- 自动生成:Agent 回答完成后,基于最近 3 条用户消息(含其间助手回复)自动生成 3 个推荐问题,以用户意图为锚点、紧扣最新进展;
- 一键直达:点击推荐项 → 内容直接提交给 Agent,自动开启新一轮回答;
- 先编辑再发送:悬停推荐项会浮现 ✏️ 编辑按钮,点击后内容填入输入框,编辑满意后自行发送;
- flash 模型 + 关闭思考:优先使用同 provider 的 flash 模型,请求携带
reasoningEffort: 'off'(DeepSeek 适配器映射为thinking: disabled),生成又快又省;找不到 flash 或适配器不支持时自动回退;失败则回退会话原模型; - 稳定兜底:按会话+轮次缓存、生成中显示骨架占位、失败显示具体原因并可一键重试;
- 只显示最新一轮:推荐项只在最新一轮回答下方显示,发起新一轮对话后自动隐藏,避免误触;同时补渲染 📎 产物行不丢失;
- 主题自适应:全部配色使用
--dsw-alias-*主题变量,自动适配明暗模式。
🧱 工作原理 / How It Works
| 半区 | 平台 | 职责 |
|---|---|---|
host.js |
Host(Node) | 通过 harness.handle('recommendations') 暴露包内 RPC(正式版经 Typert Remote 暴露 suggest/recommendations);读取会话日志(≤ 收尾 seq),以最近 3 条用户消息为锚点截取上下文;优先使用同 provider 的 flash 模型(找不到则回退会话模型)调用 llm.stream 生成 3 个推荐(关闭思考模式),带缓存与并发去重 |
client.js |
Client(浏览器) | 注册 conversation.chat.turnTail 链式插槽(priority: -1);渲染垂直左对齐的推荐列表;点击 → inputActions.setDraft + submit() 直发;点 ✏️ → 仅 setDraft 填入输入框 |
回答完成 → turn-tail 节点渲染
→ 链选举命中本插件 → 组件挂载
→ host.call('recommendations', {sessionId, turn, seq})
→ Host 截取最近 3 条用户消息上下文 → flash 模型(thinking off)生成 JSON 数组
→ 返回 3 条推荐 → 渲染为可点击列表
📁 文件结构 / Files
dsh-suggest/
├── host.js # 动态插件 Host 半:推荐生成(RPC、会话读取、LLM 调用、缓存)
├── client.js # 动态插件 Client 半:turnTail UI、点击直发、hover 编辑、产物行
├── lib/
│ ├── index.js # 正式插件 Host 半(与 host.js 同逻辑;Typert Remote + strict manifest)
│ └── client.js # 正式插件 Client 半(与 client.js 同逻辑;ModuleLoader bundle)
├── package.json # 正式插件包清单(dsh.bundle.patch + dsh.client)
├── cordis.patch.yml # 正式插件插入 host composition 的 patch 层
├── dsh.plugin.json # 正式插件清单
├── LICENSE # MIT License
└── README.md
🚀 安装与使用 / Install & Run
本插件提供两种安装方式:正式插件(从仓库安装,持久化,推荐)与动态插件(临时,进程重启即失效)。两套代码功能一致:lib/ 是正式插件形态,host.js / client.js 是其动态源。
方式一:正式插件(从仓库安装,持久化,推荐)
仓库根目录本身就是一个标准 Harness web 插件包(声明了 dsh.bundle.patch 与 dsh.client)。把仓库克隆到本地后,用 dsh plugin 直接装进 web profile,跨重启常驻。
从仓库安装:
git clone https://github.com/shaoeric/dsh-suggest
cd dsh-suggest
dsh plugin --profile web add file:$(pwd) # Linux/macOS
# Windows(PowerShell)换成仓库绝对路径,例如:
dsh plugin --profile web add file:C:/path/to/dsh-suggest
file:后面接的是仓库根目录的绝对路径——这个仓库根目录本身就是插件包(pnpm 只打包files字段列出的文件)。
dsh plugin add 会代理到 profile 目录里的 pnpm,并自动把包登记进 profile 的 dependencies 与 dsh.profile.bundles(因为包声明了 dsh.bundle)。装完重启生效:
- 在运行
dsh web的终端 Ctrl+C 停掉服务器; - 重新运行
dsh web; - 刷新浏览器页面。
重启后:Host 半随进程启动挂载(Typert Remote 端点 suggest/recommendations),浏览器半由 Web 服务器在 /plugins/dsh-suggest/client.js 提供给每个页面。
更新(改了 lib/ 或清单后):
⚠️
file:依赖缓存安装时的快照,直接重跑add不会刷新内容;先 remove 再 add。
dsh plugin --profile web remove dsh-suggest
dsh plugin --profile web add file:/绝对路径/dsh-suggest
# 然后重启 dsh web
卸载:
dsh plugin --profile web remove dsh-suggest
# 然后重启 dsh web
正式版与动态版的实现差异:
动态版(host.js / client.js) |
正式版(lib/) |
|
|---|---|---|
| 包内 RPC | harness.handle / host.call(动态 runner 注入) |
Typert Remote:suggest/recommendations 端点 + strict manifest,经 api-gateway 走 /api |
| 服务访问 | ctx.get('llm') 等(动态 sandbox 门面) |
相同(正式 Cordis ctx.get 语义一致) |
| 客户端环境 | runner 闭包注入的 React / host / styles 全局 |
require('react') + ctx.remote.$mount + <style> DOM 注入 |
方式二:动态插件(临时)
需要 DSH 的 Cordis 插件能力(「创造模式」agent preset)。
- 在 DSH Web GUI 中用「创造模式」preset 打开一个会话;
- 让 Agent 安装本插件(把仓库交给 Agent),或手动操作:
- 用
cordis_define创建插件:code.host粘贴host.js内容,code.client粘贴client.js内容; - 用
cordis_run激活返回的pluginId/packageId;
- 用
- 首次运行需在 GUI 中批准插件(单勾授权当前版本,双勾授权后续更新);
- 之后每一轮回答结束,答案下方即出现 3 个推荐问题。
⚠️ 生命周期说明:动态插件是进程内临时定义,DSH 进程重启后即丢失,需重新 define + run。如需跨重启持久存在,用上面的方式一(正式插件)。
🧰 依赖的能力 / Dependencies
- Host 服务:
llm、sessions(可选sessionQuery、agentDefaultModel作为回退);正式版另需typert(注册 Remote manifest) - Client 内置:动态版
React、host、styles;正式版remote(api-gateway client)、slots(client-runtime)与react模块 - 客户端插槽:
conversation.chat.turnTail(由dsh-client-ui-conversation声明) - 标准 Props:
sessionId、inputActions、useSession(客户端会话标准套件)
🛠️ 常见问题 / Troubleshooting
| 现象 | 处理 |
|---|---|
显示 ⚠️ 推荐生成失败 + 原因 |
按提示点击重试;错误文字含 kind 与错误码,可用于进一步定位 |
| 推荐项不出现 | 确认插件已批准并处于 running;刷新页面后查看历史轮次 |
| 希望恢复水平布局 | 把 client.js 中推荐区容器类名从 dshrec-x42-list 改回 dshrec-x42-section,并删除 list 相关 CSS |
🏷️ GitHub Topics / 仓库标签
建议在仓库 Settings → Topics 中设置:
dsh dsh-plugin deepseek deepseek-harness
同时推荐补上:deepseek-api、cordis(可选)。
🔍 技术参考 / Where the slot dispatch lives
conversation.chat.turnTail 是 DeepSeek Harness 内置的链式(chain)插槽,其声明与分发逻辑位于 DSH 源码(deepseek-ai/DeepSeek-Harness)的三个包中:
| 逻辑 | 包 | 源文件 |
|---|---|---|
插槽声明与渲染入口:turn-tail 节点注册(key: 'turn-tail',children 声明 conversation.chat.turnTail chain)并调用 renderSlotChain(..., { turn, seq, openFile }) |
@deepseek-ai/dsh-client-ui-conversation |
src/client/chat/register-node-renderers.ts、src/client/chat/TurnTailNodeView.tsx |
链式选举分发:按 priority 升序遍历条目,运行 entry.select(ownerProps),首个非 null 返回值胜出并以 matched 属性挂载;全部返回 null 则走 owner fallback |
@deepseek-ai/dsh-client-web-react |
src/scoped-slots.tsx(SlotOutlet / renderOutletContent 的 spec.kind === 'chain' 分支) |
条目注册与优先级排序:SlotCore.register 对 rec.entries 按 priority 升序排序;chain 注册必须提供 select |
@deepseek-ai/dsh-client-ui-slots |
src/index.ts(SlotCore.register,约 121–123 行的 next.sort) |
本插件即依赖这套机制:select 无条件认领(priority: -1,先于内置的产物行条目 x6),组件内自行补渲染产物行,因此推荐项出现在每个完成的回答下方。
📄 License
MIT © 2025 shaoeric
No comments yet. Be the first to write one.