Generated plan.md, research.md, data-model.md, contracts/api.md, quickstart.md, and CLAUDE.md agent context via /speckit-plan.
334 lines
7.4 KiB
Markdown
334 lines
7.4 KiB
Markdown
# 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`
|