第 1 卷
18|规则系统:基于 dsh 扩展点搭建引擎
张嘉熙 · DeepSeek Harness 前沿工程实践




你好,我是张嘉熙。
编排篇你已经会驱动、协调 Agent 完成一件件任务了,但这都还停在“单点演示”,现在还缺一步:做一个能部署上线,甚至还能自进化的完整系统。
从这一讲开始的实战篇,就用四讲补上这一步,做一个规则自进化防火墙。
这一讲我们先把“规则引擎”这块地基搭起来,用 dsh 的扩展点,把它做成一个可部署上线的 dsh 应用。
要看清这一讲,需要先回答四个问题:
引擎怎样从本地启动到生产部署?
关于环境:本讲示例在配套 cookbook 的 src/firewall/ch18/,不接模型、不需要 API key;npm install --legacy-peer-deps 后执行 npm run start:ch18 即可
看到真实运行输出。目录约定适用于 18–21 讲:chXX/ 根目录是引擎本体,即库代码加测试;demo/ 子目录是可运行示例,示例 import 根目录的库代码、不复制实现,给你一个
能直接跑起来看效果的入口。
关于示例:文档里的运行输出块,比如规则库、逐条判定、白名单段,都来自 npm run start:ch18 的真实运行,无时序波动;其余机制由 npm test 钉住,既有判定语义的单
测,比如 deny 优先与顺序无关、allow 不参与判定、candidate 不参与等,也有真实 HTTP 流量(http-ingress.test.ts)与 nginx auth_request 旁路(nginx-
auth.test.ts,需本机 nginx 或 Docker,两者都没有才跳过)的端到端测试。
一条规则怎样表达“哪些请求放行、哪些拦截”:条件树 + 动作 + 例外
先抛一个具体需求:你想拦住所有对 /admin 的访问,后台不该被随便扫;但 /admin/health 是监控探针,得留着放行。一条规则要同时说清这两件事:拦什么、给谁
留口子。它是一段 JSON,来自 rules.json:
3"when": { 4"field": "path",
5"op": "starts_with", 6"value": "/admin"
7}, 8"unless": {
9"field": "path", 10"op": "eq",
11"value": "/admin/health"
12}, 13"action": "deny"
14}
下面我们把它拆开看。一条规则就写四个字段:name 是名字,when 是命中条件,unless 是例外,action 是动作,引擎侧会再补上 id 和 state,本讲规则都是 active。unless
就是“留口子”用的。
命中条件 when 是一棵条件树:叶子由三部分构成,field 看哪部分,op 是算子,value 是比较的值;组合节点比如 and / or / not,把叶子或其他组合节点任意嵌套起来:
维度取值说明
method / path / body / ip / ua / header.<name> field(看哪部分)取请求的一个字段 / query.<name>
依次为:等于 / eq / ne / contains / starts_with / ends_with // 在列表内 / 字段
op(算子)
matches(RE2) / in / exists——这是刻意的 "/admin" 就会被
随 op 变:matc value(比的值)字符串 / 正则 / 数组 / 无此字段