基于阿里云 Milvus 构建短剧生产平台--生图生视频与内容搜索实践

简介: 本文用四个 Milvus AI Function 把 AIGC 短剧生产的完整链路串成一条「生成 → 检索」流水线,它的价值不只是"生成快",更在于把每一次生成都沉淀为可检索、可复用的素材资产,让内容团队从一次性消耗走向资产化经营。

作者:周弘懿(锦琛)


业务背景

AIGC 短剧、竖屏微短剧、品牌短视频正在成为内容供给侧增长最快的赛道之一。和传统影视工业不同,这类内容的生产节奏极快:一个团队一周要产出几十条甚至上百条成片,每条成片背后又有大量关键帧、分镜、候选素材需要生成和筛选。典型的生产链路可以拆成四段:


  1. 分镜文案:编剧或运营先写出一段一段的分镜脚本,例如"雨夜,女主撑伞站在便利店门口,暖色灯光打在脸上"。
  2. 关键帧生图:把分镜文案转成一张张关键帧图片,作为镜头的视觉锚点;有时还要在已有素材上做背景替换、风格迁移、参考图融合。
  3. 图生视频:把选定的关键帧作为首帧,用图生视频模型生成几秒的运动镜头;文生视频则直接从文案生成概念短片。
  4. 素材沉淀与检索复用:每天产生的图片、视频素材数量巨大,如果只是堆在对象存储里,下次要"找一段雨夜便利店的空镜"只能靠人肉翻文件夹。真正让素材变成资产的,是多模态内容理解 + 向量检索:给素材自动生成标题、描述、标签,把图片和视频都转成向量入库,之后就能以文搜图、以图搜视频、以文搜视频。


这条链路的业务价值:

  • 内容生产提效:分镜文案到关键帧、关键帧到运动镜头都由模型批量完成,把人从重复劳动里解放出来,只保留创意和复核环节。
  • 素材资产沉淀:所有生成物带着结构化描述和向量入库,形成可检索、可复用的素材库,而不是一次性消耗品。
  • 二次创作检索复用:新剧本里出现"复古咖啡馆"的镜头需求时,先在历史素材库里语义检索一遍,能复用的直接复用,不能复用的再走生成,边际成本持续下降。


技术挑战

如果不借助统一的向量数据库平台,自己从零对接各家生成模型、再拼一套检索系统,会撞上一连串工程痛点:

  • 生图/生视频模型接入分散:文生图、图生图、文生视频、图生视频、视频编辑往往是不同的服务、不同的鉴权、不同的返回格式,业务代码里塞满各家 SDK 和 HTTP 客户端,维护成本高。
  • 异步任务管理复杂:视频生成是典型的长耗时异步任务,创建只拿到 task_id,要不断轮询查询状态。自己管理任务队列、轮询退避、终态判定、超时兜底很容易写错——比如把客户端本地超时误判成服务端失败并无限重试。
  • 内容安全审核:AIGC 生成结果具有非确定性,肖像、品牌、版权都有合规风险,需要在生成环节引入审核与人工复核,不能盲目自动发布。
  • 生成素材缺乏统一检索入口:图片存 OSS、视频存 OSS、描述文本存业务库、向量又单独存一套向量引擎,四份数据割裂,检索时要跨系统拼装。
  • 多模态检索难、复用靠人工:以文搜图、以图搜视频这类跨模态检索,需要把文本、图片、视频映射到同一向量空间;没有语义检索时,运营找素材靠文件名和记忆,历史素材利用率极低,大量生成物变成沉没成本。


整体流程

整条流水线由四个 Milvus AI Function 串成,按"先生成、后检索"分为两个阶段、五个步骤:


四个 AI Function 的分工:

函数

作用

AI_IMAGE_EDIT

图像生成 / 编辑,支持单图编辑和多参考图融合

AI_VIDEO_EDIT

图生视频 / 文生视频,异步任务(video_edit 创建、tasks/describe 轮询)

AI_MULTI_MODAL_GENERATE

多模态内容理解,为图片 / 视频自动生成标题、描述、标签

AI_EMBEDDING

写入即向量化,把文本 / 图片 / 视频映射到同一向量空间

具体AI Function用法参考官方文档


实战操作

