过滤器
敏感词检测
Claude Code Hub 的敏感词检测系统是一个内容审核功能,用于在请求到达上游 LLM 供应商之前拦截包含敏感内容的 API 请求。该系统作为保护层,可以:
- 阻止转发包含敏感词的请求到上游供应商
- 避免计费:被拦截的请求成本记为 0
- 记录日志:详细记录被阻止的请求信息用于审计
- 返回清晰错误:向用户说明请求被阻止的原因
设计理念
该系统采用简单高效的设计,优先考虑可维护性和性能。不使用复杂的 DFA、Trie 树或 Aho-Corasick 算法,而是使用优化的字符串操作,适合典型场景(< 1000 个敏感词)。
检测流程
敏感词检测在请求处理管道的早期阶段执行:
请求到达 → 认证检查 → 敏感词检测 → 客户端检查 → 模型检查 → ...后续处理
这个位置确保:
- 只检查已认证的请求
- 被阻止的请求不消耗限流配额
- 不会联系上游供应商
- 不产生任何费用
三层匹配策略
系统使用三种匹配类型,按性能优化顺序执行检测:
1. 包含匹配(Contains)
| 属性 | 说明 |
|---|---|
| 算法 | String.prototype.includes() |
| 复杂度 | O(n×m),n=词数,m=文本长度 |
| 用途 | 通用关键词过滤,词可出现在文本任意位置 |
| 示例 | 词 "spam" 匹配 "This is spam content" |
2. 精确匹配(Exact)
| 属性 | 说明 |
|---|---|
| 算法 | Set.prototype.has() |
| 复杂度 | O(1) 平均情况 |
| 用途 | 匹配完整短语或词组 |
| 示例 | 词 "exact phrase" 只匹配 "exact phrase",不匹配 "this exact phrase here" |
3. 正则匹配(Regex)
| 属性 | 说明 |
|---|---|
| 算法 | RegExp.prototype.exec() |
| 复杂度 | 取决于模式复杂度 |
| 用途 | 复杂模式、字符变体、混淆检测 |
| 示例 | 模式 b[a@4]d[wW]o[rR]d 匹配 "badword"、"b@dword"、"b4dWord" 等变体 |
检测顺序
系统按性能从高到低的顺序检测:包含匹配 → 精确匹配 → 正则匹配。一旦匹配成功立即返回,确保最常见的检测最快完成。
文本提取
系统从请求的多个来源提取文本进行检测:
| 来源 | 说明 |
|---|---|
system 字段 | 系统提示词(字符串或数组) |
messages 数组 | 仅提取 role='user' 的消息 |
input 字段 | Response API 格式的输入 |
仅检测用户消息
系统只检测用户发送的内容(role='user'),不检测助手的回复。这是有意为之的设计。
被阻止时的响应
当检测到敏感词时,系统返回 HTTP 400 错误,包含以下信息:
- 匹配到的敏感词
- 匹配的文本内容(带上下文)
- 匹配类型(包含/精确/正则)
- 修改后重试的提示
示例错误消息:
请求包含敏感词:"spam",匹配内容:"...this is spam content...",匹配类型:包含匹配,请修改后重试。
管理界面
在 设置 → 敏感词管理 页面,管理员可以:
- 查看所有敏感词列表
- 添加新的敏感词
- 编辑现有敏感词
- 启用/禁用敏感词
- 删除敏感词
- 刷新缓存并查看统计信息
添加敏感词
添加敏感词时需要填写:
| 字段 | 必填 | 说明 |
|---|---|---|
| 敏感词 | 是 | 要检测的词或正则表达式 |
| 匹配类型 | 是 | contains / exact / regex |
| 描述 | 否 | 管理备注 |
缓存机制
敏感词列表缓存在内存中以提高性能:
- 按需加载:调用
reload()时从数据库加载 - 热重载:无需重启应用即可刷新缓存
- 故障安全:重载失败时保留现有缓存
- 并发保护:防止同时进行多次重载
- 大小写规范化:所有词转为小写进行不区分大小写匹配
缓存统计
刷新缓存按钮会显示当前统计信息:
- 包含匹配词数量
- 精确匹配词数量
- 正则匹配词数量
- 总词数
- 上次重载时间
数据库结构
敏感词存储在 sensitive_words 表中:
| 字段 | 类型 | 说明 |
|---|---|---|
id | serial | 主键 |
word | varchar(255) | 敏感词或正则表达式 |
match_type | varchar(20) | 匹配类型,默认 'contains' |
description | text | 描述(可选) |
is_enabled | boolean | 是否启用,默认 true |
created_at | timestamp | 创建时间 |
updated_at | timestamp | 更新时间 |
被阻止的请求记录在 message_request 表中,包含:
blocked_by: 值为'sensitive_word'blocked_reason: JSON 格式的匹配详情provider_id: 值为0(表示被阻止)cost_usd: 值为'0'(不计费)
边界情况处理
大小写不敏感
所有匹配都不区分大小写。"Spam"、"SPAM" 和 "spam" 被视为相同。
空缓存行为
如果没有配置任何敏感词,系统会完全跳过检测,提供性能优化。
检测失败处理
如果检测过程出错,系统采用失败开放策略——允许请求通过,确保检测系统的 bug 不会阻止所有请求。
无效正则表达式
无效的正则表达式会被记录日志并跳过,不会加载到缓存中。
词边界问题
"包含"匹配会匹配子字符串:
- 词 "cat" 会匹配 "category"(可能非预期)
- 词 "the" 会匹配 "then"、"there"、"other"
如需匹配完整单词,使用正则表达式的词边界:\bword\b
count_tokens 请求豁免
count_tokens 端点不进行敏感词检测,这是有意设计——token 计数应该对任何内容都有效。
最佳实践
配置建议
- 优先使用包含匹配:适合大多数场景,性能最好
- 精确匹配用于短语:避免误匹配
- 正则匹配用于变体:处理字符替换、混淆等情况
避免误匹配
- 测试新添加的敏感词,确保不会误拦截正常内容
- 使用描述字段记录添加原因
- 定期审查被阻止的请求日志
性能考虑
- 敏感词数量建议控制在 1000 个以内
- 复杂的正则表达式会影响性能
- 修改敏感词后记得刷新缓存
