plugin-sensitive-filter-xr 0.0.2 → 0.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,63 +1,165 @@
1
1
  # Xpert Plugin: Sensitive Filter Middleware
2
2
 
3
- `@xpert-ai/plugin-sensitive-filter` 为智能体提供输入/输出敏感内容过滤能力。
4
-
5
- ## 功能说明
6
-
7
- - `beforeAgent`:检测输入命中,决策 `block / rewrite`
8
- - `wrapModelCall`:
9
- - 输入命中 `block`:直接返回拦截提示,不调用模型
10
- - 输入命中 `rewrite`:先改写最后一条 human 消息,再调用模型
11
- - 输出命中后再次执行 `block / rewrite`
12
- - `afterAgent`:空实现(不输出审计日志)
13
-
14
- ## 规则与优先级
15
-
16
- - 支持规则类型:`keyword`、`regex`
17
- - 支持生效范围:`input`、`output`、`both`
18
- - 冲突优先级:`high > medium`;同级按配置顺序第一条
19
- - `rewrite` 为整句替换,不做局部替换
20
- - `normalize=true` 时执行文本标准化(trim + 空白折叠 + 大小写归一)
21
- - regex 在中间件初始化时预编译,非法 pattern 会直接抛错
22
-
23
- ## 配置接口
24
-
25
- ```ts
26
- type SensitiveRule = {
27
- id: string;
28
- pattern: string;
29
- type: 'keyword' | 'regex';
30
- scope: 'input' | 'output' | 'both';
31
- severity: 'high' | 'medium';
32
- action: 'block' | 'rewrite';
33
- replacementText?: string;
34
- };
35
-
36
- type GeneralPackConfig = {
37
- enabled?: boolean; // default false
38
- profile?: 'strict' | 'balanced'; // default balanced
39
- };
40
-
41
- type SensitiveFilterConfig = {
42
- rules: Array<Partial<SensitiveRule> | null>; // 必填,且至少 1 条业务规则
43
- generalPack?: GeneralPackConfig;
44
- caseSensitive?: boolean; // default false
45
- normalize?: boolean; // default true
46
- };
3
+ `@xpert-ai/plugin-sensitive-filter` 提供输入/输出敏感内容过滤,支持两种互斥模式:
4
+
5
+ - `rule`:规则匹配过滤(关键词/正则)
6
+ - `llm`:自然语言规则过滤(固定改写策略)
7
+
8
+ ## 快速上手
9
+
10
+ 1. 在工作流中添加“敏感内容过滤中间件”。
11
+ 2. 选择 `mode`:`rule` `llm`(二选一)。
12
+ 3. `rule` 模式填写规则表;`llm` 模式只需填写模型、范围、审核规则说明。
13
+ 4. 运行工作流验证输入与输出阶段效果。
14
+ 5. 查看日志中的审计记录。
15
+
16
+ ## 执行流程
17
+
18
+ - `beforeAgent`:处理输入
19
+ - `wrapModelCall`:处理输出
20
+ - `afterAgent`:记录审计
21
+
22
+ ## 顶层参数
23
+
24
+ | 参数 | 类型 | 必填 | 默认值 | 说明 |
25
+ |---|---|---|---|---|
26
+ | `mode` | `'rule' \| 'llm'` | 是 | `rule` | 过滤模式(互斥)。 |
27
+ | `rules` | `Array<Rule>` | `rule` 模式建议配置 | `[]` | 业务规则,仅 `rule` 模式生效。 |
28
+ | `generalPack` | `object` | 否 | 见子项 | 通用规则包(本地词库兜底),仅 `rule` 模式生效。 |
29
+ | `caseSensitive` | `boolean` | 否 | `false` | 是否区分大小写,仅 `rule` 模式生效。 |
30
+ | `normalize` | `boolean` | 否 | `true` | 是否标准化文本,仅 `rule` 模式生效。 |
31
+ | `llm` | `object` | `llm` 模式执行期必填 | - | LLM 过滤配置,仅 `llm` 模式生效。 |
32
+
33
+ ## rule 模式参数
34
+
35
+ ### rules[]
36
+
37
+ | 字段 | 类型 | 必填 | 默认值 | 说明 |
38
+ |---|---|---|---|---|
39
+ | `id` | `string` | 否 | 自动生成 `rule-{index+1}` | 规则标识。 |
40
+ | `pattern` | `string` | 是 | - | 匹配内容。 |
41
+ | `type` | `'keyword' \| 'regex'` | 是 | - | 匹配方式。 |
42
+ | `scope` | `'input' \| 'output' \| 'both'` | 是 | - | 生效阶段。 |
43
+ | `severity` | `'high' \| 'medium'` | 是 | - | 冲突优先级(`high > medium`)。 |
44
+ | `action` | `'block' \| 'rewrite'` | 是 | - | 命中动作。 |
45
+ | `replacementText` | `string` | 否 | `[已过滤]`(rewrite 时) | 自定义拦截/改写文本。 |
46
+
47
+ ### generalPack
48
+
49
+ | 字段 | 类型 | 必填 | 默认值 | 说明 |
50
+ |---|---|---|---|---|
51
+ | `enabled` | `boolean` | 否 | `false` | 是否启用通用规则包。 |
52
+ | `profile` | `'strict' \| 'balanced'` | 否 | `balanced` | `strict` 更严格,`balanced` 更保守。 |
53
+
54
+ ## llm 模式参数(简化版)
55
+
56
+ ### llm
57
+
58
+ | 字段 | 类型 | 必填(执行期) | 默认值 | 说明 |
59
+ |---|---|---|---|---|
60
+ | `model` | `ICopilotModel` | 是 | - | 用于判定的过滤模型。 |
61
+ | `scope` | `'input' \| 'output' \| 'both'` | 是 | - | 生效范围。 |
62
+ | `rulePrompt` | `string` | 是 | - | 审核规则说明(自然语言,不需要 JSON)。 |
63
+ | `outputMethod` | `'functionCalling' \| 'jsonMode' \| 'jsonSchema'` | 否 | `jsonMode` | 结构化输出首选方式(不支持时自动降级)。 |
64
+ | `rewriteFallbackText` | `string` | 否 | `[已过滤]` | 命中但未返回改写文本时兜底文案。 |
65
+ | `timeoutMs` | `number` | 否 | 不限 | 判定超时(毫秒,上限 `120000`)。 |
66
+
67
+ ### llm 模式行为说明
68
+
69
+ - 用户只需填写自然语言规则,不需要书写 JSON 协议。
70
+ - 命中后统一执行 `rewrite`。
71
+ - LLM 调用异常时也统一执行 `rewrite`。
72
+ - 若配置了 `timeoutMs` 且判定超时,会直接使用 `rewriteFallbackText` 作为改写结果。
73
+ - 若模型不支持当前 `outputMethod` 的 `response_format`,会自动尝试其它结构化方式并最终降级到纯文本 JSON 解析。
74
+ - 若历史配置仍携带 `onLlmError/systemPrompt/errorRewriteText`,会按兼容逻辑处理并给出弃用告警。
75
+
76
+ ## 界面怎么填(最简)
77
+
78
+ `mode=llm` 时,最少只需要填这 3 项:
79
+
80
+ 1. `llm.model`:选择过滤用模型(建议稳定、低延迟模型)。
81
+ 2. `llm.scope`:建议先用 `both`。
82
+ 3. `llm.rulePrompt`:写自然语言审核规则,不需要 JSON。
83
+
84
+ 示例(个人信息场景):
85
+
86
+ - `llm.rulePrompt`:如果文本包含手机号、身份证号、银行卡号、家庭住址等个人敏感信息,请判定为命中并给出脱敏改写内容。
87
+ - `llm.rewriteFallbackText`:`[已过滤]`
88
+
89
+ 说明:
90
+
91
+ - `llm` 模式是“只改写不拦截”。如果你要“直接拦截”,请改用 `mode=rule` 并配置 `action=block`。
92
+
93
+ ## 超时与速度说明
94
+
95
+ - `timeoutMs` 是“单次 LLM 判定调用的最长等待时间(毫秒)”,不是整条工作流总超时。
96
+ - 设置过小(例如 `3000`)时,慢模型更容易超时,超时后会直接走 `rewriteFallbackText`。
97
+ - 不设置 `timeoutMs` 时,判定会一直等待模型返回,稳定性更高,但最坏延迟会变大。
98
+ - 命中输出过滤时,通常会感觉更慢:因为需要先拿到可判定内容,再统一改写返回。
99
+ - 你看到多个 `success` 卡片是正常的:输入判定、输出判定、收尾审计是不同执行步骤。
100
+
101
+ ## 配置示例
102
+
103
+ ### 示例 1:rule 模式
104
+
105
+ ```json
106
+ {
107
+ "mode": "rule",
108
+ "rules": [
109
+ {
110
+ "pattern": "炸弹",
111
+ "type": "keyword",
112
+ "scope": "both",
113
+ "severity": "high",
114
+ "action": "block"
115
+ },
116
+ {
117
+ "pattern": "(身份证|手机号)",
118
+ "type": "regex",
119
+ "scope": "output",
120
+ "severity": "medium",
121
+ "action": "rewrite",
122
+ "replacementText": "该回答包含敏感信息,已处理。"
123
+ }
124
+ ],
125
+ "normalize": true,
126
+ "caseSensitive": false
127
+ }
128
+ ```
129
+
130
+ ### 示例 2:llm 模式(推荐)
131
+
132
+ ```json
133
+ {
134
+ "mode": "llm",
135
+ "llm": {
136
+ "model": { "provider": "openai", "model": "gpt-4o-mini" },
137
+ "scope": "both",
138
+ "rulePrompt": "若内容包含违法、暴力或隐私泄露,请改写为安全且中性的表达。",
139
+ "rewriteFallbackText": "[已过滤]",
140
+ "timeoutMs": 3000
141
+ }
142
+ }
47
143
  ```
48
144
 
49
- 业务规则字段约束:
50
- - 必填:`pattern`、`type`、`action`
51
- - 条件必填:当 `action='rewrite'` 时,`replacementText` 必填
52
- - 可选:`id`(留空自动生成)、`scope`(默认 `both`)、`severity`(默认 `medium`)
145
+ ## 常见问题
146
+
147
+ ### 1) 为什么没生效?
148
+
149
+ 按顺序检查:
150
+
151
+ 1. `mode` 是否正确。
152
+ 2. `rule` 模式是否至少有 1 条有效规则(`pattern/type/action/scope/severity`)。
153
+ 3. `llm` 模式是否填写了 `model/scope/rulePrompt`。
154
+ 4. `scope` 是否覆盖当前阶段(输入或输出)。
155
+
156
+ ### 2) 为什么提示中文校验错误?
53
157
 
54
- ## 通用规则包(General Pack)
158
+ 中间件采用“编辑期容错、执行期校验”。编辑阶段允许先保存草稿,执行时再提示缺项。
55
159
 
56
- - `enabled=false`:不启用通用规则
57
- - `enabled=true, profile=balanced`:词库较小,命中后整句替换
58
- - `enabled=true, profile=strict`:词库更广,命中后直接拦截
160
+ ### 3) 旧配置里的 systemPrompt 还能用吗?
59
161
 
60
- 说明:通用规则包用于无内建审核能力的本地模型兜底,业务规则仍建议按场景自行配置。
162
+ 可以。若未填写 `rulePrompt`,会兼容读取 `systemPrompt`。建议迁移到 `rulePrompt`。
61
163
 
62
164
  ## 开发验证
63
165
 
@@ -65,5 +167,5 @@ type SensitiveFilterConfig = {
65
167
  pnpm -C xpertai exec nx build @xpert-ai/plugin-sensitive-filter
66
168
  pnpm -C xpertai exec nx test @xpert-ai/plugin-sensitive-filter
67
169
  pnpm -C plugin-dev-harness build
68
- node plugin-dev-harness/dist/index.js --workspace ./xpertai --plugin @xpert-ai/plugin-sensitive-filter
170
+ node plugin-dev-harness/dist/index.js --workspace ./xpertai --plugin ./middlewares/sensitive-filter
69
171
  ```