下面按流水线顺序讲解五个步骤的目标与要点;为便于对照,所有可运行代码统一集中在文末的「完整示例代码」一节。

阶段一 · 生成

步骤 1:关键帧生图(AI_IMAGE_EDIT / wan2.7-image)

目标:拿一张分镜参考图(或素材图),按分镜文案做背景替换 / 风格调整,产出一张关键帧图片 URL,作为后续图生视频的首帧。AI_IMAGE_EDIT 同步返回,单图输入用 texts,编辑指令写在 params.prompt


输入:参考图 URL + 分镜文案(prompt) → 输出:关键帧图片 URL。



步骤 2:图生视频(AI_VIDEO_EDIT / happyhorse-1.1-i2v)

目标:把关键帧作为首帧(media.type=first_frame)生成一段运动镜头。视频生成是异步任务:先 POST /v2/vectordb/ai/video_edit 创建拿到 task_id,再 POST /v2/vectordb/ai/tasks/describe 轮询,直到 task_status 变为 SUCCEEDED(拿到 video_url)或终态 FAILED / CANCELED

输入:首帧图 + 运镜文案 → 输出:运动镜头 video_url(720P / 5s)。



步骤 3:素材理解,生成标题 / 标签(AI_MULTI_MODAL_GENERATE / qwen3.7-plus)

目标:在素材入库前,用多模态大模型给图片 / 视频生成一句客观的检索描述,作为标题、简介或标签来源。图片用 media_type=image,视频用 media_type=videoprompt 必填。这一步产出的 caption / tags 会随素材一起在步骤 4 入库。

输入:图片 / 视频 URL → 输出:标题、描述、标签。


阶段二 · 检索

步骤 4:多模态向量入库(AI_EMBEDDING 写入 / qwen3-vl-embedding)

目标:建一个"写入即向量化"的多模态素材 Collection(is_multimodal=truedim=2560)。把图片 URL、视频 URL 连同标题、标签写入,Milvus 会自动为素材生成 2560 维向量并落库。图片和视频共享同一向量字段和同一模型,才能在同一空间里跨模态检索。

输入:步骤 1~3 产出的 url + caption + tags → 输出:落库的 2560 维向量。


步骤 5:内容搜索——以文搜图 / 以文搜视频 / 以图搜视频(AI_EMBEDDING 查询)

目标:素材入库后,用同一个多模态模型把查询(文本或图片 URL)映射到同一向量空间,检索最相似的素材实现复用。由于 Collection 绑定了 qwen3-vl-embedding 的 Function,searchdata 里直接传原始文本或媒体 URL,Milvus 会自动向量化查询。把查询文案换成一张图片 URL,同一个 search 调用即可完成"以图搜图 / 以图搜视频";去掉 filter 则在图片和视频混合库里跨模态检索。

输入:查询文本 / 查询图片 URL → 输出:Top-K 最相似素材(可用 filter 限定模态)。


完整示例代码

将MILVUS_HOST和MILVUS_TOKEN替换成自己的集群就可以直接看到效果

"""AIGC 短剧「生成 → 检索」一体化流水线(阿里云 Milvus AI Function)
步骤1 关键帧生图 → 步骤2 图生视频 → 步骤3 素材理解 → 步骤4 向量入库 → 步骤5 内容搜索
用到四个 AI Function:AI_IMAGE_EDIT / AI_VIDEO_EDIT / AI_MULTI_MODAL_GENERATE / AI_EMBEDDING
"""
from __future__ import annotations

import json
import time
from typing import Any
from urllib.error import HTTPError
from urllib.request import Request, urlopen

from pymilvus import DataType, Function, FunctionType, MilvusClient

# ==== 全局配置(REST 与 gRPC 同在 19530 端口)====
MILVUS_ENDPOINT = "c-xxx.milvus.aliyuncs.com:19530"
MILVUS_TOKEN = "root:xxx"
MILVUS_REST_BASE_URL = f"http://{MILVUS_ENDPOINT}"
MILVUS_URI = MILVUS_REST_BASE_URL


