dsh-if:给 DSH 加一条先判断条件、成立才发送指令的 /if 命令

前言

在 DSH 里编排智能体时,经常需要“先确认条件再动手”:上一轮是否报错、用户是否确认了某个方案、测试是否通过。把判断写进提示词,判断过程会占用主上下文;判断服从规则、命令参数等还可能改写后续请求的缓存前缀。dsh-if 把这件事收成一条自定义 /if 命令:判断交给一个继承当前会话上下文的 fork 子代理,条件成立时才把 /if 下方的指令作为一条普通用户消息发给主 agent,不成立则什么都不做。

下面介绍它的能力、安装、用法与需要注意的约束。

这是什么

  • 插件名:dsh-if,仓库 tlewa/dsh-if,npm 包名 dsh-if。
  • 维护者:TLEWA,许可证 MIT。
  • 分类:工作流;社区目录页登记为 GitHub 1 stars。
  • 定位:给 DeepSeek Harness 加一条 /if 斜杠命令,用 LLM 判断一个条件,成立时才发送指令。

判断器是一个 fork 子代理,继承当前会话的工具、system prompt 与消息历史,只在末尾追加一条判断指令。判断请求与主会话下一次请求共享逐字节一致的 tools → system → messages 前缀,因此可以复用 provider 的 prompt cache / KV cache。

涉及英文原文的表述摘自该插件的 GitHub README;目录页与维护者信息来自其社区目录条目。

核心功能

下面按 README「功能」一节逐条列出,均为已核实能力。

  1. 上下文条件判断。判断器继承当前会话的工具、system prompt 与历史消息,按条件回答 YES 或 NO。
  2. 复用 KV 缓存。fork 子代理只追加一条尾部 user 消息,前缀与主会话一致,可命中 provider 前缀缓存。
  3. 只读判断。工具目录保持不变,执行期 guard 只放行 read / glob / grep / web_search / web_fetch / skill 等只读工具,写入、修改、执行命令、子代理一律拒绝。
  4. 与普通消息同队列。/if 作为一条普通消息进入输入框上方的队列面板,可编辑、删除、↑ 插话;回合开始时才判断,成立就把这条消息原地换成指令。
  5. 两种排队模式。默认 message,即与普通消息同队列;background 保留 0.2.x 行为,命令立即返回,判断在后台跑。
  6. 条件成立才发送。只有 YES 才把指令作为用户消息发给主 agent;NO 完全不触发主 agent。
  7. 判断不进上下文。判断在独立子会话完成,不写主会话事件,不调用 agent.inject。
  8. 命令不泄露参数。recordInput: false,log-only 的 command/run 也不记录条件或指令。
  9. 多行条件。第一行是条件,其余行是指令;条件有多行时用单独一行 --- 分隔。
  10. 自动回退。host 没有可继承上下文的 fork provider 时,回退到一次性 ctx.llm.stream + 约 24k 字符纯文本记录。
  11. 防止误发。条件为空、指令为空、判断输出不是 YES / NO、或判断超时,都返回错误且不发送。

安装与启用

环境要求:

  1. DSH 0.2.1-rc.5 或更新版本。
  2. Node.js 20.3 或更新版本(使用 AbortSignal.any / AbortSignal.timeout)。

从 npm 安装(推荐)

  1. 打开侧边栏「插件」页 →「安装插件」。
  2. 输入包名并确认:
dsh-if
  1. 安装完成后重启 DSH(host 插件在 profile 启动时加载)。

固定版本:

dsh-if@0.3.4

命令行等价写法:

dsh plugin --profile desktop add dsh-if@0.3.4

从 GitHub 安装

lib/ 是可直接运行的源码,不需要本地构建:

https://github.com/TLEWA/dsh-if

固定到标签或 Release 附件:

dsh plugin --profile desktop add github:TLEWA/dsh-if#v0.3.4
https://github.com/TLEWA/dsh-if/releases/download/v0.3.4/dsh-if-0.3.4.tgz

从源码安装

git clone https://github.com/TLEWA/dsh-if.git
cd dsh-if
npm test
dsh plugin --profile desktop add .

重启 DSH 后,在输入框输入 /if,斜杠面板会出现该命令。

典型用法

基本形式:第一行是条件,其余行是指令。

/if 1+1=2
请只回复 OK

条件本身有多行时,用单独一行 --- 分隔:

/if 用户已经确认方案 A
并且测试已经通过
---
请开始执行方案 A

也可以只写一行条件和一个单行指令,/if 后面的第一个换行就是分隔点。

分隔符优先:只要出现过单独一行的 ---,它右边的部分一定全是指令。所以指令正文里不要单独成行写 ---(Markdown 水平线),否则它前面的内容会被当成条件的一部分。

判断语义

  1. 判断由当前会话配置的模型完成,不是 JavaScript 或数学求值器。1+1=2 只是最小示例,条件可以是“用户是否已经确认方案”“上一条回复是否包含错误”等。
  2. 判断器只被要求输出 YES 或 NO;输出里没有可识别结果时按判断失败处理,不发送指令。
  3. 判断器可以只读地查看文件、检索代码来确认事实,但不能做任何修改。
  4. 条件需要的信息在当前上下文里找不到、或条件含糊无法确定时,判为不成立:宁可不动,也不误发指令。
  5. UI 只显示通用结果,不回显条件、指令或判断原文。

工作方式

