dsh-plugin-loud-failure:把静默的工具失败变成响亮的失败

前言

跑智能体的同行大概都遇到过这类失败:一次工具调用退出码是 0,输出看起来也正常,模型据此继续往下走,几步之后才从各种迹象里拼出真相——最初的调用其实已经失败了。pandoc 丢一个字形只打警告,PDF 照样写出;管道把 Python 的 traceback 卷走,退出码由 tail 提供;; 链让 command not found 混在一堆正常输出后面。退出码说成功,模型读到成功,错误沿下游传播。

事后翻 session 日志当然能找到原因,但更好的时机是失败发生的当下。DSH 的理念是一切皆插件,工具执行链上留有 tools/post-execute 这样的扩展点。本文介绍的 dsh-plugin-loud-failure 就挂在这个点上:在结果送达模型之前匹配警告签名,把静默失败显式化。

这是什么

dsh-plugin-loud-failure 是一个 DeepSeek Harness 插件,由 Rhymer-Lcy 维护,MIT 许可证。一句话定位:一个 tools/post-execute 策略,在成功的工具输出里匹配警告签名,命中后阻止该结果或附加一条通知。它不改动任何工具与循环。

版本与依赖写全:

  • 插件版本 0.1.1;
  • 要求 DeepSeek Harness 0.1.0-rc.6(其中 @deepseek-ai/dsh-tools@deepseek-ai/dsh-llm 固定在 0.1.0-rc.6);
  • Node.js 20+。

作者说明这是唯一测试过的版本,peer 范围按发布固定。升级 harness 后,需要留意插件是否跟进发版。

工作原理

核心是一个注册在 tools/post-execute 的瀑布监听器,按下面的顺序工作:

1、加载时,把内置规则与用户配置的 rules 合并(与内置规则同 id 的用户规则就地替换),并编译所有正则。规则表不可用——非法正则、重复 id、stateful 标志、空 message——会携带违规规则 id 拒绝插件加载。坏配置在启动时就失败,而不是等到第一次工具调用。

2、每次 tools/post-execute,把结果的文本块(可选还包含成功 canonical value 的 JSON)与适用规则匹配。when: success 的规则对已带失败标记的输出保持安静:非零的 [exit code: N] 行、非零退出码的 [status: ...] trailer、[sandbox: file access denied ...]。这些模型本来就看得到,不算静默失败。

3、任一命中规则的 actionerror 时,监听器返回 { kind: 'block' }。注册表将其转为 isError 结果:内容以说明头部开头,原始输出原样保留;canonical value 不复存在,Code Mode 程序也无法消费被污染的值。

4、只有 actioncontext 的规则命中时,监听器放行,并向决策的 additionalContexts 追加一条 UserMessage 通知,来源为 { kind: 'plugin', plugin: 'loud-failure', form: 'notice', summary },Web UI 显示为折叠的一行。

5、无命中则 next() 放行。action: off 的规则永不运行。

监听器通过 ctx.on 注册,随插件卸载;配置变更会重载插件并注册新监听器。

内置规则覆盖的静默失败

内置规则表全部来自实际观察到的、藏在成功退出码后面的失败:

观察到的调用 模型看到 实际发生
pandoc ... --pdf-engine=xelatex 打印 Missing character: There is no ₂ in font ... PDF 已写出,退出码 0 SpO₂ 的下标被从 PDF 里默默丢掉
python script.py \| tail -n 20 最后 20 行,退出码 0 traceback 已滚过,退出状态由 tail 提供
pandocc in.md -o out.pdf; ls -l out.pdf bash: pandocc: command not found 加一段列表,退出码 0 什么都没构建,退出状态由 ls 提供
python calc.py 打印 RuntimeWarning: invalid value encountered in divide 一个数组,退出码 0 数组里是 nan
PowerShell 5.1 命令加了 2>&1 NativeCommandError$? 为 false 程序其实退出 0,stderr 被 PowerShell 包装
Windows 控制台打印 ��� 文本,退出码 0 GBK/UTF-8 代码页不匹配,文本已损坏