def post_json(path: str, body: dict[str, Any], timeout: int = 200) -> tuple[int, dict[str, Any]]:
    """步骤 1~3 共用的 REST 调用工具。"""
    request = Request(
        f"{MILVUS_REST_BASE_URL.rstrip('/')}{path}",
        data=json.dumps(body, ensure_ascii=False).encode("utf-8"),
        headers={"Authorization": f"Bearer {MILVUS_TOKEN}", "Content-Type": "application/json"},
        method="POST",
    )
    try:
        with urlopen(request, timeout=timeout) as response:
            return response.status, json.loads(response.read().decode("utf-8"))
    except HTTPError as exc:
        return exc.code, json.loads(exc.read().decode("utf-8"))


# ========== 步骤 1:关键帧生图 AI_IMAGE_EDIT (wan2.7-image) 【对应正文·步骤1|代码第 37–55 行】
IMAGE_URL = "https://dashscope.oss-cn-beijing.aliyuncs.com/images/dog_and_girl.jpeg"
PROMPT = (
    "雨夜,一位女子撑着伞站在便利店门口,暖色的店内灯光打在她脸上,"
    "电影级构图,保留画面主体。"
)
status, data = post_json(
    "/v2/vectordb/ai/image_edit",
    {
        "model_name": "wan2.7-image",
        "texts": [IMAGE_URL],
        "params": {"prompt": PROMPT, "n": 1, "size": "1024*1024", "watermark": False, "timeout_sec": 180},
    },
)
assert status == 200 and data.get("code") == 0, data
keyframe_url = data["data"]["output"]["outputs"][0]
print("\n" + "=" * 64)
print("步骤 1 | AI_IMAGE_EDIT 关键帧生图(wan2.7-image)")
print("=" * 64)
print(f"关键帧图片 URL:{keyframe_url}")


# ========== 步骤 2:图生视频 AI_VIDEO_EDIT (happyhorse-1.1-i2v),异步任务 【对应正文·步骤2|代码第 56–91 行】
VIDEO_MODEL = "happyhorse-1.1-i2v"  # 创建与查询必须使用同一模型名
# 用步骤1的关键帧作首帧(media.type=first_frame)
status, data = post_json(
    "/v2/vectordb/ai/video_edit",
    {
        "model_name": VIDEO_MODEL,
        "prompt": "镜头缓缓推进,雨丝飘落,暖光在伞面上轻轻闪动,"
                  "保留首帧图中的主体。",
        "media": [{"type": "first_frame", "url": keyframe_url}],
        "params": {"resolution": "720P", "audio_setting": "none", "watermark": False, "timeout_sec": 180},
    },
)
assert status == 200 and data.get("code") == 0, data
task_id = data["data"]["output"]["task_id"]
print("\n" + "=" * 64)
print("步骤 2 | AI_VIDEO_EDIT 图生视频(happyhorse-1.1-i2v,异步任务)")
print("=" * 64)
print(f"任务已创建  task_id = {task_id}")

# 轮询查询:tasks/describe 一次只查一个 task_id,带 provider 与相同 model_name
video_url = None
for attempt in range(60):  # 本地轮询上限,达到上限只记录 task_id,不判定服务端失败
    status, data = post_json(
        "/v2/vectordb/ai/tasks/describe",
        {"provider": "aliyun_milvus", "model_name": VIDEO_MODEL, "task_id": task_id},
    )
    task_status = data.get("data", {}).get("output", {}).get("task_status")
    print(f"  轮询 #{attempt:<2} ->  {task_status}")
    if task_status == "SUCCEEDED":
        video_url = data["data"]["output"]["video_url"]
        break
    if task_status in ("FAILED", "CANCELED"):
        raise RuntimeError(f"task ended in {task_status}: {json.dumps(data, ensure_ascii=False)}")
    time.sleep(10)
assert video_url, f"轮询达到本地上限,请稍后凭 task_id={task_id} 继续查询"
print(f"运动镜头 video_url:{video_url}")


# ========== 步骤 3:素材理解 AI_MULTI_MODAL_GENERATE (qwen3.7-plus) 【对应正文·步骤3|代码第 92–117 行】
def describe_media(url: str, media_type: str, prompt: str) -> str:
    status, data = post_json(
        "/v2/vectordb/ai/multi_modal_generate",
        {
            "model_name": "qwen3.7-plus",
            "texts": [url],
            "params": {"media_type": media_type, "prompt": prompt, "temperature": 0},
        },
    )
    assert status == 200 and data.get("code") == 0, data
    return data["data"]["output"]["outputs"][0]


