O Alibaba Cloud Milvus 2.6 oferece AI Functions que executam inferência de modelos diretamente durante operações de escrita e busca. Este tutorial demonstra como usar essas funções para analisar frames de direção autônoma. Crie uma única collection, associe várias funções a ela e execute buscas de texto para frame, imagem para frame, filtragem estruturada para mineração de casos extremos e reclassificação multimodal. Ao final, você terá um pipeline em uma única collection que transforma URLs de frames brutos em vetores pesquisáveis e dados estruturados de cena.
Visão geral da solução
Em um pipeline de desenvolvimento de direção autônoma, as câmeras veiculares são o ponto inicial de entrada de dados. Um veículo de teste geralmente possui de 6 a 12 câmeras de visão surround que capturam imagens entre 20 e 30 FPS, gerando vários terabytes de vídeo por dia de testes em estrada. Esse vídeo é o ativo principal para treinar modelos de percepção, reproduzir acidentes e iterar estratégias de planejamento e controle, mas seu volume massivo, natureza não estruturada e dificuldade de busca representam desafios significativos.
O valor não está no clipe de vídeo em si, mas nos eventos que ocorrem dentro dele. A compreensão de cenas no nível do frame deve identificar participantes do tráfego (pedestres, veículos não motorizados, veículo à frente), elementos de controle de tráfego (status de semáforos, faixas, placas de limite de velocidade), o ambiente viário (cruzamento, rodovia, túnel, zona de construção) e eventos anômalos (cortes de faixa, avanço de sinal vermelho, frenagens bruscas, detritos na pista). Somente após a persistência desses resultados como dados estruturados é possível dar suporte aos seguintes cenários:
Replay de dados — Reinsira frames reais de estrada em pipelines de simulação e treinamento para iterar modelos de percepção.
Mineração de casos extremos — Recupere cenários de cauda longa, como "uma zona de construção na entrada de um túnel em uma noite chuvosa", a partir de um vasto conjunto de frames. Esses são os casos em que os modelos falham com mais frequência e as amostras são mais escassas.
Rotulagem automática — Use um modelo grande para gerar rótulos aproximados para os frames, evitando grandes volumes de anotação manual de bounding boxes e deixando a revisão e o refinamento para humanos.
Relatórios de testes de estrada — Agregue estatísticas por cena e evento para produzir relatórios periódicos quantificados. As AI Functions do Milvus 2.6 integram essas capacidades diretamente ao banco de dados vetorial. Em vez de manter um pipeline externo separado, a inferência do modelo é executada como uma função acionada internamente pelo Milvus durante operações de
insertesearch. Este tutorial usa as seguintes funções.
|
Capacidade |
Tipo de função no código |
Finalidade |
Uso na análise de frames |
|
|
|
Converte a imagem do frame em um vetor de 2.560 dimensões com |
Suporta busca de texto para frame e imagem para frame, permitindo que solicitações semânticas como "zona de construção na chuva" sejam correspondidas via recuperação vetorial. |
|
|
|
Selecione o rótulo mais adequado para cada frame a partir de um conjunto predefinido e o grava em um campo de classificação. |
Marca cada frame com um rótulo de cena, como cruzamento, rodovia, túnel ou zona de construção. |
|
|
|
Extrai elementos de um frame com base nos rótulos especificados e os grava em um campo estruturado como json. |
Extrai status de semáforo, contagem de faixas, clima e eventos anômalos para filtragem precisa. |
|
|
|
Reconhece entidades nomeadas que aparecem explicitamente em um frame. |
Extrai nomes de locais, nomes de estradas e valores de limite de velocidade a partir de placas. |
|
|
|
Pontua novamente os top-N resultados de recuperação vetorial com |
Reclassifica resultados pela consistência de sujeito, ação e cena para melhorar o ranking fino na mineração de casos extremos. |
Os nomes das capacidades (como AI_EMBEDDING) referem-se conceitualmente às funções, enquanto o código usa os valores correspondentes de function_type e task mostrados na tabela. As quatro primeiras funções são executadas automaticamente na escrita e formam o pipeline de "inferência na escrita". A função AI_RERANK aplica-se ao estágio de recuperação e atua como um aprimoramento opcional de ranking fino.
Pré-requisitos
Uma instância do Milvus 2.6. As AI Functions exigem o kernel 2.6, e não é necessário vincular um service de modelo separado após criar a instância.
Acesso à rede pública, caso você se conecte pela Internet. Na aba Security Configuration da página de detalhes da instância, ative o Public Network Access e adicione o IP de saída do seu cliente à lista de permissões de acesso público.
pymilvus instalado. Os exemplos deste tutorial foram validados com pymilvus 3.0.0.
ffmpeg instalado, caso você mesmo extraia os frames.
Imagens dos frames carregadas em um endereço acessível pelo modelo via Internet, como o OSS.
O endpoint de conexão (URI) e o token de acesso da sua instância Milvus, fornecidos na Etapa 2.
Limitações e considerações
Revise as restrições abaixo antes de executar o procedimento. Ignorar qualquer uma delas causará falhas ou resultados incorretos silenciosos.
Porta de conexão — A interface RESTful e o gRPC compartilham a porta 19530, portanto, especifique a porta explicitamente, por exemplo
http://c-xxx.milvus.aliyuncs.com:19530. Se você omitir a porta, a solicitação será direcionada à porta 80 por padrão e a conexão expirará.Dimensão do vetor — O parâmetro
dimdo campo vetorial deve corresponder aodimnos parâmetros da função de embedding (2560).Flag multimodal — Uma função multimodal deve incluir
"is_multimodal": "true".Valores padrão para rótulos de extração — Defina um valor padrão para cada rótulo de extração. Quando um campo json é nulo, uma condição
!=não corresponde a essa linha, o que remove silenciosamente frames dos resultados filtrados.Tamanho do lote de embedding — O limite de lote multimodal do
qwen3-vl-embeddingé 10. Um lote que excede esse limite retorna o erroimage batch size can should be [1, 10](retornado literalmente pelo service). Grave frames em lotes de 10 ou menos.Flush após escritas — Após uma escrita, chame
flush(). Caso contrário, uma busca executada imediatamente depois pode retornar resultados vazios.Reclassificação via REST — O parâmetro
documentsda interface REST de rerank aceita um array de strings de URL de frames e não suportatop_n, retornando uma pontuação para cada candidato.Invocações de modelo por frame — Cada frame ingerido aciona quatro invocações de função (embedding, classificação, extração e reconhecimento de entidades) executadas automaticamente na escrita. Considere isso ao planejar a capacidade para grandes conjuntos de frames.
Procedimento
Este procedimento constrói o pipeline de ponta a ponta. Observe os pontos abaixo antes de começar:
A Etapa 2 prepara o código de conexão compartilhado, do qual as Etapas 3 a 6 dependem. Execute-a primeiro.
A Etapa 1 é necessária apenas se você ainda não possuir imagens de frames.
A Etapa 6 (reclassificação) é opcional.
A Etapa 7 remove os resources criados.
Etapa 1: Extrair frames do vídeo
Câmeras veiculares capturam vídeo contínuo, então primeiro extraia frames com ffmpeg, carregue as imagens em um endereço acessível ao modelo e passe o frame_url para o código de ingestão. Durante a extração, registre o clip_id e o ts_ms de cada frame (ID do clipe de vídeo e timestamp do frame) para rastrear um resultado de busca até o vídeo original.
# Time-based extraction: take one frame every second and scale it to a width of 960
ffmpeg -i clip001.mp4 -vf "fps=1,scale=960:-1" -q:v 3 frames/clip001_%04d.jpg
# Keyframes only (I-frames): more information and less redundancy
ffmpeg -i clip001.mp4 -vf "select='eq(pict_type,I)',scale=960:-1" -fps_mode vfr -q:v 3 frames/clip001_key_%04d.jpg
# A single frame at a specific point in time (for example, 5.2s, which corresponds to ts_ms=5200)
ffmpeg -ss 5.2 -i clip001.mp4 -frames:v 1 -vf scale=960:-1 -q:v 3 frames/clip001_5200ms.jpg
Escolha a estratégia de extração com base no seu objetivo:
Extração baseada em tempo — Amostragem regular para construir conjuntos de dados de treinamento e replay.
Apenas keyframes (I-frames) — Mais informação com menos redundância. Recomendado para mineração de casos extremos em larga escala, onde reduzir a contagem de frames controla custos.
Um único frame em um momento específico — Isola um momento conhecido, como reproduzir um acidente em um timestamp exato. O valor
-1emscale=960:-1calcula a altura automaticamente mantendo a proporção original. Use-fps_mode vfrpara capturar I-frames. A sintaxe antiga-vsync vfrainda funciona, mas gera um aviso de descontinuação.
Etapa 2: Preparar o código de conexão compartilhado
O código abaixo contém as configurações de conexão e um wrapper de compatibilidade para o tipo de função TEXTTRANSFORM. Substitua MILVUS_URI e MILVUS_TOKEN pelos valores da sua instância.
from __future__ import annotations
import json
from pymilvus import DataType, Function, FunctionType, MilvusClient
MILVUS_URI = "http://c-xxxx.milvus.aliyuncs.com:19530"
MILVUS_TOKEN = "root:xxx"
# Alibaba Cloud Milvus exposes TEXTTRANSFORM as a managed extension whose function type value is 9.
# Some pymilvus versions do not include this member in the FunctionType enum, so the wrapper below adds it.
TEXTTRANSFORM_FUNCTION_TYPE = 9
def texttransform_function_type() -> FunctionType:
for type_name in ("TEXTTRANSFORM", "TEXT_TRANSFORM", "TextTransform"):
function_type = getattr(FunctionType, type_name, None)
if function_type is not None:
return function_type
existing = getattr(FunctionType, "_value2member_map_", {}).get(TEXTTRANSFORM_FUNCTION_TYPE)
if existing is not None:
return existing
extension = int.__new__(FunctionType, TEXTTRANSFORM_FUNCTION_TYPE)
extension._name_ = "TEXTTRANSFORM"
extension._value_ = TEXTTRANSFORM_FUNCTION_TYPE
FunctionType._value2member_map_[TEXTTRANSFORM_FUNCTION_TYPE] = extension
FunctionType._member_map_["TEXTTRANSFORM"] = extension
return extension
VECTOR_DIM = 2560
EMBED_MODEL = "qwen3-vl-embedding"
VLM_MODEL = "qwen3.7-plus" # Multimodal understanding model (classification, extraction, entities)
client = MilvusClient(uri=MILVUS_URI, token=MILVUS_TOKEN)
As funções AI_CLASSIFY, AI_EXTRACT e AI_ENTITY_EXTRACT são todas do tipo TEXTTRANSFORM (valor de tipo de função 9) e se distinguem pelo parâmetro task. Algumas versões do pymilvus não incluem este membro no enum FunctionType, motivo pelo qual o wrapper de compatibilidade é necessário.
Se você alterar o EMBED_MODEL, a dimensão do vetor também poderá mudar. Mantenha o VECTOR_DIM e o schema da Etapa 3 sincronizados com o modelo escolhido, pois reindexar uma collection existente é uma operação de alto custo.
O exemplo codifica credenciais apenas para fins de demonstração. Em produção, carregue MILVUS_URI e MILVUS_TOKEN a partir de variáveis de ambiente ou de um gerenciador de segredos, não faça commit deles no controle de versão e evite usar a conta root.
Etapa 3: Criar a collection e associar as AI functions
O campo vetorial serve para recuperação semântica, enquanto os outros três campos armazenam os resultados de classificação, extração estruturada e reconhecimento de entidades. Todas as quatro funções recebem frame_url como entrada e são executadas automaticamente na escrita.
Se já existir uma collection chamada driving_frames, o exemplo a excluirá permanentemente junto com seus dados. Use um nome de collection exclusivo se tiver dados existentes ou faça backup antes. A ramificação de exclusão foi incluída para permitir que você reexecute o tutorial a partir de um estado limpo.
# ===== Create the collection: attach four AI Functions in a single definition =====
collection_name = "driving_frames"
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("frame_url", DataType.VARCHAR, max_length=4096) # Frame image address
schema.add_field("clip_id", DataType.VARCHAR, max_length=128) # ID of the source video clip
schema.add_field("ts_ms", DataType.INT64) # Frame timestamp (ms)
schema.add_field("embedding", DataType.FLOAT_VECTOR, dim=VECTOR_DIM)
schema.add_field("scene", DataType.VARCHAR, max_length=64) # AI_CLASSIFY output
schema.add_field("attributes", DataType.JSON) # AI_EXTRACT output
schema.add_field("entities", DataType.JSON) # AI_ENTITY_EXTRACT output
# 1) Multimodal embedding: frame image -> 2,560-dimensional vector
schema.add_function(Function(
name="embed_frame", function_type=FunctionType.TEXTEMBEDDING,
input_field_names=["frame_url"], output_field_names=["embedding"],
params={"provider": "aliyun_milvus", "model_name": EMBED_MODEL,
"dim": VECTOR_DIM, "is_multimodal": "true"}))
# 2) Scene classification: intersection/highway/tunnel/construction zone/ordinary road
schema.add_function(Function(
name="classify_scene", function_type=texttransform_function_type(),
input_field_names=["frame_url"], output_field_names=["scene"],
params={"provider": "aliyun_milvus", "model_name": VLM_MODEL,
"task": "ai_classify", "media_type": "image",
"labels": "intersection,highway,tunnel,construction zone,ordinary road",
"prompt": "Classify the frame by the road environment it shows.", "temperature": "0"}))
# 3) Structured extraction: traffic light/lane count/weather/abnormal event
# Note: define a default value for every label to prevent the model from returning null (see the description below)
schema.add_function(Function(
name="extract_traffic", function_type=texttransform_function_type(),
input_field_names=["frame_url"], output_field_names=["attributes"],
params={"provider": "aliyun_milvus", "model_name": VLM_MODEL,
"task": "ai_extract", "media_type": "image",
"labels": "traffic_light,lane_count,weather,anomaly_event",
"prompt": "traffic_light takes red/green/yellow/none; "
"weather takes sunny/rainy/cloudy/night/unknown, use unknown when it cannot be determined; "
"lane_count takes an integer, use 0 when it cannot be determined; "
"anomaly_event describes abnormal events such as cut-ins, red light running, or accidents, use none if there are none.",
"temperature": "0"}))
# 4) Named entities: road names/place names/speed limit values
schema.add_function(Function(
name="extract_entities", function_type=texttransform_function_type(),
input_field_names=["frame_url"], output_field_names=["entities"],
params={"provider": "aliyun_milvus", "model_name": VLM_MODEL,
"task": "ai_entity_extract", "media_type": "image",
"entity_types": "LOCATION,PRODUCT",
"prompt": "Extract only the place names, road names, and speed limit values that explicitly appear on traffic signs in the frame. Do not guess from appearance.",
"temperature": "0"}))
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)
Os principais parâmetros da função são:
provider— O provedor do modelo. Este tutorial usaaliyun_milvus.model_name— O modelo que sustenta a função.task— Selecione a operação para uma funçãoTEXTTRANSFORM:ai_classify,ai_extractouai_entity_extract.media_type— Defina comoimageporque a entrada é uma imagem de frame.labels— O conjunto de rótulos separados por vírgula para classificação ou extração.entity_types— Os tipos de entidade separados por vírgula a serem reconhecidos. Este exemplo mapeia nomes de locais e estradas paraLOCATIONe valores de limite de velocidade paraPRODUCT.temperature— Defina como0para saída determinística.is_multimodaledim— Consulte Limitations and considerations. Uma função multimodal deve definiris_multimodal, e odimdo campo vetorial deve corresponder aodimda função (2560).No SDK, os valores deparamssão passados como strings (por exemplo,"is_multimodal": "true"e"temperature": "0"). No payload REST usado na Etapa 6, os valores deparamsusam tipos json nativos (por exemplo,"is_multimodal": true).
Defina um valor padrão para cada rótulo de extração. O prompt acima atribui um padrão para cada rótulo para evitar que o modelo retorne null. Isso é importante porque um campo json null não é correspondido por uma condição !=, o que remove silenciosamente frames dos resultados filtrados. Valores padrão reduzem, mas nem sempre eliminam, valores ausentes: conforme mostram os resultados de ingestão na Etapa 4, o frame do túnel ainda não possui valor de weather. Projete seus filtros para lidar com valores ausentes e consulte Limitations and considerations.
Uma ingestão bem-sucedida na Etapa 4 (campos preenchidos automaticamente) confirma que a collection e suas quatro funções estão associadas corretamente.
Etapa 4: Ingerir frames com inferência na escrita
Grave apenas os três campos frame_url, clip_id e ts_ms. As AI Functions preenchem os outros quatro campos automaticamente, sem anotação manual.
# ===== Ingest frames: inference on write populates embedding/scene/attributes/entities automatically =====
# Replace frame_url with your own image address that the model can access over the Internet.
# clip_id and ts_ms record the source clip and timestamp of the frame, so you can trace a hit back to the original video.
frames = [
{"frame_url": "https://<your-bucket>.oss-cn-hangzhou.aliyuncs.com/frames/clip_dashcam_5000.jpg",
"clip_id": "clip_dashcam", "ts_ms": 5000},
{"frame_url": "https://<your-bucket>.oss-cn-hangzhou.aliyuncs.com/frames/clip_dashcam_12000.jpg",
"clip_id": "clip_dashcam", "ts_ms": 12000},
{"frame_url": "https://<your-bucket>.oss-cn-hangzhou.aliyuncs.com/frames/clip_highway_3000.jpg",
"clip_id": "clip_highway", "ts_ms": 3000},
{"frame_url": "https://<your-bucket>.oss-cn-hangzhou.aliyuncs.com/frames/clip_highway_9000.jpg",
"clip_id": "clip_highway", "ts_ms": 9000},
{"frame_url": "https://<your-bucket>.oss-cn-hangzhou.aliyuncs.com/frames/clip_urban_2000.jpg",
"clip_id": "clip_urban", "ts_ms": 2000},
{"frame_url": "https://<your-bucket>.oss-cn-hangzhou.aliyuncs.com/frames/clip_urban_8000.jpg",
"clip_id": "clip_urban", "ts_ms": 8000},
]
# Keep each batch at or below the multimodal batch limit of 10 (see Limitations and considerations).
_BATCH = 8
for _i in range(0, len(frames), _BATCH):
client.insert(collection_name, frames[_i:_i + _BATCH])
client.flush(collection_name)
client.load_collection(collection_name)
# After the write completes, query the structured results directly to verify them
ingested_rows = client.query(
collection_name, filter="",
output_fields=["frame_url", "clip_id", "ts_ms", "scene", "attributes", "entities"],
limit=100)
for row in ingested_rows:
print(f"{row['clip_id']}@{row['ts_ms']}ms scene={row['scene']} "
f"attributes={json.dumps(row['attributes'], ensure_ascii=False)} "
f"entities={json.dumps(row['entities'], ensure_ascii=False)}")
O loop de escrita mantém cada lote com 8 itens, respeitando o limite de lote multimodal de 10. Após a escrita, chame flush(); caso contrário, uma busca imediata pode retornar resultados vazios. A consulta de verificação usa limit=100, retornando até 100 linhas para conferência rápida. Para conjuntos maiores de frames, pagine os resultados ou execute uma contagem para confirmar a escrita completa.
Em testes, escrever seis frames mais flush e load levou cerca de 12 segundos. A tabela abaixo mostra a saída de referência para o conjunto de frames de exemplo. Os valores exatos dependem dos seus próprios frames, então foque em verificar se cada campo foi preenchido automaticamente e se a classificação corresponde à imagem.
|
Frame |
scene |
attributes |
entities |
|
dashcam@5000ms |
intersection |
|
[] |
|
dashcam@12000ms |
construction zone |
|
[] |
|
highway@3000ms |
highway |
|
[{"text":"EXIT 111","type":"LOCATION"}] |
|
highway@9000ms |
tunnel |
|
[] |
|
urban@2000ms |
ordinary road |
|
[] |
|
urban@8000ms |
intersection |
|
[] |
A classificação de cena de todos os seis frames correspondeu ao conteúdo da imagem, e o status do semáforo, clima e contagem de faixas extraídos foram consistentes com as imagens nos frames onde esses campos foram preenchidos. Alguns campos ainda estavam ausentes para certos frames (por exemplo, weather para o frame do túnel e traffic_light/lane_count para o frame de estrada comum), razão pela qual você deve considerar valores ausentes ao filtrar. Entidades nomeadas foram extraídas apenas para frames que continham texto legível em placas, e o modelo não inferiu nomes de locais pela aparência, respeitando a restrição "do not guess from appearance" do prompt.
Etapa 5: Buscar frames
Os vetores e campos estruturados produzidos durante a fase de escrita combinam-se na etapa de recuperação: vetores lidam com a recuperação semântica e campos estruturados cuidam da filtragem precisa.
Para buscar por texto
O texto da consulta é mapeado para o espaço vetorial de imagens pelo mesmo modelo multimodal e recuperado diretamente.
query = "construction zone in the rain, with traffic cones and construction signs on the road"
text_search = client.search(
collection_name=collection_name, data=[query], anns_field="embedding",
limit=10, output_fields=["frame_url", "scene", "attributes"])
for hit in text_search[0]:
print(f"score={hit['distance']:.4f} scene={hit['entity']['scene']}")
Para combinar recuperação vetorial com filtragem estruturada (mineração de casos extremos)
Os campos estruturados vêm de AI_EXTRACT e AI_CLASSIFY no momento da escrita e podem ser usados diretamente no filter.
corner_query = "abnormal event on the road at night"
corner_search = client.search(
collection_name=collection_name, data=[corner_query], anns_field="embedding",
limit=10, filter='attributes["anomaly_event"] != "none"',
output_fields=["frame_url", "scene", "attributes"])
print(f"Matched {len(corner_search[0])} long-tail scenes")
A filtragem estruturada suporta igualdade de string e comparação numérica. Por exemplo, attributes["lane_count"] >= 4 selecione frames com quatro ou mais faixas. Ao combinar condições, lembre-se de que uma condição != não corresponde a uma linha cujo valor de campo json seja nulo, o que pode remover silenciosamente amostras de cauda longa. Condições combinadas devem corresponder ao conteúdo real da sua biblioteca de frames: scene == "tunnel" and attributes["anomaly_event"] != "none" exige tanto uma cena de túnel quanto um evento anômalo. Se tal frame não existir, a consulta retorna 0 linhas, o que é uma conclusão válida de mineração de casos extremos, não uma falha. Durante a depuração, confirme que o pipeline funciona com uma única condição primeiro e depois adicione condições gradualmente para refinar o escopo.
Para buscar por imagem
Substitua data por uma URL de imagem e mantenha todo o restante inalterado.
image_query_url = frames[0]["frame_url"]
image_search = client.search(
collection_name=collection_name, data=[image_query_url],
anns_field="embedding", limit=10, output_fields=["frame_url", "scene"])
for hit in image_search[0]:
print(f"score={hit['distance']:.4f} scene={hit['entity']['scene']}")
A tabela a seguir apresenta resultados de referência para o conjunto de frames de exemplo. Concentre-se na ordenação relativa, não nas pontuações absolutas, que dependem dos seus próprios frames.
|
Método de busca |
Consulta |
Resultado |
|
Busca texto para frame |
construction zone in the rain, with traffic cones and construction signs on the road |
O frame de zona de construção ficou em primeiro lugar com 0,5904, e o segundo colocado obteve apenas 0,1797, uma margem clara. |
|
Busca imagem para frame |
A URL de um frame de cruzamento usada como consulta |
A própria imagem de consulta pontuou 1,0000, um frame de cruzamento similar pontuou 0,5534 e o frame menos relevante pontuou 0,1162. |
|
Filtragem estruturada |
|
Correspondeu aos frames que realmente contêm um evento anômalo. |
Na busca imagem para frame, a imagem de consulta obtém pontuação 1,0000 contra si mesma. Trata-se de uma verificação reprodutível que você pode usar para validar a consistência da codificação de imagens.
Etapa 6: Reclassificar resultados com a função rerank (opcional)
A similaridade vetorial mede proximidade semântica, o que não equivale totalmente à relevância para a intenção da consulta. Use o qwen3-vl-rerank para refinar a classificação dos frames recuperados e melhorar a qualidade do ranking em cenários de cauda longa. Escolha um dos dois padrões antes de examinar o código:
**Anexar um ranker em
search** — Adequado quando você deseja recuperação e ranking fino em uma única chamada.Chamar a interface REST rerank — Indicada quando você já tem uma lista de frames candidatos e quer apenas reclassificá-la. Ambos os padrões produzem pontuações idênticas para o mesmo frame, então escolha aquele que melhor atender às suas necessidades de engenharia.
O Padrão 1 anexa um ranker em search, permitindo que o qwen3-vl-rerank refine a classificação dos top-N resultados da recuperação vetorial.
# ===== Multimodal reranking with AI_RERANK (optional) =====
QUERY = "cut-in behavior at a highway ramp"
reranker = Function(
name="rerank_frames", function_type=FunctionType.RERANK,
input_field_names=["frame_url"],
params={"reranker": "model", "provider": "aliyun_milvus",
"model_name": "qwen3-vl-rerank", "queries": [QUERY],
"is_multimodal": "true",
"instruct": "Rank candidate frames by relevance to the query, "
"prioritizing subject, action, scene and fine-grained visual details.",
"timeout_sec": 10})
rerank_search = client.search(
collection_name=collection_name, data=[QUERY], anns_field="embedding",
limit=20, output_fields=["frame_url", "scene"], ranker=reranker)
for hit in rerank_search[0]:
print(f"rerank_score={hit['distance']:.4f} scene={hit['entity']['scene']}")
O Padrão 2 reclassifica apenas um conjunto de URLs de frames já recuperados através do endpoint REST /v2/vectordb/ai/rerank. O helper abaixo envia uma requisição json; defina-o antes da chamada REST.
from typing import Any
from urllib.error import HTTPError
from urllib.request import Request, urlopen
def post_json(path: str, body: dict[str, Any], timeout: int = 120) -> tuple[int, dict[str, Any]]:
request = Request(
f"{MILVUS_URI.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"))
rest_docs = [f["frame_url"] for f in frames[:3]]
status, data = post_json("/v2/vectordb/ai/rerank", {
"model_name": "qwen3-vl-rerank", "query": QUERY,
"documents": rest_docs,
"params": {"is_multimodal": True,
"instruct": "Rank candidate frames by relevance to the query, "
"prioritizing subject, action, scene and fine-grained visual details.",
"timeout_sec": 10}})
assert status == 200 and data.get("code") == 0, data
for item in sorted(data["data"]["output"]["results"],
key=lambda x: x["relevance_score"], reverse=True):
print(f"index={item['index']} relevance_score={item['relevance_score']:.4f}")
A instrução assert é uma verificação apenas para o exemplo. Em produção, substitua-a por tratamento explícito de erros que inspecione o status e o code da resposta, registrando o log em vez de lançar exceções.
A reclassificação multimodal exige is_multimodal em params, e você pode usar instruct para especificar o foco da classificação, como priorizar sujeito, ação, cena e detalhes visuais granulares. O parâmetro REST documents aceita um array de strings de URL de frames e não suporta top_n, retornando uma pontuação para cada candidato.
A tabela abaixo exibe resultados de referência de reclassificação para a consulta de teste "cut-in behavior at a highway ramp". Foque na ordenação relativa, não nas pontuações absolutas.
|
Posição |
Cena do frame |
Pontuação rerank |
|
1 |
highway |
0,5898 |
|
2 |
construction zone |
0,5018 |
|
3 |
tunnel |
0,4858 |
|
4 |
intersection |
0,4830 |
|
5 |
intersection |
0,4089 |
|
6 |
ordinary road |
0,3819 |
O frame de rodovia ficou em primeiro lugar, alinhado à semântica da consulta.
Etapa 7: Limpar resources
Após concluir o tutorial, exclua os resources criados para evitar cobranças desnecessárias.
-
Exclua a collection:
client.drop_collection(collection_name) -
Confirme que a collection foi excluída. O comando abaixo deve retornar
False:print(client.has_collection(collection_name)) Exclua as imagens de frames carregadas no OSS para o tutorial, caso não precise mais delas.
(Opcional) Se você ativou o acesso à rede pública apenas para este tutorial, desative o Public Network Access na aba Security Configuration da página de detalhes da instância.
Solução de problemas
Use a tabela a seguir para diagnosticar problemas comuns. As restrições por trás desses sintomas estão descritas em Limitations and considerations.
|
Sintoma |
Causa |
Solução |
|
Uma busca logo após a escrita retorna resultados vazios. |
|
Chame |
|
A conexão expira. |
A porta foi omitida, direcionando a requisição para a porta 80. |
Especifique a porta 19530 explicitamente na URI. |
|
Um filtro combinado retorna 0 linhas. |
Nenhum frame na biblioteca corresponde a todas as condições. |
Pode ser uma conclusão válida de mineração de casos extremos, não uma falha. Valide o pipeline com uma única condição e adicione outras gradualmente. |
|
Uma escrita em lote retorna |
O lote excede o limite multimodal de 10. |
Grave frames em lotes de 10 ou menos. |
Valor da solução
A tabela abaixo compara uma solução tradicional construída internamente com a abordagem de AI Function do Milvus.
|
Dimensão |
Solução tradicional construída internamente |
AI Function do Milvus |
|
Ciclo de desenvolvimento |
Depuração conjunta envolvendo extração de frames, inferência, banco de dados vetorial e banco de metadados |
Associe funções ao criar a collection e grave/busque diretamente |
|
Operações de inferência |
Cluster de inferência GPU próprio que exige dimensionamento e recuperação de falhas |
Invocações gerenciadas pelo Milvus, sem cluster de inferência para operar |
|
Movimentação de dados |
Frames transitam repetidamente entre armazenamento de objetos, cluster de inferência e banco de dados vetorial |
Inferência na escrita, com dados permanecendo na instância Milvus |
|
Recuperação multimodal |
Exige alinhamento manual dos espaços vetoriais de texto e imagem |
|
|
Critérios de anotação |
Anotação manual é lenta, cara e sujeita a variações de critérios |
O |
Para uma equipe de direção autônoma, esta abordagem:
Acelera a mineração de casos extremos — A busca texto para frame combinada com filtragem estruturada e reclassificação multimodal recupera cenários de cauda longa a partir de uma única frase em linguagem natural.
Reduz custos de rotulagem automática —
AI_CLASSIFY,AI_EXTRACTeAI_ENTITY_EXTRACTgeram rótulos com critérios unificados no momento da escrita, delegando revisão e refinamento a humanos.Converge o pipeline — A inferência ocorre dentro do banco de dados vetorial. Escritas produzem vetores e compreensão estruturada, buscas realizam recuperação semântica multimodal e os dados de condução permanecem na instância Milvus.