2026-07-23 15:10:13 +08:00
|
|
|
|
# 数据处理接口与算法设计
|
|
|
|
|
|
|
|
|
|
|
|
本文是 `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}` | 删除预览条目 |
|
|
|
|
|
|
|
2026-07-24 11:28:07 +08:00
|
|
|
|
上传批次先全部完成有界读取和解析,再在单个事务中登记;任一文件为空、超限、
|
|
|
|
|
|
重复或格式非法时整批不落库,暂存原件也会一并清理。响应不回传整个文件,只返回
|
|
|
|
|
|
逻辑对象引用、文件 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` 导致的源文件
|
|
|
|
|
|
软删除按留存数据处理,当前版本不自动物理清除。
|
2026-07-23 15:10:13 +08:00
|
|
|
|
|
|
|
|
|
|
### 结果与发布
|
|
|
|
|
|
|
|
|
|
|
|
| 方法 | 路径 | 说明 |
|
|
|
|
|
|
| --- | --- | --- |
|
|
|
|
|
|
| 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,各项为 0~100。
|
|
|
|
|
|
- `chunk_size` 为 16~32768 token;`chunk_overlap` 必须小于
|
|
|
|
|
|
`chunk_size`;`min_chunk_size` 不得大于 `chunk_size`。
|
|
|
|
|
|
- `temperature` 为 0~2,`max_tokens` 为 1~32768。
|
|
|
|
|
|
- 任务名称在未删除任务中唯一。
|
|
|
|
|
|
- 选择 `generation_model_id` 后,启动生成时校验模型是否存在,并保存不含密钥的
|
|
|
|
|
|
模型版本快照。
|
|
|
|
|
|
- 当前运行库沿用平台现有的单租户模式,不接受客户端提交 tenant/owner/operator
|
|
|
|
|
|
字段,避免伪造隔离上下文;接入平台可信认证上下文后再启用数据库中预留的
|
|
|
|
|
|
tenant/project 字段。
|
|
|
|
|
|
|
|
|
|
|
|
## 4. 格式解析与标准化
|
|
|
|
|
|
|
2026-07-24 11:28:07 +08:00
|
|
|
|
上传格式按处理类型约束:
|
|
|
|
|
|
|
|
|
|
|
|
- 结构化数据支持 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 没有文本层时明确提示需要 OCR;当前流程不执行 OCR。加密、损坏或
|
|
|
|
|
|
超出页数/工作表/行列/解压规模限制的文件整批拒绝。
|
|
|
|
|
|
|
|
|
|
|
|
现代 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
|
|
|
|
|
|
块,关闭时允许按正常长度切分。
|
2026-07-23 15:10:13 +08:00
|
|
|
|
|
|
|
|
|
|
脱敏是不可逆掩码:
|
|
|
|
|
|
|
|
|
|
|
|
- 邮箱:`[EMAIL]`
|
|
|
|
|
|
- 中国大陆手机号:`[PHONE]`
|
|
|
|
|
|
- 18 位身份证号:`[ID_CARD]`
|
2026-07-24 11:28:07 +08:00
|
|
|
|
- 高置信姓名:`[NAME]`
|
2026-07-23 15:10:13 +08:00
|
|
|
|
|
|
|
|
|
|
源文件原文与脱敏后的预览分开保存,结果不得反向覆盖源文件。
|
|
|
|
|
|
|
|
|
|
|
|
## 5. 切片算法
|
|
|
|
|
|
|
2026-07-24 11:28:07 +08:00
|
|
|
|
首阶段只提供三种切片策略:
|
|
|
|
|
|
|
|
|
|
|
|
- `structure` 先识别 Markdown、中文章节及编号标题,再由 LlamaIndex
|
|
|
|
|
|
`SentenceSplitter` 在章节内按段落和中英文句界限长;章节之间不共享 overlap。
|
|
|
|
|
|
- `fixed` 使用 LlamaIndex `TokenTextSplitter` 按目标 token 窗口切分。
|
|
|
|
|
|
- `custom` 使用用户给定分隔符,在找不到合适分隔点时回退到固定窗口。
|
|
|
|
|
|
|
|
|
|
|
|
不提供 `semantic` 和旧 `heading` 配置;创建或更新任务时传入这些值会直接拒绝。
|
|
|
|
|
|
LlamaIndex 只负责通用切分,原文 offset、行号、标题路径和 Markdown 保护块仍由
|
|
|
|
|
|
项目适配层统一维护。
|
2026-07-23 15:10:13 +08:00
|
|
|
|
|
|
|
|
|
|
首版使用可替换的确定性 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`,有限重试耗尽后继续处理下一条,避免整批丢失。
|
|
|
|
|
|
|
|
|
|
|
|
每条结果总分为 0~100:
|
|
|
|
|
|
|
|
|
|
|
|
```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
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
命令输出只显示主机、端口和数据库名,不显示用户名或密码。
|