如果你的工作流里这类场景常见,内置规则开箱即用;命中后要么得到一个 isError 结果,要么在下一轮请求里看到一条通知。

安装与启用

下面按从简到繁给出三种方式。

从 release tarball 安装

无构建步骤、无需构建授权,命令如下:

dsh plugin --profile web add https://github.com/Rhymer-Lcy/dsh-plugin-loud-failure/releases/download/v0.1.1/dsh-plugin-loud-failure-0.1.1.tgz

安装后确认配置里出现了插件层:

dsh --profile web --dump-config

输出中应能看到一个 # == dsh-plugin-loud-failure 层。

另外,dsh plugin add 打印 @deepseek-ai/* 的 peer 依赖警告属预期:profile 从 harness 安装中解析这些包,而不是在插件旁安装。

从 GitHub 固定 commit 安装

dsh plugin --profile web add github:Rhymer-Lcy/dsh-plugin-loud-failure#<commit-sha>

git 安装会拉取源码,pnpm 需要被允许运行本包的 prepare 脚本(内容是 tsc)。首次 add 会失败并打印需要允许的完整 key,把它追加到 profile 的 pnpm-workspace.yaml 后重试:

# $DSH_HOME/profiles/web/pnpm-workspace.yaml(key 从 pnpm 的提示里复制)
allowBuilds:
  "dsh-plugin-loud-failure@https://codeload.github.com/Rhymer-Lcy/dsh-plugin-loud-failure/tar.gz/<commit-sha>": true

从源码 checkout 直接运行

先构建:

git clone https://github.com/Rhymer-Lcy/dsh-plugin-loud-failure.git
cd dsh-plugin-loud-failure && pnpm install && pnpm run build

然后在 overlay.yml 里插入一行,指向 lib/index.js

# overlay.yml
- insert:
    - id: loud-failure
      name: /absolute/path/to/dsh-plugin-loud-failure/lib/index.js
dsh web --patch ./overlay.yml

卸载

dsh plugin --profile web remove dsh-plugin-loud-failure

配置

安装后,bundle 会插入 id: loud-failure 的一行,带上 schema 默认值。已核实的配置键有两个:

  • rules:用户规则数组,默认为空。与内置规则同 id 就地替换,新 id 按顺序追加;两个用户规则同 id 会导致加载失败。
  • shellTools:指定被检查的工具,默认 [bash, pwsh, job_output]

覆盖配置时有一个容易踩的坑:patch 替换的是整行 config。如果你在自己的 profile 的 cordis.patch.yml 里覆盖 loud-failure 的配置,必须重述所有要保留的键。

适用场景与注意事项

适合谁:

  • 工作流里有大量 shell 类工具调用、被静默失败坑过的人,上面六种场景内置规则直接覆盖;
  • 想把团队自己的警告签名沉淀成规则的人:rules 可配置,action 支持 errorcontextoff 三种结果。

使用前注意:

1、版本耦合。要求 DeepSeek Harness 0.1.0-rc.6 与 Node.js 20+,插件仅在该版本测试过。

2、git 安装的授权要当回事。允许 prepare 脚本,意味着仓库代码会在安装时于任何 agent 沙箱之外运行;务必固定 commit。release tarball 安装没有这个环节。

3、插件以当前 dsh 进程的权限运行,安装任何第三方插件前应先检查源码与许可证。本插件许可证为 MIT,源码公开在 GitHub。

结语

这个插件的思路很克制:不改工具、不改循环,只在 tools/post-execute 一个环节上,把退出码 0 背后的警告签名变成模型无法忽略的 isError 结果,或者一条可见的通知。对长期跑链式工具调用的人来说,它把「几步之后才发现」提前到「当下就看到」。

  • GitHub 仓库:https://github.com/Rhymer-Lcy/dsh-plugin-loud-failure
  • 社区目录页:https://www.skillhub.cn/plugins/Rhymer-Lcy/dsh-plugin-loud-failure

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

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

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

小夜