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.
 

11 KiB

AI 对话生成任务创建修复

日期: 2026-02-12
类型: Bug 修复
模块: AI 对话 (ai_conversations)
影响范围: 后端 API
优先级: 🔴


📋 问题描述

现象 1:任务创建后 404 错误

用户在对话中触发 AI 生成后,前端轮询任务状态时返回 404 错误:

POST /api/v1/ai/conversations/{id}/generate
# 返回 jobId: 019c4fb4-06b6-75f1-97a1-f4346172b0a1

GET /api/v1/ai/jobs/019c4fb4-06b6-75f1-97a1-f4346172b0a1
# 返回 404 Not Found

现象 2:Gemini API 参数错误

任务创建成功,但执行时报错:

Error code: 400 - {
  'error': {
    'message': 'Invalid JSON payload received. Unknown name "prompt": Cannot find field.
                Invalid JSON payload received. Unknown name "quality": Cannot find field.
                Invalid JSON payload received. Unknown name "style": Cannot find field.
                ...'
  }
}

根本原因

原因 1:任务记录未创建

AIConversationService.trigger_ai_generation() 方法中:

# ❌ 旧代码:仅生成 UUID,未创建数据库记录
# TODO: 调用 AI Service 创建任务
# 当前返回模拟数据
job_id = str(generate_uuid())
task_id = f"task-{job_id[:8]}"

原因 2:参数未过滤

# ❌ 旧代码:将所有前端参数传给 AI Service
result = await ai_service.generate_image(
    ...,
    **params  # 包含 modelId, aspectRatio, cameraSettings, mode 等
)

