【AI项目】从零构建 AI 视频生成系统:多模型编排的工程实践


✨文末附开源仓库地址,做 AI 应用落地的同学可以直接拉下来跑。

本文为技术演示项目,仅供学习和交流参考,不建议直接用于生产环境

当 LLM 遇见文生图、图生视频、TTS,如何把它们串成一个完整的产品?


前言

最近在关注多模型编排的工程化落地,发现很多团队在做 AI 产品时都会遇到类似的问题:

  • 多个模型如何协同
  • 长时异步任务怎么处理
  • 某个环节失败怎么保证整体不崩

因此抽时间做了一套可运行的 Demo,把多模型链式调用、异步轮询、降级策略这些工程实践落地成代码。

把开发过程中遇到的真实问题在 Demo 中复现并给出解决方案,方便自己复盘总结,也给有需要的同学参考。


项目目标:为餐饮行业打造一套智能视觉内容生成系统,用户只需输入菜名,系统自动生成菜品图片和宣传视频。

技术挑战

  • 如何编排多个 AI 模型协同工作?
  • 如何处理长时异步任务(视频生成需要几十秒)?
  • 如何保证系统的健壮性(某个环节失败不能全盘崩溃)?

系统架构

整体设计

复制代码
┌─────────────────────────────────────────────────────────────────┐
│                        前端 Vue 3                                │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────────────────┐ │
│  │ 图片生成页  │  │ 视频编排页  │  │ 拖拽排序 (vuedraggable) │ │
│  └─────────────┘  └─────────────┘  └─────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│                      后端 FastAPI                                │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │                   任务编排引擎                            │   │
│  │  ┌─────┐   ┌─────┐   ┌─────┐   ┌─────┐   ┌─────────┐  │   │
│  │  │ LLM │──▶│文生图│──▶│图生视频│──▶│ TTS │──▶│视频合并 │  │   │
│  │  └─────┘   └─────┘   └─────┘   └─────┘   └─────────┘  │   │
│  └─────────────────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│                    阿里云百炼 API                                 │
│  Qwen-Plus    wan2.6-t2i    wan2.7-i2v    qwen3-tts-flash      │
└─────────────────────────────────────────────────────────────────┘

核心流程

整个系统的核心是一个多模型链式调用流程:

  1. LLM 生成提示词:用户输入菜名,Qwen-Plus 自动生成 4 种不同风格的提示词
  2. 文生图:调用 wan2.6-t2i 生成菜品图片
  3. 用户选择:用户挑选满意的图片,编排视频顺序
  4. LLM 生成口播文案:根据菜名生成简短的宣传文案(≤20字)
  5. TTS 语音合成:调用 qwen3-tts-flash 合成口播音频
  6. 图生视频:调用 wan2.7-i2v,将音频作为 driving_audio 传入
  7. 视频合并:ffmpeg 拼接所有片段,添加字幕

使用演示

Step 1:输入菜名,批量生成图片

用户只需输入菜名(每行一个),系统自动调用 Qwen 大模型为每个菜名生成 4 种不同风格的提示词,然后批量生成图片:

如图:输入「宫爆鸡丁」和「尖椒鸡」两个菜名,系统自动计算 2 个菜品 × 4 种风格 = 8 张图片,右侧实时展示生成结果。

Step 2:挑选图片,跳转视频编排

生成完成后,用户勾选满意的图片,点击右上角「去编排视频」按钮进入视频编排页面:

如图:8 张图片全部生成完毕,用户勾选了宫爆鸡丁和尖椒鸡各一张,点击「去编排视频 (2)」即可进入下一步。

Step 3:编排顺序,一键生成宣传视频

在视频编排页面,用户可以拖拽调整视频顺序、编辑口播文案,点击生成后系统自动完成:口播文案生成 → 语音合成 → 图生视频 → 视频合并,最终输出一条带口播和字幕的完整宣传视频。

技术选型思考

为什么选 FastAPI?

  • 原生异步:视频生成是长时任务,需要异步处理
  • 类型安全:Pydantic 自动校验请求参数
  • 文档自动生成:Swagger UI 方便前后端联调

