docs: apply eng review findings to design doc and impl plan
Architecture fixes: - Image QA: presigned URL → base64 (RustFS is internal, GLM-4V is cloud) - Add GET /health endpoint + Docker healthcheck - Video size limit: add get_object_size() to StorageClient ABC, check before background task - Video size configurable via MAX_VIDEO_SIZE_MB env var (no image rebuild needed) - Fix image_service.py except clause redundancy (Exception absorbs KeyError/TypeError) Config additions: - video.max_file_size_mb: 200 in config.yaml - MAX_VIDEO_SIZE_MB env override in _ENV_OVERRIDES
This commit is contained in:
@@ -125,6 +125,7 @@ backend: {} # callback_url 由 .env 注入
|
||||
|
||||
video:
|
||||
frame_sample_count: 8 # 视频转文本时均匀抽取的代表帧数
|
||||
max_file_size_mb: 200 # 视频文件大小上限(超过则拒绝,防止 OOM)
|
||||
|
||||
models:
|
||||
default_text: "glm-4-flash"
|
||||
@@ -139,6 +140,7 @@ STORAGE_ACCESS_KEY=minioadmin
|
||||
STORAGE_SECRET_KEY=minioadmin
|
||||
STORAGE_ENDPOINT=http://rustfs:9000
|
||||
BACKEND_CALLBACK_URL=http://backend:8080/internal/video-job/callback
|
||||
# MAX_VIDEO_SIZE_MB=200 # 可选,覆盖 config.yaml 中的视频大小上限
|
||||
```
|
||||
|
||||
### 3.4 config 模块实现
|
||||
@@ -154,12 +156,13 @@ _ROOT = Path(__file__).parent.parent.parent
|
||||
|
||||
# 环境变量 → YAML 路径映射
|
||||
_ENV_OVERRIDES = {
|
||||
"ZHIPUAI_API_KEY": ["zhipuai", "api_key"],
|
||||
"STORAGE_ACCESS_KEY": ["storage", "access_key"],
|
||||
"STORAGE_SECRET_KEY": ["storage", "secret_key"],
|
||||
"STORAGE_ENDPOINT": ["storage", "endpoint"],
|
||||
"BACKEND_CALLBACK_URL": ["backend", "callback_url"],
|
||||
"LOG_LEVEL": ["server", "log_level"],
|
||||
"ZHIPUAI_API_KEY": ["zhipuai", "api_key"],
|
||||
"STORAGE_ACCESS_KEY": ["storage", "access_key"],
|
||||
"STORAGE_SECRET_KEY": ["storage", "secret_key"],
|
||||
"STORAGE_ENDPOINT": ["storage", "endpoint"],
|
||||
"BACKEND_CALLBACK_URL": ["backend", "callback_url"],
|
||||
"LOG_LEVEL": ["server", "log_level"],
|
||||
"MAX_VIDEO_SIZE_MB": ["video", "max_file_size_mb"],
|
||||
}
|
||||
|
||||
def _set_nested(d: dict, keys: list[str], value: str):
|
||||
@@ -351,6 +354,17 @@ app = FastAPI(title="Label AI Service", lifespan=lifespan)
|
||||
|
||||
统一前缀:`/api/v1`。FastAPI 自动生成 Swagger 文档(`/docs`)。
|
||||
|
||||
### 5.0 健康检查
|
||||
|
||||
**`GET /health`**
|
||||
|
||||
```json
|
||||
// 响应(200 OK)
|
||||
{"status": "ok"}
|
||||
```
|
||||
|
||||
用于 Docker healthcheck、Nginx 上游探测、运维监控。无需认证,不访问外部依赖。
|
||||
|
||||
### 5.1 文本三元组提取
|
||||
|
||||
**`POST /api/v1/text/extract`**
|
||||
@@ -541,7 +555,7 @@ POST {BACKEND_CALLBACK_URL}
|
||||
}
|
||||
```
|
||||
|
||||
图像 QA 生成时,AI 服务通过 `get_presigned_url` 获取裁剪图临时访问 URL,构造多模态消息后调用 GLM-4V。
|
||||
图像 QA 生成时,AI 服务通过 `storage.download_bytes` 重新下载裁剪图,base64 编码后直接嵌入多模态消息,避免 RustFS 内网 presigned URL 无法被云端 GLM-4V 访问的问题。
|
||||
|
||||
### 5.7 提交微调任务
|
||||
|
||||
@@ -628,6 +642,8 @@ def extract_text(data: bytes, filename: str) -> str:
|
||||
**抽帧(BackgroundTask)**:
|
||||
|
||||
```
|
||||
0. storage.get_object_size(bucket, file_path) → 字节数
|
||||
超过 video.max_file_size_mb 限制 → 回调 FAILED(路由层提前校验,返回 400)
|
||||
1. storage.download_bytes → bytes → 写入 tempfile
|
||||
2. cv2.VideoCapture 打开临时文件
|
||||
3. interval 模式:按 frame_interval 步进读帧
|
||||
@@ -659,9 +675,10 @@ def extract_text(data: bytes, filename: str) -> str:
|
||||
|
||||
图像 QA:
|
||||
遍历四元组列表
|
||||
storage.get_presigned_url(cropped_image_path) → 临时 URL
|
||||
构造多模态消息(image_url + 问题指令)
|
||||
storage.download_bytes(bucket, cropped_image_path) → bytes → base64 编码
|
||||
构造多模态消息(data:image/jpeg;base64,... + 问题指令)
|
||||
llm.chat_vision → 解析 → 含 image_path 的 QAPairList
|
||||
(注:不使用 presigned URL,因 RustFS 为内网部署,云端 GLM-4V 无法访问内网地址)
|
||||
```
|
||||
|
||||
### 6.5 finetune_service — GLM 微调对接
|
||||
@@ -764,6 +781,12 @@ ai-service:
|
||||
- backend
|
||||
networks:
|
||||
- label-net
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
|
||||
interval: 30s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
start_period: 10s
|
||||
```
|
||||
|
||||
### 10.3 requirements.txt
|
||||
@@ -799,9 +822,9 @@ ZhipuAI 官方 SDK 是同步阻塞调用,直接 `await` 不生效。通过 `lo
|
||||
|
||||
项目规模适中,视频处理任务由 ADMIN 手动触发,并发量可控。FastAPI `BackgroundTasks` 无需额外中间件(Redis 队列、Celery Worker),部署简单,任务状态通过回调接口传递给 Java 后端管理,符合整体架构风格。
|
||||
|
||||
### 11.4 为何图像 QA 生成用 presigned URL 而非 base64
|
||||
### 11.4 为何图像 QA 生成用 base64 而非 presigned URL
|
||||
|
||||
裁剪图已存储在 RustFS,GLM-4V 支持通过 URL 直接访问图片。presigned URL 避免将图片内容重新加载到 AI 服务内存后再 base64 编码,减少内存压力,适合多张图片批量生成的场景。
|
||||
RustFS 部署在 Docker 内网(`http://rustfs:9000`),presigned URL 指向内网地址,云端 GLM-4V API 无法访问,会导致所有图像 QA 请求失败。因此将裁剪图重新下载为 bytes,base64 编码后直接嵌入多模态消息体,与 `image_service` 处理原图的方式保持一致,无需 RustFS 有公网地址。
|
||||
|
||||
### 11.5 config.yaml + .env 分层配置的原因
|
||||
|
||||
|
||||
Reference in New Issue
Block a user