Files
YG_FT/docs/data-process-design.md
caoxiaozhu 91b4ae2287 feat(data_process): 显式关闭 docling OCR 并拒绝无文本层 PDF
- docling 转换器通过 PdfPipelineOptions 显式设置 do_ocr=False,
  混合 PDF 的图片页不再产出 OCR 文字;不提供重新开启 OCR 的参数。
- 无文本层 PDF 的错误文案改为"扫描版或图片型 PDF 不支持",
  上传阶段整批拒绝,保留混合 PDF 的可处理判定。
- 新增 test_layout_converter_disables_ocr 守护开关状态,
  同步设计文档与 disable-ocr 实施计划/设计说明。
2026-08-18 15:49:00 +08:00

280 lines
14 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 数据处理接口与算法设计
本文是 `team-development-plan.md` 板块 C 的落地契约,约束
`/modelTF/data-process/*`、前端数据处理向导以及 PostgreSQL 数据模型。
## 1. 处理闭环
```text
创建草稿任务
→ 上传并登记源文件格式、SHA-256、版本
→ 预处理(标准化、无效过滤、去重、可选脱敏)
→ 构建可编辑预览(来源偏移与行号)
→ 生成标准训练记录
→ 质量评分与稳定数据集划分
→ 人工编辑/恢复
→ 幂等发布为数据集(保留完整来源链路)
```
任务只使用以下五种状态:
```text
pending ──start/generate──> running ──success──> completed
▲ │ ├──error───────> failed
│ │ └──stop────────> stopped
└────────retry────────────┴────────retry─────┘
```
- `pending` 允许修改配置、增删源文件和重建预览。
- `running` 拒绝重复启动、修改配置和删除任务。
- `failed``stopped` 可重试;重试前清理上一次未完成结果。
- `completed` 可编辑结果和发布;重复发布返回同一个数据集。
- 非法状态转换返回 HTTP 409。
- 每次生成分配独立 `generation_run_id`;停止或重试会使旧代次立即失效,
旧后台任务不能覆盖新代次的结果或状态。
## 2. 接口契约
所有路径由请求层统一添加 `/modelTF`,响应统一为
`{ "code": 0, "message": "ok", "data": ... }`
### 任务与进度
| 方法 | 路径 | 说明 |
| --- | --- | --- |
| GET | `/data-process` | 分页查询任务,支持 keyword/status/process_type |
| POST | `/data-process` | 创建 `pending` 草稿 |
| GET | `/data-process/{id}` | 查询任务详情,不内嵌全部结果 |
| PUT | `/data-process/{id}` | 更新草稿配置 |
| DELETE | `/data-process/{id}` | 软删除非运行任务 |
| POST | `/data-process/{id}/start` | 重建预览并生成的一键编排入口 |
| POST | `/data-process/{id}/generate` | 使用已确认预览生成结果 |
| POST | `/data-process/{id}/stop` | 请求停止运行任务 |
| GET | `/data-process/{id}/progress` | 查询阶段、进度与计数 |
### 源文件与预览
| 方法 | 路径 | 说明 |
| --- | --- | --- |
| POST | `/data-process/{id}/source-files` | multipart 上传,字段名 `files` |
| DELETE | `/data-process/{id}/source-files/{file_id}` | 删除源文件及其预览 |
| GET | `/data-process/{id}/source-files/{file_id}/content` | 按行窗口读取源文 |
| POST | `/data-process/{id}/preview/build` | 后端预处理并重建预览 |
| GET | `/data-process/{id}/preview` | 分页查询预览 |
| POST | `/data-process/{id}/preview` | 手工增加预览条目 |
| PUT | `/data-process/{id}/preview/{preview_id}` | 保存人工编辑 |
| DELETE | `/data-process/{id}/preview/{preview_id}` | 删除预览条目 |
上传批次先全部完成有界读取和解析,再在单个事务中登记;任一文件为空、超限、
重复或格式非法时整批不落库,暂存原件也会一并清理。响应不回传整个文件,只返回
逻辑对象引用、文件 ID、格式、原始字节数、记录数和原始 SHA-256。二进制文档必须
由对应解析器显式处理;不支持的格式返回 415绝不能静默替换成示例正文。
原始上传字节与解析正文采用双层存储:原件默认保存在
`backend/storage/data-process/<task_id>/<file_id>/v<version>/<安全文件名>`,数据库的
`storage_object_id` 只保存 `local://data-process/...` 逻辑引用,不保存或返回宿主机
绝对路径;完整解析正文继续保存在 `data_process_source_files.content`,列表摘要使用
`content_preview`,因此 PDF、Office 等文件的预览无需反复解析原始二进制。可通过
`DATA_PROCESS_STORAGE_DIR` 指定其他本地根目录;从 `start.sh` 启动时,该变量应在
当前终端导出。历史 `db://data-process/...` 记录继续从数据库正文预览。
单独删除源文件时先提交数据库软删除,再立即删除受控目录中的原件;若物理删除
失败,接口仍按数据库结果返回成功并标记 `storage_cleanup_pending=true`,软删除记录
中的逻辑引用可供运维补偿清理。任务软删除以及修改 `process_type` 导致的源文件
软删除按留存数据处理,当前版本不自动物理清除。
### 结果与发布
| 方法 | 路径 | 说明 |
| --- | --- | --- |
| GET | `/data-process/{id}/results` | 分页查询,支持 keyword/status/split |
| PUT | `/data-process/{id}/results/{result_id}` | 保存人工编辑并重评分 |
| POST | `/data-process/{id}/results/{result_id}/restore` | 恢复生成时的原值 |
| POST | `/data-process/{id}/publish` | 幂等发布为数据集 |
## 3. 配置校验
- `process_type``structured | unstructured | external`
- 数据集划分的 `train + validation + test` 必须等于 100各项为 0100。
- `chunk_size` 为 1632768 token`chunk_overlap` 必须小于
`chunk_size``min_chunk_size` 不得大于 `chunk_size`
- `temperature` 为 02`max_tokens` 为 132768。
- 任务名称在未删除任务中唯一。
- 选择 `generation_model_id` 后,启动生成时校验模型是否存在,并保存不含密钥的
模型版本快照。
- 当前运行库沿用平台现有的单租户模式,不接受客户端提交 tenant/owner/operator
字段,避免伪造隔离上下文;接入平台可信认证上下文后再启用数据库中预留的
tenant/project 字段。
## 4. 格式解析与标准化
上传格式按处理类型约束:
- 结构化数据支持 JSON、JSONL/NDJSON、CSV/TSV 和 XLSX。XLSX 能识别纵向、
横向合并单元格组成的多级表头,并稳定展平为 `销售.Q1` 一类字段;公式只读取
文件中已缓存的计算结果,不在服务端执行。
- 非结构化数据支持 UTF-8/UTF-8 BOM 的 TXT、Markdown、JSON/JSONL以及
文本型 PDF、DOCX 和 PPTX。PDF 按页抽取文本DOCX 抽取段落与表格PPTX
抽取幻灯片文本与表格,随后统一进入切片算法。
- 旧版二进制 DOC、XLS、PPT 不直接解析,返回 415 并提示分别转换为
DOCX、XLSX、PPTX。
- 扫描 PDF 没有文本层时明确提示扫描版或图片型 PDF 不支持。加密、损坏或
超出页数/工作表/行列/解压规模限制的文件整批拒绝。
现代 Office 文件在交给解析库前检查 ZIP 成员路径、重复成员、加密标记、活动
XML、单成员大小、总解压大小和压缩比避免路径穿越、实体扩展与 ZIP bomb。
结构化选项按固定顺序执行,关闭某项时不会隐式执行对应业务变换:
1. `detect_structure`展平嵌套对象XLSX 上传解析阶段识别合并单元格和多级表头。
2. `normalize_format`:字段名转 snake_case执行 Unicode NFKC、换行和容器值规范化
输出键顺序稳定的 canonical JSON账号、邮编等字符串不会转成数值。
3. `clean_invalid`:删除全空列和全空记录;存在 `id/uuid/key/code/*_id` 身份字段时,
删除身份字段残缺的行,但不会因备注等可选字段为空误删有效记录。
4. `filter_anomaly`:仅对不少于 8 个样本的非身份数值字段使用 Tukey IQR 过滤离群行,
同时过滤明确乱码、不可打印或极端超长文本;小样本和 ID 字段不参与统计过滤。
5. `deduplicate`:先按整行 canonical JSON 精确去重,再按非空
`id/uuid/key/code/*_id` 字段稳定保留首条;空关键值互不视为重复。
6. `desensitize`:对结构化姓名字段和正文中的高置信上下文姓名、邮箱、手机号、
身份证号进行不可逆掩码,并分别记录命中数。
非结构化“智能预处理”由六个可独立执行的底层选项组成:
- `clean_invalid_content` 删除确定为空、不可读或纯重复符号的无效块。
- `detect_document_structure` 识别 Markdown、中文章节和数字标题切片不跨章节
并在预览质量详情中保存 `heading_path`
- `merge_short_content` 在同一章节中合并短块,合并后不突破 `chunk_size`
- `filter_low_quality` 在生成前过滤乱码、不可打印、重复或极端超长内容。
- `deduplicate_content` 先精确去重,再对足够长的内容进行保守近重复判断;数字或
否定含义变化时始终保留。
- `preserve_context` 才启用相邻切片 overlap关闭时切片不共享正文上下文且上下文
永不跨文件或章节。
表格、围栏代码块和连续列表保护是三个独立参数。启用时切点避开相应 Markdown
块,关闭时允许按正常长度切分。
脱敏是不可逆掩码:
- 邮箱:`[EMAIL]`
- 中国大陆手机号:`[PHONE]`
- 18 位身份证号:`[ID_CARD]`
- 高置信姓名:`[NAME]`
源文件原文与脱敏后的预览分开保存,结果不得反向覆盖源文件。
## 5. 切片算法
首阶段只提供三种切片策略:
- `structure` 先识别 Markdown、中文章节及编号标题再由 LlamaIndex
`SentenceSplitter` 在章节内按段落和中英文句界限长;章节之间不共享 overlap。
- `fixed` 使用 LlamaIndex `TokenTextSplitter` 按目标 token 窗口切分。
- `custom` 使用用户给定分隔符,在找不到合适分隔点时回退到固定窗口。
不提供 `semantic` 和旧 `heading` 配置;创建或更新任务时传入这些值会直接拒绝。
LlamaIndex 只负责通用切分,原文 offset、行号、标题路径和 Markdown 保护块仍由
项目适配层统一维护。
首版使用可替换的确定性 token 估算器,中文字符、标点和英文词分别计数;
所有偏移以 Python/JavaScript 都能稳定表达的 Unicode 文本偏移为准。
算法必须满足:
- 每轮游标严格前进,异常分隔符不能产生死循环。
- overlap 是最大重叠量,尾部过短切片合并到上一片。
- 代码块、Markdown 表格和连续列表在启用保护时不从中间切开。
- 每个预览条目记录 `source_file_id`、字符偏移、起止行、token 数和算法版本。
## 6. 生成与质量评分
结构化记录优先识别以下字段:
1. `instruction/input/output`
2. `question/context/answer`
3. `prompt/input/response`
已有标准字段时只做标准化;需要语义生成时调用所选模型的 OpenAI 兼容接口,
并固化模型 ID、模型版本、prompt、temperature、max_tokens 和 JSON mode 快照。
模型地址可输入域名、`/v1` 基础地址或完整地址:例如输入
`www.caoxiaozhu.com` 会规范为
`https://www.caoxiaozhu.com/v1/chat/completions`,无需用户手工拼接路径。
单条失败记录为 `invalid`,有限重试耗尽后继续处理下一条,避免整批丢失。
每条结果总分为 0100
```text
总分 = 完整性 35% + 长度合理性 20% + 可读性 20%
+ 来源相关性 15% + 非重复性 10%
```
- instruction 或 output 为空时格式硬失败并标记 `invalid`
- 开启短文本过滤且 output 低于 `min_output_length` 时标记过滤原因。
- 评分详情、命中规则与过滤原因必须落库并返回前端,不只返回一个总分。
## 7. 稳定划分
划分不能依赖结果插入顺序。对每条记录计算:
```text
bucket = SHA256(task_id + ":" + result_id) mod 10000
```
按万分位阈值映射为 `train/validation/test`。同一任务重试、分页或进程重启后,
同一结果仍落入相同 split。
## 8. 发布与来源链路
发布在一个数据库事务中完成:
```text
source_file
→ data_process_task
→ data_process_result
→ dataset
→ dataset_file + dataset_file_version
→ dataset_record
```
只发布 `valid/modified` 且满足质量门槛的结果。输出 JSONL 先计算 checksum
再登记文件版本和记录。发布请求中的 split 会重新进行稳定划分。任务的
`output_dataset_id` 是幂等键;重复调用返回已有数据集,目标数据集若已被外部
删除则解除断链并重新发布。当前运行库只开放 `local` 存储类型,正文保存在
当前平台的 `dataset_files.content`,不虚假宣称已上传 MinIO 或云存储。
## 9. 安全边界
- 文件名只保留 basename响应不返回宿主机绝对路径。
- 上传限制单文件、批次文件数与批次总大小,解析采用有界读取。
- 外部数据源凭据不写日志、不进入 localStorage、不在详情接口回显。
- 外部 PostgreSQL 只允许单条 `SELECT/WITH`、只读事务、5 秒连接超时、
30 秒语句超时和 50 MiB 响应上限;默认阻止回环、链路本地及私网地址。
可信内网部署必须显式设置 `DATA_PROCESS_ALLOW_PRIVATE_EXTERNAL_DB=true`
- SQL 迁移独立存放,应用启动不会隐式修改当前远程数据库。
## 10. 迁移边界
`backend/app/db/sql/002_data_process.sql` 只面向当前运行脚本
`001_platform_runtime.sql` 的 TEXT/最小表模型。它会在执行前检查
`datasets.id` 类型;若检测到 `docs/postgres-schema.sql` 的 UUID/JSONB 目标模型,
会直接失败而不是进行一半成功、一半失败的危险迁移。目标模型后续应由独立
Alembic 迁移和对应存储实现承接。
`DataProcessStore.ensure_schema()` 仅供受控管理命令显式调用API 路由和应用启动
均不会自动执行该迁移。本次开发和测试没有修改任何远程数据库。
在已加载 `DATABASE_URL` 的终端中可先只读检查:
```bash
cd backend
.venv/bin/python -m app.modules.data_process.schema_cli --check
```
确认目标主机和数据库名称无误后,才显式执行:
```bash
cd backend
.venv/bin/python -m app.modules.data_process.schema_cli --apply --yes
```
命令输出只显示主机、端口和数据库名,不显示用户名或密码。