A AI Function do Alibaba Cloud Milvus oferece dois recursos para redução de custos: o Embedding Cache reutiliza vetores existentes para conteúdo duplicado, e o AI Batch processa grandes volumes de tarefas que podem ser executadas offline. Neste tutorial, você configure ambos os recursos, decide onde cada um se aplica e confirme um acerto de cache usando critérios confiáveis.
Visão geral da solução
Após a entrada em produção de uma aplicação de IA, a pressão sobre os custos geralmente vem de dois tipos de desperdício:
O mesmo conteúdo passa por embedding repetidamente — títulos de product são sincronizados várias vezes, FAQs de suporte são republicadas e parágrafos da base de conhecimento reentram no modelo de embedding devido a retentativas ou importações incrementais. O texto não mudou, então o vetor geralmente também não muda, mas a invocação do modelo e a espera por ela acontecem da mesma forma.
Tarefas que podem esperar usam invocação em tempo real — a invocação em tempo real é o caminho síncrono que retorna cada resultado na resposta, ao contrário dos lotes offline executados pelo AI Batch. Preencher descrições de product, resumir conversas históricas, traduzir conteúdo e inicializar uma base de conhecimento podem ser executados à noite e entregues na manhã seguinte sem afetar a experiência do usuário. Enviar essas tarefas via invocação em tempo real significa pagar preços de tempo real e, ao mesmo tempo, consumir a cota do modelo necessária para buscas online e perguntas e respostas.
Os dois caminhos correspondentes para redução de custos são: gerar embedding do mesmo conteúdo apenas uma vez e processar tarefas que podem esperar em lotes offline. A AI Function do Alibaba Cloud Milvus cobre esses dois casos com Embedding Cache e AI Batch, respectivamente:
|
Recurso |
O que faz |
Desperdício eliminado |
|
Embedding Cache |
Busca um vetor existente por correspondência exata no texto e no contexto de invocação, reutilizando-o em caso de acerto. |
O mesmo conteúdo recebe embedding apenas uma vez, economizando tokens duplicados e tempo de espera. |
|
AI Batch |
As solicitações são gravadas em JSONL e enviadas de forma assíncrona, e a plataforma as executa offline em lotes. |
Grandes tarefas que podem esperar são concluídas a um preço unitário menor, sem consumir cota online. |
Cada recurso aborda um tipo diferente de desperdício. A tabela a seguir mapeia características de negócio para cada recurso:
|
Característica do negócio |
Embedding Cache |
AI Batch |
|
Padrão de dados |
O mesmo texto aparece repetidamente |
Grandes volumes de dados processados pela primeira vez |
|
Requisito de resposta |
Retornado online |
Pode ser concluído posteriormente |
|
Fonte de economia |
Menos invocações duplicadas do modelo |
Execução em lote offline a um preço unitário menor |
|
Cenários comuns |
FAQs e respostas de referência, títulos e atributos populares de product, parágrafos de base de conhecimento importados repetidamente, termos de busca de alta frequência |
Geração de resumos ou tags para documentos arquivados, processamento em lote de dados existentes de product, geração em lote de descrições de ativos, avaliação de modelos e rotulagem de dados, reconstruções periódicas e tarefas noturnas |
Responda a duas perguntas para escolha seu caminho:
O usuário está esperando pelo resultado? Se sim, use invocação em tempo real. Caso contrário, e se o volume for grande, considere o AI Batch.
O conteúdo aparece repetidamente? Em caso afirmativo, ative o Embedding Cache. Se todo o conteúdo aparecer pela primeira vez, o cache trará benefícios limitados; portanto, concentre-se no AI Batch e no controle do número de invocações.
Cenários interativos, como respostas de chat e autocompletar de busca, continuam usando invocação em tempo real por padrão. Enquanto um usuário estiver aguardando o resultado, não mova a solicitação para o AI Batch.
Os dois recursos não são mutuamente exclusivos, e combiná-los é o caminho comum em produção. Considere um cenário de e-commerce. Novos product continuam recebendo embedding em tempo real, e sincronizações repetidas de product populares reutilizam vetores por meio do Embedding Cache. Resumos e tags para vários milhões de product arquivados são entregues ao AI Batch para conclusão durante a noite.
Pré-requisitos
Uma instância Milvus 2.6. A AI Function depende do kernel 2.6, e nenhuma vinculação separada de service de modelo é necessária após a criação da instância.
Para acessar a instância pela Internet, ative o Public Access na aba Security Configuration da página de detalhes da instância e adicione o IP de saída do cliente à lista de permissões de acesso público.
pymilvus instalado para os exemplos de Embedding Cache. Os exemplos neste tópico foram verificados com pymilvus 3.0.0. Os exemplos de AI Batch chamam a API RESTful e usam apenas a biblioteca padrão do Python.
A API RESTful compartilha a porta 19530 com gRPC, portanto especifique a porta explicitamente ao chamá-la, por exemplo http://c-xxx.milvus.aliyuncs.com:19530. Se você omitir a porta, a solicitação irá para a porta 80 por padrão e a conexão atingirá o tempo limite.
Embedding Cache: gere embedding do mesmo conteúdo apenas uma vez
Como o Embedding Cache funciona e onde se aplica
O Embedding Cache primeiro busca um vetor existente pelo texto e pelo contexto de invocação, reutiliza-o diretamente em caso de acerto e invoca o modelo apenas para conteúdo que aparece pela primeira vez. O cache usa correspondência exata, então dois textos são computados separadamente se forem escritos de forma diferente. O benefício vem inteiramente da taxa de duplicação de conteúdo: quanto mais duplicação, mais solicitações de modelo você economiza. Para uma estimativa de ordem de grandeza, consulte Cost estimation.
Se o cache estiver indisponível ou se uma leitura atingir o tempo limite, o Milvus continua a invocar o modelo, de modo que suas gravações e consultas não são interrompidas.
Ativar o Embedding Cache
Adicione a configuração cache aos params da Embedding Function. Defina ttl_hours de acordo com a frequência de alteração do conteúdo: aumente o valor para conteúdo que muda raramente, como títulos de product e FAQs, e reduza-o para conteúdo de mudança rápida, para que novas versões sejam recomputadas mais cedo.
import json
import uuid
from urllib.error import HTTPError
from urllib.request import Request, urlopen
from pymilvus import DataType, Function, FunctionType, MilvusClient
MILVUS_URI = "http://c-xxx.milvus.aliyuncs.com:19530" # The port must be 19530
MILVUS_TOKEN = "root:xxx"
MODEL_NAME = "text-embedding-v4"
VECTOR_DIM = 1024
# Cache configuration: exact match, Redis backend, 24-hour TTL
CACHE_CONFIG = json.dumps({
"enabled": True,
"exact_cache": {
"enabled": True,
"backend": "redis",
"ttl_hours": 24,
},
})
client = MilvusClient(uri=MILVUS_URI, token=MILVUS_TOKEN)
collection_name = "ai_embedding_cache_demo"
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("content", DataType.VARCHAR, max_length=4096)
schema.add_field("embedding", DataType.FLOAT_VECTOR, dim=VECTOR_DIM)
schema.add_function(
Function(
name="embed_content_with_cache",
function_type=FunctionType.TEXTEMBEDDING,
input_field_names=["content"],
output_field_names=["embedding"],
params={
"provider": "aliyun_milvus",
"model_name": MODEL_NAME,
"dim": VECTOR_DIM,
"cache": CACHE_CONFIG, # ← Enable Embedding Cache
},
)
)
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)
# Content that hits the cache during a write generates no model invocation
row = {"content": "Milvus is an open-source vector database."}
client.insert(collection_name, [row])
client.flush(collection_name)
Conteúdo que atinge o cache durante uma gravação não gera invocação do modelo. Para confirme que o cache está funcionando, consulte Verify a cache hit.
Verificar um acerto de cache
Confirme um acerto de cache a partir da resposta do endpoint RESTful de embedding, que retorna usage.total_tokens e request_id. Em um acerto de cache, nenhuma invocação de modelo ocorre de fato, então o consumo de tokens cai para zero e nenhum ID de solicitação é gerado no lado do modelo.
Nem a comparação de vetores nem a latência de gravação são critérios válidos para um acerto de cache. Este passo é fácil de interpretar erroneamente.
Duas verificações intuitivas não funcionam:
Comparação de vetores — o modelo retorna o mesmo vetor para a mesma entrada. Nos testes deste tópico, escrever o mesmo texto duas vezes com o cache completamente desativado ainda produziu uma diferença elemento a elemento de 0.
Latência de gravação — a sobrecarga inerente de um
insertmaisflushexcede em muito uma única invocação de modelo e, nos testes, a segunda gravação pode ser até mais lenta que a primeira.
O código a seguir envia o mesmo texto novo três vezes e relata o resultado de cada solicitação. Execute-o no mesmo script ou sessão da etapa anterior, pois ele reutiliza as importações e constantes definidas lá.
# ==================== Verify whether the cache is actually hit ====================
# Criteria: whether usage.total_tokens drops to zero and whether request_id is empty.
# When both hold, no model invocation actually happened, which means a cache hit.
def post_json(path, body, timeout=180):
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"))
# Use a brand-new piece of text so that the first request is guaranteed to miss
text = f"cache hit verification {uuid.uuid4().hex[:12]}"
body = {
"model_name": MODEL_NAME,
"texts": [text],
"params": {"dim": VECTOR_DIM, "cache": CACHE_CONFIG},
}
for i in (1, 2, 3):
status, data = post_json("/v2/vectordb/ai/embedding", body)
assert status == 200 and data.get("code") == 0, data
usage = data["data"].get("usage", {})
request_id = data["data"].get("request_id", "")
total_tokens = usage.get("total_tokens")
hit = (total_tokens == 0) and not request_id
print(f"Request {i}: total_tokens={total_tokens} "
f"request_id={'(empty)' if not request_id else request_id} "
f"-> {'cache hit' if hit else 'cache miss, the model was invoked'}")
A tabela a seguir lista os resultados medidos de três solicitações consecutivas para o mesmo texto novo:
|
Ordem |
** |
** |
Resultado |
|
Solicitação 1 |
20 |
Tem valor |
Falha no cache, o modelo foi realmente invocado |
|
Solicitação 2 |
0 |
Vazio |
Acerto de cache |
|
Solicitação 3 |
0 |
Vazio |
Acerto de cache |
Ambos os campos estão disponíveis diretamente no corpo da resposta, e nenhuma permissão extra é necessária. Para validação cruzada no lado do servidor, observe como o número de invocações do modelo de embedding muda no console.
AI Batch: processe tarefas que podem esperar offline
Como o AI Batch funciona e onde se aplica
O AI Batch recebe solicitações escritas em JSONL, aceita-as de forma assíncrona e as executa offline em lotes. Um modelo operacional típico em e-commerce reserva a cota do modelo durante o dia para usuários que estão pesquisando e fazendo perguntas. Os dados de product e conversas de suporte acumulados durante o dia são então processados à noite: gerando descrições de product em massa, preenchendo tags e transformando longas conversas em resumos. Os resultados são gravados de volta nos sistemas de negócio antes do próximo dia útil, sem pagar preços de tempo real por esses dados.
Preparar o arquivo de entrada e executar a tarefa em lote
A entrada é JSONL, com uma linha por tarefa. Use custom_id para vincular cada tarefa aos dados originais, de modo que os resultados possam ser mapeados de volta para registros específicos:
{"custom_id":"article-001","method":"POST","url":"/v1/chat/completions","body":{"model":"qwen3.7-max","messages":[{"role":"user","content":"Generate a one-sentence summary for this knowledge base article..."}],"enable_thinking":false}}
{"custom_id":"article-002","method":"POST","url":"/v1/chat/completions","body":{"model":"qwen3.7-max","messages":[{"role":"user","content":"Generate a one-sentence summary for this knowledge base article..."}],"enable_thinking":false}}
O valor body.model em cada linha deve corresponder ao model_name declarado no momento do upload, caso contrário a tarefa falhará.
O processo completo tem quatro etapas: fazer upload do arquivo JSONL, crie a tarefa, consultar o status e baixe os resultados. O AI Batch usa a API RESTful, portanto o bloco a seguir define as constantes e as funções auxiliares para solicitações autenticadas, upload multipart e download de resultados. Execute este bloco primeiro, pois cada uma das quatro etapas reutiliza essas funções.
Etapa 1: Fazer upload do arquivo de entrada
Faça upload do input.jsonl e guarde o input_file_id retornado, que identifica a entrada da tarefa que você criará na próxima etapa.
# 1) Upload the JSONL input file
status, data = upload_input_file("input.jsonl")
input_file_id = (data.get("data") or {}).get("id")
if status != 200 or not input_file_id:
raise SystemExit(f"Upload failed: HTTP={status} message={data.get('message')}")
print(f"input_file_id = {input_file_id}")
Etapa 2: Criar a tarefa em lote
Crie a tarefa a partir do input_file_id e declare a janela de conclusão. Em testes, tarefas desse tipo podem levar várias horas da criação à conclusão, então completion_window geralmente é definido como 24h.
# 2) Create the Batch job and declare the completion window
status, data = post_json("/v2/vectordb/ai/batch/jobs/create", {
"provider": PROVIDER,
"input_file_id": input_file_id,
"endpoint": ENDPOINT,
"completion_window": "24h",
})
batch_id = (data.get("data") or {}).get("id")
if status != 200 or not batch_id:
raise SystemExit(f"Creation failed: HTTP={status} message={data.get('message')}")
print(f"batch_id = {batch_id}")
Etapa 3: Consultar o status da tarefa
Tarefas em lote são tarefas assíncronas em escala horária, portanto não faça consultas intensivas em primeiro plano. Nos testes, uma tarefa enviada permanece no estado in_progress por muito tempo. Em produção, registre o batch_id após o envio e consulte o status posteriormente a partir de uma tarefa agendada, em vez de bloquear um thread de negócio.
# 3) Poll the job status. Batch jobs run asynchronously on an hourly scale. In production,
# record the batch_id and poll later from a scheduled task instead of polling intensively in the foreground.
while True:
status, data = post_json("/v2/vectordb/ai/batch/jobs/describe",
{"provider": PROVIDER, "batch_id": batch_id})
batch = data.get("data") or {}
batch_status = batch.get("status")
print(f"{batch_status} {batch.get('request_counts')}")
if batch_status in {"completed", "failed", "expired", "cancelled"}:
break
time.sleep(300) # Polling once every 5 minutes is enough
Etapa 4: Baixar os resultados
Não baixe os resultados até que o status seja completed. Linhas com falha são coletadas em error_file_id e podem ser tentadas novamente separadamente.
# 4) Download the results after completion. Failed lines are in error_file_id and can be retried separately
if batch_status == "completed":
download_batch_file(batch_id, "output", Path("output.jsonl"))
if batch.get("error_file_id"):
download_batch_file(batch_id, "error", Path("error.jsonl"))
Confirmar e usar os resultados
A tabela a seguir lista as respostas medidas das quatro operações:
|
Etapa |
Operação |
Resposta |
|
Upload |
|
Retorna um |
|
Criação |
|
Retorna um |
|
Consulta |
|
|
|
Download |
|
O fluxo do arquivo de resultado. Relata |
A operação de download localiza o arquivo por batch_id mais file_type, e não por ID de arquivo, o que é fácil de interpretar erroneamente.
Após a conclusão da tarefa, o arquivo de resultado ainda carrega o custom_id original, para que sua aplicação possa gravar cada resumo de volta no registro correspondente. Se parte dos dados falhar, baixe apenas o arquivo de erro correspondente ao error_file_id e tente novamente as linhas com falha. Os resultados concluídos são mantidos como estão, e não há necessidade de reexecutar todo o lote.
Estimativa de custos
O Embedding Cache e o AI Batch economizam custos de maneiras diferentes, portanto são estimados de forma distinta:
|
Recurso |
Base de estimativa |
Efeito |
|
Embedding Cache |
100.000 chamadas de embedding por dia, média de 50 tokens por chamada, taxa de acerto de cache de 50% |
A parte que atinge o cache não gera invocação de modelo, e o consumo geral de tokens cai aproximadamente pela metade |
|
AI Batch |
100.000 entradas por dia, 50 tokens de entrada / 500 tokens de saída |
Execução em lote offline, então as taxas de invocação do modelo são calculadas a um preço unitário menor |
Esses números ilustram apenas de onde vêm as economias e sua ordem de grandeza. As taxas reais dependem do modelo, da região e dos preços atuais, e entrada e saída geralmente têm preços unitários diferentes, portanto consulte a página oficial de preços e sua fatura real.
Solução de problemas
A tabela a seguir lista os erros mais facilmente acionados ao configure o Embedding Cache e o AI Batch:
|
Sintoma |
Causa |
Ação |
|
A conexão atinge o tempo limite ao chamar a API RESTful. |
A porta foi omitida, então a solicitação vai para a porta 80 por padrão. |
Especifique a porta 19530 explicitamente, por exemplo |
|
A tarefa em lote falha. |
O valor |
Alinhe o |
|
A solicitação de download relata |
A tarefa não está concluída. |
Consulte |
|
A solicitação de download não localiza o arquivo de resultado. |
A solicitação identifica o arquivo por ID de arquivo. |
Chame |
|
Duas gravações do mesmo texto retornam vetores idênticos, ou a segunda gravação é mais lenta que a primeira. |
Comparação de vetores e latência de gravação não são critérios para acerto de cache. |
Verifique |