215 lines
8.2 KiB
Markdown
215 lines
8.2 KiB
Markdown
# Info-Privacy 开发指南
|
||||
|
|
|
|||
|
|
## 项目结构
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
info-privacy/
|
|||
|
|
├── src/info_privacy/
|
|||
|
|
│ ├── api/
|
|||
|
|
│ │ ├── main.py # FastAPI 入口,路由注册
|
|||
|
|
│ │ ├── routes/
|
|||
|
|
│ │ │ ├── analyze.py # POST /api/v1/analyze
|
|||
|
|
│ │ │ └── redact.py # POST /api/v1/redact
|
|||
|
|
│ │ └── models.py # Pydantic 请求/响应模型
|
|||
|
|
│ ├── classifier/
|
|||
|
|
│ │ └── doc_classifier.py # 保密文件拦截(关键词匹配)
|
|||
|
|
│ ├── detectors/
|
|||
|
|
│ │ ├── regex_detector.py # Regex PII 检测
|
|||
|
|
│ │ ├── ner_detector.py # 规则词典姓名/地址检测
|
|||
|
|
│ │ └── face_detector.py # MediaPipe RKNN 人脸检测(硬件依赖)
|
|||
|
|
│ ├── parsers/
|
|||
|
|
│ │ ├── pdf_parser.py # pdfminer 文字层解析
|
|||
|
|
│ │ ├── office_parser.py # python-docx / openpyxl
|
|||
|
|
│ │ ├── image_parser.py # PaddleOCR RKNN 图像 OCR(硬件依赖)
|
|||
|
|
│ │ └── parser_factory.py # 按文件扩展名路由
|
|||
|
|
│ ├── redactors/
|
|||
|
|
│ │ ├── text_redactor.py # 文字节点替换(字节级安全)
|
|||
|
|
│ │ └── image_redactor.py # OpenCV 黑色矩形覆盖
|
|||
|
|
│ ├── models.py # 核心数据模型(TextBlock, Entity, DetectionReport)
|
|||
|
|
│ └── pipeline.py # 串联 parser → classifier → detector → redactor
|
|||
|
|
├── configs/
|
|||
|
|
│ ├── pii_rules.yaml # Regex 规则 + 保密关键词 + NER 配置
|
|||
|
|
│ └── surnames.txt # 中文姓氏词典(NER 姓名检测)
|
|||
|
|
├── tests/
|
|||
|
|
│ ├── test_models.py # 数据模型单元测试
|
|||
|
|
│ ├── test_classifier.py # 文档分类测试
|
|||
|
|
│ ├── test_detectors.py # Regex/NER/人脸检测测试
|
|||
|
|
│ ├── test_parsers.py # PDF/Office/图像解析测试
|
|||
|
|
│ ├── test_redactors.py # 文字/图像遮罩测试
|
|||
|
|
│ ├── test_pipeline.py # 端到端流水线测试
|
|||
|
|
│ ├── test_api.py # FastAPI 接口测试
|
|||
|
|
│ └── test_simulation.py # RKNN 硬件仿真测试(x86 可运行)
|
|||
|
|
├── scripts/
|
|||
|
|
│ ├── start_server.sh # 一键启动服务
|
|||
|
|
│ └── verify.py # 端到端验证脚本
|
|||
|
|
├── docs/plans/ # 设计文档(历史归档)
|
|||
|
|
├── models/ # 本地模型软链(指向 sibling 项目)
|
|||
|
|
├── tmp/ # 临时文件(已加 .gitignore)
|
|||
|
|
├── pyproject.toml
|
|||
|
|
├── Makefile
|
|||
|
|
└── setup_venv.sh
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 开发环境搭建
|
|||
|
|
|
|||
|
|
### x86 开发机
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
# 1. 初始化虚拟环境
|
|||
|
|
bash setup_venv.sh
|
|||
|
|
source venv/bin/activate
|
|||
|
|
|
|||
|
|
# 2. 验证安装
|
|||
|
|
python -c "from info_privacy.pipeline import PrivacyPipeline; print('OK')"
|
|||
|
|
|
|||
|
|
# 3. 运行全量测试
|
|||
|
|
make test # 155 passed, 4 skipped(RKNN 相关跳过)
|
|||
|
|
make lint # ruff 代码检查
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### RK3588 板端
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
# 同步代码(开发机执行)
|
|||
|
|
rsync -av /data/rockchip/info-privacy/ pi@192.168.0.127:/home/pi/Desktop/info-privacy/
|
|||
|
|
|
|||
|
|
# 板端环境(SSH 执行)
|
|||
|
|
cd /home/pi/Desktop/info-privacy
|
|||
|
|
bash setup_venv.sh && source venv/bin/activate
|
|||
|
|
python -c "from info_privacy.pipeline import PrivacyPipeline; print('OK')"
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
板端需额外安装 `rknn-toolkit-lite2`,OpenCV/NumPy 已预装。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 常用开发命令
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
make test # 运行所有测试
|
|||
|
|
make test-one FILE=tests/test_api.py # 单文件测试
|
|||
|
|
make lint # ruff 代码检查
|
|||
|
|
make server # 启动服务(--reload 热重载)
|
|||
|
|
make verify # 端到端验证(需先启动服务)
|
|||
|
|
make clean # 清理 __pycache__ 和 tmp/
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 测试框架说明
|
|||
|
|
|
|||
|
|
### 分层测试策略
|
|||
|
|
|
|||
|
|
| 测试文件 | 类型 | 是否需要硬件 |
|
|||
|
|
|---------|------|------------|
|
|||
|
|
| `test_models.py` | 单元 | 否 |
|
|||
|
|
| `test_classifier.py` | 单元 | 否 |
|
|||
|
|
| `test_detectors.py` | 单元 | 否(人脸检测用 skip) |
|
|||
|
|
| `test_parsers.py` | 单元 | 否(图像解析用 skip) |
|
|||
|
|
| `test_redactors.py` | 单元 | 否 |
|
|||
|
|
| `test_pipeline.py` | 集成 | 否 |
|
|||
|
|
| `test_api.py` | 接口 | 否 |
|
|||
|
|
| `test_simulation.py` | 仿真 | 否(mock 替换 RKNN) |
|
|||
|
|
| `test_integration_real.py` | 集成(真实文件) | 否 |
|
|||
|
|
| `test_integration_complex.py` | 集成(边界/复杂场景) | 否 |
|
|||
|
|
|
|||
|
|
### RKNN 仿真测试
|
|||
|
|
|
|||
|
|
`test_simulation.py` 使用 `unittest.mock.patch.dict(sys.modules, ...)` 注入 mock 模块,
|
|||
|
|
在 x86 环境下验证 `FaceDetector` 和 `ImageParser` 的业务逻辑(bbox 转换、entity 构建、置信度过滤等)。
|
|||
|
|
|
|||
|
|
板端真机测试时,`test_detectors.py` 和 `test_parsers.py` 中的 skip 测试将自动运行。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 扩展指南
|
|||
|
|
|
|||
|
|
### 添加新的 PII 类型(Regex)
|
|||
|
|
|
|||
|
|
1. 在 `configs/pii_rules.yaml` 的 `regex` 节下添加规则:
|
|||
|
|
```yaml
|
|||
|
|
passport:
|
|||
|
|
pattern: '[A-Z]{1,2}\d{7}'
|
|||
|
|
security_level: high
|
|||
|
|
```
|
|||
|
|
2. 在 `src/info_privacy/models.py` 的 `EntityType` 枚举中增加 `PASSPORT = "passport"`
|
|||
|
|
3. 在 `tests/test_detectors.py` 中增加对应测试用例
|
|||
|
|
|
|||
|
|
### 添加新的 PII 类型(NER)
|
|||
|
|
|
|||
|
|
1. 在 `configs/pii_rules.yaml` 的 `ner` 节下添加触发词配置
|
|||
|
|
2. 在 `src/info_privacy/detectors/ner_detector.py` 中实现匹配逻辑
|
|||
|
|
3. 添加对应 `EntityType` 枚举值
|
|||
|
|
|
|||
|
|
### 添加新的文档格式
|
|||
|
|
|
|||
|
|
1. 在 `src/info_privacy/parsers/` 下创建新的 Parser 类,实现 `parse(file_path) -> (dict, list[TextBlock])` 接口
|
|||
|
|
2. 在 `src/info_privacy/parsers/parser_factory.py` 中注册新格式的扩展名映射
|
|||
|
|
3. 新 Parser 的 `meta` 字典必须包含 `format`、`has_text_layer`、`redact_strategy` 三个字段
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 数据流
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
[POST /analyze]
|
|||
|
|
文件 → parser_factory → TextBlock[]{text, bbox, page, layer}
|
|||
|
|
→ doc_classifier → 如果 blocked: 直接返回 DetectionReport(blocked=True)
|
|||
|
|
→ regex_detector + ner_detector + face_detector → Entity[]
|
|||
|
|
→ doc_classifier(实体密度评估) → warning?
|
|||
|
|
→ 返回 DetectionReport
|
|||
|
|
|
|||
|
|
[POST /redact]
|
|||
|
|
文件 + redact_types → analyze() → Entity[]
|
|||
|
|
→ 按 entity.layer 分流:
|
|||
|
|
layer=="text" → text_redactor(删除文字节点)
|
|||
|
|
layer=="image" → image_redactor(OpenCV 黑框覆盖)
|
|||
|
|
→ 输出文档 + X-Security-Report Header
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 关键配置文件
|
|||
|
|
|
|||
|
|
### configs/pii_rules.yaml
|
|||
|
|
|
|||
|
|
- `regex`:各类型的检测正则表达式和安全等级
|
|||
|
|
- `confidential_keywords`:保密文件拦截关键词列表
|
|||
|
|
- `ner.name.surnames_file`:中文姓氏词典路径(相对于项目根目录)
|
|||
|
|
- `ner.address.triggers`:地址检测触发词(省/市/区/路/号等)
|
|||
|
|
|
|||
|
|
### 安全等级说明
|
|||
|
|
|
|||
|
|
- `high`:遮罩时默认强制处理,体现在 `X-Security-Report` 中
|
|||
|
|
- `medium`:遮罩时按用户 `redact_types` 指定处理
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 常见问题
|
|||
|
|
|
|||
|
|
**Q: `make test` 显示 4 个 skipped 测试**
|
|||
|
|
|
|||
|
|
正常现象。这 4 个测试依赖 RKNN 运行时(仅 RK3588 设备可用)。x86 环境下等效的仿真测试在 `test_simulation.py` 中覆盖(15 个)。
|
|||
|
|
|
|||
|
|
**Q: 身份证号为何不会同时被标注为银行卡**
|
|||
|
|
|
|||
|
|
系统内置去重逻辑:18 位身份证号满足银行卡格式,但 `RegexDetector` 在检测完成后会过滤掉与 `id_card` 值重叠的 `bank_card` 实体,避免双重计数影响阈值判断。
|
|||
|
|
|
|||
|
|
**Q: 中文字符前的数字 PII 为何能被检测**
|
|||
|
|
|
|||
|
|
正则边界使用 `(?<!\d)` / `(?!\d)` 而非 `\b`。Python 3 的 `\b` 是 Unicode-aware,中文字符属于 `\w`,与纯数字之间不形成词边界,故改用纯数字前后断言。
|
|||
|
|
|
|||
|
|
**Q: 板端运行时 face_detector 报 ImportError**
|
|||
|
|
|
|||
|
|
确认 MediaPipe-RKNN 项目路径存在:`/data/rockchip/mediapipe/src/mediapipe_rknn/`。FaceDetector 在 `pipeline.py` 中延迟加载,首次 analyze 图像文档时才触发。
|
|||
|
|
|
|||
|
|
**Q: PDF 遮罩后用 PDF 阅读器仍能选中文字**
|
|||
|
|
|
|||
|
|
检查 PDF 是否含文字层(`meta["has_text_layer"]`)。文字层 PDF 必须用 `text_redactor`;单纯覆盖图像层无法防止文字提取。
|
|||
|
|
|
|||
|
|
**Q: 中文姓名检测误报**
|
|||
|
|
|
|||
|
|
NER 基于姓氏词典 + 上下文触发词("姓名:"、"申请人:"等)。调整 `configs/pii_rules.yaml` 中 `ner.name.context_triggers` 可降低误报率。
|