READMESource: master@d99f3227
dsh-send-debounce
给 DSH 的「连发消息」加一层缓冲:用户在聊天框里一句接一句地发,中间不产生 N 次唤醒/回复; 停手若干秒后,这几条合并成一轮送进模型,只回一次。
英文说明见 README.en.md。 / English: see README.en.md.
它做什么
- 拦截点:服务端每个 agent 的
followup()(@deepseek-ai/dsh-agent-loop)。 - 用户消息立刻进排队区(网页上看得到),但先不唤醒模型。
- 默认静默 5 秒;期间每来一条短消息就重置计时。
- 到点后把这一批合并成一条消息,再唤醒一次。
- 长消息(默认 ≥120 字)或带图片/附件的消息立即发出,不延迟。
steer()(插话)和agent.followup的插件来源(如dsh-proactive、dsh-tick)不受影响。
配置(环境变量,改完重启 dsh 生效)
| 变量 | 默认 | 含义 |
|---|---|---|
DSH_SEND_DEBOUNCE |
开 | off / 0 / false / no 一键关闭 |
DSH_SEND_DEBOUNCE_MS |
5000 |
静默时长(毫秒);0 等于关闭延迟;非法值退回默认 |
DSH_SEND_DEBOUNCE_MAX_CHARS |
120 |
「短话」上限;0 = 一律延迟;非法值退回默认 |
安装
从 npm(公开包):
dsh plugin --profile web add dsh-send-debounce
从本地目录(自用/开发):
dsh plugin --profile web add file:/path/to/dsh-send-debounce
装完把 dsh-send-debounce 加进该 profile 的 dsh.profile.bundles(cordis.patch.yml 就是这一层),
然后重启并确认网页还在:
dsh --profile web --dump-config | grep -n dsh-send-debounce
sudo systemctl restart dsh
curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:3080/
安全边界(很重要)
这个插件的原则是只做缓冲,别的什么都不碰:
- 只处理网页用户自己发的消息(
message.source.kind === "user")。插件来源的消息 (dsh-proactive、dsh-tick等)原样放行,否则会卡住它们的whenIdle()。 - 不碰
steer()(插话),也不主动修改会话历史;只把自己入队的那几条消息在到点后 合并成一条,交给原生followup()。 - 长消息和带附件/图片的消息立即发出,绝不延迟,避免大段内容被拖着。
- 原文不改写:合并只是在两条消息之间插入空行,保留各自原文与顺序。
- 任何异常都退回原生行为:读队列失败、合并失败、发送失败都会被
try/catch兜住, 逐条补发或直接放行,不让聊天框卡住。 - 不联网、不读工作区、不写文件、不读环境里的密钥:只读三个
DSH_SEND_DEBOUNCE*配置项。 源码全部在index.mjs,可自行核对。
已知限制
- 静默期是进程内定时器:dsh 在合并触发前重启,这批消息仍持久化在会话里,但不会自动 被唤醒,要等下一条消息进来(或手动触发)才会继续。重启前请等几秒让缓冲落地。
- 环境变量改动需重启 dsh,不支持热更新。
- 长短按字符数判断,不看语义;一句很短但很急的话也会被缓 5 秒。
- 合并后模型看到的是合并文本,不再区分原来是几轮输入;原始入队记录仍在会话的 inbox 事件里,但这只对排查有用。
- 只在服务端生效(手机浏览器同样受益),不改变前端界面。
回滚
按 profile 备份还原:
cp ~/.dsh/profiles/web/package.json.bak-<日期> ~/.dsh/profiles/web/package.json
sudo systemctl restart dsh
或只把 dsh.profile.bundles 里的 dsh-send-debounce 删掉再重启即可完全卸载
(卸载会清掉定时器并恢复原生 followup())。
自测
不需要 dsh 环境,直接用 Node 跑:
npm test # 等价于 node test.mjs
覆盖:连发合并、长消息直发、插件来源直通、图片直发、间隔超时各成一轮、卸载恢复、 关闭开关、配置解析与非法值兜底、重复安装/卸载、agent 销毁清定时器、纯函数。
许可证
MIT,见 LICENSE。
No comments yet. Be the first to write one.