# 技术方案 - 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 数字边界** | 所有数字相关正则必须用 `(? **注意**: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` 和 `(? 改用 `(?= 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 上线内容回溯为正式技术方案 | 宋献 |