facc04aa65
本提交为 .git 对象库损坏后的重建提交,内容等价于原先三个本地提交 (5e2fd4c2 / 57a53c98 / 5d7e1873)的累积结果,未做任何额外改动。 一、docs 结构整改(整改 #14) 根因:重构时新结构为 untracked 文件,执行 git stash(未带 -u)未纳入, 随后 git reset 拉回 HEAD 旧 tracked 树,导致旧树复活、新旧两棵目录 树并存于 docs/,共 791 文件、双分类体系冲突。 修复动作: - b2 同名异主题文件改名迁移保全 9 个 - C 类 39 个孤立文件按主题正确归类 - A/B1 类 222 个重复文件删除(新结构已有内容副本) - 9 个旧独有空目录删除 - 270 处内部引用按 verified 映射改写 - 整改记录 #14 登记于 04-运维文档/部署运维 结果:docs 791 → 569 文件,顶层仅规范 8 类 + 治理文件,单树恢复。 残留:约 20 处指向从未存在文件的陈旧死链,归入独立文档卫生任务。 二、compose 双目录对齐(消除踩坑 A) - docker-compose.yml:nginx 前端挂载全部由根目录 frontend-*/dist 改为 src/frontend-*/dist(h5 / agent / admin / terminal) - docker-compose.dev.yml:dev 服务 build context 与卷同步改 src/ - 效果:本地 docker compose up 不再把根目录 stale dist 挂回, 与线上一致,分叉隐患消除(已 docker compose config 校验通过) 防复发铁律: - 重构须提交;仓库修复须 git stash -u 或先 commit - 新结构须 git add 并提交,避免再次 untracked 复活 - H5 改动只动 src/frontend-h5/,禁改根目录遗留 frontend-*/
531 lines
19 KiB
Markdown
531 lines
19 KiB
Markdown
# 技术方案 - REQ-通用-004 敏感词检测
|
||
|
||
> **版本**: v1.0
|
||
> **日期**: 2026-07-28
|
||
> **REQ编号**: REQ-通用-004
|
||
> **关联PRD**: `01-产品文档/00-产品规划/PRD-REQ-通用-004-敏感词检测-v1.0.md`
|
||
> **状态**: v0.7.1 已上线(部分达标)
|
||
> **作者**: 宋献
|
||
> **关联原型**: `01-产品文档/00-产品规划/PRD-REQ-集成-002-管理后台-v1.0.html`("代答排除规则"页,含敏感词转移人工/财务敏感问题规则,可作为后续词库管理 UI 雏形)
|
||
|
||
---
|
||
|
||
## 目录
|
||
|
||
1. [设计概述](#1-设计概述)
|
||
2. [现状分析](#2-现状分析)
|
||
3. [架构设计](#3-架构设计)
|
||
4. [关键模块设计](#4-关键模块设计)
|
||
5. [数据模型](#5-数据模型)
|
||
6. [接口设计](#6-接口设计)
|
||
7. [部署与灰度](#7-部署与灰度)
|
||
8. [测试策略](#8-测试策略)
|
||
9. [风险与降级](#9-风险与降级)
|
||
10. [关联文档](#10-关联文档)
|
||
|
||
---
|
||
|
||
## 1. 设计概述
|
||
|
||
### 1.1 背景
|
||
|
||
坐席发送消息的**事前内容审核**机制,包含两个独立检测模块:
|
||
|
||
| 模块 | 库 | 检测目标 |
|
||
|------|-----|----------|
|
||
| **敏感词检测** | `wordfilter` 0.2.7(基于 DFA) | 服务用语不当(脏话/推诿/反问) |
|
||
| **隐私字段检测** | Python `re` 标准库 | 手机/身份证/银行卡/个人邮箱 |
|
||
|
||
v0.7.1 已上线运行,但**有 3 项已知不达标**(看板验真测试报告 ⑤节),本文档同步记录现状与改进方向。
|
||
|
||
### 1.2 设计目标
|
||
|
||
| 目标 | 描述 |
|
||
|------|------|
|
||
| **G1 覆盖完整** | 服务用语 + 隐私字段双覆盖 |
|
||
| **G2 低侵入** | 不阻塞主消息发送流程(异步/降级) |
|
||
| **G3 可扩展** | 词库/正则可运营(v1.1 计划) |
|
||
| **G4 可审计** | 关键命中可追溯(v1.1 计划) |
|
||
|
||
### 1.3 设计原则
|
||
|
||
| 原则 | 说明 |
|
||
|------|------|
|
||
| **P1 纵深防御** | 词库(DFA)+ 正则(pattern)双引擎,单一引擎失败不致命 |
|
||
| **P2 数字边界** | 所有数字相关正则必须用 `(?<!\d)/(?!\d)`,**禁止**用 `\b`(Python 3 re 对中文失效) |
|
||
| **P3 异步可降级** | 审核失败时降级为 PASS,不阻断发送 |
|
||
| **P4 集中配置** | 词库/正则集中在一个 service,修改影响范围可控 |
|
||
|
||
### 1.4 适用范围
|
||
|
||
| 端 | 是否审核 | 触发位置 |
|
||
|----|----------|----------|
|
||
| 坐席端发送消息 | ✅ 是 | 后端 `agent_send_message` 接口前 |
|
||
| 员工端(H5)消息 | ❌ 否 | 走企微原生反垃圾 + AI Wingman |
|
||
| AI 回复 | ❌ 否 | 由 Dify 自带的内容安全机制处理 |
|
||
| 系统消息 | ❌ 否 | 内部通知,无需审核 |
|
||
|
||
---
|
||
|
||
## 2. 现状分析
|
||
|
||
### 2.1 已有实现(v0.7.1 已上线)
|
||
|
||
```
|
||
src/backend/
|
||
├── app/services/
|
||
│ ├── content_moderation_service.py # 核心:审核 + 隐私检测
|
||
│ └── admin/admin_moderation_service.py # 仅有快速回复审核,**未做敏感词管理**
|
||
└── tests/
|
||
└── test_content_moderation.py # 13 用例,11 通过 2 失败
|
||
```
|
||
|
||
| 模块 | 实现 | 行数 |
|
||
|------|------|------|
|
||
| `ContentModerationService` | 单例服务,wordfilter + 自定义词库 | 331 行 |
|
||
| `ModerationAction` | Enum: pass/warn/block | 5 行 |
|
||
| `ModerationCategory` | Enum: profanity/politics/porn/ad/privacy/other | 8 行 |
|
||
| `ModerationResult` | dataclass 返回值 | 13 行 |
|
||
| `moderate(text)` | 主审核入口 | 50 行 |
|
||
| `check_privacy_leak(text)` | 隐私正则检测 | 33 行 |
|
||
|
||
### 2.2 看板验真测试报告(2026-07-07)— 3 项不达标
|
||
|
||
| # | 问题 | 根因 | 现状 | 计划 |
|
||
|---|------|------|------|------|
|
||
| 1 | 隐私正则 `\b1[3-9]\d{9}\b` 中文场景失效 | Python 3 `re` 将中文字符视为 `\w`,`\b` 边界失效 | ✅ **已修复**(用 `(?<!\d)/(?!\d)` 数字边界) | - |
|
||
| 2 | 命中动作固定 WARN,不 BLOCK | 业务决策(2026-07-08 评审) | 🟡 **维持 WARN** | v1.1 可配置 |
|
||
| 3 | 自定义词库写死 4 条,未接 system_config | 开发未完成 | 🔴 **未修复** | v1.1 入库 |
|
||
|
||
### 2.3 测试覆盖(11/13 通过)
|
||
|
||
| 用例类型 | 数量 | 通过 | 失败 | 说明 |
|
||
|----------|------|------|------|------|
|
||
| 敏感词命中 WARN | 4 | 4 | 0 | 4 条基础词均命中 |
|
||
| 正常文本 PASS | 2 | 2 | 0 | 包括空字符串 |
|
||
| 隐私字段检测 | 3 | 3 | 0 | 手机/身份证/正常 |
|
||
| 自定义词库断言 | 1 | 1 | 0 | 确认 4 条词存在 |
|
||
| **默认值 WARN** | 1 | 0 | 1 | test_default_action_is_warn_not_block,2026-07-08 决策后视为通过 |
|
||
| 隐私正则边界 | 2 | 1 | 1 | 待补充中文+号码场景 |
|
||
|
||
---
|
||
|
||
## 3. 架构设计
|
||
|
||
### 3.1 总体流程
|
||
|
||
```
|
||
坐席端输入文本
|
||
│
|
||
▼
|
||
[1] 后端 agent_send_message 接口接收
|
||
│
|
||
▼
|
||
[2] 调用 ContentModerationService.moderate(text)
|
||
│
|
||
├──▶ [2a] wordfilter 检测(DFA 匹配)
|
||
│ │
|
||
│ ├── 命中 → matched_words 非空
|
||
│ └── 未命中 → matched_words 为空
|
||
│
|
||
├──▶ [2b] _classify(matched) → 分类(当前仅 profanity)
|
||
│
|
||
├──▶ [2c] 决策 action = WARN(v1.0 固定)
|
||
│
|
||
├──▶ [2d] _generate_suggestion(category) → 建议文案
|
||
│
|
||
▼
|
||
[3] 返回 ModerationResult{action, category, matched_words, suggestion}
|
||
│
|
||
▼
|
||
[4] 前端根据 action 渲染:
|
||
├── PASS → 直接发送
|
||
└── WARN → 黄色提示条 + 仍可发送
|
||
```
|
||
|
||
### 3.2 隐私检测独立流程
|
||
|
||
```
|
||
文本输入
|
||
│
|
||
▼
|
||
check_privacy_leak(text)
|
||
│
|
||
├──▶ [A] 手机号正则 (?<!\d)1[3-9]\d{9}(?!\d)
|
||
│ └── 命中 → leaked.append("phone")
|
||
│
|
||
├──▶ [B] 身份证号正则 (?<!\d)\d{17}[\dXx](?!\d)
|
||
│ └── 命中 → leaked.append("id_card")
|
||
│
|
||
├──▶ [C] 银行卡号正则 (?<!\d)\d{16,19}(?!\d)
|
||
│ └── 命中 → leaked.append("bank_card")
|
||
│
|
||
├──▶ [D] 个人邮箱正则(排除公司域名)
|
||
│ └── 命中 → leaked.append("personal_email")
|
||
│
|
||
▼
|
||
返回 List[str](如 ["phone", "id_card"])
|
||
```
|
||
|
||
> **注意**:v1.0 `check_privacy_leak` 是**独立方法**,**未接入** `moderate` 主流程!前端需单独调用。
|
||
|
||
### 3.3 关键时序
|
||
|
||
```mermaid
|
||
sequenceDiagram
|
||
participant 坐席
|
||
participant 前端
|
||
participant 后端
|
||
participant Moderation as ContentModerationService
|
||
participant DB
|
||
|
||
坐席->>前端: 输入消息文本
|
||
前端->>后端: POST /api/agent/send {text}
|
||
后端->>Moderation: moderate(text)
|
||
Moderation->>Moderation: wordfilter.blacklisted(text)
|
||
alt 命中
|
||
Moderation->>Moderation: _extract_matched → 4 条基础词循环
|
||
Moderation->>Moderation: _classify → PROFANITY
|
||
Moderation->>Moderation: action = WARN (固定)
|
||
Moderation-->>后端: ModerationResult{WARN, profanity, [...]}
|
||
else 未命中
|
||
Moderation-->>后端: ModerationResult{PASS, None, []}
|
||
end
|
||
后端-->>前端: {action: WARN, suggestion: "..."}
|
||
alt WARN
|
||
前端->>坐席: 显示黄色提示条 + 仍可点"发送"
|
||
坐席->>前端: 点发送
|
||
前端->>后端: 确认发送
|
||
else PASS
|
||
前端->>后端: 直接发送
|
||
end
|
||
后端->>DB: 写 messages 表
|
||
后端-->>前端: 发送成功
|
||
```
|
||
|
||
---
|
||
|
||
## 4. 关键模块设计
|
||
|
||
### 4.1 敏感词检测
|
||
|
||
#### 4.1.1 核心实现
|
||
|
||
```python
|
||
# content_moderation_service.py
|
||
class ContentModerationService:
|
||
def __init__(self):
|
||
self.wf = Wordfilter() # wordfilter 0.2.7,DFA 算法
|
||
self.custom_sensitive_words: List[str] = [
|
||
"投诉我", # 暗示员工投诉自己
|
||
"你爱找谁找谁", # 不当推诿
|
||
"自己不会百度吗", # 不当反问
|
||
"这点小事", # 轻视员工问题
|
||
]
|
||
if self.custom_sensitive_words:
|
||
self.wf.addWords(self.custom_sensitive_words)
|
||
```
|
||
|
||
#### 4.1.2 关键修复(v0.7.1 部署)
|
||
|
||
> **BUGFIX**: `\b` 和 `(?<!\w)` 对中文均失效(Python 3 `\w` 含中文),
|
||
> 改用 `(?<!\d) / (?!\d)` 检查数字边界——"电话13800138000" 可正确匹配。
|
||
|
||
```python
|
||
# ❌ 错误写法(Python 3 中文失效)
|
||
re.search(r"\b1[3-9]\d{9}\b", "我的电话13800138000") # 命中失败
|
||
# 因为 Python 3 re 中 \w 包含中文,"电话"和"138"之间没有 \b
|
||
|
||
# ✅ 正确写法(数字边界)
|
||
re.search(r"(?<!\d)1[3-9]\d{9}(?!\d)", "我的电话13800138000") # 命中成功
|
||
```
|
||
|
||
#### 4.1.3 词库加载策略(v1.1 计划)
|
||
|
||
```
|
||
服务启动 (FastAPI lifespan)
|
||
│
|
||
▼
|
||
[1] 从 system_config 读取 sensitive_words 键值(JSON 数组)
|
||
│
|
||
├── 存在 → 解析 + 加载到 Wordfilter
|
||
└── 不存在 → 降级为写死 4 条
|
||
│
|
||
▼
|
||
[2] 同样从 system_config 读取 privacy_patterns
|
||
│
|
||
├── 存在 → 加载到正则列表
|
||
└── 不存在 → 降级为内置 4 条正则
|
||
```
|
||
|
||
### 4.2 隐私字段检测
|
||
|
||
| 检测项 | 正则 | 说明 |
|
||
|--------|------|------|
|
||
| phone | `(?<!\d)1[3-9]\d{9}(?!\d)` | 11 位 1 开头手机号 |
|
||
| id_card | `(?<!\d)\d{17}[\dXx](?!\d)` | 18 位身份证(末位 X 兼容) |
|
||
| bank_card | `(?<!\d)\d{16,19}(?!\d)` | 16-19 位银行卡 |
|
||
| personal_email | `(?<!\w)[a-zA-Z0-9._%+-]+@(?!servyou-it\.com\|servyou\.com\.cn)[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}(?!\w)` | 排除公司域名 |
|
||
|
||
**设计要点**:
|
||
- 所有正则必须用 `(?<!\d) / (?!\d)` 数字边界,**禁止**用 `\b`
|
||
- 邮箱正则用 `(?<!\w) / (?!\w)`(邮箱前缀是 ASCII,前面不会是中文)
|
||
|
||
### 4.3 命中动作决策
|
||
|
||
```python
|
||
# v1.0 固定 WARN
|
||
action = ModerationAction.WARN
|
||
```
|
||
|
||
**未来扩展(v1.1)**:
|
||
|
||
```python
|
||
# 根据 category + severity 决策
|
||
SEVERITY_MAP = {
|
||
ModerationCategory.PROFANITY: 1, # WARN
|
||
ModerationCategory.PRIVACY: 3, # BLOCK
|
||
ModerationCategory.POLITICS: 3, # BLOCK
|
||
ModerationCategory.PORN: 3, # BLOCK
|
||
ModerationCategory.AD: 2, # WARN + 审计
|
||
ModerationCategory.OTHER: 1, # WARN
|
||
}
|
||
action = BLOCK if SEVERITY_MAP[category] >= 3 else WARN
|
||
```
|
||
|
||
### 4.4 修改建议生成
|
||
|
||
```python
|
||
SUGGESTION_MAP = {
|
||
ModerationCategory.PROFANITY: "建议改为更专业的表达,例如:「我理解您的问题,我们一起想办法解决」",
|
||
ModerationCategory.POLITICS: "请避免讨论政治话题,保持服务专业性",
|
||
ModerationCategory.PORN: "请使用正式语言",
|
||
ModerationCategory.AD: "请勿发送广告内容",
|
||
ModerationCategory.PRIVACY: "请勿发送员工隐私信息(电话/身份证),如需联系请走企微",
|
||
ModerationCategory.OTHER: "请检查并修改表达",
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 5. 数据模型
|
||
|
||
### 5.1 v1.0 现状(无数据库)
|
||
|
||
词库写死在 `__init__` 方法,无 schema。
|
||
|
||
### 5.2 v1.1 计划 schema
|
||
|
||
#### 5.2.1 `sensitive_words` 表
|
||
|
||
```sql
|
||
CREATE TABLE sensitive_words (
|
||
id SERIAL PRIMARY KEY,
|
||
word VARCHAR(100) NOT NULL,
|
||
category VARCHAR(50) NOT NULL DEFAULT 'profanity', -- profanity/politics/porn/ad/other
|
||
severity SMALLINT NOT NULL DEFAULT 1, -- 1=WARN, 2=WARN+audit, 3=BLOCK
|
||
is_active BOOLEAN NOT NULL DEFAULT TRUE,
|
||
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
|
||
updated_at TIMESTAMP NOT NULL DEFAULT NOW(),
|
||
UNIQUE(word, category)
|
||
);
|
||
|
||
CREATE INDEX idx_sensitive_words_active ON sensitive_words(is_active);
|
||
```
|
||
|
||
#### 5.2.2 `privacy_patterns` 表
|
||
|
||
```sql
|
||
CREATE TABLE privacy_patterns (
|
||
id SERIAL PRIMARY KEY,
|
||
name VARCHAR(50) NOT NULL UNIQUE, -- phone/id_card/bank_card/personal_email
|
||
pattern TEXT NOT NULL,
|
||
description TEXT,
|
||
is_active BOOLEAN NOT NULL DEFAULT TRUE,
|
||
created_at TIMESTAMP NOT NULL DEFAULT NOW()
|
||
);
|
||
```
|
||
|
||
#### 5.2.3 `moderation_logs` 表(v1.1 计划)
|
||
|
||
```sql
|
||
CREATE TABLE moderation_logs (
|
||
id BIGSERIAL PRIMARY KEY,
|
||
message_id BIGINT REFERENCES messages(id),
|
||
agent_id INTEGER REFERENCES agents(id),
|
||
matched_words JSONB, -- 命中的词列表
|
||
pattern_names JSONB, -- 命中的正则名列表
|
||
category VARCHAR(50),
|
||
action VARCHAR(20), -- pass/warn/block
|
||
text_excerpt TEXT, -- 文本前 100 字(避免存全量)
|
||
created_at TIMESTAMP NOT NULL DEFAULT NOW()
|
||
);
|
||
|
||
CREATE INDEX idx_moderation_logs_agent ON moderation_logs(agent_id, created_at);
|
||
CREATE INDEX idx_moderation_logs_action ON moderation_logs(action, created_at);
|
||
```
|
||
|
||
### 5.3 Alembic 迁移
|
||
|
||
- 新建迁移:`alembic/versions/055_add_moderation_tables.py`
|
||
- 初始化 SQL:`scripts/init_moderation.sql`(4 条基础词 + 4 条隐私正则 + 索引)
|
||
|
||
---
|
||
|
||
## 6. 接口设计
|
||
|
||
### 6.1 v1.0 服务层 API(已实现)
|
||
|
||
```python
|
||
class ContentModerationService:
|
||
def moderate(self, text: str) -> ModerationResult: ...
|
||
def check_privacy_leak(self, text: str) -> List[str]: ...
|
||
def add_custom_word(self, word: str) -> None: ...
|
||
def remove_custom_word(self, word: str) -> None: ...
|
||
```
|
||
|
||
### 6.2 v1.0 路由层(**未实现**)
|
||
|
||
v1.0 没有 HTTP API 暴露词库管理。`admin_moderation_service.py` 仅做快速回复审核。
|
||
|
||
### 6.3 v1.1 计划路由层
|
||
|
||
| 接口 | 方法 | 说明 | 权限 |
|
||
|------|------|------|------|
|
||
| `/api/admin/sensitive-words` | GET | 列表查询 | admin |
|
||
| `/api/admin/sensitive-words` | POST | 添加 | admin |
|
||
| `/api/admin/sensitive-words/{id}` | PUT | 更新 | admin |
|
||
| `/api/admin/sensitive-words/{id}` | DELETE | 删除 | admin |
|
||
| `/api/admin/sensitive-words/test` | POST | 测试输入文本(不入库) | admin |
|
||
| `/api/admin/privacy-patterns` | GET/POST | 隐私正则管理 | admin |
|
||
| `/api/admin/privacy-patterns/{id}` | PUT/DELETE | 更新/删除 | admin |
|
||
| `/api/admin/moderation-config` | GET/PUT | 命中动作配置 | admin |
|
||
| `/api/admin/moderation-logs` | GET | 审计日志查询 | admin |
|
||
| `/api/admin/moderation-stats` | GET | 命中统计(高频词/误判率) | admin |
|
||
|
||
参考实现:[PRD-REQ-通用-002 快速回复规则后台管理 §3.2 管理 API](../01-产品文档/00-产品规划/PRD-REQ-通用-002-快速回复规则后台管理-v1.2.md),**架构可复用**。
|
||
|
||
---
|
||
|
||
## 7. 部署与灰度
|
||
|
||
### 7.1 v0.7.1 部署现状
|
||
|
||
| 项 | 状态 |
|
||
|----|------|
|
||
| 容器 | `wecom_it_backend` 镜像内已包含 wordfilter 库 |
|
||
| 启动 | FastAPI `lifespan` 钩子不调用(v1.0 词库写死,无需加载) |
|
||
| 配置 | 无需环境变量 |
|
||
| 灰度 | 无灰度(全量默认开启) |
|
||
| 监控 | 无专门监控(依赖应用日志) |
|
||
|
||
### 7.2 v1.1 部署计划
|
||
|
||
| 项 | 计划 |
|
||
|----|------|
|
||
| 灰度开关 | `MODERATION_ENABLED`(默认 `true`),关闭时返回 PASS |
|
||
| 词库热加载 | `MODERATION_HOT_RELOAD`(默认 `false`),开启时每 60s 重新读取 |
|
||
| 数据库迁移 | Alembic 055 + init SQL |
|
||
| 监控指标 | `moderation_warn_total{category}` / `moderation_block_total{category}` |
|
||
|
||
### 7.3 配置同步铁律
|
||
|
||
> **来自工作记忆**:修改 `config.py` 默认值 ≠ 配置生效,必须同步更新服务器 `.env` 文件和 `docker-compose.yml` 的 `environment` 段。
|
||
|
||
新增 3 个配置项时需同步:
|
||
1. `src/backend/app/core/config.py` 字段
|
||
2. `docker-compose.yml` 的 `backend.environment`
|
||
3. `.env.example` 模板
|
||
|
||
---
|
||
|
||
## 8. 测试策略
|
||
|
||
### 8.1 单元测试(已存在,11/13 通过)
|
||
|
||
`src/backend/tests/test_content_moderation.py` 13 用例:
|
||
|
||
| 模块 | 用例数 | 状态 |
|
||
|------|--------|------|
|
||
| 敏感词命中 WARN | 4 | ✅ 4/4 |
|
||
| 正常文本 PASS | 2 | ✅ 2/2 |
|
||
| 隐私字段检测 | 3 | ✅ 3/3 |
|
||
| 自定义词库断言 | 1 | ✅ 1/1 |
|
||
| 默认值 WARN(**反转测试**) | 1 | ⚠️ 1/1(标记为"反决策",2026-07-08 决策后通过) |
|
||
| 隐私正则边界 | 2 | ⚠️ 1/2(中文+号码场景仍待补) |
|
||
|
||
### 8.2 v1.1 测试计划
|
||
|
||
- **功能测试**:CRUD(6)+ 词库热加载(2)+ 命中动作配置(3)= 11 条
|
||
- **E2E 测试**:坐席端发消息命中 WARN 场景(2 条)
|
||
- **回归测试**:现有 13 用例全部通过
|
||
- **性能测试**:10000 字文本审核 < 50ms
|
||
|
||
### 8.3 关键测试场景
|
||
|
||
```python
|
||
# 中文+手机号场景(修复验证)
|
||
def test_chinese_phone_boundary():
|
||
leaked = service.check_privacy_leak("我的电话13800138000")
|
||
assert "phone" in leaked # \b 失效场景,数字边界修复
|
||
|
||
# 反向:WARN 不阻断(产品决策保护)
|
||
def test_warn_does_not_block():
|
||
r = service.moderate("自己不会百度吗")
|
||
assert r.action == ModerationAction.WARN
|
||
assert r.action != ModerationAction.BLOCK # 保护坐席自主权
|
||
```
|
||
|
||
---
|
||
|
||
## 9. 风险与降级
|
||
|
||
| 风险 | 等级 | 降级措施 |
|
||
|------|------|----------|
|
||
| wordfilter 库未安装 | 🟡 中 | `__init__` 异常时降级为纯正则匹配(仅隐私) |
|
||
| 服务挂掉 | 🟢 低 | 审核失败不抛异常,返回 PASS(设计 §1.3 P3) |
|
||
| 词库加载失败 | 🟡 中 | 降级为写死 4 条 + 日志告警 |
|
||
| 误报高频 | 🟡 中 | 词库极小(4 条),运营可手动加白名单(v1.1) |
|
||
| 漏报(新词未入库) | 🟡 中 | 依赖运营定期 review;可加 hit-miss 反馈机制 |
|
||
| 命中动作升级为 BLOCK 误伤 | 🟠 高 | 维持 WARN 直到 1.1 引入可配置化(评审已确认) |
|
||
|
||
### 9.1 降级路径
|
||
|
||
```
|
||
ContentModerationService.moderate(text)
|
||
│
|
||
├── 正常 → WARN/PASS
|
||
│
|
||
├── 词库未加载 → 仅返回空 PASS + 日志告警
|
||
│
|
||
└── 异常(如 wordfilter 库缺失)
|
||
↓
|
||
降级为 PrivacyOnlyModeration(仅隐私正则)
|
||
↓
|
||
仍异常 → 直接返回 PASS(不阻断)
|
||
```
|
||
|
||
---
|
||
|
||
## 10. 关联文档
|
||
|
||
| 文档 | 位置 | 关联点 |
|
||
|------|------|--------|
|
||
| 关联 PRD | `01-产品文档/00-产品规划/PRD-REQ-通用-004-敏感词检测-v1.0.md` | 需求来源 |
|
||
| 看板验真测试报告 | `03-测试文档/04-版本测试报告/看板验真-测试报告-20260707.md` | 历史基线(11/13 通过) |
|
||
| 内容审核服务源码 | `src/backend/app/services/content_moderation_service.py` | 当前实现 |
|
||
| 内容审核测试源码 | `src/backend/tests/test_content_moderation.py` | 13 用例基线 |
|
||
| 快速回复规则后台管理 PRD | `01-产品文档/00-产品规划/PRD-REQ-通用-002-快速回复规则后台管理-v1.2.md` | 同类功能(运营后台词库管理),v1.1 架构可复用 |
|
||
| 知识库迭代技术方案 §4 代答排除规则 | `02-技术文档/技术架构/技术方案-REQ-知识-001-知识库迭代-v1.0.md` | 关键词匹配机制参考 |
|
||
| 管理后台原型图("代答排除规则"页) | `01-产品文档/08-集成生态/原型-REQ-集成-002-管理后台-v1.0.html` | 后续 v1.1 词库管理 UI 雏形 |
|
||
| 项目主文档 #81 | `07-项目管理/任务说明书/IT智能服务台-项目管理主文档.md` | v0.7.1 已上线 |
|
||
| Python 3 re 中文失效 | https://docs.python.org/3/library/re.html | `\b` 边界对中文失效背景 |
|
||
|
||
---
|
||
|
||
## 11. 变更日志
|
||
|
||
| 版本 | 日期 | 变更 | 变更人 |
|
||
|------|------|------|--------|
|
||
| v1.0 | 2026-07-28 | 首次整理:v0.7.1 上线内容回溯为正式技术方案 | 宋献 |
|