image_caption = describe_media(
    keyframe_url, "image",
    "用一句中文客观描述这张关键帧图片的主体、场景和色调,便于以文搜图。",
)
video_caption = describe_media(
    video_url, "video",
    "用一句中文客观描述这段视频的主体、动作和画面氛围,便于按镜头检索。",
)
print("\n" + "=" * 64)
print("步骤 3 | AI_MULTI_MODAL_GENERATE 素材理解(qwen3.7-plus)")
print("=" * 64)
print(f"[图片描述] {image_caption}")
print(f"[视频描述] {video_caption}")


# ========== 步骤 4:建多模态 Collection 并向量入库 AI_EMBEDDING (qwen3-vl-embedding) 【对应正文·步骤4|代码第 118–168 行】
EMBED_MODEL = "qwen3-vl-embedding"
VECTOR_DIM = 2560  # qwen3-vl-embedding 多模态维度,向量字段维度必须与 dim 一致

client = MilvusClient(uri=MILVUS_URI, token=MILVUS_TOKEN)

collection_name = "aigc_assets_multimodal"
if client.has_collection(collection_name):
    client.drop_collection(collection_name)

schema = MilvusClient.create_schema(auto_id=True, enable_dynamic_field=False)
schema.add_field("id", DataType.INT64, is_primary=True)
schema.add_field("media_type", DataType.VARCHAR, max_length=16)     # image / video
schema.add_field("media_ref", DataType.VARCHAR, max_length=4096)    # 用于向量化的图片/视频 URL
schema.add_field("caption", DataType.VARCHAR, max_length=2048)      # 步骤3 生成的描述
schema.add_field("tags", DataType.VARCHAR, max_length=512)
schema.add_field("embedding", DataType.FLOAT_VECTOR, dim=VECTOR_DIM)

# 写入 media_ref 时自动调用 qwen3-vl-embedding 生成 2560 维向量
schema.add_function(
    Function(
        name="embed_media",
        function_type=FunctionType.TEXTEMBEDDING,
        input_field_names=["media_ref"],
        output_field_names=["embedding"],
        params={
            "provider": "aliyun_milvus",
            "model_name": EMBED_MODEL,
            "dim": VECTOR_DIM,
            "is_multimodal": "true",
        },
    )
)

index_params = client.prepare_index_params()
index_params.add_index(field_name="embedding", index_type="AUTOINDEX", metric_type="COSINE")
client.create_collection(collection_name=collection_name, schema=schema, index_params=index_params)

# 把步骤1~3 的产出(url + caption + tags)向量化入库
client.insert(
    collection_name,
    [
        {"media_type": "image", "media_ref": keyframe_url, "caption": image_caption, "tags": "关键帧,生成图"},
        {"media_type": "video", "media_ref": video_url, "caption": video_caption, "tags": "运动镜头,生成视频"},
    ],
)
client.flush(collection_name)
client.load_collection(collection_name)
print("\n" + "=" * 64)
print("步骤 4 | AI_EMBEDDING 多模态向量入库(qwen3-vl-embedding, 2560 维)")
print("=" * 64)
print(f"图 + 视频已向量化入库,Collection:{collection_name}")


# ========== 步骤 5:内容搜索 以文搜图 / 以文搜视频 / 以图搜视频 【对应正文·步骤5|代码第 169–196 行】
def search_assets(query: str, top_k: int = 5, media_type: str | None = None):
    """query 可以是一段中文/英文文案,也可以是一张查询图片的 URL。"""
    filter_expr = f'media_type == "{media_type}"' if media_type else ""
    results = client.search(
        collection_name=collection_name,
        data=[query],                 # 文本或图片 URL,均由 qwen3-vl-embedding 向量化
        anns_field="embedding",
        limit=top_k,
        filter=filter_expr,
        output_fields=["media_type", "media_ref", "caption", "tags"],
    )
    for rank, hit in enumerate(results[0], 1):
        e = hit["entity"]
        url = e["media_ref"].split("?")[0]   # 去掉 OSS 签名参数,展示更干净
        print(f"  {rank}. [相似度 {hit['distance']:.4f}] [{e['media_type']}] {e['caption'][:42]}")
        print(f"       {url}")


