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-*/
277 lines
12 KiB
Markdown
277 lines
12 KiB
Markdown
# 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 |
|
||
| 手机号正则 | `(?<!\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 已实现)
|
||
|
||
- [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 是已接受的产品决策)。
|