Files
label_ai_service/specs/001-ai-service-requirements/contracts/api.md
wh 092f9dbfc5 docs: add speckit planning artifacts for 001-ai-service-requirements
Generated plan.md, research.md, data-model.md, contracts/api.md,
quickstart.md, and CLAUDE.md agent context via /speckit-plan.
2026-04-10 14:58:13 +08:00

334 lines
7.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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 2xxJSON 响应体
- 错误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`