# Data Model: AI 服务 **Branch**: `001-ai-service-requirements` | **Date**: 2026-04-10 --- ## 实体定义 ### TripleItem(文本三元组) 从文档中提取的一条知识关系。 | 字段 | 类型 | 约束 | 说明 | |------|------|------|------| | subject | string | 非空 | 主语实体 | | predicate | string | 非空 | 谓语/关系 | | object | string | 非空 | 宾语实体 | | source_snippet | string | 非空 | 原文中的证据片段(直接引用) | | source_offset.start | int | ≥0 | 证据片段在全文中的起始字符偏移 | | source_offset.end | int | >start | 证据片段在全文中的结束字符偏移 | **状态转换**: 无(只读输出) --- ### QuadrupleItem(图像四元组) 从图像中提取的一条知识关系,带图像位置信息。 | 字段 | 类型 | 约束 | 说明 | |------|------|------|------| | subject | string | 非空 | 主体实体 | | predicate | string | 非空 | 关系/属性 | | object | string | 非空 | 客体实体 | | qualifier | string | 可为空 | 修饰信息(时间、条件、场景) | | bbox.x | int | ≥0 | 边界框左上角 x 像素坐标 | | bbox.y | int | ≥0 | 边界框左上角 y 像素坐标 | | bbox.w | int | >0 | 边界框宽度(像素) | | bbox.h | int | >0 | 边界框高度(像素) | | cropped_image_path | string | 非空 | 裁剪图在 RustFS 中的存储路径 | **派生规则**: `cropped_image_path = "crops/{task_id}/{item_index}.jpg"`,由 image_service 自动生成并上传 --- ### QAPair(文本问答对) 由文本三元组生成的训练候选问答对。 | 字段 | 类型 | 约束 | 说明 | |------|------|------|------| | question | string | 非空 | 问题文本 | | answer | string | 非空 | 答案文本 | --- ### ImageQAPair(图像问答对) 由图像四元组生成的训练候选图文问答对。 | 字段 | 类型 | 约束 | 说明 | |------|------|------|------| | question | string | 非空 | 问题文本 | | answer | string | 非空 | 答案文本 | | image_path | string | 非空 | 对应裁剪图的存储路径(来源于 QuadrupleItem.cropped_image_path) | --- ### FrameInfo(视频帧信息) 视频帧提取任务中单帧的元数据。 | 字段 | 类型 | 约束 | 说明 | |------|------|------|------| | frame_index | int | ≥0 | 帧在视频中的原始帧序号 | | time_sec | float | ≥0.0 | 帧对应的时间点(秒) | | frame_path | string | 非空 | 帧图在 RustFS 中的存储路径 | **派生规则**: `frame_path = "frames/{source_id}/{upload_index}.jpg"` --- ### VideoJobCallback(视频任务回调) 异步视频任务完成后发送给 Java 后端的通知载荷。 | 字段 | 类型 | 约束 | 说明 | |------|------|------|------| | job_id | int | 非空 | 由 Java 后端分配的任务 ID | | status | string | SUCCESS \| FAILED | 任务最终状态 | | frames | FrameInfo[] \| null | 仅帧提取时非 null | 提取的帧列表(可为空列表) | | output_path | string \| null | 仅视频转文本时非 null | 输出文字描述的存储路径 | | error_message | string \| null | 仅 FAILED 时非 null | 错误描述 | --- ### FinetuneJob(微调任务) 微调任务的状态快照。 | 字段 | 类型 | 约束 | 说明 | |------|------|------|------| | job_id | string | 非空 | 由 ZhipuAI 平台分配的任务 ID(如 "glm-ft-xxxxxx") | | status | string | RUNNING \| SUCCESS \| FAILED | 当前状态 | | progress | int \| null | 0-100 \| null | 完成百分比(ZhipuAI 支持时) | | error_message | string \| null | 仅 FAILED 时非 null | 错误描述 | **状态映射**: ``` ZhipuAI "running" → RUNNING ZhipuAI "succeeded" → SUCCESS ZhipuAI "failed" → FAILED 其他 → RUNNING(保守处理) ``` --- ## RustFS 存储路径规范 | 资源类型 | 存储桶 | 路径格式 | |----------|--------|----------| | 上传文本文件 | `source-data` | `text/{年月}/{source_id}.txt` | | 上传图片 | `source-data` | `image/{年月}/{source_id}.jpg` | | 上传视频 | `source-data` | `video/{年月}/{source_id}.mp4` | | 视频帧图 | `source-data` | `frames/{source_id}/{upload_index}.jpg` | | 视频转译文本 | `source-data` | `video-text/{source_id}/{timestamp}.txt` | | 图像/帧 bbox 裁剪图 | `source-data` | `crops/{task_id}/{item_index}.jpg` | | 导出 JSONL 文件 | `finetune-export` | `export/{batchUuid}.jsonl` | --- ## 配置模型 ### config.yaml(非敏感,提交 git) ```yaml server: port: 8000 log_level: INFO storage: buckets: source_data: "source-data" finetune_export: "finetune-export" backend: {} # callback_url 由 .env 注入 video: frame_sample_count: 8 # 视频转文本时均匀采样帧数 max_file_size_mb: 200 # 视频大小上限(可通过 MAX_VIDEO_SIZE_MB 覆盖) models: default_text: "glm-4-flash" default_vision: "glm-4v-flash" ``` ### 环境变量覆盖映射 | 环境变量 | YAML 路径 | 说明 | |----------|-----------|------| | ZHIPUAI_API_KEY | zhipuai.api_key | 必填 | | STORAGE_ACCESS_KEY | storage.access_key | 必填 | | STORAGE_SECRET_KEY | storage.secret_key | 必填 | | STORAGE_ENDPOINT | storage.endpoint | RustFS 地址 | | BACKEND_CALLBACK_URL | backend.callback_url | Java 后端回调接口 | | LOG_LEVEL | server.log_level | 日志级别 | | MAX_VIDEO_SIZE_MB | video.max_file_size_mb | 视频大小上限 |