dsh-tool-call-leak-fix:拦截工具调用泄漏与 turn 静默失败

前言

在 DSH(DeepSeek Harness)里跑带大段代码或 HTML 的工具调用时,有时会出现一种很误导人的现象:界面显示 turn 已正常完成,实际上没有任何工具被执行,而 <invoke>、<parameter> 或带 antml: 前缀的调用标记却混进了 assistant 正文。Harness 不报错,agent 循环直接当作 {kind:'completed'} 结束。坏样本还会留在历史里,模型容易继续模仿同一格式;fork 会把父会话里的损坏输出一并继承。社区里也有人把这类连锁失败叫作 chain reaction。

dsh-tool-call-leak-fix 是维护者 IRwDONE 发布的工作流类宿主插件(GitHub 仓库 IRwDONE/dsh-tool-call-leak-fix,npm 包 @irwdone/dsh-tool-call-leak-fix,当前资料中的版本为 0.5.1,MIT 许可证,零运行时依赖)。它针对「调用标记泄漏为正文、调用未执行却 turn 静默关闭」这一类失败,在分发前拦截、在同一 turn 内纠偏、fork 时简报、并在模型可见面上作废已进入历史的损坏输出。下面介绍它解决的问题、机制、安装方式与配置边界。

这是什么

一句话:DSH 宿主插件,把「工具调用没跑、标记却进正文、turn 像成功一样关掉」从静默 no-op 变成可感知、可纠正的行为,并在 fork 与上下文回放侧尽量切断坏样本对后续模型的影响。

维护者 IRwDONE;分类为工作流;插件在 SkillHub 目录页为 dsh-tool-call-leak-fix。实现为纯 ESM JavaScript,仅使用 Node 内置模块,不需要额外构建或前端半边。

故障长什么样

README 里保留了一段真实形态的节选:思考内容里模型甚至意识到该用 antml: 前缀,但整段调用仍作为正文输出,随后 turn 结束且没有工具卡。常见形态包括:

  1. V1 结束序列增殖:调用尾部结束标记重复出现。
  2. V2 开场标记缺失:只剩参数开场,没有外层 invoke 开场。
  3. V3 泄漏为正文:调用信封混进 assistant 正文,未被识别为真正工具调用。
  4. V4(相关):容错 JSON 解析把非法参数降级为 {},工具带着空参数执行。

泄漏方言既有裸标签(<invoke / <parameter),也有 antml: 前缀形式;还有「伪造工具结果」——正文出现结果形标记但该步并未调用工具。三种核心形态都是静默失败:不执行、不报错,turn 正常关闭。

用户在对话里让模型自行修复、仅 fork、或 compact,往往不能从根上去掉历史里的坏样本;README 说明装上本插件后,fork 之后损坏输出可在模型可见面上被作废,后续对话可不再出现同类泄漏型失败(具体还依赖 M9 等开关,见下文配置)。

核心功能

插件 README 将能力归纳为多条守卫(平时不主动打扰用户):

拦截(M2)

工具调用分发前扫描已解析参数。若某字符串参数边界已损坏(例如尾部结束序列增殖、结构中途截断),且体量达到阈值(默认 ≥2000 字符或 ≥20 行),则拒绝本次调用并给出可操作说明(如建议拆成 ≤100 行分块重发)。同时处理「声明必填却收到空 {}」的情况。已现场验证:真实会话中 22 行以上且结构截断的参数在分发前被拒,目标文件未被创建。

纠偏(M3)

写入类工具执行后,若结果中有机器可读的空转证据(如 noop: true、status: "noop"、written: 0 且确实带了内容),在结果后补一句短提示,不做自由文本猜测。由配置项 noopCheck 控制总开关。

自愈(M4)

观察已提交的 assistant 消息:若某步输出损坏且该步没有任何工具调用,在 turn 即将关闭时注入纠正,保持 turn 打开、让模型在同一 turn 内重发。纠正次数有上限,由 maxCorrectionsPerTurn 控制;本 bundle 的 patch 将默认改为 2,以便「第一次纠正后又写坏」时还能再给一次更硬的指令。到顶即停,避免循环。已现场验证 turn 不关闭并继续的完整链条。

fork 简报(M7)

fork 会话继承父历史且历史本身难以改写(README 称冻结请求 D-8)。插件在 fork 的第一个模型步之前注入一次代码生成的简报:把历史里「没有对应工具卡的孤立调用标记」标为损坏、未执行,请模型勿模仿。每个会话只放一次;非 fork 零开销。由 forkBrief 控制。

上下文修复(M9)

