2026-04-16 11:10:38 +08:00
2026-04-16 10:17:54 +08:00
2026-04-15 11:13:40 +08:00
2026-04-16 10:17:54 +08:00
2026-04-16 09:50:13 +08:00
2026-04-16 10:02:13 +08:00
2026-04-15 11:09:09 +08:00
2026-04-16 11:10:38 +08:00
2026-04-16 09:50:13 +08:00

label_ai_service

label_ai_service 是知识图谱智能标注平台的 AI 计算服务,基于 FastAPI 提供独立部署的推理与预处理能力。它不直接访问数据库,而是通过 ZhipuAI GLM 系列模型完成结构化抽取,通过 RustFS 读写原始文件和处理结果,并通过 HTTP 回调把异步视频任务结果通知上游后端。

当前服务覆盖 6 类核心能力:

  • 文本三元组提取,支持 TXTPDFDOCX
  • 图像四元组提取,并自动裁剪 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,真实处理结果走回调。
  • 服务默认不做鉴权,通常由上游网关或后端负责访问控制。

项目结构

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

当前项目默认配置如下:

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 示例:

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

本地运行

方式一:直接运行

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 可以使用:

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

启动命令:

docker compose up --build

如果你要联调视频异步任务,请确保 BACKEND_CALLBACK_URL 指向一个可访问的后端地址。否则任务本身会继续处理,但回调会失败并记录错误日志。

API 使用示例

1. 健康检查

curl http://localhost:8000/health

返回:

{"status":"ok"}

2. 文本三元组提取

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. 图像四元组提取

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. 视频抽帧

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. 视频转文本

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. 文本问答生成

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. 图像问答生成

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. 微调任务提交

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 调用和后台任务也会输出结构化字段,方便排查接口超时、回调失败和模型解析错误。

错误码

统一错误返回格式:

{"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 未捕获异常

测试

安装依赖后可直接运行:

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/servicestests,最后进入实现细节

已知约束

  • 文本提取目前只支持 txtpdfdocx
  • 视频接口依赖对象存储可读取文件大小
  • 视频任务状态不持久化在本服务内,由上游系统负责管理
  • 图像问答采用 base64 内联图片,不依赖外网可访问的 presigned URL
  • 如果 .env 中的回调地址不可达,视频任务会记录错误日志,但不会自动重试

开发建议

  • 新增接口时同步补齐 Pydantic 模型、Router 测试和 README/API 文档
  • 如果替换模型厂商,优先扩展 app/clients/llm
  • 如果替换存储实现,优先扩展 app/clients/storage
  • 任何输出路径规则变更,都应同步更新 README 和设计文档
Description
No description provided
Readme 224 KiB
Languages
Python 97.1%
Shell 1.5%
Dockerfile 1.4%