问题

  • modelId, aspectRatio 等前端参数被直接传给 Provider
  • Gemini API 不支持 OpenAI 风格的参数(prompt, n, quality, size, style
  • 导致 API 返回 400 错误

解决方案

修复内容

修复 1:创建真实任务记录

将模拟实现替换为真实的 AI Service 调用:

# ✅ 新代码:调用 AIService 创建真实任务
from app.services.ai_service import AIService

ai_service = AIService(self.db)

if generation_type == 'image':
    # 解析参数
    model_id = params.get('modelId', 'gemini-2.5-flash-image')
    aspect_ratio = params.get('aspectRatio', '16:9')
    
    # 解析宽高
    if aspect_ratio == '16:9':
        width, height = 1024, 576
    elif aspect_ratio == '1:1':
        width, height = 1024, 1024
    elif aspect_ratio == '9:16':
        width, height = 576, 1024
    else:
        width, height = 1024, 1024
    
    # 调用生成图片方法(会创建数据库记录 + Celery 任务)
    result = await ai_service.generate_image(
        user_id=str(user_id),
        prompt=message.content,
        model=model_id,
        width=width,
        height=height,
        project_id=str(conversation.project_id) if conversation.project_id else None,
        reference_images=reference_images,  # 传递 @ 提及的参考图
        **extra_kwargs  # 仅传递有效参数
    )

修复 2:过滤前端参数

# ✅ 新代码:仅传递 AI Service 支持的参数
extra_kwargs = {}
if 'cameraSettings' in params:
    extra_kwargs['cameraSettings'] = params['cameraSettings']
if 'mode' in params:
    extra_kwargs['mode'] = params['mode']

# 排除不支持的参数:
# - modelId (已单独处理)
# - aspectRatio (已单独处理)
# - generationType (已单独处理)
# 避免传递给 Provider 导致 API 错误

修复后的完整流程

1. 用户触发 AI 生成
   POST /ai/conversations/.../generate
   ↓
2. trigger_ai_generation()
   ├─ 验证对话会话
   ├─ 验证权限
   ├─ 获取消息
   ├─ 提取参考图(从 @ 提及)
   ├─ ✅ 调用 ai_service.generate_image()
   │   ├─ 验证用户积分
   │   ├─ 预扣积分
   │   ├─ 创建 ai_jobs 记录  ← 修复关键!
   │   ├─ 提交 Celery 任务
   │   └─ 返回 job_id + task_id
   ├─ 更新消息的 ai_job_id
   ├─ 创建系统消息
   └─ 更新对话统计信息
   ↓
3. 返回响应给前端
   ↓
4. 前端轮询任务状态
   GET /api/v1/ai/jobs/{job_id}
   ↓
5. ✅ 成功查询到任务记录

🔧 修改的文件

1. server/app/services/ai_conversation_service.py

修改方法: trigger_ai_generation()

主要变更:

  • 移除模拟 UUID 生成逻辑
  • 添加 AIService 调用(创建真实任务记录)
  • 实现图片生成任务创建
  • 实现视频生成任务创建
  • 支持 aspectRatio 参数解析(图片)
  • 支持 videoType 参数解析(视频:text2video / img2video)
  • 参数过滤:仅传递 AI Service 支持的参数(修复 Gemini API 400 错误)
  • 传递 reference_images 参考图
  • 更新对话统计信息(message_count, last_message_at
  • 返回 estimatedCredits 字段
  • 优化导入结构(移到文件顶部)

新增功能:

  • 支持图片生成:16:9、1:1、9:16 等宽高比
  • 支持视频生成:text2video(文本转视频)、img2video(图片转视频)
  • 支持将消息内容作为 prompt
  • 支持从 @ 提及中提取参考图
  • img2video 自动使用第一张参考图作为输入
  • 参数白名单过滤:避免传递不支持的参数给 AI Provider

🧪 测试验证

测试步骤

  1. 创建对话并发送消息

    POST /api/v1/ai/conversations
    POST /api/v1/ai/conversations/{id}/messages
    
  2. 触发 AI 生成(图片)

    POST /api/v1/ai/conversations/{id}/generate
    {
      "messageId": "019c...",
      "generationType": "image",
      "params": {
        "modelId": "gemini-2.5-flash-image",
        "aspectRatio": "16:9",
        "mode": "storyboard-image"
      }
    }
    

2.1 触发 AI 生成(视频 - 文本转视频)

POST /api/v1/ai/conversations/{id}/generate
{
  "messageId": "019c...",
  "generationType": "video",
  "params": {
    "modelId": "runway-gen3-turbo",
    "videoType": "text2video",
    "duration": 5,
    "fps": 30
  }
}

2.2 触发 AI 生成(视频 - 图片转视频)

POST /api/v1/ai/conversations/{id}/generate
{
  "messageId": "019c...",  # 消息中必须包含 @ 提及的图片
  "generationType": "video",
  "params": {
    "modelId": "runway-gen3-turbo",
    "videoType": "img2video",
    "duration": 5,
    "fps": 30
  }
}
  1. 验证响应

    {
      "code": 0,
      "data": {
        "jobId": "019c4fb4-06b6-75f1-97a1-f4346172b0a1",
        "taskId": "celery-task-id",
        "status": "pending",
        "estimatedCredits": 10,
        "referenceImagesCount": 2
      }
    }
    
  2. 查询任务状态

    GET /api/v1/ai/jobs/019c4fb4-06b6-75f1-97a1-f4346172b0a1
    # ✅ 应返回 200,包含任务详情
    
  3. 验证数据库

    SELECT * FROM ai_jobs WHERE ai_job_id = '019c4fb4-06b6-75f1-97a1-f4346172b0a1';
    -- ✅ 应该存在该记录
    

预期结果

  • 任务创建成功
  • 数据库中存在 ai_jobs 记录
  • 前端可以正常轮询任务状态
  • Celery 任务已提交
  • 积分已预扣

📊 影响范围

受影响的功能

功能 影响 状态
对话触发图片生成 修复 已完成
对话触发视频生成 修复 已完成
任务状态查询 修复 已完成
@ 提及参考图 增强 已支持
积分预扣 修复 已完成
Celery 任务提交 修复 已完成

不受影响的功能

  • 直接调用 /api/v1/ai/generate 接口(独立接口,未修改)
  • 其他 AI 功能(音频、配音等)

⚠️ 注意事项

1. 图片转视频需要参考图

if video_type == 'img2video':
    if reference_images:
        image_url = reference_images[0]  # 使用第一张参考图
    else:
        raise ValidationError("图片转视频需要提供参考图,请在消息中 @ 提及图片资源")

要求: 使用 img2video 时,必须在消息中 @ 提及至少一张图片资源

2. 参数过滤机制

问题: 前端传来的参数可能包含 AI Provider 不支持的字段(如 modelId, aspectRatio, mode

解决: 使用白名单过滤

# 图片生成:仅传递 cameraSettings 和 mode
extra_kwargs = {}
if 'cameraSettings' in params:
    extra_kwargs['cameraSettings'] = params['cameraSettings']
if 'mode' in params:
    extra_kwargs['mode'] = params['mode']

# 视频生成:仅传递 quality 和 style
extra_kwargs = {}
if 'quality' in params:
    extra_kwargs['quality'] = params['quality']
if 'style' in params:
    extra_kwargs['style'] = params['style']

避免的错误:

  • modelId → 前端参数,不应传给 Provider
  • aspectRatio → 前端参数,已转换为 width/height
  • generationType → 前端参数,不应传给 Provider

3. 视频生成参数

支持的参数:

  • videoType: text2videoimg2video
  • duration: 视频时长(秒),默认 5
  • fps: 帧率,默认 30
  • modelId: 模型 ID,默认 runway-gen3-turbo

4. 宽高比映射(图片生成)

当前硬编码了几种常见比例:

if aspect_ratio == '16:9':
    width, height = 1024, 576
elif aspect_ratio == '1:1':
    width, height = 1024, 1024
elif aspect_ratio == '9:16':
    width, height = 576, 1024
else:
    width, height = 1024, 1024  # 默认 1:1

建议: 可以考虑将映射关系提取为配置文件

5. 对话统计信息更新

修复中添加了对话统计信息更新:

conversation.message_count += 1
conversation.last_message_at = datetime.now(timezone.utc)

原因: 创建系统消息时需要同步更新计数器


🔄 后续优化建议

1. 支持更多生成类型

  • 实现视频生成 (generate_video) - 已完成
  • 实现音频生成 (generate_sound)
  • 实现配音生成 (generate_voice)

1. 增强参数验证

  • 验证 aspectRatio 合法性(添加支持更多比例)
  • 验证 modelId 是否存在
  • 验证 cameraSettings 参数范围
  • 验证 videoType 合法性

2. 优化参数过滤

当前使用硬编码白名单:

extra_kwargs = {}
if 'cameraSettings' in params:
    extra_kwargs['cameraSettings'] = params['cameraSettings']
if 'mode' in params:
    extra_kwargs['mode'] = params['mode']

建议: 为每个模型配置支持的参数列表

# 配置文件
MODEL_PARAMS_WHITELIST = {
    'gemini-2.5-flash-image': ['cameraSettings', 'mode'],
    'runway-gen3-turbo': ['quality', 'style', 'motion_level']
}

# 动态过滤
allowed_params = MODEL_PARAMS_WHITELIST.get(model_id, [])
extra_kwargs = {k: v for k, v in params.items() if k in allowed_params}

3. 优化错误处理

  • 区分积分不足、模型不存在等错误类型
  • 返回更友好的错误提示
  • 记录详细的错误日志

4. 性能优化

  • 缓存模型配置
  • 批量查询资源信息
  • 异步处理参考图上传

📝 相关文档


审核清单

  • 代码已修改
  • 语法检查通过
  • 功能逻辑正确
  • 错误处理完善
  • 日志记录清晰
  • 文档已更新
  • 单元测试通过(待编写)
  • 集成测试通过(待验证)

修复者: Claude (AI Assistant)
审核者: 待审核
版本: v1.2.0 (修复 Gemini API 参数错误)