前言¶
在 DSH 里跑多轮对话时,助手上一轮的语气往往不会自动「带进」下一轮:模型只看到 transcript,没有一条显式的「我此刻处于什么状态」的通道。常见做法是让用户侧情绪感知插件去改回复风格;方向相反的是,把判定对象换成工作模型自己在上一轮输出里流露的状态,再在下一轮开始前悄悄写进请求,让行为随状态延续,而不把判定过程暴露给用户。
下面介绍社区插件 dsh-emotion-feedback(维护者 A-f-ish,分类:模型推理)。它在一包内同时提供宿主侧 bundle(dsh.bundle)与浏览器面板(dsh.client),无需构建步骤。
这是什么¶
dsh-emotion-feedback 是 DeepSeek Harness 插件:每轮结束时判定当前工作模型自身的情绪,并在下一轮开始前静默重新注入,形成「自观测 → 状态携带 → 行为偏移」的闭环。判定步骤不会进入用户可见的 transcript;用户感知到的是下一轮 tone 的变化,而不是判定 machinery。
- 仓库:https://github.com/A-f-ish/dsh-emotion-feedback
- 社区目录页:https://www.skillhub.cn/plugins/A-f-ish/dsh-emotion-feedback
- 许可证:MIT(见仓库
LICENSE) - 当前 package 版本:0.1.0(以
package.json为准)
工作循环¶
先做判定,再做注入;两阶段挂钩在 agent 生命周期上。
① turn 结束 agent/turn-stopping(串行、await)
取本轮最后一条 assistant 消息
↓ 本地关键词层优先(命中则 0 次模型调用)
↓ 否则在 useLlm=true 时 → 一次轻量 llm.stream 判定
(temperature 0,单行 JSON,协议 EMOTION_TONE_JUDGE v1)
结果更新本会话情绪状态;不追加 session 事件,判定对用户不可见
② 下一轮开始 agent/pre-step({ prepend: true })
将当前状态渲染为一条 synthetic user 消息
(source.kind = 'emotion-feedback')并 append 到本次请求
↓
工作模型带着该状态作答,并被要求不要解释状态来源
同一会话同一轮注入是幂等的;强度低于 minIntensity 时整段注入跳过。
关键词层命中时不调用模型;未命中且允许 LLM 判定时,仅一次轻量调用。
情绪状态与混合规则¶
判定结果在进程内维护,结构大致如下(标签不在词表内时保留原文并标记 outOfList: true,不再改写为「平静」):
{
emotion: '愧疚',
intensity: 0.62, // 0-1
reason: '关键词「抱歉」',
source: 'rule', // 'rule' | 'llm'
outOfList: true,
turn: 7,
history: [{ turn: 7, emotion: '愧疚', intensity: 0.62 }, /* … */],
}
要点(与默认配置行为一致):
- 否定 guard:关键词附近出现否定词(如
不、没、无等,列表可配negations;设为[]可关闭)时丢弃该命中,避免「不确定」「风险可控」等反向误触。 - 混合而非覆盖:旧强度先按
turnDecay ^ (turns-elapsed)衰减;同情绪取max(old, new);切换情绪时取max(old × takeoverRetain, new)。 - 空闲衰减已禁用:原
halfLifeMinutes相关逻辑在代码中注释保留,当前强度仅通过blend()变化。
安装与启用¶
安装会执行 pnpm add、校验 package.json 中的 dsh.bundle,并将 cordis.patch.yml 合并进 profile。合并内容仅一行 insert:
- insert:
- id: emotion-feedback
name: 'dsh-emotion-feedback'
官方 README 给出的安装方式如下(路径请换成你的本机目录;npm 发布后可改用包名):
# from npm (once published)
dsh plugin --profile <profile> add dsh-emotion-feedback
# from a local checkout — the path is recorded as a junction, so editing the
# checkout is the same as editing the installed plugin
dsh plugin --profile <profile> add "C:\path\to\dsh-emotion-feedback"
必须完整重启 DSH。安装只合并 bundle patch;插件模块加载与 apply() 发生在宿主组装 plugin generation 时。浏览器半部分是静态 bundle、无 HMR,新开浏览器标签不够。
也可在 DSH 会话内让 agent 通过 plugin_manager 工具的 install_bundle 指向插件目录。中文逐步说明见仓库 INSTALL.md;README 中文版为 README.zh.md。
安装前请自行审阅源码与 MIT 许可证;插件以当前 DSH 进程权限运行。
配置¶
在 profile patch 里给插件行增加 config:,各字段均有默认值。完整 schema 见 lib/types/index.d.ts 中的 Config。
- id: emotion-feedback
config:
minIntensity: 0.3 # injection threshold (default 0.2)
judgeModel: '' # judge model; empty = the session's default model
judgeProvider: '' # same, for the provider
useLlm: true # allow the model judge when keywords miss
inject: true # false = judge and keep state, never inject
includeReason: false # true = append the judge's reason to the injected text
timeoutMs: 6000 # single judge timeout; on timeout the old state stays
turnDecay: 0.9 # per-turn intensity decay
takeoverRetain: 0.35 # retained fraction of the old intensity on emotion switch
disabled: false # emergency switch: true = silences everything (wins over enabled)
debug: false # true = print the judge result each turn
| 字段 | 默认 | 作用 |
|---|---|---|
enabled / inject |
true |
总开关 / 是否注入(关 = 仅判定) |
disabled |
false |
紧急开关,优先于 enabled |
minIntensity |
0.2 |
注入阈值 |
judgeProvider / judgeModel |
'' |
判定模型;空则回退 session 默认 |
useLlm |
true |
是否允许模型判定层 |
timeoutMs |
6000 |
单次判定超时;超时保留旧状态 |
turnDecay / takeoverRetain |
0.9 / 0.35 |
混合规则 |
halfLifeMinutes |
10 |
未生效(仅保留字段) |
injectPerStep |
false |
是否在同轮后续 step 也注入 |
emotions / rules / prompt / template |
内置 | 词表(15 标签)/ 关键词规则 / 判定 prompt / 注入模板 |
negations |
见 README | 否定词表;[] 关闭 guard |
panelEvent / panelRoute / statusTool |
true |
会话事件 / 只读 HTTP 路由 / emotion_status 工具 |
persistHistory |
true |
会话历史落盘(保留 30 天) |
界面与只读接口¶
浏览器面板¶
挂在 conversation.composer.dock,标签为「情绪」。收起时为色点 + 情绪 + 强度;展开后显示强度条、最近一次判定(时间、上一情绪、原因、来源「关键词」/「模型判定」、词表外标记等)、40 点强度曲线与最近 8 次判定。
面板不依赖额外专用 endpoint:每次注入写入 session log 为 user/message,且 source.kind = 'emotion-feedback'、surfaceOp: 'append';客户端通过 slot prop useChat((s) => s.legacy.nodes) 读对话节点,曲线反映的是模型实际收到的内容。可选 GET /emotion-feedback/state?sessionId=…(Web 部署)补充宿主侧细节;不可达时面板静默降级。
emotion_status 工具¶
只读工具,供模型查询当前情绪、强度、原因、来源(rule / llm)与近期轨迹,与面板读同一进程内状态。依赖 @deepseek-ai/dsh-tools 懒加载注册;宿主无该包时仅失去工具,其余不受影响。
历史文件¶
路径:<DSH_HOME>/emotion-feedback/history/<sessionId>.json,按轮记录判定,保留 30 天。persistHistory: false 可关闭。
典型用法与适用场景¶
- 在 profile 中安装插件并重启 DSH,按需调整
minIntensity、useLlm、inject。 - 多轮对话中观察 composer 上方「情绪」面板;需要排查时可开
debug: true看每轮判定输出。 - 若只希望维护状态、不向模型注入,设
inject: false。 - 临时关闭整条链路而不卸载:
disabled: true。
适合希望在不增加用户可见「情绪分析」话术的前提下,让助手跨轮保持与上一轮自我表述一致语气倾向的 DSH 开发者。不适合把判定过程展示给终端用户的场景——设计上刻意对用户隐藏判定步骤。
README 中有本地 injection ON/OFF 对照实验描述(正文在仓库内,此处不展开未完整抓取的数据)。使用前请自行评估额外 LLM 判定调用(关键词未命中时)的成本与延迟;timeoutMs 超时不会更新状态。
依赖与环境¶
- Node:
^22.19.0 || >=24.0.0(见package.jsonengines) - peer:
@deepseek-ai/cordis>=4.0.4 <5.0.0,@deepseek-ai/dsh>=0.2.0-rc.2 <0.3.0-0(dsh 为 optional peer)
社区插件目录 skillhub.cn 独立于 DeepSeek / 幻方,并非官方应用商店;选型与安装责任在使用者。
结尾¶
dsh-emotion-feedback 把判定方向从「用户情绪」换成「助手自身状态」,用 turn 末判定 + 下轮 pre-step 注入完成闭环,并配合关键词零调用路径与可选轻量 LLM 判定。进一步细节、中文安装清单与变更记录见 GitHub 仓库与 INSTALL.md。