Generated plan.md, research.md, data-model.md, contracts/api.md, quickstart.md, and CLAUDE.md agent context via /speckit-plan.
77 lines
4.5 KiB
Markdown
77 lines
4.5 KiB
Markdown
# Research: AI 服务实现方案
|
||
|
||
**Branch**: `001-ai-service-requirements` | **Date**: 2026-04-10
|
||
**Status**: 完成(所有决策已在设计阶段确定,无待研究项)
|
||
|
||
---
|
||
|
||
## 决策记录
|
||
|
||
### D-001: 异步框架选型
|
||
|
||
**Decision**: FastAPI + uvicorn
|
||
**Rationale**: 原生 async/await 支持、Pydantic 自动校验、自动生成 Swagger 文档、Python 生态系中性能和开发效率的最优权衡。
|
||
**Alternatives considered**: Django(过重)、Flask(无原生异步)、aiohttp(无自动文档和类型校验)
|
||
|
||
---
|
||
|
||
### D-002: ZhipuAI SDK 调用方式
|
||
|
||
**Decision**: 同步 SDK 通过 `asyncio.get_event_loop().run_in_executor(None, ...)` 在线程池中调用
|
||
**Rationale**: ZhipuAI 官方 SDK 为同步阻塞设计,直接在 async 函数中调用会阻塞事件循环。`run_in_executor` 将阻塞调用卸载到线程池,保持 FastAPI 事件循环响应能力。
|
||
**Alternatives considered**: 使用 `asyncio.to_thread()`(Python 3.9+ 语法糖,等效实现,选择 run_in_executor 保持向后兼容性);使用 httpx 直接调用 ZhipuAI HTTP API(绕过 SDK 但增加维护负担)
|
||
|
||
---
|
||
|
||
### D-003: 图像 QA 生成的图片传输方式
|
||
|
||
**Decision**: base64 编码嵌入消息体(`data:image/jpeg;base64,...`)
|
||
**Rationale**: RustFS 部署在 Docker 内网(endpoint: `http://rustfs:9000`),presigned URL 指向内网地址,云端 GLM-4V 无法访问。base64 编码将图片内容直接内联到 API 请求,不依赖网络可达性。
|
||
**Alternatives considered**: presigned URL(不可行,内网地址云端不可达);公网 RustFS 暴露(增加安全风险)
|
||
|
||
---
|
||
|
||
### D-004: 视频长任务处理机制
|
||
|
||
**Decision**: FastAPI BackgroundTasks + HTTP 回调通知 Java 后端
|
||
**Rationale**: 视频处理耗时不可控(几秒到几分钟),同步等待会超时。BackgroundTasks 无需额外中间件(Redis/Celery),部署简单,任务状态通过回调接口由 Java 后端管理,符合整体架构风格。并发量有限(≤5个同时任务),BackgroundTasks 完全够用。
|
||
**Alternatives considered**: Celery(需 Redis broker,引入额外运维负担);asyncio.create_task(进程重启会丢失任务)
|
||
|
||
---
|
||
|
||
### D-005: 分层配置方案
|
||
|
||
**Decision**: config.yaml(稳定非敏感配置)+ .env(密钥和环境差异项),环境变量优先级高于 YAML
|
||
**Rationale**: YAML 提供结构化可读性,适合 git 追踪非敏感配置变更;.env 格式为 Docker `env_file` 原生支持;环境变量覆盖机制使容器部署时无需重建镜像即可切换配置。
|
||
**Alternatives considered**: 纯 .env 文件(缺乏结构化,复杂配置难维护);数据库存储配置(过重)
|
||
|
||
---
|
||
|
||
### D-006: 视频大文件 OOM 防护
|
||
|
||
**Decision**: 在视频路由层(接受请求后、启动后台任务前)通过 `storage.get_object_size()` 查询文件大小,超限返回 HTTP 400
|
||
**Rationale**: 在下载前拒绝,避免实际 OOM;大小限制通过 config.yaml + MAX_VIDEO_SIZE_MB 环境变量运行时可配置,无需重建镜像;实现简单,无需引入流式下载的新抽象。
|
||
**Alternatives considered**: 流式下载(Completeness: 9/10,但 YAGNI,当前规模不需要);不限制(Completeness: 4/10,有 OOM 风险)
|
||
|
||
---
|
||
|
||
### D-007: 视频关键帧检测算法
|
||
|
||
**Decision**: 帧差分(frame difference)近似检测:计算当前帧与前帧灰度图的像素差均值,差值超过阈值(默认 30.0)判定为场景切换
|
||
**Rationale**: OpenCV 无原生 I 帧检测 API(`CAP_PROP_POS_FRAMES` 是帧定位,非 I 帧标识)。帧差分简单有效,对场景切换检测准确,且无需视频解码器底层支持。
|
||
**Alternatives considered**: 基于编码信息的 I 帧检测(需 FFmpeg 支持,引入额外依赖);固定间隔(不够智能,不适合关键帧模式)
|
||
|
||
---
|
||
|
||
### D-008: 测试策略
|
||
|
||
**Decision**: pytest + pytest-asyncio,Service 层和 Router 层分别测试,使用 AsyncMock 模拟外部依赖
|
||
**Rationale**: Service 层测试业务逻辑,不依赖 HTTP;Router 层使用 TestClient 测试完整请求流程。视频 service 测试使用真实小视频文件(OpenCV VideoWriter 生成),验证帧提取逻辑正确性。
|
||
**Alternatives considered**: 仅集成测试(需要真实 RustFS 和 ZhipuAI,CI 成本高);全部单元测试(无法覆盖路由和异常处理器集成)
|
||
|
||
---
|
||
|
||
## 无待解决项
|
||
|
||
所有 NEEDS CLARIFICATION 均已在设计阶段通过用户确认或合理默认值解决。本 research.md 仅作决策存档。
|