print("\n" + "=" * 64)
print("步骤 5 | 多模态内容搜索(qwen3-vl-embedding 统一向量空间)")
print("=" * 64)
# 以文搜图:限定只搜图片
print("[5.1] 以文搜图:")
search_assets("室内暖光下的条纹毛衣造型", media_type="image")

# 以文搜视频:限定只搜视频
print("\n[5.2] 以文搜视频:")
search_assets("角色近景,镜头缓慢推进的概念片", media_type="video")

# 以图搜视频:用一张关键帧图片 URL 去召回风格相近的视频素材
print("\n[5.3] 以图搜视频(用关键帧图召回同源视频):")
search_assets(keyframe_url, media_type="video")


实战效果

依次跑通 入库 → 检索 → 重排等步骤。下图是一次完整运行的真实控制台输出:


上图是这条流水线一次完整跑通的输出:从一张分镜参考图出发,AI_IMAGE_EDIT 产出关键帧图片 URL,AI_VIDEO_EDIT 以关键帧为首帧异步生成运动镜头 video_url,AI_MULTI_MODAL_GENERATE 为图片与视频各生成一句检索描述,随后连同 URL、标签一起写入多模态 Collection(写入即向量化),最后以文搜图 / 以文搜视频 / 以图搜视频三路检索都能召回刚入库的素材并带出相似度分数。

这条「生成 → 检索」链路之所以能一次跑通、且结果可复用,关键在三点:

  • 生成即入库:关键帧、运动镜头连同标题、描述、标签一起落库,每个生成物都带着结构化信息和向量,而不是用完即弃的一次性产物。
  • 同一向量空间:图片和视频共享同一个 qwen3-vl-embedding 模型与同一向量字段,才能在一个 Collection 里做以文搜图、以图搜视频这类跨模态检索。
  • 写入即向量化、查询即推理:应用侧无需自己调 embedding,写入时 Collection Function 自动生成 2560 维向量,search 里直接传原始文本或媒体 URL 即可完成召回。


与自己从零对接各家生成模型、再拼一套检索系统相比,简化非常直观:

维度

传统自建方案

阿里云 Milvus 一站式

生成模型接入

文生图 / 图生图 / 文生视频各家 SDK、鉴权、返回格式分散对接

AI Function 统一封装生图 / 生视频 / 内容理解

异步任务管理

自建任务队列、轮询退避、终态判定与超时兜底

tasks/describe 标准轮询接口,终态判定清晰

素材理解

自建或外接标题、描述、标签服务

AI_MULTI_MODAL_GENERATE 内置多模态理解

向量化链路

应用侧先调 embedding 再写入

写入即向量化(Collection Function 自动生成)

检索入口

图片 / 视频 / 文本 / 向量四份数据割裂,跨系统拼装

单库单次 search 跨模态检索

总结

本文用四个 Milvus AI Function 把 AIGC 短剧生产的完整链路串成一条「生成 → 检索」流水线:

  • 生成AI_IMAGE_EDIT 把分镜文案变成关键帧;AI_VIDEO_EDIT 用创建 + tasks/describe 轮询的异步模型把关键帧变成运动镜头;AI_MULTI_MODAL_GENERATE 让每个素材自带标题和描述。
  • 检索AI_EMBEDDING(多模态 qwen3-vl-embedding)把图片和视频统一映射到同一向量空间,写入即向量化、查询即推理,实现以文搜图、以图搜视频、以文搜视频的跨模态检索。


它的价值不只是"生成快",更在于把每一次生成都沉淀为可检索、可复用的素材资产,让内容团队从一次性消耗走向资产化经营。

让 AI 落地,不必再从复杂链路开始

阿里云 Milvus 是目前唯一支持 AI Native 的 Milvus 云服务。它不仅提供高性能、全托管的向量检索能力,更将 AI Function 与 AI-Gateway 原生嵌入数据链路,让模型能力真正成为数据库的一部分。

从数据进入阿里云Milvus 的那一刻起,理解、加工、检索和生成便可以在一条原生链路中完成。阿里云 Milvus 正在把“接入 AI”变成“原生拥有 AI”。

5eb6e969ca911bd95d3c30bd2d6a7ab5 (1).png

