用 dsh-tool-regex 给 DeepSeek Harness 装上确定性正则工具

前言

智能体处理日志、校验用户给的 pattern、从一段文本里抽出字段,正则几乎是默认手段。问题是:模型「心算」正则的错误率很高,也没法把中间结果交给人核对。常见替代是让模型现写一段 node -e 或 Python,再通过 bash 跑——多一次进程开销,也多一层现写脚本的正确性风险。

DeepSeek Harness(dsh)内置的 grep 只能做文件域搜索,不能对任意字符串做测试、提取、替换,更不能解释一段 pattern 到底在匹配什么。社区插件 dsh-tool-regex 就是为这件事准备的:给当前 dsh 进程注册一个 regex 工具,用确定性的纯函数结果代替心算。

本文按插件目录页、GitHub 仓库 README 与源码交叉核对后整理:它是什么、四个 action 怎么用、怎么安装,以及 ReDoS 相关的边界。

这是什么

dsh-tool-regex 是一款面向 DeepSeek Harness 的工具与能力插件,由 GitHub 组织 omdsh-dev 维护,仓库地址是 omdsh-dev/dsh-tool-regex。目录页收录日期为 2026-08-14,许可证 MIT,主要语言 TypeScript。截至本文查阅时,仓库星标为 3。

它解决的是这一类任务:

  • 判断一段文本是否匹配给定 pattern
  • 从日志或任意字符串中提取编号捕获组、命名捕获组
  • 做带 $1 / $2 / $$ 语义的安全替换
  • 不执行匹配的前提下,把 pattern 拆成人能读的解释节点

插件以 Profile Bundle 形式接入。package.json 里包名声明为 @deepseek-ai/dsh-tool-regex,安装后会在 profile 的 layer stack 插入 row id tool-regex。这是社区开源插件,不是 DeepSeek / 幻方的官方应用;DeepSeek Harness 本身的设计原则是「一切皆插件」,社区目录 deepseek-harness-plugin.com 是独立站点,与官方仓库无从属关系。

运行时没有第三方依赖:package.json 没有 dependencies 字段,peer 依赖由 profile 提供(@deepseek-ai/cordis@deepseek-ai/dsh-tools@deepseek-ai/dsh-invariants)。引擎侧是纯函数;test / find / replace 会再进 worker 线程执行。

核心功能

插件只注册一个工具:regex。调用时用 action 区分四类操作,统一返回 JSON 文本字符串。

四个 action

action 做什么 输出形态
test 判断是否匹配 {"matched":true}{"matched":false}
find 收集全部匹配:下标、完整匹配、编号组 captures、命名组 groups 数组;零匹配返回 []
replace 全局安全替换,返回结果文本和替换次数 {"result":"...","replaced":1}
explain 静态解析 pattern,输出人读节点序列 节点数组,例如 {"kind":"escape","text":"\\d","meaning":"A digit [0-9]"}

几点行为需要单独记住:

  1. test 不会自动加首尾锚点。整串匹配要由模型自己写成 ^...$
  2. findreplace 在没有 g 时会自动补 g,否则只能拿到第一处匹配。
  3. explain 是差异化能力:只做线性 tokenizer,不构造 RegExp 实例、不执行匹配,因此天然免疫 ReDoS。节点上限 4,096,超限返回 regex: explain: pattern too complex

README 里的三个可复现例子如下。

提取捕获组:

regex { action: "find", pattern: "(\\w+)@(\\w+)", input: "a@b x c@d" }

返回两处匹配,第一处大致是 {"index":0,"match":"a@b","captures":["a","b"],"groups":null},第二处从下标 6 开始,对应 c@d

交换两个单词:

regex { action: "replace", pattern: "(\\w+) (\\w+)", input: "hello world", replacement: "$2 $1" }

得到 {"result":"world hello","replaced":1}

解释一段日期片段:

regex { action: "explain", pattern: "\\d{4}-\\d{2}" }

会拆成转义 \d、量词 {4}、字面量 - 等节点,并带上英文 meaning 字段。

工具参数

参数 必填 说明
action test / find / replace / explain
pattern JavaScript 正则语法,不要带外围 /;上限 16KB
input test/find/replace 必需 待匹配文本;上限 64,000 字节(UTF-8)
flags "gi";允许 g i m s u y d v,必须唯一且合法
replacement replace 时需要 替换文本,走 JS 原生字符串替换路径;上限 16KB
limit find 最多报告多少处匹配,默认 50,钳制到 1,000

flags 是逐字符校验的:非法字符报 regex: invalid flag "q",重复报 regex: duplicate flag "g"。无效 pattern 捕获 SyntaxError,错误信息里通常带位置,格式为 regex: invalid pattern: ...,不会把宿主打崩。

replace 明确走 String.prototype.replace字符串替换路径,没有 new Function,也没有 eval$1 / $2 是编号组,$$ 是字面量 $,命名组走 JS 原生 $<name> 语义;未知引用按 V8 规则字面保留(例如 $0)。

ReDoS 多层防线

