PRD - 敏感词检测
需求编号: REQ-通用-004
版本: v1.0
状态: [已上线/部分达标]
作者: 宋献
日期: 2026-07-28
关联任务: 项目主文档 #81(v0.7.1 已上线)
关联文档:
- 技术方案:
02-技术文档/技术架构/技术方案-REQ-通用-004-敏感词检测-v1.0.md
- 测试用例:
03-测试文档/03-功能测试用例/TC-通用-004-敏感词检测.md
- 看板验真测试报告(历史基线):
03-测试文档/04-版本测试报告/看板验真-测试报告-20260707.md
- 内容审核服务源码:
src/backend/app/services/content_moderation_service.py
- 内容审核测试源码:
src/backend/tests/test_content_moderation.py
1. 需求描述
1.1 背景
IT 智能服务台坐席在工作过程中会发送大量文字消息(回复员工、推送通知、催办等),存在两类内容风险:
| 风险类别 |
典型场景 |
后果 |
| 服务态度风险 |
坐席使用轻视/推诿/反问式语气("你爱找谁找谁"、"自己不会百度吗") |
员工投诉,IT 服务台品牌受损 |
| 隐私泄露风险 |
坐席误发员工手机号、身份证号、银行卡号、个人邮箱 |
公司合规风险,员工个人隐私暴露 |
历史经验表明,人工巡检难以 100% 覆盖。需要在坐席发送消息前做一次内容审核,给出风险提示。
1.2 目标
建立坐席发送消息的内容审核机制,做到:
- 覆盖风险面:敏感服务用语(脏话/不当推诿/反问)+ 隐私字段(手机/身份证/银行卡/个人邮箱)
- 低干扰:命中后仅警告不阻断(WARN 策略,2026-07-08 决策),保留坐席自主权
- 可扩展:词库/规则可运营(理论上可由管理后台维护,当前 v1.0 为写死基线)
1.3 范围
| 范围项 |
v1.0 状态 |
说明 |
| 敏感服务用语检测 |
✅ 已实现 |
wordfilter 库 + 自定义词库(写死 4 条) |
| 隐私字段检测 |
✅ 已实现 |
正则匹配手机/身份证/银行卡/邮箱 |
| 命中动作 |
✅ 维持 WARN |
不阻断发送,仅提示 |
| 修改建议 |
✅ 已实现 |
按分类返回固定建议文案 |
| 词库运营管理 |
❌ 未实现 |
当前写死,PRD §6 数据需求中已规划 |
| 后台配置 UI |
❌ 未实现 |
运营需改代码发布 |
| 命中动作可配置 |
❌ 未实现 |
当前固定 WARN,无法升级为 BLOCK |
1.4 Non-goals
| 不做 |
原因 |
| 图像/附件内容审核 |
仅做文本审核;图片走企微原生反垃圾 |
| AI 实时生成建议 |
当前为固定文案模板;AI 改写后续再评估 |
| 阻断(BLOCK)动作 |
2026-07-08 决策:维持 WARN,避免误伤业务 |
| 员工端(H5)输入审核 |
员工端走企微原生反垃圾 + AI Wingman |
2. 用户故事
2.1 坐席
| 优先级 |
用户故事 |
| P0 |
作为坐席,我希望发送"自己不会百度吗"前收到警告,知道这不合适 |
| P0 |
作为坐席,我希望看到具体哪些词被命中(matched_words) |
| P0 |
作为坐席,我希望能继续发送(不被强制阻断),由我自己判断 |
| P1 |
作为坐席,我希望看到修改建议(suggestion),学习如何更专业 |
| P1 |
作为坐席,我希望知道为什么被警告(category:脏话/隐私/...) |
2.2 运营人员
| 优先级 |
用户故事 |
| P1 |
作为运营,我希望能调整词库(添加/删除敏感词),无需改代码 |
| P2 |
作为运营,我希望能调整隐私正则(如新增"军官证号"),支持业务扩展 |
| P2 |
作为运营,我希望能查看词库命中统计(高频误判词/低频词) |
2.3 管理员
| 优先级 |
用户故事 |
| P1 |
作为管理员,我希望命中动作可配置(WARN/BLOCK),应对合规升级 |
| P2 |
作为管理员,我希望审核日志可追溯(谁发了什么被警告) |
3. 功能需求
3.1 敏感词检测(写死词库)
| 字段 |
规格 |
| 检测范围 |
坐席发送的所有文本消息(员工消息不审) |
| 词库来源 |
content_moderation_service.py::ContentModerationService.__init__ 写死 4 条 |
| 词库当前值 |
["投诉我", "你爱找谁找谁", "自己不会百度吗", "这点小事"] |
| 匹配算法 |
wordfilter 库(基于 DFA 的 Aho-Corasick 变体) |
| 分类 |
当前仅支持 profanity(脏话),其它分类保留扩展位 |
| 命中动作 |
ModerationAction.WARN(固定) |
3.2 隐私字段检测
| 字段 |
规格 |
| 检测项 |
phone / id_card / bank_card / personal_email |
| 手机号正则 |
(?<!\d)1[3-9]\d{9}(?!\d)(已修复:用数字边界替代 \b,修复 Python3 re 中文失效) |
| 身份证正则 |
(?<!\d)\d{17}[\dXx](?!\d) |
| 银行卡正则 |
(?<!\d)\d{16,19}(?!\d) |
| 个人邮箱正则 |
排除 servyou-it.com 和 servyou.com.cn 后缀 |
| 返回值 |
命中的字段描述列表(如 ["phone", "id_card"]) |
| 命中动作 |
ModerationAction.WARN(隐私检测暂未接入 moderate 主流程,仅提供独立方法) |
3.3 提示与建议
| 字段 |
规格 |
| 提示形式 |
坐席端发送按钮上方黄色提示条 |
| 提示内容 |
"⚠️ 检测到敏感词:[xxx] 建议修改为:xxx" |
| 阻断行为 |
无(坐席可继续发送) |
| 审计日志 |
当前未写审计日志(v1.0 限制) |
3.4 词库管理(v1.0 留接口,未实现 UI)
| 字段 |
规格 |
| 数据存储 |
计划存 system_config 表(key=sensitive_words, value=JSON 数组) |
| 加载时机 |
服务启动时一次性加载到内存(Wordfilter.addWords) |
| 热更新 |
未实现(v1.0 限制;改词库需重启后端) |
| 增删 API |
service.add_custom_word(word) / service.remove_custom_word(word) 已有,未挂载到路由 |
| 后台 UI |
❌ 未实现(PRD §6 数据需求规划) |
3.5 命中动作分级(v1.0 限制)
| 分类 |
v1.0 动作 |
后续规划 |
| profanity |
WARN |
可配置为 BLOCK |
| politics |
WARN(理论) |
应升级为 BLOCK |
| porn |
WARN(理论) |
应升级为 BLOCK |
| ad |
WARN(理论) |
可配置 |
| privacy |
WARN(理论) |
应升级为 BLOCK |
| other |
WARN(理论) |
可配置 |
决策记录:2026-07-08 项目评审决定 v1.0 维持 WARN,不升级 BLOCK。理由:避免误伤业务(WARN 已经能让坐席知道问题,且坐席有最终决策权)。
4. 非功能需求
| 维度 |
要求 |
| 性能 |
单次审核 < 5ms(wordfilter DFA 算法,已实测) |
| 可用性 |
不阻塞主流程:审核失败不阻断消息发送(v1.0 异常吞掉) |
| 可维护性 |
词库/正则集中在一个 service,修改影响范围可控 |
| 可测试性 |
13 个单元测试用例,覆盖率 ≥ 85% |
| 兼容性 |
Python 3.11+ / FastAPI / PostgreSQL / Redis(与现有架构一致) |
| 国际化 |
当前仅中文(敏感词库和提示文案) |
5. 接口需求
5.1 服务层 API(已实现)
| 方法 |
签名 |
返回 |
moderate(text) |
str -> ModerationResult |
审核结果(action/category/matched_words/suggestion) |
check_privacy_leak(text) |
str -> List[str] |
命中的隐私字段名 |
add_custom_word(word) |
str -> None |
动态加词(v1.0 未挂路由) |
remove_custom_word(word) |
str -> None |
动态删词(v1.0 未挂路由) |
5.2 路由层 API(计划中)
| 接口 |
方法 |
说明 |
状态 |
/api/admin/sensitive-words |
GET |
词库列表 |
❌ 未实现 |
/api/admin/sensitive-words |
POST |
添加词 |
❌ 未实现 |
/api/admin/sensitive-words/{id} |
DELETE |
删除词 |
❌ 未实现 |
/api/admin/sensitive-words/test |
POST |
测试输入文本(不入库) |
❌ 未实现 |
/api/admin/privacy-patterns |
GET/POST |
隐私正则管理 |
❌ 未实现 |
/api/admin/moderation-config |
GET/PUT |
命中动作配置(WARN/BLOCK) |
❌ 未实现 |
现状:v1.0 仅服务层可用,无 HTTP API 暴露。
6. 数据需求
6.1 词库存储(v1.0 写死,v1.1 计划入库)
| 字段 |
规格 |
| 表名 |
sensitive_words(v1.1 计划新建) |
| 字段 |
id / word / category / severity / is_active / created_at / updated_at |
| severity |
1=低(仅 WARN)/ 2=中(WARN+审计)/ 3=高(BLOCK) |
| 初始化 |
通过 Alembic 迁移 + init SQL 导入基础词库 |
| 缓存 |
服务启动时全量加载到 Wordfilter 实例 |
6.2 隐私正则存储(同上)
| 字段 |
规格 |
| 表名 |
privacy_patterns(v1.1 计划新建) |
| 字段 |
id / name / pattern / description / is_active / created_at |
| 名称示例 |
phone / id_card / bank_card / personal_email |
| 初始化 |
同上,Alembic + init SQL |
6.3 命中审计日志(v1.1 计划)
| 字段 |
规格 |
| 表名 |
moderation_logs |
| 字段 |
id / message_id / agent_id / matched_words / category / action / created_at |
| 用途 |
追溯谁发了什么被警告 |
7. 风险与约束
| 风险 |
等级 |
缓解措施 |
| 命中仅 WARN,违规坐席可忽略 |
🟡 中 |
PRD §3.5 已记录决策;后续可配置 |
| 词库写死,运营无法调整 |
🟡 中 |
v1.1 计划入库(PRD §6) |
| 隐私正则覆盖有限(未含军官证/护照/车牌) |
🟢 低 |
v1.1 计划扩展正则集合 |
| 误报(正常消息触发 WARN) |
🟡 中 |
词库极小(4 条),v1.1 后由运营调整 |
| 漏报(新敏感词未及时入库) |
🟡 中 |
依赖运营定期 review |
| 后台 UI 缺失 |
🟡 中 |
v1.1 计划开发(参考通用-002 快速回复规则后台管理) |
8. 验收标准
8.1 必达项(v1.0 已实现)
8.2 已知不达标项(v1.0 接受,v1.1 解决)
9. 关联文档
| 文档 |
位置 |
关联点 |
| 看板验真测试报告 |
03-测试文档/04-版本测试报告/看板验真-测试报告-20260707.md |
历史基线测试(11/13 通过) |
| 项目状态看板 #81 |
07-项目管理/任务说明书/IT智能服务台-项目管理主文档.md |
v0.7.1 已上线 |
| 快速回复规则后台管理 PRD |
01-产品文档/00-产品规划/PRD-REQ-通用-002-快速回复规则后台管理-v1.2.md |
同类功能(运营后台词库管理),可复用架构 |
| 知识库迭代技术方案 §4 代答排除规则 |
02-技术文档/技术架构/技术方案-REQ-知识-001-知识库迭代-v1.0.md |
关键词匹配机制可参考 |
| 内容审核服务源码 |
src/backend/app/services/content_moderation_service.py |
当前实现 |
| 内容审核测试源码 |
src/backend/tests/test_content_moderation.py |
13 用例基线 |
10. 变更日志
| 版本 |
日期 |
变更 |
变更人 |
| v1.0 |
2026-07-28 |
首次整理:v0.7.1 上线内容回溯为正式 PRD |
宋献 |
备注:本文档是对 v0.7.1 已上线功能的回溯性 PRD 化,用于补全项目文档体系。功能本身已在生产稳定运行(命中即 WARN 是已接受的产品决策)。