相关文章
|
7天前
|
存储 弹性计算 缓存
阿里云服务器租赁费用:新版租赁收费标准及活动报价参考
本文更新了2026年阿里云全系列云服务器租赁活动报价,所有特惠资源均可前往阿里云活动中心选购,整体覆盖从个人入门到企业级高性能场景的全梯度需求。其中轻量应用服务器主打极致性价比,2核2G峰值200M带宽配置每日10点、15点限时抢购价仅38元/年,2核4G配置379元/年起;高性价比的经济型e实例、通用算力型u2i实例覆盖2核4G至4核32G全档位,适配开发测试与中小型企业业务;搭载英特尔至强6处理器的第九代c9i企业级实例算力较上代提升20%,支撑高并发生产环境,不同实例规格价差清晰,用户可根据自身业务负载与预算灵活选型。
1750 117
|
8天前
|
人工智能 程序员 API
Codex 接入 DeepSeek-V4-Flash:还能补上识图,提供两套方案
Codex 接入 DeepSeek-V4-Flash 怎么配?本文覆盖 CLI 与桌面端,再用 qwen3-vl-flash 补识图,两套方案可直接照做
1253 9
|
14天前
|
云安全 人工智能 运维
阿里云联动百位企业安全专家,共识Agent防御最佳实践
当Agent成为新员工,你的安全边界在哪里?
1956 9
阿里云联动百位企业安全专家,共识Agent防御最佳实践
|
8天前
|
编解码 人工智能 安全
2核4G/4核8G/8核16G阿里云服务器如何选择实例?经济型e、通用算力型u2i与计算型c9i选哪个?
本文介绍了阿里云2核4G、4核8G、8核16G三档主流配置下经济型e、通用算力型u2i和计算型c9i三种实例的最新活动价格与适用场景。同配置下三者价差显著,以2核4G为例,经济型e低至599.93元/年,计算型c9i则高达1742.08元/年。文章详细解析了各实例的性能定位:经济型e适合轻负载入门场景,u2i兼顾稳定算力与性价比,c9i凭借第9代至强处理器与芯片级安全能力支撑高性能业务。同时提示用户可叠加满减优惠券享受折上折,建议根据业务负载与预算综合决策。
543 112
缓存 安全 IDE
967 2
|
20天前
|
人工智能 前端开发 Linux
Codex 桌面版安装 + CC Switch 接入第三方 API 完整教程(2026 最新)
2026最新教程:手把手教你安装Codex桌面版,通过CC Switch v3.17.0一键接入Fenno等国产API(兼容OpenAI Responses格式),跳过账号登录,完整启用代码审查、多步任务与上下文感知功能。零基础友好,全程图文实操。(239字)
2943 4
|
8天前
|
人工智能 JSON Shell
2026AI漫剧本地全开源方案(附各个软件模型链接),8G显卡也能流畅运行
这是一套完全本地化部署的AI漫剧生成技术链路:涵盖LLM剧本分镜生成、FLUX文生图(IP-Adapter人脸锁定)、StoryDiffusion时序连贯控制、LTX-2.3唇形同步视频生成,及ComfyUI全流程调度。零云端费用,仅耗硬件算力,单集2–4小时可产出竖屏短视频,适配抖音/B站分发。
|
5天前
|
编解码 弹性计算 云计算
MiniMax-H3 视频生成模型 — 一键部署与使用指南
MiniMax-H3是MiniMax开源的33B全模态视频生成模型,支持文生视频、图生视频、参考生视频三种模式,原生输出2K/15秒带立体声音频视频,已原生适配ComfyUI,并可通过阿里云计算巢一键部署。(239字)
|
12天前
|
存储 人工智能 关系型数据库
阿里云AI产品与云产品最新组合套餐:Token Plan、AI coding及云服务器和建站等组合优惠价
阿里云推出全新“算力+模型+应用”一站式云与AI组合套餐活动,覆盖从个人开发者到中大型企业的全场景需求。核心亮点为分三档定价的Token Plan订阅服务,支持Qwen3.8-Max-Preview大模型调用,错峰时段最低可享0.2折优惠。活动同步推出AI Coding、智能体部署、云电脑托管、0代码建站等十余类场景化组合,搭配99元/年的普惠云服务器、88元/年的入门数据库等经典特惠产品,还为企业提供1V1定制化AI转型方案,大幅降低了不同用户群体拥抱AI的技术门槛与采购成本。
748 111