# API Contract: AI 服务接口定义 **Branch**: `001-ai-service-requirements` | **Date**: 2026-04-10 **Base URL**: `http://ai-service:8000` **API Prefix**: `/api/v1` **Swagger**: `/docs`(FastAPI 自动生成) --- ## 通用约定 ### 请求格式 - 所有请求体:`Content-Type: application/json` - 无认证机制(内网服务,仅 Java 后端调用) ### 响应格式 - 成功:HTTP 2xx,JSON 响应体 - 错误:HTTP 4xx/5xx,统一错误格式: ```json {"code": "ERROR_CODE", "message": "具体描述"} ``` ### 错误码 | HTTP 状态码 | code | 触发条件 | |------------|------|---------| | 400 | UNSUPPORTED_FILE_TYPE | 文件格式不支持(如 .xlsx) | | 400 | VIDEO_TOO_LARGE | 视频文件超过大小上限 | | 502 | STORAGE_ERROR | RustFS 不可达或文件不存在 | | 502 | LLM_PARSE_ERROR | GLM 返回非合法 JSON | | 503 | LLM_CALL_ERROR | GLM API 限流 / 超时 | | 500 | INTERNAL_ERROR | 未捕获异常 | --- ## 端点一览 | 端点 | 方法 | 功能 | 响应码 | |------|------|------|--------| | `/health` | GET | 健康检查 | 200 | | `/api/v1/text/extract` | POST | 文档三元组提取 | 200 | | `/api/v1/image/extract` | POST | 图像四元组提取 | 200 | | `/api/v1/video/extract-frames` | POST | 视频帧提取(异步) | 202 | | `/api/v1/video/to-text` | POST | 视频转文本(异步) | 202 | | `/api/v1/qa/gen-text` | POST | 文本问答对生成 | 200 | | `/api/v1/qa/gen-image` | POST | 图像问答对生成 | 200 | | `/api/v1/finetune/start` | POST | 提交微调任务 | 200 | | `/api/v1/finetune/status/{jobId}` | GET | 查询微调状态 | 200 | --- ## 端点详情 ### GET /health 健康检查端点,无需认证,无请求体。 **响应(200 OK)**: ```json {"status": "ok"} ``` --- ### POST /api/v1/text/extract 从存储中指定路径的文档提取文本三元组。 **请求体**: ```json { "file_path": "text/202404/123.txt", "file_name": "设备规范.txt", "model": "glm-4-flash", "prompt_template": "..." } ``` | 字段 | 类型 | 必填 | 说明 | |------|------|------|------| | file_path | string | 是 | RustFS 中的文件路径 | | file_name | string | 是 | 带扩展名的文件名(用于判断格式) | | model | string | 否 | 模型名,默认使用 config 中的 default_text | | prompt_template | string | 否 | 自定义提示词,不传使用内置模板 | **支持格式**: `.txt`, `.pdf`, `.docx` **响应(200 OK)**: ```json { "items": [ { "subject": "变压器", "predicate": "额定电压", "object": "110kV", "source_snippet": "该变压器额定电压为110kV", "source_offset": {"start": 120, "end": 150} } ] } ``` --- ### POST /api/v1/image/extract 从存储中指定路径的图片提取知识四元组,并自动裁剪 bbox 区域。 **请求体**: ```json { "file_path": "image/202404/456.jpg", "task_id": 789, "model": "glm-4v-flash", "prompt_template": "..." } ``` | 字段 | 类型 | 必填 | 说明 | |------|------|------|------| | file_path | string | 是 | RustFS 中的图片路径 | | task_id | int | 是 | 标注任务 ID(用于构造裁剪图存储路径) | | model | string | 否 | 默认使用 config 中的 default_vision | | prompt_template | string | 否 | 自定义提示词 | **响应(200 OK)**: ```json { "items": [ { "subject": "电缆接头", "predicate": "位于", "object": "配电箱左侧", "qualifier": "2024年检修现场", "bbox": {"x": 10, "y": 20, "w": 100, "h": 80}, "cropped_image_path": "crops/789/0.jpg" } ] } ``` --- ### POST /api/v1/video/extract-frames 触发视频帧提取后台任务,立即返回。 **请求体**: ```json { "file_path": "video/202404/001.mp4", "source_id": 10, "job_id": 42, "mode": "interval", "frame_interval": 30 } ``` | 字段 | 类型 | 必填 | 说明 | |------|------|------|------| | file_path | string | 是 | RustFS 中的视频路径 | | source_id | int | 是 | 原始资料 ID(用于构造帧存储路径) | | job_id | int | 是 | 由 Java 后端分配的任务 ID | | mode | string | 否 | `interval`(默认)或 `keyframe` | | frame_interval | int | 否 | interval 模式专用,按帧数步进,默认 30 | **响应(202 Accepted)**: ```json {"message": "任务已接受,后台处理中", "job_id": 42} ``` **完成后回调 Java 后端**(POST `{BACKEND_CALLBACK_URL}`): ```json { "job_id": 42, "status": "SUCCESS", "frames": [ {"frame_index": 0, "time_sec": 0.0, "frame_path": "frames/10/0.jpg"} ], "error_message": null } ``` --- ### POST /api/v1/video/to-text 触发视频片段转文字后台任务,立即返回。 **请求体**: ```json { "file_path": "video/202404/001.mp4", "source_id": 10, "job_id": 43, "start_sec": 0, "end_sec": 120, "model": "glm-4v-flash", "prompt_template": "..." } ``` | 字段 | 类型 | 必填 | 说明 | |------|------|------|------| | file_path | string | 是 | RustFS 中的视频路径 | | source_id | int | 是 | 原始资料 ID | | job_id | int | 是 | 由 Java 后端分配的任务 ID | | start_sec | float | 是 | 分析起始时间(秒) | | end_sec | float | 是 | 分析结束时间(秒) | | model | string | 否 | 默认使用 config 中的 default_vision | | prompt_template | string | 否 | 自定义提示词 | **响应(202 Accepted)**: ```json {"message": "任务已接受,后台处理中", "job_id": 43} ``` **完成后回调 Java 后端**(POST `{BACKEND_CALLBACK_URL}`): ```json { "job_id": 43, "status": "SUCCESS", "output_path": "video-text/10/1712800000.txt", "error_message": null } ``` --- ### POST /api/v1/qa/gen-text 基于文本三元组批量生成候选问答对。 **请求体**: ```json { "items": [ { "subject": "变压器", "predicate": "额定电压", "object": "110kV", "source_snippet": "该变压器额定电压为110kV" } ], "model": "glm-4-flash", "prompt_template": "..." } ``` **响应(200 OK)**: ```json { "pairs": [ {"question": "变压器的额定电压是多少?", "answer": "该变压器额定电压为110kV。"} ] } ``` --- ### POST /api/v1/qa/gen-image 基于图像四元组生成候选图文问答对。图片由 AI 服务从存储自动获取,调用方只需提供路径。 **请求体**: ```json { "items": [ { "subject": "电缆接头", "predicate": "位于", "object": "配电箱左侧", "qualifier": "2024年检修现场", "cropped_image_path": "crops/789/0.jpg" } ], "model": "glm-4v-flash", "prompt_template": "..." } ``` **响应(200 OK)**: ```json { "pairs": [ { "question": "图中电缆接头位于何处?", "answer": "图中电缆接头位于配电箱左侧。", "image_path": "crops/789/0.jpg" } ] } ``` --- ### POST /api/v1/finetune/start 向 ZhipuAI 提交微调任务。 **请求体**: ```json { "jsonl_url": "https://rustfs.example.com/finetune-export/export/xxx.jsonl", "base_model": "glm-4-flash", "hyperparams": {"learning_rate": 1e-4, "epochs": 3} } ``` **响应(200 OK)**: ```json {"job_id": "glm-ft-xxxxxx"} ``` --- ### GET /api/v1/finetune/status/{jobId} 查询微调任务状态。 **路径参数**: `jobId` — 微调任务 ID(由 `/finetune/start` 返回) **响应(200 OK)**: ```json { "job_id": "glm-ft-xxxxxx", "status": "RUNNING", "progress": 45, "error_message": null } ``` `status` 取值: `RUNNING` | `SUCCESS` | `FAILED`