DeepSeek Harness 插件:dsh-quote-annotate-pro

前言

在对话式智能体开发中,引用原文并附带批注是常见需求。主流做法是把引用收进 chip 或标签,气泡里只剩一个「引用1」或「批注 ×N」,原文要点开或悬停才能看到。这种方式让对话气泡更”干净”,但常要对照原文时需要多一次操作。

DSH(DeepSeek Harness)有一款插件 dsh-quote-annotate-pro,采用不同的思路:引用块在输入框和已发送消息里始终可见,显示紧凑的原文片段(最多3行),点击即可跳回原文并高亮。它让引用成为对话的一部分,而不是被藏起来的元数据。

插件定位

dsh-quote-annotate-pro 是一个客户端插件,负责划词批注与引用锚点。它将选中的文字变成带锚点的引用块,输入框里和已发送消息里都显示紧凑的原文片段,点击跳回原文并高亮。发给模型的文本仍标明批注编号、轮次和角色,要求模型按 Annotation N: 逐条回应,回复里的 Annotation N: 会渲染成「批注 N」芯片,悬浮显示原文与评语。

插件由 DoggyHU 维护,基于上游 dsh-quote-annotate-pro(MIT 许可证,原维护者为 zhao-guang-ai)进行本地 fork,额外在文档预览划词时让引用块带上来源文件路径。

核心功能

划词批注与自动编号

在任意消息里选中一段文字,选区上方会浮出小气泡,点「批注」后进入编辑框,可写评语(可留空),按 Enter 插入。同一条消息里可以连续批注多处,插件会自动编号(批注 1、批注 2…),删除中间某条不会重号。

紧凑引用块

插入批注后,输入框里出现紧凑引用块:显示原文片段(位置行隐藏)+ 批注:评语。与发送后的消息里完全一致。点击引用块可跳回原文并高亮。输入框与消息侧使用同一套视觉规则,观感一致。

模型侧精确定位

发送后,消息文本仍以位置行开头(界面隐藏),模型收到的文本包含 【批注 N · 第N轮 · 角色】,明确知道引用的是哪一处、哪一轮、谁说的。模型回复里出现 Annotation 1:、Annotation 2:,插件将其渲染成「批注 N」芯片,悬浮显示原文 + 评语,点击跳回原文。

文档预览划词:带文件路径

在右侧栏的文档预览(docx / pdf / xlsx / 图片 / Markdown / 代码)里划词批注时,引用块位置行会带来源文件:

  • 聊天消息:【批注 2 · 第5轮 · DSH 回复】
  • 文档预览:【批注 1 · 文件 · sidebar-test\测试文档.docx】

文本类追加 :L12,PDF/Office 追加 :p3。路径默认显示为「工作区内相对会话 cwd、工作区外绝对路径」,想一律用绝对路径,把 lib/client.js 顶部的 DOC_PATH_STYLE 改成 'absolute'。取不到路径时自动退回 【批注 1】,不报错。

文件批注列表

只对不可编辑的文档预览生效(docx / pdf / xlsx / 官方 md 预览 / 文本与代码预览)。可编辑预览(milkdown、sidebar-editor)和聊天输入框里的选区仍然不会弹气泡。

在文档预览里划词 → 浮出「批注」→ 写意见 → 加入批注列表(不再立即写输入框)。文件有批注后,预览底部会浮出一条半透明胶囊:批注 · N + 展开/收起 + 全部发送 + 全部删除。展开后在上方浮出列表:单条点击原文可展开/收起;每条可编辑(行内输入框,Enter 保存 / Esc 取消)或删除。

点浮条/列表以外任意位置、或按 Esc 自动收起(行内编辑时 Esc 只取消该条编辑)。「全部发送」把 N 条按 【批注 N · 文件 · <路径>[:p页|:L行]】 一次性写进对话输入框,按回车即可发送。「全部删除」清空本文件批注。

列表按「会话 + 文件绝对路径」存 localStorage,刷新/切 tab 不丢;发送后列表保留,可继续补充或手动清空。聊天消息里的划词批注不受影响,仍然「立即插入 + 位置行 + 点击跳回」。

锚点持久化

ref → 锚点 映射写入 localStorage(按会话,最多60条),刷新页面后消息侧的引用块仍能精确跳回原文。

安装与启用

# 从 npm 安装(推荐,一步到位)
dsh plugin --profile <profile> add dsh-quote-annotate-pro

# 或直接从 GitHub 安装
dsh plugin --profile <profile> add github:zhao-guang-ai/dsh-quote-annotate-pro

# 本地开发(链接本地仓库)
dsh plugin --profile <profile> add <本仓库绝对路径>

装好后 profile 的 package.json 会出现:

"dsh-quote-annotate-pro": "link:<本仓库绝对路径>"

典型用法

  1. 在任意消息里选中一段文字 → 选区上方浮出小气泡 → 点「批注」→ 写评语(可留空)→ Enter;同一条消息里可以连续批注多处,插件会自动编号(批注 1、批注 2…)。
  2. 输入框里出现紧凑引用块(原文片段 + 批注:评语,位置行隐藏),点它可跳回原文。
  3. 发送后气泡里仍是紧凑引用块,点它随时回到原文并高亮;模型回复里的 Annotation 1:、Annotation 2: 会变成 批注 N 芯片,悬浮看这条批注的原文与评语,点击跳回原文。