JS 正则的灾难性回溯是真实威胁,典型例子是 (a+)+$ 配超长输入。仓库 README 和工具描述都写了同一套防线,源码 src/index.tssrc/engine.ts 与之对应:

  1. worker 硬超时test / find / replace 在可终止的 worker 线程里同步执行,预算 1,000ms,到期调用 worker.terminate(),返回 regex: execution timed out (1000ms)。工具管道自己的 timeoutMs 对同步阻塞体只是协作式的,单靠它不够,所以才单独开 worker。
  2. 入口拒绝,不截断:输入超过 64KB、pattern 超过 16KB、replacement 超过 16KB,直接报错,不进入回溯。
  3. 输出与匹配数上限:输出超过 1MB(例如 $`` /$’造成的替换放大)拒绝而非截断;findlimit` 默认 50、上限 1,000。
  4. explain 零执行:只做静态扫描,任何 pattern 都即时返回。

工具描述和 README 都警告模型:不要对不可信的大输入使用无锚点的嵌套量词,例如 (a+)+(.*)*。超时能挡住宿主被挂死,但不能把病理 pattern 变成「安全可用」。

安装与启用

目录页给出的安装命令是(以页面原文为准):

dsh plugin add github:omdsh-dev/dsh-tool-regex

需要可复现安装时,固定 commit 哈希:

dsh plugin add github:omdsh-dev/dsh-tool-regex#commit

#commit 换成实际哈希。仓库 main 在 2026-08-14 的最新提交是 457c84fed7849003dd006145fe7838519c8fc132。固定哈希之后,上游再推送不会悄悄改变你机器上跑的代码。

README 推荐按 profile 安装。web(交互式网页)和 headless(dsh run 默认)是两套不同的 profile,装到一边不会自动覆盖另一边:

# 交互式(web)profile
dsh plugin --profile web add github:omdsh-dev/dsh-tool-regex

# 一次性任务(headless)profile
dsh plugin --profile headless add github:omdsh-dev/dsh-tool-regex

也可以先在仓库里 npm pack,再用生成的 tarball 安装:

npm pack
dsh plugin --profile web add ./dsh-tool-regex-<version>.tgz

包内 cordis.patch.yml 会在安装后把插件插入 layer stack,row id 为 tool-regex。缺失的 peer 依赖由 profile 的 profiles/node_modules 回退安装提供。Windows 路径请用正斜杠,例如 C:/...

package.json 声明的 Node 引擎是 ^22.19.0 || >=24.0.0。README 写明本插件已按 @deepseek-ai/dsh@0.1.0-rc.6(npm 私有包)做过隔离消费验证,启动方式示例为 npx -p @deepseek-ai/dsh@0.1.0-rc.6 dsh web,并明确不要 install -g 全局安装。这是仓库自述的兼容线,不是对所有 dsh 快照的保证。

验证是否装上:

dsh --profile web --dump-config | grep tool-regex

跑一次真实调用:

dsh run "使用 regex 工具测试 d+ 是否匹配 abc123"

典型用法

下面按 README 的契约,把四个 action 对应到常见任务。pattern 一律按 JavaScript 语法写,不要加 /.../ 外围斜杠。

1. 先解释,再执行。 用户丢过来一段看不懂的正则时,先 explain。它不跑匹配,只返回节点序列,适合把「这段 pattern 在干什么」展示给人看。未闭合的 [ / ( 会带位置报错,例如 regex: explain: unmatched "[" at position N

2. 从日志里抽字段。find,需要命名组时写成 (?<name>...),结果里的 groups 会带上名字;只要编号组时看 captureslimit 默认 50,日志很长时显式设一个更小的值,避免输出膨胀。

3. 做可核对的替换。replace,依赖 $1 这类引用,而不是让模型手写拼接。零匹配时返回原文且 replaced 为 0,便于判断「到底有没有改到」。

4. 只问是否命中。test。若要「整串等于」而不是「中间出现」,pattern 自己加 ^$test 不会自动补 g,避免 lastIndex 把后续判断带偏。

空 pattern 是合法的(匹配空串)。带 u / v 时,空匹配按 code point 推进,避免 UTF-16 代理对被命中两次。这些边界在 src/engine.ts 里按 ECMAScript AdvanceStringIndex 实现,测试文件 engine.spec.ts 覆盖了 flags、64KB 上限和病理 pattern 的 worker 取消。

适用场景与注意事项

适合这些情况:

  • 智能体要验证用户提供的正则,并给出可展示的解释,而不是口头保证「这段能用」
  • 从一段内存中的文本(日志片段、表单值、模型刚生成的字符串)提取字段,而不是搜工作区文件——搜文件仍应走内置 grep
  • 需要带捕获组的替换,且不希望模型去拼 node -e 脚本
  • 担心病理正则把宿主卡死,需要硬超时和输入上限

不适合、或需要额外小心的情况:

  • 把不可信的超长输入配上无锚点嵌套量词。超时能终止 worker,但这次调用仍然失败。
  • explain 当成完整的正则语义引擎。它是 tokenizer:能识别锚点、字符类、分组、量词、转义、交替,解释文本是英文 meaning;复杂到超过 4,096 个节点会直接拒绝。
  • 只装了 web profile、却用 dsh run 做一次性任务。dsh run 默认走 headless,两边要分别安装。
  • 把目录页或 scoped 包名理解成官方出品。维护者是 omdsh-dev,许可证文件版权人为 2026 whiteicey,包名带 @deepseek-ai/ 前缀是 DSH 插件常见写法,不代表由 DeepSeek 官方发布。

目录页的安全提示需要照做:插件以当前 dsh 进程的权限运行,安装时可能执行代码。 安装前检查源代码仓库和许可证;需要可复现安装时固定 commit 哈希。这不是走个过场——GitHub 源安装拿到的是源码而不是预构建产物,信任边界就是你本机上的 dsh 进程。

小结

dsh-tool-regex 把「对任意文本做正则」收成一个确定性工具:test 判断、find 提取、replace 安全替换、explain 静态解释。和让模型现写脚本再丢给 bash 相比,它少一层正确性风险;和内置 grep 相比,它不依赖文件。真正要看的是边界:worker 1 秒硬超时、输入 / pattern / 输出上限,以及 explain 不执行代码。

目录页:https://deepseek-harness-plugin.com/zh-CN/plugins/dsh-tool-regex/

GitHub:https://github.com/omdsh-dev/dsh-tool-regex

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

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

小夜