@@ -1 +1 @@
1
- {"version":3,"file":"sensitive-filter.module.d.ts","sourceRoot":"","sources":["../../src/lib/sensitive-filter.module.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqB,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAI/F,qBAIa,qBAAsB,YAAW,kBAAkB,EAAE,gBAAgB;IAChF,OAAO,CAAC,UAAU,CAAQ;IAE1B,iBAAiB,IAAI,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAMzC,eAAe,IAAI,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;CAKxC"}
1
+ {"version":3,"file":"sensitive-filter.module.d.ts","sourceRoot":"","sources":["../../src/lib/sensitive-filter.module.ts"],"names":[],"mappings":"AACA,OAAO,EAAqB,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAA;AAI9F,qBAIa,qBAAsB,YAAW,kBAAkB,EAAE,gBAAgB;IAChF,OAAO,CAAC,UAAU,CAAO;IAEzB,iBAAiB,IAAI,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAMzC,eAAe,IAAI,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;CAKxC"}
@@ -1,5 +1,6 @@
1
1
  var SensitiveFilterPlugin_1;
2
2
  import { __decorate } from "tslib";
3
+ import { CqrsModule } from '@nestjs/cqrs';
3
4
  import { XpertServerPlugin } from '@xpert-ai/plugin-sdk';
4
5
  import chalk from 'chalk';
5
6
  import { SensitiveFilterMiddleware } from './sensitiveFilter.js';
@@ -20,7 +21,7 @@ let SensitiveFilterPlugin = SensitiveFilterPlugin_1 = class SensitiveFilterPlugi
20
21
  };
