阿里云Milvus Embedding服务:一站式完成文本与多模态向量化
阿里云 Milvus Embedding 服务是内置于 Milvus 的托管式向量化能力。开通后,在数据写入和检索时可直接传入原始文本或多模态数据(图片、视频),由系统自动完成向量生成,无需单独部署 Embedding 推理服务。
功能概览

功能 | 说明 |
控制台一站式管理 | 在控制台完成 Embedding 模型服务的开通、配置与实例绑定。 |
托管式模型能力 | 平台提供托管式 Embedding 服务,无需自建推理服务。 |
原始数据直接写入和检索 | 写入、更新和查询时直接传入原始文本或多模态内容,系统自动完成向量化。 |
多模型支持 | 支持多个 Embedding 模型,可根据业务场景选择和切换。 |
调用量与 Token 统计 | 提供实例级的调用量与 Token 用量统计。 |
监控告警 | 支持 QPS、成功率、RT 等指标的监控与告警配置。 |
使用步骤
开启 Embedding 服务
在 Milvus 控制台左侧导航中选择AI 中心,进入 Embedding 服务页面,单击 开通 Embedding 服务 完成开通。
开通后页面展示可用的模型列表,包括:
text-embedding-v4:基于 Qwen3 的多语言文本向量模型,支持 64~2048 维自定义向量维度。
text-embedding-v3:通用文本向量模型。
text-embedding-v2:通用文本向量模型。
qwen3-vl-embedding:多模态向量模型,支持文本、图片、视频输入。
Embedding 服务按区域独立开通,切换区域后需在新区域开通。
关联 Milvus 实例
Embedding 能力依赖 Milvus 2.6 版本实例,支持以下两种关联方式:
创建 2.6 版本集群时直接开启 Embedding。
进入 AI 中心对存量 2.6 版本实例启用 Embedding 模型。
在Embedding 服务列表中,单击目标服务 ID右侧的批量启用,在批量启用弹窗中选择实例。
查看调用指标
进入AI 中心在Embedding 服务页面,单击模型对应的 查看调用信息,可查看以下指标:
Token Usage Overview:Token 消耗总量趋势。
QPS:每秒请求数。
Success Rate:请求成功率。
Token Usage/s:每秒 Token 使用量。
Average RT:平均响应时间。
支持按实例 ID、时间范围和采样间隔进行筛选。
案例一:文搜文语义检索
场景说明
将一批原始文本写入 Milvus,无需预先生成向量。查询时直接输入自然语言问题,Milvus 自动完成查询向量化并返回语义最相关的结果。适用于知识库问答、文档搜索、FAQ 检索等场景。
操作步骤
创建 Collection,定义原始文本字段
document和向量字段dense。通过
Function绑定text-embedding-v4模型。插入测试文本数据。
使用自然语言问题发起检索,返回最相关的文本片段。
示例代码
import random
from pymilvus import MilvusClient, DataType, Function, FunctionType
client = MilvusClient(
uri="http://c-xxxx.milvus.aliyuncs.com:19530",
token='root:xxx',
)
# ========== 创建 Collection ==========
collection_name = 'demo1'
schema = client.create_schema()
schema.add_field("id", DataType.INT64, is_primary=True, auto_id=False)
schema.add_field("document", DataType.VARCHAR, max_length=9000)
schema.add_field("dense", DataType.FLOAT_VECTOR, dim=1024)
text_embedding_function = Function(
name="dashscope_api_test123",
function_type=FunctionType.TEXTEMBEDDING,
input_field_names=["document"],
output_field_names=["dense"],
params={
"provider": "aliyun_milvus",
"model_name": "text-embedding-v4"
}
)
schema.add_function(text_embedding_function)
index_params = client.prepare_index_params()
index_params.add_index(
field_name="dense",
index_type="AUTOINDEX",
metric_type="COSINE"
)
client.drop_collection(collection_name)
client.create_collection(
collection_name=collection_name,
schema=schema,
index_params=index_params
)
insert_data = []
record_id = 1
mock_texts = [
"向量数据库通过将文本转换为高维向量来实现语义检索,能够理解查询的真实意图。",
"深度学习模型可以从大量图片数据中学习特征表示,用于图像分类和目标检测等任务。",
"检索增强生成通过召回相关文档为大模型提供外部知识,减少幻觉并提升答案可靠性。",
"在客服场景中,语义搜索可以快速定位历史工单,提高首次响应效率和问题解决率。",
"相似度搜索常用余弦距离衡量向量方向接近程度,适合文本语义匹配任务。",
"数据清洗是向量化前的重要步骤,去噪和统一格式能显著提升召回质量。",
"embedding 维度并非越高越好,需要在效果、延迟和存储成本之间做平衡。",
"通过分块策略将长文档切分为小段,可以提高检索命中率并减少上下文冗余。",
"为每条知识添加来源字段有助于结果可解释性,方便在前端展示引用证据。",
"多语言检索可借助统一向量空间实现跨语言匹配,提升国际化系统体验。",
"索引参数如 ef 和 M 会影响 HNSW 的召回率与查询性能,需要结合业务调优。",
"在上线前进行离线评测可以量化不同模型和参数组合的检索质量差异。",
"向量库中的元数据过滤能够与语义召回结合,实现更精准的范围限定搜索。",
"对于高频问题,可将检索结果做短时缓存以降低后端计算压力和响应时间。",
"语义检索链路应记录查询日志,便于后续分析零结果查询并持续优化知识库。",
"当知识内容更新频繁时,增量索引策略可以减少全量重建带来的资源消耗。",
"在问答系统中加入重排序模型可以提升前几条结果的相关性和可读性。",
"为敏感数据建立访问控制策略是企业级向量检索系统的基础安全要求。",
"合理设置 topK 能在覆盖率与噪声之间取得平衡,避免返回过多低相关结果。",
"将用户反馈回流到训练与评估流程,可以持续改进检索与问答整体效果。",
]
for text in mock_texts:
insert_data.append({
"id": record_id,
"document": text,
})
record_id += 1
BATCH_SIZE = 30
print(f"准备插入 {len(insert_data)} 条数据,batch_size={BATCH_SIZE}...")
for batch_start in range(0, len(insert_data), BATCH_SIZE):
batch = insert_data[batch_start:batch_start + BATCH_SIZE]
client.insert(collection_name, batch)
print(f" 已插入 {min(batch_start + BATCH_SIZE, len(insert_data))}/{len(insert_data)} 条")
print("数据插入完成!\n")
# ========== 测试1:文搜文(通过 dense 字段做纯文本语义检索) ==========
print("=" * 60)
print("测试1:文搜文 - 查询与向量数据库相关的内容")
print("=" * 60)
results = client.search(
collection_name=collection_name,
data=['什么是语义检索?向量数据库如何工作?'],
anns_field='dense',
limit=3,
output_fields=['document'],
)
for hits in results:
for hit in hits:
print(f" id={hit['id']}, distance={hit['distance']:.4f}, document={hit['entity']['document'][:50]}")运行结果
准备插入 20 条数据,batch_size=30...
已插入 20/20 条
数据插入完成!
============================================================
测试1:文搜文 - 查询与向量数据库相关的内容
============================================================
id=1, distance=0.8197, document=向量数据库通过将文本转换为高维向量来实现语义检索,能够理解查询的真实意图。
id=13, distance=0.6906, document=向量库中的元数据过滤能够与语义召回结合,实现更精准的范围限定搜索。
id=18, distance=0.6563, document=为敏感数据建立访问控制策略是企业级向量检索系统的基础安全要求。案例二:多模态检索
场景说明
在零售、电商和内容平台场景中,检索对象除文本外还包括图片和视频。Milvus Embedding 服务支持将文本与视觉内容纳入统一的向量化与检索流程,实现文搜图/视频、图搜图/视频等跨模态检索能力。
本示例中使用的图片,可从此处下载。
操作步骤
将本地测试图片或视频上传到 OSS,生成可访问的签名 URL。
创建 Collection,定义文本字段
document、多媒体地址字段url、文本向量字段dense和多模态向量字段dense_mm。分别为文本和多媒体字段绑定
text-embedding-v4与qwen3-vl-embedding。插入测试素材及对应文本描述。
执行文搜文、文搜图/视频、图搜图/视频等检索验证。
示例代码
import glob
import os
import random
import oss2
from pymilvus import MilvusClient, DataType, Function, FunctionType
client = MilvusClient(
uri="http://c-xxxx.milvus.aliyuncs.com:19530",
token='root:xxx',
)
# ========== OSS 配置:上传多媒体资源并生成签名 URL ==========
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
OSS_ACCESS_KEY_ID = os.environ['OSS_ACCESS_KEY_ID']
OSS_ACCESS_KEY_SECRET = os.environ['OSS_ACCESS_KEY_SECRET']
OSS_ENDPOINT = 'https://oss-cn-hangzhou.aliyuncs.com'
OSS_BUCKET_NAME = '002test'
auth = oss2.Auth(OSS_ACCESS_KEY_ID, OSS_ACCESS_KEY_SECRET)
bucket = oss2.Bucket(auth, OSS_ENDPOINT, OSS_BUCKET_NAME)
def upload_and_sign(relative_path, oss_key_prefix="milvus-embedding-test"):
"""上传本地多媒体资源到 OSS 并返回签名 URL(有效期 1 小时)"""
full_path = os.path.join(SCRIPT_DIR, relative_path)
oss_key = f"{oss_key_prefix}/{os.path.basename(relative_path)}"
bucket.put_object_from_file(oss_key, full_path)
signed_url = bucket.sign_url('GET', oss_key, 3600)
print(f" 上传成功: {oss_key} -> {signed_url[:80]}...")
return signed_url
def upload_directory_with_patterns(
directory_relative_path,
patterns,
oss_key_prefix="milvus-embedding-test",
random_pick_count=None,
):
"""上传目录下匹配后缀的多媒体文件到 OSS,返回 {文件名: 签名 URL}"""
full_dir = os.path.join(SCRIPT_DIR, directory_relative_path)
matched_files = []
for pattern in patterns:
matched_files.extend(glob.glob(os.path.join(full_dir, pattern)))
matched_files = sorted(set(matched_files))
if random_pick_count is not None and len(matched_files) > random_pick_count:
matched_files = random.sample(matched_files, random_pick_count)
result = {}
for file_path in matched_files:
filename = os.path.basename(file_path)
relative_path = os.path.join(directory_relative_path, filename)
signed_url = upload_and_sign(relative_path, oss_key_prefix)
result[filename] = signed_url
return result
# 上传 banana 和 orange 目录下的测试素材到 OSS
print("上传测试素材到 OSS...")
banana_urls = upload_directory_with_patterns("qwen-vl/train/banana", ["*.JPEG"])
orange_urls = upload_directory_with_patterns("qwen-vl/train/orange", ["*.JPEG"])
print(f"banana 目录上传 {len(banana_urls)} 张,orange 目录上传 {len(orange_urls)} 张")
print("图片素材上传完成!\n")
# 上传视频到 OSS(与图片素材逻辑一致)
print("上传视频到 OSS...")
selected_video_urls = upload_directory_with_patterns(
"qwen-vl/short_video_10_of_42",
["*.mp4", "*.MP4", "*.mov", "*.MOV"],
random_pick_count=5,
)
print(f"short_video_10_of_42 目录上传并选用 {len(selected_video_urls)} 个视频")
print("视频上传完成!\n")
banana_url_list = list(banana_urls.values())
orange_url_list = list(orange_urls.values())
selected_video_url_list = list(selected_video_urls.values())
# ========== 创建 Collection ==========
collection_name = 'demo11'
schema = client.create_schema()
schema.add_field("id", DataType.INT64, is_primary=True, auto_id=False)
schema.add_field("document", DataType.VARCHAR, max_length=9000)
schema.add_field("url", DataType.VARCHAR, max_length=9000)
schema.add_field("dense", DataType.FLOAT_VECTOR, dim=1024)
schema.add_field("dense_mm", DataType.FLOAT_VECTOR, dim=1024)
text_embedding_function = Function(
name="dashscope_api_test123",
function_type=FunctionType.TEXTEMBEDDING,
input_field_names=["document"],
output_field_names=["dense"],
params={
"provider": "aliyun_milvus",
"model_name": "text-embedding-v4"
}
)
mm_embedding_function = Function(
name="dashscope_api_mm123",
function_type=FunctionType.TEXTEMBEDDING,
input_field_names=["url"],
output_field_names=["dense_mm"],
params={
"provider": "aliyun_milvus",
"model_name": "qwen3-vl-embedding",
"dim": "1024"
}
)
schema.add_function(text_embedding_function)
schema.add_function(mm_embedding_function)
index_params = client.prepare_index_params()
index_params.add_index(
field_name="dense",
index_type="AUTOINDEX",
metric_type="COSINE"
)
index_params.add_index(
field_name="dense_mm",
index_type="AUTOINDEX",
metric_type="COSINE"
)
client.drop_collection(collection_name)
client.create_collection(
collection_name=collection_name,
schema=schema,
index_params=index_params
)
# ========== 插入中文测试数据,多媒体资源使用 OSS 签名 URL ==========
banana_descriptions = [
'一串成熟的黄色香蕉挂在热带果园的树上,阳光透过叶片洒下斑驳的光影。',
'超市水果区摆放着整齐的香蕉,旁边的价签标注着今日特价,吸引了不少顾客驻足挑选。',
'厨房桌上放着几根香蕉和一杯牛奶,这是一份简单而健康的早餐搭配。',
'刚从树上摘下来的青皮香蕉整齐地码放在竹筐里,等待自然催熟后上市销售。',
'小朋友手里拿着一根香蕉,开心地在公园里边走边吃,脸上洋溢着满足的笑容。',
'烘焙师将熟透的香蕉捣成泥,准备制作一款经典的香蕉蛋糕,厨房里弥漫着甜香。',
'热带雨林中野生的香蕉树成片生长,巨大的叶片在微风中轻轻摇曳。',
'早餐桌上切好的香蕉片搭配燕麦和酸奶,是一份营养均衡的健康早餐。',
'水果摊老板正在给顾客称量一大串新鲜香蕉,秤上显示刚好三斤。',
'几根香蕉和苹果、橙子一起摆放在果盘中,色彩鲜艳,赏心悦目。',
]
orange_descriptions = [
'果园里挂满枝头的橙子在阳光下泛着金黄色的光泽,丰收的季节令人喜悦。',
'一杯鲜榨橙汁放在桌上,旁边摆着切开的橙子,果肉饱满多汁。',
'妈妈正在厨房里剥橙子,空气中弥漫着清新的柑橘香气,孩子们围在旁边等着吃。',
'超市货架上整齐排列着脐橙和血橙,不同品种的橙子各有特色。',
'冬日午后,一盘切好的橙子摆在茶几上,是全家人最爱的下午茶水果。',
'橙子皮被巧手的奶奶晒干后泡茶,据说有理气健脾的功效。',
'果农小心翼翼地将刚采摘的橙子装进纸箱,准备发往全国各地的客户手中。',
'甜品店橱窗里展示着精美的橙子慕斯蛋糕,橙色的外观十分诱人。',
'一颗被切成两半的橙子露出鲜嫩的果肉,汁水丰富,让人垂涎欲滴。',
'小区门口的水果店挂出了赣南脐橙到货的招牌,引来不少居民排队购买。',
]
video_descriptions = [
"一段水果主题短视频,展示果园采摘与运输过程。",
"一段水果门店陈列短视频,镜头近距离展示果实细节。",
"一段饮品制作短视频,包含切片、压榨与装杯过程。",
"一段甜品制作短视频,展示果肉装饰和成品呈现。",
]
insert_data = []
record_id = 1
for idx, img_url in enumerate(banana_url_list):
insert_data.append({
'id': record_id,
'document': banana_descriptions[idx],
'url': img_url,
})
record_id += 1
for idx, img_url in enumerate(orange_url_list):
insert_data.append({
'id': record_id,
'document': orange_descriptions[idx],
'url': img_url,
})
record_id += 1
for idx, video_url in enumerate(selected_video_url_list):
insert_data.append({
"id": record_id,
"document": video_descriptions[idx % len(video_descriptions)],
"url": video_url,
})
record_id += 1
# 纯文本数据(url 字段填文本,dense_mm 也会生成对应的文本向量)
insert_data.append({
'id': record_id,
'document': '向量数据库通过将文本转换为高维向量来实现语义检索,能够理解查询的真实意图。',
'url': '向量数据库通过将文本转换为高维向量来实现语义检索,能够理解查询的真实意图。',
})
record_id += 1
insert_data.append({
'id': record_id,
'document': '深度学习模型可以从大量图片数据中学习特征表示,用于图像分类和目标检测等任务。',
'url': '深度学习模型可以从大量图片数据中学习特征表示,用于图像分类和目标检测等任务。',
})
BATCH_SIZE = 20
print(f"准备插入 {len(insert_data)} 条数据,batch_size={BATCH_SIZE}...")
for batch_start in range(0, len(insert_data), BATCH_SIZE):
batch = insert_data[batch_start:batch_start + BATCH_SIZE]
client.insert(collection_name, batch)
print(f" 已插入 {min(batch_start + BATCH_SIZE, len(insert_data))}/{len(insert_data)} 条")
print("数据插入完成!\n")
# ========== 测试1:文搜文(通过 dense 字段做纯文本语义检索) ==========
print("=" * 60)
print("测试1:文搜文 - 查询与向量数据库相关的内容")
print("=" * 60)
results = client.search(
collection_name=collection_name,
data=['什么是语义检索?向量数据库如何工作?'],
anns_field='dense',
limit=3,
output_fields=['document', 'url'],
)
for hits in results:
for hit in hits:
print(f" id={hit['id']}, distance={hit['distance']:.4f}, document={hit['entity']['document'][:50]}")
# ========== 测试2:文搜图 / 视频(通过 dense_mm 字段,用文本查询匹配多媒体内容) ==========
print("\n" + "=" * 60)
print("测试2:文搜图 / 视频 - 用文本描述搜索香蕉相关的图片和视频素材")
print("=" * 60)
results = client.search(
collection_name=collection_name,
data=['黄色香蕉'],
anns_field='dense_mm',
limit=3,
output_fields=['document', 'url'],
)
for hits in results:
for hit in hits:
has_image = "有图" if hit['entity'].get('url') else "无图"
print(f" id={hit['id']}, distance={hit['distance']:.4f}, [{has_image}] document={hit['entity']['document'][:50]}")
# ========== 测试3:图搜图 / 视频(通过 dense_mm 字段,用视觉素材查询匹配多媒体内容) ==========
print("\n" + "=" * 60)
print("测试3:图搜图 / 视频 - 用 orange 目录中的视觉素材搜索相似图片和视频")
print("=" * 60)
results = client.search(
collection_name=collection_name,
data=[orange_url_list[0]],
anns_field='dense_mm',
limit=3,
output_fields=['document', 'url'],
)
for hits in results:
for hit in hits:
has_media = "有媒体" if hit['entity'].get('url') else "无媒体"
print(f" id={hit['id']}, distance={hit['distance']:.4f}, [{has_media}] document={hit['entity']['document'][:50]}")运行结果
准备插入 27 条数据,batch_size=20...
已插入 20/27 条
已插入 27/27 条
数据插入完成!
============================================================
测试1:文搜文 - 查询与向量数据库相关的内容
============================================================
id=26, distance=0.8197, document=向量数据库通过将文本转换为高维向量来实现语义检索,能够理解查询的真实意图。
id=27, distance=0.3091, document=深度学习模型可以从大量图片数据中学习特征表示,用于图像分类和目标检测等任务。
id=22, distance=0.2337, document=一段水果门店陈列短视频,镜头近距离展示果实细节。
============================================================
测试2:文搜图 / 视频 - 用文本描述搜索香蕉相关的图片和视频素材
============================================================
id=9, distance=0.4862, [有图] document=水果摊老板正在给顾客称量一大串新鲜香蕉,秤上显示刚好三斤。
id=1, distance=0.4834, [有图] document=一串成熟的黄色香蕉挂在热带果园的树上,阳光透过叶片洒下斑驳的光影。
id=7, distance=0.4797, [有图] document=热带雨林中野生的香蕉树成片生长,巨大的叶片在微风中轻轻摇曳。
============================================================
测试3:图搜图 / 视频 - 用 orange 目录中的视觉素材搜索相似图片和视频
============================================================
id=11, distance=1.0000, [有媒体] document=果园里挂满枝头的橙子在阳光下泛着金黄色的光泽,丰收的季节令人喜悦。
id=12, distance=0.9898, [有媒体] document=一杯鲜榨橙汁放在桌上,旁边摆着切开的橙子,果肉饱满多汁。
id=13, distance=0.9858, [有媒体] document=妈妈正在厨房里剥橙子,空气中弥漫着清新的柑橘香气,孩子们围在旁边等着吃。