# 数据处理接口与算法设计 本文是 `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}` | 删除预览条目 | 上传批次先全部完成有界读取、UTF-8 解码和解析,再在单个事务中登记;任一文件 为空、超限、重复或格式非法时整批不落库。响应不回传整个文件,只返回文件 ID、 格式、字节数、记录数和 SHA-256。二进制文档必须由对应解析器显式处理; 不支持的格式返回 415,绝不能静默替换成示例正文。 ### 结果与发布 | 方法 | 路径 | 说明 | | --- | --- | --- | | 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. 格式解析与标准化 首版文本解析支持 UTF-8/UTF-8 BOM 的 TXT、Markdown、CSV、JSON、JSONL。 后续 PDF、DOCX、XLSX 必须接入明确的解析器后再开放前端选择。 处理顺序固定为: 1. 严格解码并识别格式;非法字节或畸形 JSON/JSONL 返回可定位错误。 2. Unicode NFKC 标准化,统一 CRLF,清理 NUL、零宽字符和不可读控制字符。 3. 结构化数据转为 canonical JSON;非结构化数据保留 Markdown 语义块。 4. 若启用脱敏,替换邮箱、手机号和身份证号,同时保存各类型命中计数。 5. 使用标准化正文的 SHA-256 去重;重复条目不进入生成阶段并计入 `duplicate_count`。 脱敏是不可逆掩码: - 邮箱:`[EMAIL]` - 中国大陆手机号:`[PHONE]` - 18 位身份证号:`[ID_CARD]` 源文件原文与脱敏后的预览分开保存,结果不得反向覆盖源文件。 ## 5. 切片算法 `fixed` 按目标 token 窗口切分;`semantic` 优先在空行、换行和中英文句末 标点结束;`heading` 进一步优先在 Markdown/中文章节标题之前结束; `custom` 使用用户给定分隔符。 首版使用可替换的确定性 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 ``` 命令输出只显示主机、端口和数据库名,不显示用户名或密码。