为什么前端用 Vue 3?

  • Composition API:复杂状态管理更清晰
  • 生态成熟:Element Plus 组件库开箱即用
  • 拖拽交互:vuedraggable 实现视频编排排序

为什么用阿里云百炼?

  • 模型齐全:LLM、文生图、图生视频、TTS 一站式配齐
  • API 统一:一个 SDK 调用所有模型,降低集成成本
  • 国内访问:无需翻墙,延迟低

核心代码实现

1. 异步任务轮询

视频生成是异步任务,需要轮询查询状态:

python 复制代码
def _poll_task_status(task_id: str, max_wait: int = 600) -> dict:
    """轮询任务状态,最长等待 max_wait 秒"""
    waited = 0
    interval = 15  # 每 15 秒查询一次
    
    while waited < max_wait:
        response = dashscope.VideoSynthesis.async_fetch(
            task_id=task_id,
            api_key=DASHSCOPE_API_KEY
        )
        
        status = response.output.task_status
        if status == "SUCCEEDED":
            return response.output
        elif status == "FAILED":
            raise Exception(f"任务失败: {response.output.message}")
        
        time.sleep(interval)
        waited += interval
    
    raise TimeoutError(f"任务超时: {task_id}")

2. 首尾帧控制

为了让视频片段无缝衔接,采用首尾帧模式

python 复制代码
def generate_video_from_first_last_frame(
    first_frame_path: str,
    last_frame_path: str,
    prompt: str,
    duration: int
) -> str:
    """
    首尾帧模式:
    - 第 1 帧 = first_frame(本段开始画面)
    - 最后 1 帧 = last_frame(上一段结束画面,保证衔接)
    """
    # 提取首帧 base64
    first_frame_b64 = encode_image(first_frame_path)
    # 提取尾帧 base64
    last_frame_b64 = encode_image(last_frame_path)
    
    payload = {
        "model": VIDEO_MODEL,
        "input": {
            "media": [
                {"type": "first_frame", "url": first_frame_b64},
                {"type": "last_frame", "url": last_frame_b64},
            ],
            "prompt": prompt,
        },
        "parameters": {"duration": duration}
    }
    # ... 调用 API

3. 口播音频集成

视频 API 支持传入驱动音频,实现画面与语音同步:

python 复制代码
def generate_video_with_audio(
    image_path: str,
    prompt: str,
    audio_url: str,  # TTS 合成的音频 URL
    duration: int
) -> str:
    payload = {
        "model": VIDEO_MODEL,
        "input": {
            "media": [
                {"type": "image", "url": image_b64},
                {"type": "driving_audio", "url": audio_url},  # 关键!
            ],
            "prompt": prompt,
        },
        "parameters": {"duration": duration}
    }

4. 降级策略

某个环节失败时,不能让整个流程崩溃:

python 复制代码
# 口播生成失败,视频照常生成(无音频)
audio_url = ""
if dish_name:
    try:
        tts_result = generate_voiceover_for_dish(dish_name, duration)
        audio_url = tts_result["audio_url"]
    except Exception as e:
        logger.warning(f"口播生成失败,视频将无音频: {e}")

# 首尾帧模式失败,降级为首帧模式
try:
    result = generate_video_from_first_last_frame(...)
except Exception as e:
    logger.warning(f"首尾帧模式失败,降级为首帧: {e}")
    result = generate_video_from_first_frame(...)

踩坑记录

1. 万相 API 响应格式

问题response.output.image 报错 'dict' object has no attribute 'image'

原因:新版 SDK 返回的是 dict,不是对象

解决

python 复制代码
# 错误
image_url = response.output.image.url

# 正确
image_url = response.output["image"]["url"]
# 或兼容写法
image_url = response.output.get("image", {}).get("url")

2. ffmpeg drawtext 中文字幕

问题 :Windows 上 ffmpeg drawtext 报错 Fontconfig error: Cannot load default config file

原因:standalone ffmpeg 没有 fontconfig 配置