21
22
  SensitiveFilterPlugin = SensitiveFilterPlugin_1 = __decorate([
22
23
  XpertServerPlugin({
23
- imports: [],
24
+ imports: [CqrsModule],
24
25
  providers: [SensitiveFilterMiddleware]
25
26
  })
26
27
  ], SensitiveFilterPlugin);
@@ -1,9 +1,12 @@
1
- import { TAgentMiddlewareMeta } from '@metad/contracts';
2
- import { AgentMiddleware, IAgentMiddlewareContext, IAgentMiddlewareStrategy, PromiseOrValue } from '@xpert-ai/plugin-sdk';
1
+ import type { TAgentMiddlewareMeta } from '@metad/contracts';
2
+ import { AgentMiddleware, IAgentMiddlewareContext, IAgentMiddlewareStrategy } from '@xpert-ai/plugin-sdk';
3
3
  import { SensitiveFilterConfig } from './types.js';
4
4
  export declare class SensitiveFilterMiddleware implements IAgentMiddlewareStrategy<SensitiveFilterConfig> {
5
+ private readonly commandBus;
5
6
  readonly meta: TAgentMiddlewareMeta;
6
- createMiddleware(options: SensitiveFilterConfig, _context: IAgentMiddlewareContext): PromiseOrValue<AgentMiddleware>;
7
+ createMiddleware(options: SensitiveFilterConfig, context: IAgentMiddlewareContext): Promise<AgentMiddleware>;
8
+ private createRuleModeMiddleware;
9
+ private createLlmModeMiddleware;
7
10
  }