工作原理

选中文字 → 气泡「批注」→ 编辑框 → Enter 插入。插件调用 input.setDraft(...) 写入纯文本 【批注 N · 第N轮 · 角色】 + 原文 + 「批注:评语」。输入框装饰立刻把这三段折叠成紧凑引用块(位置行隐藏,原文浅色块)。发送后消息文本 = 输入框文本,原样带上位置行。模型收到批注编号、轮次、角色、原文与评语。模型回复里出现 Annotation N: → 插件渲染成「批注 N」芯片(悬浮看原文+评语,点击跳回原文)。界面侧:插件在渲染时把位置行与引用正文包成紧凑块(隐藏位置行、只留原文),点击引用块 → 按锚点 scrollIntoView + 高亮(无可见提示)。

与上游的差异

本地 fork 对上游 dsh-quote-annotate(MIT,作者 wangwei-wade)进行了增强和修复:

兼容性修复(否则整插件静默失效):

  1. 缺 cordis 依赖声明 —— 上游 apply(ctx) 里 const slots = ctx.get('slots'),拿不到就 return。没有声明 inject: ['slots'] 时 apply 可能早于 slots 服务就绪执行,于是无声退出,一个 UI 都不注册。本 fork 声明 exports.name + exports.inject = ['slots'],并给每一步注册加 try/catch + 上报。

  2. conversation.chat.turnTail 已从 chain 变 list(DSH 0.1.6-alpha.2 起)—— 上游仍用 { select, priority } 注册,会抛错。改为 { name, id, order }。

  3. chip 插入 API 变了 —— 上游调用 input.insertReference(...),该对象在本版本没有此方法,永远退化为纯文本。改走官方会话事件 slash/input-insert-reference。

  4. 回合尾部按钮不出现(快照形状变了) —— 上游用 useSession 的快照 + Array.isArray(snapshot.nodes) + 节点 seq 来取该轮文本。本版本真实形状是 Chat 快照(useChat)、nodes 是 Map、节点身份是 anchorSeq、文本在 user.data.content[{type:'text'}] / assistant-step.data.blocks[{kind:'text'}]。全部按本版本形状重写取文与锚点解析。

能力增强:

  1. 当前会话解析 —— 上游读 useSessions(s => s.current),而本版本会话列表快照没有 current 字段(恒为 undefined),导致点「批注」只关掉气泡、什么都不发生。改为由会话作用域槽位(conversation.input.dock 探针)回填 sessionId,并去掉”必须先解析到会话才开编辑框”的门槛(解析失败也照常进入编辑,插入时再解析)。

  2. 引用块带轮次/角色 —— 上游只发原文。首行加 【引用 · 第N轮 · 角色】(轮次取自 data-chat-turn,角色取自 data-chat-flow-kind)。

  3. 输入框里的引用块 + 点击/悬停回原文 —— 输入框里直接写入纯文本(位置行 + 原文 + 评语),由插件装饰成与消息侧一致的紧凑引用块,点击即可跳回原文;不依赖 chip。ref → 锚点 映射仍落 localStorage(按会话,最多60条)。

开发

插件使用手写 bundle,无构建步骤 —— 改完保存即生效。包含以下文件:

lib/client.js          全部功能
tools/diag-sink.mjs    本地诊断接收端(可选)
tools/check-manifest.mjs  清单一致性自检
npm run check    # 校验 package.json / patch / bundle 注册 id 是否一致
npm test         # 客户端纯函数自检(编号 / 位置行解析 / 输入框摘要,无需浏览器与网络)
npm run diag     # 起诊断接收端(默认 http://127.0.0.1:47821)

改完 lib/client.js 无需重启 DSH:宿主按磁盘内容提供 bundle,客户端 HMR 检测到内容变化会自动热更该插件。

诊断通道(开发期):lib/client.js 顶部有 DIAG_ENABLED = true 和 DIAG_URL = 'http://127.0.0.1:47821/dsh-quote-pro'。插件启动时 POST 一次 /ping;探测不到接收端就本次运行静默(不会产生网络噪音)。配合 npm run diag,终端会实时打出全链路事件。

隐私/网络说明:本插件是纯客户端插件,不访问任何外部服务、不碰凭证,唯一的数据落点是 localStorage 里的锚点表(每会话 ≤60 条)。但默认会向本机 http://127.0.0.1:47821 发一次探测(以及探测成功后的诊断事件)。这是 DIAG_ENABLED = true 的默认行为,不要写成”零网络请求”。想让”无网络请求”成为事实,就把 DIAG_ENABLED 改成 false。

适用场景与注意

适合需要频繁对照原文、在对话中保留引用上下文、以及在文档预览中批量批注的场景。插件以当前 dsh 进程权限运行,安装前应检查源码与许可证。

结尾

dsh-quote-annotate-pro 让引用成为对话的一部分,而不是被藏起来的元数据。如果你常要”这段是我引的哪一句”来回对照,这款插件能显著提升开发体验。

  • 插件目录页:https://www.skillhub.cn/plugins/DoggyHU/dsh-quote-annotate-pro-standalone
  • GitHub 仓库:https://github.com/DoggyHU/dsh-quote-annotate-pro-standalone
羽毛球分组比赛记分
小程序二维码

欢迎使用《羽毛球分组比赛记分》微信小程序

Xiaoye