dsh-policy:流式输出下的判拒检测与静默重试

前言

在 DeepSeek Harness(DSH)里接大模型流式回答时,模型有时会以「抱歉,我不能…」「Sorry, I can’t…」一类话术直接拒答。若只在 HTTP 反向代理层处理,通常要把整段响应缓冲完再判断,流式打字效果会断掉。DSH 走客户端插件与结构化 chunk,可以在只预读开头若干字符后做决定,被拒那次不向用户展示,再静默重发,正常回复的流式体验基本保留。下面介绍社区插件 dsh-policy(维护者 hbx111,分类:模型推理)的实现思路与配置方式。

这是什么

dsh-policy 是挂载在 DSH 上的插件(当前包版本 1.1.0,MIT 许可证),承担两件彼此独立的工作:

  1. 流内判拒与自动重试(README 中的 headline 能力):若模型正文以拒答模式开头,丢弃该次尝试并静默重新发起请求。
  2. 策略文本注入(可选,默认关闭):在出站请求尾部追加一段 policy 文本,作为 user 消息发给模型。

中文 README 概括为:在 dsh 客户端侧对模型响应做流内判拒并自动重发;只预读开头一两句,被拒那次不吐给上层,正常回复的流式打字几乎不受影响。插件零外部依赖,仅使用 Node 内置模块;要求 Node >=18,DSH >=0.2.0-rc.1。

与「纯代理」的差异可以概括为:

plugin:  llm/stream (waterfall)  →  peek first N chars  →  refusal?  →  re-issue & swallow
proxy:   full response buffered  →  judge at the end    →  no streaming at all

核心功能

判拒与重试

  • 廉价预读:默认只缓冲开头 peekChars(默认 200)个字符再判定;普通长回答几乎直通,延迟很小。
  • 推理块感知:reasoning-delta 不计为「正文已开始」;模型先长段推理再拒答仍可识别。
  • 工具调用感知:若第一个块是 tool call,不按拒答处理,不重试。
  • 上游过滤感知:content_filter / guardrail_intervened / risk control 等终止视为终态,不再重试,避免烧 token,并在失败信息上附注。
  • 中英拒答模式:覆盖如 Sorry, I can't…、抱歉,我不能提供…、我无法写这类…,以及中文后置否定(例如「继母与继子这类家庭亲属关系的性内容我不写」一类句式)。
  • 降低误判:技术表述如「这个函数我不写注释」不应触发重试;规则用窄模式与对象词白名单约束。

判拒逻辑 looksRefusalHead(text) 对前 200 字符分四类:开头道歉+不能、开头不能+动作、指示词+不能、后置否定等。后置否定用于补全仅以句首「抱歉」「我不能」锚定会漏掉的中文拒答。

策略注入(可选)

通过 ~/.dsh/policy/config.json 的 mode 控制注入位置:off | user | system | both;文档说明 user 是较可靠送达模型的位置。重试与 mode 无关,mode 为 off 时仍可单独开启重试。

onlyAgentLoop 默认为 true:仅对携带 tool definitions 的请求注入策略,标题生成等内部调用不注入。

配置热更新与日志

  • 配置目录:~/.dsh/policy/,含 config.json、policy-sys.txt(mode != off 时注入的文本)、policy.log(决策日志,上限 256 KiB)。
  • 修改 config.json 的 mtime 后插件会重读,无需重启 dsh;修改 index.js 需重启 dsh。
  • log: true 时向 policy.log 追加决策记录。

默认配置示例(README 原文):

{
  "mode": "off",
  "onlyAgentLoop": true,
  "log": true,
  "retry": {
    "enabled": true,
    "maxRetries": 2,
    "peekChars": 200,
    "peekMs": 1200,
    "minTextChars": 24,
    "textStallMs": 1500,
    "maxWaitMs": 10000,
    "backoffMs": 350
  }
}

各键含义与默认值见下表(摘自 README;retry.enabled 文档表内默认值为 false,示例 JSON 中为 true,以你实际写入的配置为准):

Key Default Meaning
mode off 策略注入:off | user | system | both
onlyAgentLoop true 仅在带 tool 定义的请求上注入
log true 写 policy.log
retry.enabled false 拒答重试总开关
retry.maxRetries 2 拒答后最多重发次数
retry.peekChars 200 判定前缓冲字符数
retry.peekMs 1200 等待首段文字的最长时间
retry.minTextChars 24 低于此长度继续等待
retry.textStallMs 1500 文字停止增长多久后放行
retry.maxWaitMs 10000 预读窗口硬上限
retry.backoffMs 350 重发前等待

安装与启用

README 给出两种方式,未提供 dsh plugin add github:… 一类一键命令,安装前请自行阅读仓库源码与许可证(插件以当前 dsh 进程权限运行)。

方式一:将插件目录放在 dsh profile 旁并注册。

方式二(推荐):作为 profile bundle 安装;包内已含 cordis.patch.yml,不必在 profile 里重复写入。

若不以 bundle 安装,需在 profile 的 cordis.patch.yml 中插入:

# profile/cordis.patch.yml — only needed if you are NOT installing as a bundle
- insert:
    - id: dsh-policy
      name: "dsh-policy"

以 bundle 安装时,包内 cordis.patch.yml 已包含上述条目,不要在 profile 中重复。

启用重试时,将 ~/.dsh/policy/config.json 中 retry.enabled 设为 true,并按需调整 peekChars、maxRetries 等。需要策略注入时编辑 policy-sys.txt 并设置 mode。

本地测试

仓库提供无网络、无需启动 dsh 进程的测试,驱动伪造 chunk 流经真实 listener:

node test-retry.mjs

README 记载 16 个用例,覆盖拒答命中、正常内容不误判、重发后用户只见第二次、长推理后拒答、工具调用不重试、上游 content_filter 不重试等场景。

适用场景与限制

适合:在 DSH 智能体/对话链路中希望保留流式 UX,又对「开口就拒」做有限次自动重试的开发者;可与可选 policy 注入配合约束模型行为。

限制(README 原文要点):

  • 重试是统计意义上的缓解;若同一输入每次都被拒,重试只会多耗 token,最后一次原样返回。
  • 检测基于前缀;拒答出现在多段正常正文之后则抓不到。
  • 预读窗口会在首 token 慢时增加延迟,可通过降低 peekMs 权衡首字延迟与检出率。

参考链接

DSH 生态强调「一切皆插件」;SkillHub 等目录为社区站点,与 DeepSeek / 幻方无官方从属关系。dsh-policy 当前 GitHub 星标为 0,是否采用请结合自有合规要求与源码审阅后决定。

羽毛球分组比赛记分
小程序二维码

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

小夜