8
11
  export type { SensitiveFilterConfig };
9
12
  //# sourceMappingURL=sensitiveFilter.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"sensitiveFilter.d.ts","sourceRoot":"","sources":["../../src/lib/sensitiveFilter.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAA;AAEvD,OAAO,EACL,eAAe,EAEf,uBAAuB,EACvB,wBAAwB,EACxB,cAAc,EACf,MAAM,sBAAsB,CAAA;AAC7B,OAAO,EAGL,qBAAqB,EAKtB,MAAM,YAAY,CAAA;AA6QnB,qBAEa,yBAA0B,YAAW,wBAAwB,CAAC,qBAAqB,CAAC;IAC/F,QAAQ,CAAC,IAAI,EAAE,oBAAoB,CAgKlC;IAED,gBAAgB,CACd,OAAO,EAAE,qBAAqB,EAC9B,QAAQ,EAAE,uBAAuB,GAChC,cAAc,CAAC,eAAe,CAAC;CA4GnC;AAED,YAAY,EAAE,qBAAqB,EAAE,CAAA"}
1
+ {"version":3,"file":"sensitiveFilter.d.ts","sourceRoot":"","sources":["../../src/lib/sensitiveFilter.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAa,oBAAoB,EAA8B,MAAM,kBAAkB,CAAA;AAGnG,OAAO,EACL,eAAe,EAGf,uBAAuB,EACvB,wBAAwB,EAEzB,MAAM,sBAAsB,CAAA;AAC7B,OAAO,EAOL,qBAAqB,EAMtB,MAAM,YAAY,CAAA;AAwanB,qBAEa,yBAA0B,YAAW,wBAAwB,CAAC,qBAAqB,CAAC;IAE/F,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAY;IAEvC,QAAQ,CAAC,IAAI,EAAE,oBAAoB,CA6SlC;IAEK,gBAAgB,CACpB,OAAO,EAAE,qBAAqB,EAC9B,OAAO,EAAE,uBAAuB,GAC/B,OAAO,CAAC,eAAe,CAAC;IAa3B,OAAO,CAAC,wBAAwB;IA+KhC,OAAO,CAAC,uBAAuB;CA2ShC;AAED,YAAY,EAAE,qBAAqB,EAAE,CAAA"}