前言¶
在 DeepSeek Harness(DSH)里接大模型流式回答时,模型有时会以「抱歉,我不能…」「Sorry, I can’t…」一类话术直接拒答。若只在 HTTP 反向代理层处理,通常要把整段响应缓冲完再判断,流式打字效果会断掉。DSH 走客户端插件与结构化 chunk,可以在只预读开头若干字符后做决定,被拒那次不向用户展示,再静默重发,正常回复的流式体验基本保留。下面介绍社区插件 dsh-policy(维护者 hbx111,分类:模型推理)的实现思路与配置方式。
这是什么¶
dsh-policy 是挂载在 DSH 上的插件(当前包版本 1.1.0,MIT 许可证),承担两件彼此独立的工作:
- 流内判拒与自动重试(README 中的 headline 能力):若模型正文以拒答模式开头,丢弃该次尝试并静默重新发起请求。
- 策略文本注入(可选,默认关闭):在出站请求尾部追加一段 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,是否采用请结合自有合规要求与源码审阅后决定。