阿里云百炼实战(三):Qwen-Omni 多模态——视频、音频、图像理解
Qwen-Omni 生产实践:四种输入、文档没强调的流式必填、配上一个真实可跑的视频理解示例和合理的像素预算。
CK
Chen Kai
· 4 min read · 1657 words
百炼里把我从最多产品坑里拽出来的就是 Qwen-Omni。“能告诉我这条 2 分钟广告片在讲什么吗"以前是个 3 周项目——抽帧、逐帧 caption、再 stitch。Qwen-Omni 一次 HTTP 请求搞定。但文档对踩坑警告稀疏,有一个(流式必填)让不止一个团队损失了半天。咱们别成为下一个。
Qwen-Omni 接受什么
按 Qwen API reference 的多模态部分,单条 user 消息的 content 数组里可以混 text、image、audio、video 部分。这个能力的核心不是"支持图像”,而是"任意组合":

每种类型的结构(来自 API reference):
| 类型 | 字段 | 备注 |
|---|
text | text: "..." | 纯字符串。 |
image_url | image_url: {url} | URL 或 base64 data URI。min_pixels / max_pixels 控缩放。 |
input_audio | data, format | mp3、wav 等。URL 或本地 base64。 |
video_url | video_url: {url} | URL 或 data URI。或 video 数组形式的帧图列表。 |
真实调用:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
| from openai import OpenAI
import os
client = OpenAI(
api_key=os.environ["DASHSCOPE_API_KEY"],
base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
)
stream = client.chat.completions.create(
model="qwen3-omni-flash",
messages=[{
"role": "user",
"content": [
{"type": "text", "text": "用两句话描述这条视频在讲什么。"},
{"type": "video_url",
"video_url": {"url": "https://your-bucket.oss-cn-shanghai.aliyuncs.com/clips/promo.mp4"}},
],
}],
stream=True, # <- 必须
)
for chunk in stream:
delta = chunk.choices[0].delta.content
if delta:
print(delta, end="", flush=True)
|
流式不是可选——这是陷阱
文档把流式当成功能特性写的,但对 Qwen-Omni 是必填。设 stream=False 直接 400。

理由想想就知道:模型在处理几 MB 视频和产长响应。协议假设增量送达。攒成一坨发,客户端阻塞几十秒、毫无进度信号。
下游想拿完整字符串就自己 buffer:
1
2
3
4
5
| def call_omni_buffered(messages):
stream = client.chat.completions.create(
model="qwen3-omni-flash", messages=messages, stream=True,
)
return "".join(c.choices[0].delta.content or "" for c in stream)
|
一个函数,写一次以后用。
像素和帧率预算——花钱的地方
文档对成本旋钮含糊:图像的 min_pixels / max_pixels,视频的 fps / resize。默认 Qwen-Omni 按原始分辨率和默认帧率处理。一段 2 分钟 1080p 等价的 token 很多,账单跟着膨胀。
我生产里的做法:
- 理解任务的图像 —
max_pixels: 1280*720。“图里有什么"几乎无质量损失,省钱可观。min_pixels: 640*480 防止小图被放大。 - 描述任务的视频 — 上传前预先 resize 到 720p,静态内容(人讲话)downsample 到 4 fps,动态内容(运动、快剪)8 fps。8 fps 以上多半在为冗余帧付钱。
- 长视频 — 切。模型有上下文上限。3 分钟以上切 90 秒一段,每段 summarize,再用
qwen-plus summary-of-summary。和长文档 RAG 同模式。
发本地视频
两条路,文档都讲了。

路径 1(推荐):上传 OSS,发签名 URL。
1
2
3
4
5
| import oss2, os
auth = oss2.Auth(os.environ["OSS_AK"], os.environ["OSS_SK"])
bucket = oss2.Bucket(auth, "https://oss-cn-shanghai.aliyuncs.com", "your-bucket")
bucket.put_object_from_file("clips/promo.mp4", "/tmp/promo.mp4")
signed = bucket.sign_url("GET", "clips/promo.mp4", 600) # 10 分钟过期
|
把 signed 当 url 字段传。30 秒以上的视频走这条——base64 把 payload 撑大 33%。
路径 2:base64 内联。 短片、想省 OSS 一来一回时用。
1
2
3
4
5
6
7
| import base64
with open("/tmp/short.mp4", "rb") as f:
b64 = base64.b64encode(f.read()).decode()
content = [{
"type": "video_url",
"video_url": {"url": f"data:video/mp4;base64,{b64}"},
}]
|
真实经验: Qwen-Omni 报 400 时先确认 URL 在公网能拿到。模型服务进不了你的 VPC。签名 URL 行;不签名的私有 OSS 不行。
音频理解
形状基本一样,type: "input_audio":
1
2
3
4
| content = [
{"type": "text", "text": "把这段转写出来,并判断说话人情绪。"},
{"type": "input_audio", "input_audio": {"data": signed_audio_url, "format": "mp3"}},
]
|
纯转写百炼有更便宜的 Paraformer 专用 ASR 模型。转写用 Paraformer,需要理解(情感、摘要、“是否提到价格”)才上 Qwen-Omni。
一个真实产品场景
我在 AI 营销里反复用的模式:创意团队上传 60 秒产品片;要一段结构化字幕(scene_description、product_features、target_audience、music_style)。一次 Qwen-Omni 调用,开 JSON mode(多模态也支持),720p 输入 4 秒之内出结果。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| sys = ("分析这段产品视频,返回 JSON,键为:scene_description、product_features(列表)、"
"target_audience、music_style。")
stream = client.chat.completions.create(
model="qwen3.5-omni-plus",
messages=[
{"role": "system", "content": sys},
{"role": "user", "content": [
{"type": "video_url", "video_url": {"url": signed_url}},
]},
],
response_format={"type": "json_object"},
stream=True,
)
text = "".join(c.choices[0].delta.content or "" for c in stream)
import json; result = json.loads(text)
|
下一篇
第四篇跳到生产侧——万相文生视频。完全异步、只能原生协议、失败模式完全不同(队列深度、输出 URL 过期)。也是我花最多时间调 prompt 的 API。
Liked this piece?
Follow on GitHub for the next one — usually one a week.
GitHub →