已进入历史的坏样本仍会被逐字回放。插件在模型可见 surface 上遮蔽损坏步骤(该步损坏且无工具调用),换成静态说明(「此处原为损坏输出、已作废,请勿模仿」),日志原始事件保留,零模型调用。启用前用自建探针自检 surfaceOp.replace 语义;自检失败则整个会话故障关闭。fork 继承的损坏步骤可在每会话第一次 turn 结束时一并扫描遮蔽。由 repairContext 控制,设为 false 可完全停用。

装上之后的行为对比(摘要)

以前 现在(插件生效时)
坏调用不执行、不报错 分发前拦截并说明原因
泄漏后 turn 静默关闭 turn 保持打开,同 turn 内纠正重发
写入工具自报成功却无改动 结果后补空转提示(M3)
历史坏样本持续被模仿 当场机制缩短故障窗口;不能改写已写入的历史(见边界)

README 明确:刻意不做 system-prompt 规则块注入,也不做「强制小块写入工具」。

安装与启用

用 Harness 自带的插件管理器安装,不要手改 profile 来「假装挂上」。三种方式任选其一。

方式 A:在插件市场 / 插件管理器搜索 dsh-tool-call-leak-fix 或 @irwdone/dsh-tool-call-leak-fix,点击安装。

方式 B:一条命令安装时使用 npm 包名或 GitHub 地址(资料原文如下,勿自行拼接其它 install 语法):

@irwdone/dsh-tool-call-leak-fix

或(仓库需公开):

https://github.com/IRwDONE/dsh-tool-call-leak-fix

方式 C:克隆仓库后,对 install_bundle 的 target 填仓库根目录(含 package.json),不要填到 plugin/ 子目录。

安装之后:

  1. install_bundle 返回 application: "applied" 即表示已生效;若提示重启则重启 Harness。
  2. 判活:插件管理器列表,或 Config 中出现 include:tool-call-leak-fix;更硬判据是是否真的注入了纠正。
  3. 不要仅通过 grep ~/.dsh/profiles/<profile>/cordis.patch.yml 判断——bundle 自带 patch(plugin/cordis.patch.yml,由 package.json 的 dsh.bundle.patch 声明),该行本就不一定出现在 profile 的 patch 里。
  4. 停用:管理器禁用,或将该行 config.enabled 设为 false。

配置

config 会校验并归一化;非法值回退默认。数值阈值须为整数且 ≥1(仅 maxCorrectionsPerTurn 允许 0)。

键 默认 含义
enabled true false 时不注册任何监听器
maxLines 100 单字符串叶子行数上限(过长预警)
maxChars 20000 单字符串叶子字符数上限
closingTagCount 8 </ 出现次数作「密集」证据,不单独导致拦截
endSequenceRepeats 2 尾部连续结束序列达到几个判损坏
maxCorrectionsPerTurn 1 每 turn 最多自愈次数;本 bundle patch 设为 2
lang zh 注入文案语言:zh / en
noopCheck true M3 空转检查总开关
debugLog false 诊断日志;false 时不写文件
debugLogPath '' 空串由系统临时目录推导
contextGuard false 内部保留、未接线
contextGuardNote '' 内部保留、未接线
forkBrief true M7 fork 首步前简报
forkBriefNote '' 空串用内置中英文简报
repairContext true M9 模型可见面遮蔽损坏步骤

典型用法与适用场景

本插件没有单独的「业务 API」:安装并启用后,守卫在工具分发、turn 关闭、fork 首步、会话 surface 替换等接缝上自动工作。典型适用场景包括:

  • 经常通过 DSH 调用「大段 code / HTML / JSX」类参数的工具,且遇到过 invoke/parameter 标记出现在正文里。
  • fork 父会话后继续开发,希望 inherited 历史里的损坏调用标记被简报或 M9 作废,减少模仿。
  • 需要把静默 no-op 变成带原因的拦截或同 turn 重试,而不是误以为任务已完成。

注意:

  • 插件以当前 DSH 进程权限运行;安装前请自行阅读仓库源码与 MIT 许可证,确认可接受其行为(含 M9 自检失败时关闭整个会话等策略)。
  • README「已知边界」:不能改写历史(M5 / D-8);坏样本「不洗白过去」,只能缩短故障窗口。绕法包括新开会话,或明确让模型忽略某段损坏内容。
  • M2 拦截、M4 自愈、M7 fork 简报、M9 上下文修复在 README 中标注为已现场验证;做不到的部分以仓库「已知边界」为准。

结尾

dsh-tool-call-leak-fix 把 DSH 里一类常见的工具调用静默失败——标记泄漏、turn 假完成、历史污染——拆成拦截、同 turn 自愈、fork 简报与模型可见面作废四层处理,零依赖、仅依赖 Host 半边接缝。社区目录页:SkillHub · dsh-tool-call-leak-fix。源码与完整说明:GitHub · IRwDONE/dsh-tool-call-leak-fix。

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

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

小夜