You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 

6.7 KiB

修复 AI 任务 output_data 存储 Base64 数据问题

日期: 2026-02-14
类型: Bug Fix
影响范围: AI 图片生成任务
严重程度: Medium (数据库膨胀、查询性能)

问题描述

背景

在图片生成任务 (generate_image_task) 中,当 AI Provider 返回 Base64 格式的图片数据时,系统会:

  1. 解码 Base64 数据为二进制
  2. 上传到 MinIO 获取永久 URL
  3. 更新任务的 output_data 字段

问题

在第 3 步时,原始的 b64_json 字段没有被移除,导致:

  • 1024x1024 PNG 图片的 Base64 约 1-2 MB
  • 频繁生成图片会导致数据库快速膨胀
  • 查询 ai_jobs 表时加载大量无用数据
  • 影响 API 响应性能

受影响的 Provider

  • OpenAI (DALL-E): 默认返回 Base64
  • Gemini (Imagen): 返回 Base64
  • 其他使用 Base64 响应格式的 Provider

对比

视频/音频生成任务已正确处理此问题:

result.pop('audio_data', None)  # ✅ 显式移除二进制数据
result.update(file_metadata)

修复方案

代码变更

文件: server/app/tasks/ai_tasks.py

修改前 (第 471-494 行):

if result.get('b64_json'):
    file_data = base64.b64decode(result['b64_json'])
    file_metadata = await _upload_file_from_bytes(...)
    # ❌ 仅更新 result,未移除 b64_json
    result.update(file_metadata)
elif result.get('url'):
    file_metadata = await _download_and_upload_file(...)
    # ❌ 仅更新 result,未移除 url
    result.update(file_metadata)

修改后:

if result.get('b64_json'):
    file_data = base64.b64decode(result['b64_json'])
    file_metadata = await _upload_file_from_bytes(...)
    # ✅ 移除 Base64 数据(避免存入数据库)
    result.pop('b64_json', None)
    result.pop('url', None)
    result.update(file_metadata)
elif result.get('url'):
    file_metadata = await _download_and_upload_file(...)
    # ✅ 移除临时 URL(避免存入数据库)
    result.pop('url', None)
    result.pop('b64_json', None)
    result.update(file_metadata)

修复后的 output_data 结构

{
  "file_url": "http://minio:9000/jointo/ai-generated/images/...",
  "file_size": 245678,
  "checksum": "sha256:abc123...",
  "mime_type": "image/png",
  "storage_provider": "minio",
  "storage_path": "ai-generated/images/image_xxx.png",
  "metadata": {
    "model": "dall-e-3",
    "size": "1024x1024",
    "quality": "standard",
    "style": "vivid",
    "revised_prompt": "..."
  }
}

移除的字段:

  • b64_json: Base64 编码的图片数据 (1-2 MB)
  • url: AI Provider 的临时 URL (已过期)

保留的字段:

  • file_url: MinIO 永久 URL
  • metadata: 模型元数据 (< 1 KB)

影响评估

数据库影响

修复前:

  • 单个图片任务 output_data: 1-2 MB
  • 100 个图片任务: 100-200 MB

修复后:

  • 单个图片任务 output_data: < 10 KB
  • 100 个图片任务: < 1 MB

节省空间: 约 99% (200MB → 1MB)

API 性能影响

  • 查询 ai_jobs 表时数据传输量减少 99%
  • 前端轮询任务状态的响应速度提升
  • 数据库 JSONB 索引效率提升

历史数据

  • 已存储的历史任务不受影响
  • 可选:运行清理脚本移除历史数据中的 Base64 字段

验证方法

1. 测试图片生成

# 生成测试图片
curl -X POST http://localhost:8000/api/v1/ai/jobs/generate-image \
  -H "Authorization: Bearer $TOKEN" \
  -d '{
    "prompt": "A beautiful sunset",
    "model": "dall-e-3",
    "width": 1024,
    "height": 1024
  }'

# 查询任务结果
curl http://localhost:8000/api/v1/ai/jobs/{job_id}

2. 验证 output_data 大小

-- 查询最新图片任务的 output_data 大小
SELECT 
    ai_job_id,
    created_at,
    pg_column_size(output_data) as size_bytes,
    pg_column_size(output_data) / 1024 as size_kb,
    jsonb_object_keys(output_data) as keys
FROM ai_jobs
WHERE job_type = 1  -- IMAGE
  AND status = 3    -- COMPLETED
ORDER BY created_at DESC
LIMIT 5;

预期结果: size_kb< 10 KB

3. 验证字段不存在

-- 确认 output_data 中不包含 b64_json 或临时 url
SELECT 
    ai_job_id,
    output_data ? 'b64_json' as has_base64,
    output_data ? 'file_url' as has_file_url,
    output_data->'metadata'->>'model' as model
FROM ai_jobs
WHERE job_type = 1 AND status = 3
ORDER BY created_at DESC
LIMIT 5;

预期结果: has_base64 应为 false

清理历史数据(可选)

如需清理历史数据中的 Base64 字段:

-- 1. 查看需要清理的任务数量
SELECT 
    COUNT(*) as count,
    SUM(pg_column_size(output_data)) / 1024 / 1024 as total_mb
FROM ai_jobs
WHERE job_type = 1 
  AND output_data ? 'b64_json';

-- 2. 清理 Base64 字段(谨慎操作,建议先备份)
UPDATE ai_jobs
SET output_data = output_data - 'b64_json' - 'url'
WHERE job_type = 1 
  AND output_data ? 'b64_json';

-- 3. 验证清理结果
SELECT 
    COUNT(*) as remaining_count
FROM ai_jobs
WHERE job_type = 1 
  AND output_data ? 'b64_json';
-- 应返回 0

回滚方案

如需回滚此修复:

cd server
git revert <commit_hash>

注意: 回滚后新生成的图片任务会再次存储 Base64 数据。

相关问题

其他任务类型状态

  • 视频生成: 已正确移除 video_data
  • 音频生成: 已正确移除 audio_data
  • 配音生成: 已正确移除 audio_data
  • 批量对话配音: 无大字段问题
  • ⚠️ 剧本解析: parsed_data 可能包含大量文本 (待优化)

剧本解析优化建议

剧本解析任务的 output_data.parsed_data 可能包含:

  • 完整剧本内容
  • 角色/场景/道具描述
  • 分镜详情和对白

建议:

  1. 仅保存摘要统计(角色数、场景数、分镜数)
  2. 详细数据已存入业务表,无需重复存储
  3. 或标记为大字段,允许 API 选择性加载

测试清单

  • 修复代码并通过 Linter 检查
  • 测试 OpenAI DALL-E 图片生成
  • 测试 Gemini Imagen 图片生成
  • 验证 output_data 大小 < 10 KB
  • 验证不包含 b64_json 字段
  • 查询历史数据统计
  • (可选) 执行历史数据清理

参考文档

  • AI 任务模型: server/app/models/ai_job.py
  • AI 任务处理: server/app/tasks/ai_tasks.py
  • OpenAI Adapter: server/app/services/ai_providers/sdk_adapters/openai_sdk_adapter.py
  • Gemini Adapter: server/app/services/ai_providers/sdk_adapters/gemini_sdk_adapter.py

总结

此修复解决了图片生成任务中 Base64 数据存入数据库的问题,预期可:

  • 节省数据库空间 99%
  • 提升查询性能
  • 优化 API 响应速度
  • 与视频/音频任务保持一致的处理逻辑

修复已生效,新生成的图片任务将不再存储 Base64 数据。