停止追踪pycache
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -4,6 +4,7 @@ __pycache__/
|
||||
*.pyo
|
||||
*.pyd
|
||||
.Python
|
||||
*$py.class
|
||||
*.egg-info/
|
||||
dist/
|
||||
build/
|
||||
|
||||
419
README.md
419
README.md
@@ -0,0 +1,419 @@
|
||||
# label_ai_service
|
||||
|
||||
`label_ai_service` 是知识图谱智能标注平台的 AI 计算服务,基于 FastAPI 提供独立部署的推理与预处理能力。它不直接访问数据库,而是通过 ZhipuAI GLM 系列模型完成结构化抽取,通过 RustFS 读写原始文件和处理结果,并通过 HTTP 回调把异步视频任务结果通知上游后端。
|
||||
|
||||
当前服务覆盖 6 类核心能力:
|
||||
|
||||
- 文本三元组提取,支持 `TXT`、`PDF`、`DOCX`
|
||||
- 图像四元组提取,并自动裁剪 `bbox` 区域图
|
||||
- 视频抽帧,支持固定间隔和近似关键帧两种模式
|
||||
- 视频转文本,将视频片段描述输出为文本文件
|
||||
- 基于文本或图片证据生成问答对
|
||||
- 向 ZhipuAI 提交微调任务并查询状态
|
||||
|
||||
## 适用场景
|
||||
|
||||
这个服务适合作为 `label-backend` 的 AI 能力侧车服务,也可以单独运行,用于验证文件解析、图像理解、视频预处理和问答生成流程。
|
||||
|
||||
典型调用链如下:
|
||||
|
||||
1. Java 后端把原始文本、图片或视频上传到 RustFS。
|
||||
2. Java 后端调用 `label_ai_service` 的 REST API。
|
||||
3. AI 服务从 RustFS 读取文件,调用 GLM 模型做抽取或生成。
|
||||
4. 结果以 JSON 返回,或写回 RustFS 后通过回调通知上游。
|
||||
|
||||
## 功能概览
|
||||
|
||||
| 能力 | 接口 | 说明 |
|
||||
|---|---|---|
|
||||
| 健康检查 | `GET /health` | 用于容器存活探测和联调自检 |
|
||||
| 文本三元组提取 | `POST /api/v1/text/extract` | 从文档中提取 `subject / predicate / object / source_snippet / source_offset` |
|
||||
| 图像四元组提取 | `POST /api/v1/image/extract` | 从图片中提取 `subject / predicate / object / qualifier / bbox`,并输出裁剪图路径 |
|
||||
| 视频抽帧 | `POST /api/v1/video/extract-frames` | 异步抽取视频帧,结果通过回调返回 |
|
||||
| 视频转文本 | `POST /api/v1/video/to-text` | 异步抽样视频代表帧,生成中文描述文本并上传到对象存储 |
|
||||
| 文本问答生成 | `POST /api/v1/qa/gen-text` | 基于三元组和原文证据生成问答对 |
|
||||
| 图像问答生成 | `POST /api/v1/qa/gen-image` | 基于裁剪图和四元组生成问答对 |
|
||||
| 微调任务提交 | `POST /api/v1/finetune/start` | 向 ZhipuAI 提交微调任务 |
|
||||
| 微调状态查询 | `GET /api/v1/finetune/status/{job_id}` | 查询微调任务状态和进度 |
|
||||
|
||||
## 技术栈
|
||||
|
||||
- Python 3.12
|
||||
- FastAPI
|
||||
- Pydantic v2
|
||||
- ZhipuAI Python SDK
|
||||
- boto3
|
||||
- OpenCV
|
||||
- pdfplumber
|
||||
- python-docx
|
||||
- httpx
|
||||
- pytest / pytest-asyncio
|
||||
|
||||
## 架构说明
|
||||
|
||||
### 外部依赖
|
||||
|
||||
- ZhipuAI
|
||||
- 文本与多模态推理
|
||||
- 微调任务提交与查询
|
||||
- RustFS 或任意 S3 兼容对象存储
|
||||
- 原始文件读取
|
||||
- 裁剪图、视频帧、视频描述文本写回
|
||||
- 上游回调接口
|
||||
- 视频任务完成后接收结果
|
||||
|
||||
### 处理边界
|
||||
|
||||
- 服务本身不负责文件上传,也不维护任务状态库。
|
||||
- 文本、图像接口是同步返回。
|
||||
- 视频接口是异步返回 `202 Accepted`,真实处理结果走回调。
|
||||
- 服务默认不做鉴权,通常由上游网关或后端负责访问控制。
|
||||
|
||||
## 项目结构
|
||||
|
||||
```text
|
||||
label_ai_service/
|
||||
├── app/
|
||||
│ ├── main.py
|
||||
│ ├── clients/
|
||||
│ │ ├── llm/
|
||||
│ │ └── storage/
|
||||
│ ├── core/
|
||||
│ ├── models/
|
||||
│ ├── routers/
|
||||
│ └── services/
|
||||
├── docs/
|
||||
│ └── superpowers/
|
||||
├── specs/
|
||||
├── tests/
|
||||
├── config.yaml
|
||||
├── .env
|
||||
├── Dockerfile
|
||||
├── docker-compose.yml
|
||||
├── requirements.txt
|
||||
└── README.md
|
||||
```
|
||||
|
||||
目录职责:
|
||||
|
||||
- `app/main.py`
|
||||
- FastAPI 应用入口,注册中间件、异常处理器和所有路由
|
||||
- `app/clients`
|
||||
- 第三方依赖适配层,当前包含 ZhipuAI 和 RustFS
|
||||
- `app/services`
|
||||
- 业务核心逻辑,负责文件解析、提示词拼装、结果转换和异步任务处理
|
||||
- `app/routers`
|
||||
- HTTP 接口层
|
||||
- `app/models`
|
||||
- 请求与响应模型
|
||||
- `app/core`
|
||||
- 配置、日志、中间件、异常等通用模块
|
||||
- `tests`
|
||||
- Router、Service、Config 和 Client 的测试
|
||||
|
||||
## 配置说明
|
||||
|
||||
配置采用 `config.yaml + .env` 分层方式:
|
||||
|
||||
- `config.yaml`
|
||||
- 存放稳定、可提交的结构化配置
|
||||
- `.env`
|
||||
- 存放密钥和环境差异项
|
||||
|
||||
环境变量会覆盖 `config.yaml` 中的同名配置。
|
||||
|
||||
### config.yaml
|
||||
|
||||
当前项目默认配置如下:
|
||||
|
||||
```yaml
|
||||
server:
|
||||
port: 8000
|
||||
log_level: INFO
|
||||
|
||||
storage:
|
||||
buckets:
|
||||
source_data: "source-data"
|
||||
finetune_export: "finetune-export"
|
||||
|
||||
backend: {}
|
||||
|
||||
video:
|
||||
frame_sample_count: 8
|
||||
max_file_size_mb: 200
|
||||
keyframe_diff_threshold: 30.0
|
||||
|
||||
models:
|
||||
default_text: "glm-4-flash"
|
||||
default_vision: "glm-4v-flash"
|
||||
```
|
||||
|
||||
### .env
|
||||
|
||||
建议至少配置这些变量:
|
||||
|
||||
| 变量名 | 必填 | 说明 |
|
||||
|---|---|---|
|
||||
| `ZHIPUAI_API_KEY` | 是 | ZhipuAI API Key |
|
||||
| `STORAGE_ACCESS_KEY` | 是 | RustFS/S3 Access Key |
|
||||
| `STORAGE_SECRET_KEY` | 是 | RustFS/S3 Secret Key |
|
||||
| `STORAGE_ENDPOINT` | 是 | RustFS/S3 Endpoint,例如 `http://rustfs:9000` |
|
||||
| `BACKEND_CALLBACK_URL` | 否 | 视频异步任务回调地址 |
|
||||
| `LOG_LEVEL` | 否 | 日志级别,默认 `INFO` |
|
||||
| `MAX_VIDEO_SIZE_MB` | 否 | 覆盖视频大小上限 |
|
||||
|
||||
`.env` 示例:
|
||||
|
||||
```ini
|
||||
ZHIPUAI_API_KEY=your-zhipuai-api-key-here
|
||||
STORAGE_ACCESS_KEY=your-storage-access-key
|
||||
STORAGE_SECRET_KEY=your-storage-secret-key
|
||||
STORAGE_ENDPOINT=http://rustfs:9000
|
||||
BACKEND_CALLBACK_URL=http://label-backend:8080/api/ai/callback
|
||||
LOG_LEVEL=INFO
|
||||
```
|
||||
|
||||
## 本地运行
|
||||
|
||||
### 方式一:直接运行
|
||||
|
||||
```bash
|
||||
python -m venv .venv
|
||||
source .venv/bin/activate
|
||||
pip install -r requirements.txt
|
||||
python -m uvicorn app.main:app --host 0.0.0.0 --port 8000 --reload
|
||||
```
|
||||
|
||||
Windows PowerShell 可以使用:
|
||||
|
||||
```powershell
|
||||
python -m venv .venv
|
||||
.\.venv\Scripts\Activate.ps1
|
||||
pip install -r requirements.txt
|
||||
python -m uvicorn app.main:app --host 0.0.0.0 --port 8000 --reload
|
||||
```
|
||||
|
||||
启动后访问:
|
||||
|
||||
- Swagger UI: `http://localhost:8000/docs`
|
||||
- 健康检查: `http://localhost:8000/health`
|
||||
|
||||
### 方式二:Docker Compose
|
||||
|
||||
项目自带的 Compose 文件会启动:
|
||||
|
||||
- `ai-service`
|
||||
- `rustfs`
|
||||
|
||||
启动命令:
|
||||
|
||||
```bash
|
||||
docker compose up --build
|
||||
```
|
||||
|
||||
如果你要联调视频异步任务,请确保 `BACKEND_CALLBACK_URL` 指向一个可访问的后端地址。否则任务本身会继续处理,但回调会失败并记录错误日志。
|
||||
|
||||
## API 使用示例
|
||||
|
||||
### 1. 健康检查
|
||||
|
||||
```bash
|
||||
curl http://localhost:8000/health
|
||||
```
|
||||
|
||||
返回:
|
||||
|
||||
```json
|
||||
{"status":"ok"}
|
||||
```
|
||||
|
||||
### 2. 文本三元组提取
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:8000/api/v1/text/extract \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"file_path": "text/202404/123.txt",
|
||||
"file_name": "设备规范.txt"
|
||||
}'
|
||||
```
|
||||
|
||||
### 3. 图像四元组提取
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:8000/api/v1/image/extract \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"file_path": "image/202404/456.jpg",
|
||||
"task_id": 789
|
||||
}'
|
||||
```
|
||||
|
||||
### 4. 视频抽帧
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:8000/api/v1/video/extract-frames \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"file_path": "video/202404/001.mp4",
|
||||
"source_id": 10,
|
||||
"job_id": 42,
|
||||
"mode": "interval",
|
||||
"frame_interval": 30
|
||||
}'
|
||||
```
|
||||
|
||||
### 5. 视频转文本
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:8000/api/v1/video/to-text \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"file_path": "video/202404/001.mp4",
|
||||
"source_id": 10,
|
||||
"job_id": 43,
|
||||
"start_sec": 0,
|
||||
"end_sec": 60
|
||||
}'
|
||||
```
|
||||
|
||||
### 6. 文本问答生成
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:8000/api/v1/qa/gen-text \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"items": [
|
||||
{
|
||||
"subject": "变压器",
|
||||
"predicate": "额定电压",
|
||||
"object": "110kV",
|
||||
"source_snippet": "该变压器额定电压为110kV"
|
||||
}
|
||||
]
|
||||
}'
|
||||
```
|
||||
|
||||
### 7. 图像问答生成
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:8000/api/v1/qa/gen-image \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"items": [
|
||||
{
|
||||
"subject": "电缆接头",
|
||||
"predicate": "位于",
|
||||
"object": "配电箱左侧",
|
||||
"cropped_image_path": "crops/1/0.jpg"
|
||||
}
|
||||
]
|
||||
}'
|
||||
```
|
||||
|
||||
### 8. 微调任务提交
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:8000/api/v1/finetune/start \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"jsonl_url": "https://example.com/train.jsonl",
|
||||
"base_model": "glm-4-flash",
|
||||
"hyperparams": {
|
||||
"epochs": 3,
|
||||
"learning_rate": 0.0001
|
||||
}
|
||||
}'
|
||||
```
|
||||
|
||||
## 数据输出约定
|
||||
|
||||
当前服务会主动写入这些派生结果:
|
||||
|
||||
| 类型 | 路径模式 | 说明 |
|
||||
|---|---|---|
|
||||
| 图像裁剪图 | `crops/{task_id}/{index}.jpg` | 图像提取结果的局部证据图 |
|
||||
| 视频抽帧图片 | `frames/{source_id}/{index}.jpg` | 视频帧提取结果 |
|
||||
| 视频文本描述 | `video-text/{source_id}/{timestamp}.txt` | 视频转文本结果 |
|
||||
|
||||
说明:
|
||||
|
||||
- 这些对象默认写入 `storage.buckets.source_data`
|
||||
- 原始文件的上传路径由上游系统决定
|
||||
- 服务不会替上游生成原始文件路径,只消费请求里传入的 `file_path`
|
||||
|
||||
## 日志与错误处理
|
||||
|
||||
### 日志
|
||||
|
||||
日志使用 JSON 格式输出,适合直接接入容器日志平台。请求日志会带上:
|
||||
|
||||
- `method`
|
||||
- `path`
|
||||
- `status`
|
||||
- `duration_ms`
|
||||
|
||||
LLM 调用和后台任务也会输出结构化字段,方便排查接口超时、回调失败和模型解析错误。
|
||||
|
||||
### 错误码
|
||||
|
||||
统一错误返回格式:
|
||||
|
||||
```json
|
||||
{"code":"ERROR_CODE","message":"具体描述"}
|
||||
```
|
||||
|
||||
当前主要错误码:
|
||||
|
||||
| 错误码 | HTTP 状态码 | 含义 |
|
||||
|---|---|---|
|
||||
| `UNSUPPORTED_FILE_TYPE` | 400 | 文本提取文件格式不支持 |
|
||||
| `VIDEO_TOO_LARGE` | 400 | 视频大小超过限制 |
|
||||
| `STORAGE_ERROR` | 502 | 对象存储访问失败 |
|
||||
| `LLM_PARSE_ERROR` | 502 | 模型返回内容无法解析为预期 JSON |
|
||||
| `LLM_CALL_ERROR` | 503 | 模型调用或微调接口调用失败 |
|
||||
| `INTERNAL_ERROR` | 500 | 未捕获异常 |
|
||||
|
||||
## 测试
|
||||
|
||||
安装依赖后可直接运行:
|
||||
|
||||
```bash
|
||||
python -m pytest
|
||||
```
|
||||
|
||||
测试覆盖了这些主要模块:
|
||||
|
||||
- 健康检查接口
|
||||
- 文本、图像、视频、QA、微调路由
|
||||
- 各 Service 的基本成功与异常路径
|
||||
- 配置加载和客户端适配
|
||||
|
||||
## 设计文档
|
||||
|
||||
项目内已有更详细的设计资料,可配合 README 阅读:
|
||||
|
||||
- `docs/superpowers/specs/2026-04-10-ai-service-design.md`
|
||||
- `docs/superpowers/plans/2026-04-10-ai-service-impl.md`
|
||||
- `specs/001-ai-service-requirements/`
|
||||
|
||||
如果你刚接手这个服务,建议阅读顺序是:
|
||||
|
||||
1. 本 README,先搞清楚服务职责、接口和运行方式
|
||||
2. 设计文档,再看架构和设计决策
|
||||
3. `app/services` 与 `tests`,最后进入实现细节
|
||||
|
||||
## 已知约束
|
||||
|
||||
- 文本提取目前只支持 `txt`、`pdf`、`docx`
|
||||
- 视频接口依赖对象存储可读取文件大小
|
||||
- 视频任务状态不持久化在本服务内,由上游系统负责管理
|
||||
- 图像问答采用 base64 内联图片,不依赖外网可访问的 presigned URL
|
||||
- 如果 `.env` 中的回调地址不可达,视频任务会记录错误日志,但不会自动重试
|
||||
|
||||
## 开发建议
|
||||
|
||||
- 新增接口时同步补齐 Pydantic 模型、Router 测试和 README/API 文档
|
||||
- 如果替换模型厂商,优先扩展 `app/clients/llm`
|
||||
- 如果替换存储实现,优先扩展 `app/clients/storage`
|
||||
- 任何输出路径规则变更,都应同步更新 README 和设计文档
|
||||
|
||||
@@ -44,3 +44,7 @@ app.include_router(image.router, prefix="/api/v1")
|
||||
app.include_router(video.router, prefix="/api/v1")
|
||||
app.include_router(qa.router, prefix="/api/v1")
|
||||
app.include_router(finetune.router, prefix="/api/v1")
|
||||
|
||||
if __name__ == "__main__":
|
||||
import uvicorn
|
||||
uvicorn.run(app, host="0.0.0.0", port=8000)
|
||||
|
||||
Reference in New Issue
Block a user