# 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 目标 建立坐席发送消息的**内容审核机制**,做到: 1. **覆盖风险面**:敏感服务用语(脏话/不当推诿/反问)+ 隐私字段(手机/身份证/银行卡/个人邮箱) 2. **低干扰**:命中后**仅警告不阻断**(WARN 策略,2026-07-08 决策),保留坐席自主权 3. **可扩展**:词库/规则**可运营**(理论上可由管理后台维护,当前 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 | | 手机号正则 | `(? **决策记录**: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 已实现) - [x] `moderate("你爱找谁找谁")` 返回 WARN,matched_words 含该词 - [x] `moderate("您好,电脑无法开机")` 返回 PASS - [x] `check_privacy_leak("电话13800138000")` 返回 `["phone"]` - [x] `check_privacy_leak("身份证11010119900307123X")` 返回 `["id_card"]` - [x] 命中动作固定为 WARN,不 BLOCK - [x] 自定义词库包含 4 条基础词 - [x] 隐私正则使用数字边界(修复 Python3 中文失效) ### 8.2 已知不达标项(v1.0 接受,v1.1 解决) - [ ] 命中动作可配置(v1.0 固定 WARN) - [ ] 词库可数据库化(v1.0 写死) - [ ] 后台管理 UI(v1.0 无) - [ ] 命中审计日志(v1.0 无) - [ ] 隐私正则可扩展(v1.0 仅 4 类) --- ## 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 是已接受的产品决策)。