README 把实现拆成九步,摘要如下。

  1. 通过 ctx.effect(() => ctx.commands.register(...)) 注册 /if,并设置 recordInput: false;注册挂在插件 fiber 上,停用时一并注销。
  2. 入队。message 模式把原文 /if <条件>\n<指令> 作为普通 user 消息 agent.followup() 到会话队列,进的是宿主 durable inbox,客户端排队面板照常显示、可编辑、可删除、可插话。
  3. 轮次边界拦截。回合开始时宿主按 agent/pre-step waterfall 领取这批消息,插件把以 /if 开头的条目挑出来先在原地判断:成立就把 content 换成指令,不成立就从批次删除。判断失败时该条同样删除,并在会话里记一张「【/if 未执行】」错误命令卡片,可用卡片右侧「取回 /if」一键放回。
  4. 首选 fork 子代理判断。用 ctx.subagents.getProvider('fork') 取 provider(要求 inheritsParentContext === true),再以 startActivation({ provider: 'fork', childId, request: { parent, agentOptions, prompt }, delivery: 'caller' }) 启动判断子会话。
  5. 丢掉继承来的待办。fork 的种子会带回父会话入队的 next-turn 消息和未领走的 next-step 插话,插件在拿到子 agent 时把这两条队列中除自己判断 prompt 以外的待办全部 remove,父会话队列不受影响。
  6. 执行期只读限制。ctx.tools.guard 仅对判断子会话生效,只读工具放行,写入、命令、子代理被拒;工具目录不裁剪,缓存前缀不变。
  7. 判断超时 60 秒;只有 YES 时把指令作为带 id 和 role: 'user' 的已识别消息交给主 agent。
  8. 回退路径。宿主没有可继承上下文的 fork provider 时,historyTranscript() 读取 agent.session.deriveMessages(),只保留 user / assistant / tool 消息,生成最多约 24k 字符纯文本记录(每条最多 3k 字符,保留头尾),再用 ctx.llm.stream() 发起一次性判断请求;该请求不带主会话 sessionId,命中不了主缓存。
  9. background 模式跳过上面的入队与轮次拦截两步,命令立即返回“已排队”,判断由后台任务完成;多个 /if 按发送顺序串行判断。

配置

配置写在 profile 的 cordis.patch.yml:

# profile 的 cordis.patch.yml
- id: if-command
  name: 'dsh-if'
  config:
    mode: message     # 默认。background = 0.2.x 的老行为
  • mode: message(默认):/if 先作为普通用户消息排队,轮次开始时再判断。条目内容是 /if <条件> + 指令原文,出现在队列面板里,编辑它即等同于改条件与指令。
  • mode: background:0.2.x 行为。命令立即返回“已排队”,判断交给后台任务,不占队列位置。判断失败时会在会话里记一张 if 命令卡片说明原因。

也可以不改文件:插件 → dsh-if → 配置 里有「判断方式」下拉框,切换即时生效(mode 声明为 volatile 字段,设置页直接改写运行中的配置)。插件用 Config(@deepseek-ai/schemastery)描述该字段,mode 缺省即为 message,未知字段被忽略。

KV 缓存与上下文隔离

这部分是刻意的设计约束,不是可选项。

  1. /if 命令、条件、指令、判断 prompt、判断结果都不写入主会话;command/run 和 command/done 都是 log-only 事件。
  2. 判断在 fork 子代理里完成,tools、system prompt、历史消息全部继承,只在末尾追加一条 user 消息,因此前缀与主会话下一步逐字节一致,provider 的 prefix cache / KV cache 可以直接复用。
  3. 判断子会话是独立会话,不追加主会话的 session 事件,只读历史;工具目录不裁剪,裁剪反而会改变前缀、破坏缓存。
  4. 代价是透明的:每次判断都会把整段上下文重发一次,token 计数按全量算,命中缓存的部分按 provider 的缓存价计费;会话越长,单次判断的绝对 token 数越大。
  5. 条件不成立时主会话没有任何变化;条件成立时唯一改动是在历史末尾追加一条普通用户消息,已有前缀不变,不会调用 agent.inject,也不会编辑、替换或删除已有 session 事件。

适用场景与注意

适合需要在主流程里插入条件门控的场景:方案确认后执行、测试通过后再继续、上一轮有错时再补一条指令。判断会消耗一次上下文重发,长会话下绝对 token 数不小,适合低频、条件明确的门控,不适合把每个判断都铺到高频路径。

注意两点:

  1. 插件以当前 dsh 进程的权限运行,相关判断逻辑在 lib/ 中是可直接运行的源码,安装前建议检查源码与许可证;该插件为 MIT 许可。
  2. 判断依赖宿主提供的 fork provider 与 ctx.tools.guard,缺失时自动回退到 transcript 判断,该退路命中不了主缓存。其他 host 能力边界请对照 README 的「兼容性」一节。

结尾

dsh-if 的价值在于把“判断条件”和“执行指令”分开:判断发生在独立子会话,不污染主上下文,成立时只追加一条普通消息,从而保住主请求的可复用前缀。它定位明确、约束写在明面上,适合需要条件门控的 DSH 工作流。

  • 目录页:https://www.skillhub.cn/plugins/TLEWA/dsh-if
  • GitHub:https://github.com/TLEWA/dsh-if

社区目录是独立站点,与 DeepSeek、幻方无官方从属关系。

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

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

小夜