解决:显式指定字体路径,并转义冒号:

python 复制代码
# Windows 路径的冒号需要转义为 \:
font_path = "C\\:/Windows/Fonts/msyh.ttc"
drawtext = f"drawtext=text='{text}':fontfile='{font_path}':fontsize=48"

3. subprocess 错误信息丢失

问题:ffmpeg 执行失败,但日志看不到具体错误

原因capture_output=True 捕获了 stderr,但异常时没有打印

解决

python 复制代码
try:
    result = subprocess.run(cmd, check=True, capture_output=True, text=True)
except subprocess.CalledProcessError as e:
    logger.error(f"ffmpeg 失败: {e.stderr}")  # 显式记录 stderr
    raise

工程化实践

1. 配置管理

python 复制代码
# 支持 PyInstaller 打包后的路径
def _get_data_path():
    if getattr(sys, 'frozen', False):
        return getattr(sys, '_MEIPASS', os.path.dirname(__file__))
    return os.path.dirname(__file__)

# API Key 从环境变量读取
DASHSCOPE_API_KEY = os.getenv("DASHSCOPE_API_KEY", "")

2. 日志系统

python 复制代码
# 同时输出到控制台和文件
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s [%(name)s] %(levelname)s: %(message)s',
    handlers=[
        logging.StreamHandler(),
        logging.FileHandler('logs/app.log', encoding='utf-8')
    ]
)

3. 前端交互

  • 拖拽排序:vuedraggable 实现视频片段编排
  • 实时进度:轮询后端接口,显示当前处理进度
  • 预览弹窗:点击缩略图预览视频
  • 历史管理:支持删除/清空生成的文件

性能数据

任务 耗时 说明
LLM 生成提示词 ~3s 4 种风格
单张图片生成 ~10s 含网络传输
5秒视频生成 ~45s 异步轮询
口播文案生成 ~3s LLM 调用
语音合成 ~2s TTS 调用
视频合并(2段) ~2s 本地 ffmpeg

完整流程:2 个菜品的宣传视频(含口播),总耗时约 2 分钟。

总结

这个项目让我对 AI 应用落地有了更深的理解:

  1. 模型编排是核心:单个模型能力有限,串联起来才能解决复杂问题
  2. 异步处理是必须:视频生成动辄几十秒,必须异步 + 轮询
  3. 降级策略很重要:生产环境不能因为某个环节失败就全盘崩溃
  4. 工程化决定成败:配置管理、日志、错误处理,这些决定了系统能否真正上线

如果你也在做 AI 应用开发,希望这些经验对你有帮助。


项目地址Gitee (欢迎 Star)

本 Demo 仅做技术复盘与学习演示,基于多模型编排工程实践经验提炼,后续还会持续迭代补充更多场景优化。

相关推荐
audyxiao0011 小时前
重磅发布|《上海市教育发展“十五五”规划》及其解读
人工智能·十五五·教育发展
兰亭妙微UI设计公司1 小时前
兰亭妙微UI设计:Neemo Project 企业AI项目管理后台全案运营价值解析
人工智能·ui
Bode_20022 小时前
智能制造系统(SoI)中“3I”指什么
大数据·人工智能
%KT%2 小时前
大模型提示词在标点符号上的一些习惯
人工智能·prompt
单片机杂货铺2 小时前
【单片机毕业设计选题】基于单片机的智能牛奶保鲜箱设计
人工智能·stm32·单片机·物联网·毕业设计·课程设计
lancyu2 小时前
# Agent Loop:AI Agent 的唯一直心骨
开发语言·javascript·人工智能
zx_741484812 小时前
【计算机视觉入门】基于 OpenCV 的银行卡号识别综合案例
人工智能·opencv·计算机视觉
龙虾PRO2 小时前
2026 Headless CMS实操指南:区别传统与解耦CMS,落地全渠道内容复用
人工智能
fīɡЙtīиɡ ℡2 小时前
RAG优化
人工智能
语音之家2 小时前
多方言ASR自蒸馏:学会方言,不忘普通话
人工智能·消融实验·方言 sft