Toplu çıkarım, JSONL formatındaki metin veya çok modlu Chat Completions isteklerini eşzamansız toplu görevler olarak gönderir. Gece boyunca özetleme, geçmiş veri etiketleme, model değerlendirme ve veri açıklamaları gibi dakikalardan saatlere sürebilen uzun süreli çevrimdışı iş yükleri için bu özelliği kullanın.
Nasıl çalışır
Toplu görevler, OpenAI Batch dosya girişi ve sonuç ilişkilendirme desenini takip eder: Her istek benzersiz bir custom_id kullanır ve işlem tamamlandıktan sonra iş verileri, bu tanımlayıcı kullanılarak çıktı veya hata JSONL dosyasından geri yazılır.
Görevi gönderin — Birden fazla istek içeren UTF-8 kodlamalı bir JSONL dosyası yükleyin, ardından döndürülen dosya kimliğini kullanarak bir toplu görev oluşturun.
Eşzamansız işleme — Sunucu, arka planda istekleri satır satır doğrular ve yürütür. Uygulama,
batch_idkullanarakvalidating,in_progressvefinalizinggibi durumları sorgular.
Sonuçları indirin — Görev son duruma (görevin tamamlandığını ve artık değişmeyeceğini belirten durum) ulaştığında,
outputdosyasını indirin. Başarısız satırlar varsa,errordosyasını indirin ve sonuçları geri yazmak veya sorunları bulmak içincustom_iddeğerini kullanın.
Toplu çıkarım; çevrimdışı model değerlendirmesi, geçmiş veri açıklamaları, içerik toplu işleme ve zamanlanmış varlık işleme senaryoları için uygundur. Anında sonuç gerektiren sohbet veya arama tamamlamaları gibi çevrimiçi istekler için uygun değildir.
Komut formatı
Toplu yürütme dört adımdan oluşur: giriş dosyasını yükleme → görev oluşturma → durumu sorgulama → sonuçları indirme.
Giriş dosyası UTF-8 kodlamalı JSONL olmalıdır. Her satır, benzersiz bir custom_id, sabit method="POST" değeri ve göreve uygun url ile body.model değerlerini içeren bağımsız bir istektir.
REST API
REST API
POST /v2/vectordb/ai/batch/files/upload
multipart/form-data: file, model_name, endpoint, purpose=batch
POST /v2/vectordb/ai/batch/jobs/create
{"input_file_id":"<file ID>","endpoint":"/v1/chat/completions","completion_window":"24h"}
POST /v2/vectordb/ai/batch/jobs/describe
{"batch_id":"<task ID>"}
POST /v2/vectordb/ai/batch/files/content
{"batch_id":"<task ID>","file_type":"output|error"}
Python
Python
upload_status, upload_data = post_multipart_upload("<input.jsonl>")
create_status, create_data = post_json(
"/v2/vectordb/ai/batch/jobs/create",
{
"input_file_id": upload_data["data"]["id"],
"endpoint": "<endpoint>",
"completion_window": "24h",
},
)
describe_status, describe_data = post_json(
"/v2/vectordb/ai/batch/jobs/describe",
{"batch_id": create_data["data"]["id"]},
)
content_status, content_data = post_json(
"/v2/vectordb/ai/batch/files/content",
{"batch_id": create_data["data"]["id"], "file_type": "output"},
)
REST betiği jq aracına bağlıdır.
Parametreler
| Parametre | Açıklama |
file |
Yükleme için gereklidir. JSONL giriş dosyasıdır. Sunucu, ilk satırın custom_id, method, url ve body.model alanlarını doğrular. |
model_name |
Yükleme için gereklidir; ayrıca model ile uyumludur. JSONL'in ilk satırındaki body.model değeriyle eşleşmelidir. |
endpoint |
Yükleme ve oluşturma için gereklidir. Chat Completions için /v1/chat/completions, metin yerleştirme görevleri için /v1/embeddings kullanın. Aynı dosyadaki tüm satırlar aynı uç noktayı kullanmalıdır. |
purpose |
Yükleme için isteğe bağlıdır. Yalnızca boş veya batch olabilir. |
input_file_id |
Görev oluşturmak için gereklidir. Yükleme API'si tarafından döndürülen dosya kimliğidir. OSS URL'leri veya harici dosya tanımlayıcıları kabul edilmez. |
completion_window |
Görev oluşturmak için gereklidir. Tamamlanma penceresidir; 24h ile 336h arasında veya gün birimlerini destekler. |
metadata.ds_name/metadata.ds_description |
İsteğe bağlıdır. Görev adı en fazla 100 karakter, açıklama en fazla 200 karakter olabilir. |
batch_id |
Sorgulama ve indirme için gereklidir. Oluşturma API'si tarafından döndürülen görev kimliğidir. |
file_type |
İndirme için gereklidir. Başarılı satırları indirmek için output, başarısız satırları indirmek için error kullanın. |
provider |
İsteğe bağlıdır; varsayılan olarak aliyun_milvus değerini alır. Yükleme, oluşturma, sorgulama ve indirme işlemleri aynı Alibaba Cloud hesabı, bölge ve çalışma alanı altında yapılmalıdır. |
body.enable_thinking |
İsteğe bağlıdır, modele bağlıdır. JSONL satırında body.model ile aynı seviyede olmalıdır. Varsayılan olarak düşünme özelliği etkin olan modellerde, istenmeyen düşünme belirteçlerini önlemek için bu değeri açıkça false olarak ayarlayın. Bu parametreyi extra_body içine yerleştirmeyin. |
Dönüş değerleri
Bir görev başarıyla oluşturulduktan sonra, data.id değeri batch_id olarak döndürülür. Yoklama durumları arasında validating, in_progress, finalizing ve cancelling bulunur. Son durumlar (görevin tamamlandığını ve artık değişmeyeceğini belirten durumlar) arasında completed, failed, expired ve cancelled yer alır.
Toplu görevler bir kuyrukta eşzamansız olarak işlenir. Oluşturmadan son duruma geçiş süresi genellikle birkaç dakikadan birkaç saate kadar değişir ve bu süre boyunca durum in_progress olarak kalır. En güncel status bilgisini almak için herhangi bir zamanda batch_id ile POST /v2/vectordb/ai/batch/jobs/describe sorgu API'sini çağırabilirsiniz. Örnek betiğin yoklama işlemi görev tamamlanmadan sonlanırsa, durum completed olarak değiştiğinde batch_id kullanarak daha sonra tekrar sorgulama yapabilir ve sonuçları indirebilirsiniz. İndirme API'si JSONL içeriğini doğrudan döndürür; her satır, sonuçları iş verilerine geri yazmak için giriş custom_id değerini korur. Toplu görevler, anında yanıt gerektiren çevrimiçi etkileşimler için uygun değildir.
Örnek 1: Gece boyunca toplu bilgi tabanı özet oluşturma (metin)
Bir operasyon ekibi, o gün eklenen makaleler için toplu olarak özetler oluşturmak ister. Her satır bir makale isteği içerir. Temel parametreler body.model=qwen3.7-max, url=/v1/chat/completions ve completion_window=24h şeklindedir.
REST API
REST API
#!/usr/bin/env bash
set -euo pipefail
MILVUS_REST_BASE_URL="http://c-xxxx.milvus.aliyuncs.com:19530"
MILVUS_AUTH_TOKEN="<yourUsername>:<yourPassword>"
post_json() {
local path="$1"
local body="$2"
curl -X POST \
"$MILVUS_REST_BASE_URL$path" \
-H "Authorization: Bearer $MILVUS_AUTH_TOKEN" \
-H "Content-Type: application/json" \
-d "$body"
}
MODEL_NAME="qwen3.7-max"
INPUT_FILE="${AIFUNC_AI_BATCH_INPUT_FILE:-}"
POLL_INTERVAL_SEC="${AIFUNC_AI_BATCH_POLL_INTERVAL_SEC:-5}"
MAX_POLL_ATTEMPTS="${AIFUNC_AI_BATCH_MAX_POLL_ATTEMPTS:-120}"
if [ -z "$INPUT_FILE" ]; then
echo "Save the sample JSONL requests to a file first, then set AIFUNC_AI_BATCH_INPUT_FILE to the file path." >&2
exit 1
fi
download_batch_file() {
local file_type="$1"; local output_file="$2"; local content_body
content_body="$(jq -nc --arg batch_id "$BATCH_ID" --arg file_type "$file_type" '{provider:"aliyun_milvus", batch_id:$batch_id, file_type:$file_type}')"
curl --fail --silent --show-error -X POST "$MILVUS_REST_BASE_URL/v2/vectordb/ai/batch/files/content" \
-H "Authorization: Bearer $MILVUS_AUTH_TOKEN" -H "Content-Type: application/json" \
-d "$content_body" --output "$output_file"
}
UPLOAD_RESPONSE="$(curl --fail --silent --show-error -X POST "$MILVUS_REST_BASE_URL/v2/vectordb/ai/batch/files/upload" \
-H "Authorization: Bearer $MILVUS_AUTH_TOKEN" \
-F "provider=aliyun_milvus" -F "model_name=$MODEL_NAME" -F "endpoint=/v1/chat/completions" \
-F "purpose=batch" -F "file=@$INPUT_FILE;type=application/jsonl")"
INPUT_FILE_ID="$(echo "$UPLOAD_RESPONSE" | jq -r '.data.id // empty')"
[ -n "$INPUT_FILE_ID" ] || exit 1
CREATE_BODY="$(jq -nc --arg input_file_id "$INPUT_FILE_ID" '{provider:"aliyun_milvus", input_file_id:$input_file_id, endpoint:"/v1/chat/completions", completion_window:"24h", metadata:{ds_name:"milvus-ai-function-example", ds_description:"AI Batch REST example"}}')"
CREATE_RESPONSE="$(post_json "/v2/vectordb/ai/batch/jobs/create" "$CREATE_BODY")"
BATCH_ID="$(echo "$CREATE_RESPONSE" | jq -r '.data.id // empty')"
[ -n "$BATCH_ID" ] || exit 1
for ((attempt = 1; attempt <= MAX_POLL_ATTEMPTS; attempt++)); do
DESCRIBE_BODY="$(jq -nc --arg batch_id "$BATCH_ID" '{provider:"aliyun_milvus", batch_id:$batch_id}')"
DESCRIBE_RESPONSE="$(post_json "/v2/vectordb/ai/batch/jobs/describe" "$DESCRIBE_BODY")"
BATCH_STATUS="$(echo "$DESCRIBE_RESPONSE" | jq -r '.data.status // empty')"
case "$BATCH_STATUS" in
completed)
OUTPUT_FILE="${AIFUNC_AI_BATCH_OUTPUT_FILE:-./ai_batch_output_${BATCH_ID}.jsonl}"
download_batch_file "output" "$OUTPUT_FILE"
echo "Batch output downloaded to: $OUTPUT_FILE"
exit 0 ;;
failed|expired|cancelled)
echo "Batch $BATCH_ID ended with status: $BATCH_STATUS" >&2; exit 1 ;;
validating|in_progress|finalizing|cancelling)
[ "$attempt" -lt "$MAX_POLL_ATTEMPTS" ] && sleep "$POLL_INTERVAL_SEC" ;;
*) echo "Unknown status: ${BATCH_STATUS:-empty}" >&2; exit 1 ;;
esac
done
echo "Batch $BATCH_ID did not finish after $MAX_POLL_ATTEMPTS checks." >&2
exit 1
Python
Python
from __future__ import annotations
import json
import os
import shutil
import sys
import tempfile
import time
import uuid
from pathlib import Path
from typing import Any
from urllib.error import HTTPError
from urllib.request import Request, urlopen
MILVUS_REST_BASE_URL = "http://c-xxxx.milvus.aliyuncs.com:19530"
MILVUS_AUTH_TOKEN = "<yourUsername>:<yourPassword>"
MODEL_NAME = "qwen3.7-max"
INPUT_MODE = os.getenv("AIFUNC_AI_BATCH_INPUT_MODE", "text").lower()
POLL_INTERVAL_SEC = int(os.getenv("AIFUNC_AI_BATCH_POLL_INTERVAL_SEC", "5"))
MAX_POLL_ATTEMPTS = int(os.getenv("AIFUNC_AI_BATCH_MAX_POLL_ATTEMPTS", "120"))
def post_json(path: str, body: dict[str, Any], timeout: int = 120) -> tuple[int, dict[str, Any]]:
request = Request(
f"{MILVUS_REST_BASE_URL.rstrip('/')}{path}",
data=json.dumps(body, ensure_ascii=False).encode("utf-8"),
headers={"Authorization": f"Bearer {MILVUS_AUTH_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"))
def batch_request(custom_id: str, content: Any, *, enable_thinking: bool | None = None) -> dict[str, Any]:
body: dict[str, Any] = {"model": MODEL_NAME, "messages": [{"role": "user", "content": content}]}
if enable_thinking is not None:
body["enable_thinking"] = enable_thinking
return {"custom_id": custom_id, "method": "POST", "url": "/v1/chat/completions", "body": body}
def default_requests(input_mode: str) -> list[dict[str, Any]]:
if input_mode == "text":
return [
batch_request("milvus-summary-1", "For a support knowledge base, summarize in one English sentence: Milvus stores and searches vector embeddings for RAG, recommendation, and multimodal retrieval.", enable_thinking=False),
batch_request("milvus-summary-2", "Turn this incident note into an English FAQ title and one-sentence answer: after documents are updated, regenerate embeddings before users search the new content.", enable_thinking=False),
batch_request("milvus-summary-3", "Create a one-sentence English catalog description for an enterprise RAG case that retrieves policy passages before the assistant answers.", enable_thinking=False),
]
if input_mode == "image":
return [batch_request("milvus-image-1", [
{"type": "image_url", "image_url": {"url": "https://dashscope.oss-cn-beijing.aliyuncs.com/images/dog_and_girl.jpeg"}},
{"type": "text", "text": "For a pet-service media library, return a concise English accessibility caption and up to three searchable subject tags for this uploaded case photo."},
])]
if input_mode == "video":
return [batch_request("milvus-video-1", [
{"type": "video", "video": "https://help-static-aliyun-doc.aliyuncs.com/file-manage-files/zh-CN/20260409/dozxak/Wan_Video_Edit_33_1.mp4"},
{"type": "text", "text": "For a marketing asset library, return a one-sentence English scene summary, three retrieval keywords, and whether manual brand-safety review is needed."},
])]
if input_mode == "audio":
return [batch_request("milvus-audio-1", [
{"type": "input_audio", "input_audio": {"data": "https://dashscope.oss-cn-beijing.aliyuncs.com/audios/welcome.mp3", "format": "mp3"}},
{"type": "text", "text": "For a customer-service hotline greeting archive, transcribe the welcome message and assess whether its purpose, service availability, and recording or privacy notice are clear; mark details not present in the audio as unconfirmed."},
])]
print("AIFUNC_AI_BATCH_INPUT_MODE must be one of text, image, video, or audio", file=sys.stderr)
sys.exit(1)
def post_multipart_upload(input_file: str) -> tuple[int, dict[str, Any]]:
boundary = f"----milvus-ai-batch-{uuid.uuid4().hex}"
fields = {"provider": "aliyun_milvus", "model_name": MODEL_NAME, "endpoint": "/v1/chat/completions", "purpose": "batch"}
with tempfile.TemporaryFile(mode="w+b") as payload:
for name, value in fields.items():
payload.write(f"--{boundary}\r\n".encode())
payload.write(f'Content-Disposition: form-data; name="{name}"\r\n\r\n'.encode())
payload.write(value.encode())
payload.write(b"\r\n")
payload.write(f"--{boundary}\r\n".encode())
payload.write(b'Content-Disposition: form-data; name="file"; filename="input.jsonl"\r\n')
payload.write(b"Content-Type: application/jsonl\r\n\r\n")
with open(input_file, "rb") as input_handle:
shutil.copyfileobj(input_handle, payload)
payload.write(b"\r\n")
payload.write(f"--{boundary}--\r\n".encode())
payload_length = payload.tell()
payload.seek(0)
request = Request(
f"{MILVUS_REST_BASE_URL.rstrip('/')}/v2/vectordb/ai/batch/files/upload",
data=payload,
headers={"Authorization": f"Bearer {MILVUS_AUTH_TOKEN}", "Content-Type": f"multipart/form-data; boundary={boundary}", "Content-Length": str(payload_length)},
method="POST",
)
try:
with urlopen(request, timeout=600) 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"))
def download_batch_file(batch_id: str, file_type: str, output_file: Path) -> None:
request = Request(
f"{MILVUS_REST_BASE_URL.rstrip('/')}/v2/vectordb/ai/batch/files/content",
data=json.dumps({"provider": "aliyun_milvus", "batch_id": batch_id, "file_type": file_type}, ensure_ascii=False).encode("utf-8"),
headers={"Authorization": f"Bearer {MILVUS_AUTH_TOKEN}", "Content-Type": "application/json"},
method="POST",
)
with urlopen(request, timeout=600) as response, output_file.open("wb") as output:
shutil.copyfileobj(response, output)
temporary_input = None
INPUT_FILE = os.getenv("AIFUNC_AI_BATCH_INPUT_FILE")
if INPUT_FILE is None:
temporary_input = tempfile.NamedTemporaryFile(mode="w", suffix=".jsonl", delete=False, encoding="utf-8")
for request in default_requests(INPUT_MODE):
temporary_input.write(json.dumps(request, ensure_ascii=False, separators=(",", ":")) + "\n")
temporary_input.close()
INPUT_FILE = temporary_input.name
try:
status, data = post_multipart_upload(INPUT_FILE)
if status != 200 or data.get("code") != 0:
sys.exit(1)
input_file_id = data.get("data", {}).get("id")
status, data = post_json("/v2/vectordb/ai/batch/jobs/create", {
"provider": "aliyun_milvus", "input_file_id": input_file_id, "endpoint": "/v1/chat/completions",
"completion_window": "24h", "metadata": {"ds_name": "milvus-ai-function-example", "ds_description": "AI Batch example"},
})
if status != 200 or data.get("code") != 0:
sys.exit(1)
batch_id = data.get("data", {}).get("id")
for attempt in range(1, MAX_POLL_ATTEMPTS + 1):
status, data = post_json("/v2/vectordb/ai/batch/jobs/describe", {"provider": "aliyun_milvus", "batch_id": batch_id})
batch_data = data.get("data", {})
batch_status = batch_data.get("status")
if batch_status == "completed":
output_file = Path(os.getenv("AIFUNC_AI_BATCH_OUTPUT_FILE", f"ai_batch_output_{batch_id}.jsonl"))
download_batch_file(batch_id, "output", output_file)
print(f"Batch output downloaded to: {output_file}")
if batch_data.get("error_file_id"):
download_batch_file(batch_id, "error", Path(f"ai_batch_errors_{batch_id}.jsonl"))
break
if batch_status in {"failed", "expired", "cancelled"}:
print(f"Batch {batch_id} ended with status: {batch_status}", file=sys.stderr)
sys.exit(1)
if attempt < MAX_POLL_ATTEMPTS:
time.sleep(POLL_INTERVAL_SEC)
finally:
if temporary_input is not None:
os.unlink(temporary_input.name)
Beklenen sonuç: Görev completed durumuna ulaştığında, indirilen output.jsonl dosyasındaki her satır custom_id değerini korur. Örneğin:
{"custom_id":"milvus-summary-1","response":{"status_code":200,"body":{"choices":[{"message":{"role":"assistant","content":"Milvus is a vector database that serves RAG, recommendation, and multimodal retrieval scenarios."}}]}}}
Uygulama tarafı, custom_id kullanarak özetleri ilgili makalelere geri yazar. Bir error.jsonl dosyası oluşturulursa, yalnızca başarısız olan makaleleri yeniden deneyin.
Örnek 2: Toplu evcil hayvan hizmeti vaka görüntüsü açıklaması oluşturma (görüntü)
Bir evcil hayvan hizmeti platformu, satıcılar tarafından yüklenen geçmiş vaka görüntüleri için erişilebilirlik açıklamaları ve getirme etiketleri oluşturmak ister. Her satırın content alanı bir image_url ve bir metin talimatı içerir. Görüntü anlamayı destekleyen çok modlu bir model kullanın; yükleme sırasında belirtilen model_name değeri ile eşleşmelidir.
REST API
REST API
#!/usr/bin/env bash
set -euo pipefail
MILVUS_REST_BASE_URL="http://c-xxxx.milvus.aliyuncs.com:19530"
MILVUS_AUTH_TOKEN="<yourUsername>:<yourPassword>"
post_json() {
local path="$1"
local body="$2"
curl -X POST \
"$MILVUS_REST_BASE_URL$path" \
-H "Authorization: Bearer $MILVUS_AUTH_TOKEN" \
-H "Content-Type: application/json" \
-d "$body"
}
MODEL_NAME="qwen3-vl-plus"
INPUT_FILE="${AIFUNC_AI_BATCH_INPUT_FILE:-}"
POLL_INTERVAL_SEC="${AIFUNC_AI_BATCH_POLL_INTERVAL_SEC:-5}"
MAX_POLL_ATTEMPTS="${AIFUNC_AI_BATCH_MAX_POLL_ATTEMPTS:-120}"
if [ -z "$INPUT_FILE" ]; then
echo "Save the sample JSONL requests to a file first, then set AIFUNC_AI_BATCH_INPUT_FILE to the file path." >&2
exit 1
fi
download_batch_file() {
local file_type="$1"; local output_file="$2"; local content_body
content_body="$(jq -nc --arg batch_id "$BATCH_ID" --arg file_type "$file_type" '{provider:"aliyun_milvus", batch_id:$batch_id, file_type:$file_type}')"
curl --fail --silent --show-error -X POST "$MILVUS_REST_BASE_URL/v2/vectordb/ai/batch/files/content" \
-H "Authorization: Bearer $MILVUS_AUTH_TOKEN" -H "Content-Type: application/json" \
-d "$content_body" --output "$output_file"
}
UPLOAD_RESPONSE="$(curl --fail --silent --show-error -X POST "$MILVUS_REST_BASE_URL/v2/vectordb/ai/batch/files/upload" \
-H "Authorization: Bearer $MILVUS_AUTH_TOKEN" \
-F "provider=aliyun_milvus" -F "model_name=$MODEL_NAME" -F "endpoint=/v1/chat/completions" \
-F "purpose=batch" -F "file=@$INPUT_FILE;type=application/jsonl")"
INPUT_FILE_ID="$(echo "$UPLOAD_RESPONSE" | jq -r '.data.id // empty')"
[ -n "$INPUT_FILE_ID" ] || exit 1
CREATE_BODY="$(jq -nc --arg input_file_id "$INPUT_FILE_ID" '{provider:"aliyun_milvus", input_file_id:$input_file_id, endpoint:"/v1/chat/completions", completion_window:"24h", metadata:{ds_name:"milvus-ai-function-example", ds_description:"AI Batch REST example"}}')"
CREATE_RESPONSE="$(post_json "/v2/vectordb/ai/batch/jobs/create" "$CREATE_BODY")"
BATCH_ID="$(echo "$CREATE_RESPONSE" | jq -r '.data.id // empty')"
[ -n "$BATCH_ID" ] || exit 1
for ((attempt = 1; attempt <= MAX_POLL_ATTEMPTS; attempt++)); do
DESCRIBE_BODY="$(jq -nc --arg batch_id "$BATCH_ID" '{provider:"aliyun_milvus", batch_id:$batch_id}')"
DESCRIBE_RESPONSE="$(post_json "/v2/vectordb/ai/batch/jobs/describe" "$DESCRIBE_BODY")"
BATCH_STATUS="$(echo "$DESCRIBE_RESPONSE" | jq -r '.data.status // empty')"
case "$BATCH_STATUS" in
completed)
OUTPUT_FILE="${AIFUNC_AI_BATCH_OUTPUT_FILE:-./ai_batch_output_${BATCH_ID}.jsonl}"
download_batch_file "output" "$OUTPUT_FILE"
echo "Batch output downloaded to: $OUTPUT_FILE"
exit 0 ;;
failed|expired|cancelled)
echo "Batch $BATCH_ID ended with status: $BATCH_STATUS" >&2; exit 1 ;;
validating|in_progress|finalizing|cancelling)
[ "$attempt" -lt "$MAX_POLL_ATTEMPTS" ] && sleep "$POLL_INTERVAL_SEC" ;;
*) echo "Unknown status: ${BATCH_STATUS:-empty}" >&2; exit 1 ;;
esac
done
echo "Batch $BATCH_ID did not finish after $MAX_POLL_ATTEMPTS checks." >&2
exit 1
Python
Python
from __future__ import annotations
import json
import os
import shutil
import sys
import tempfile
import time
import uuid
from pathlib import Path
from typing import Any
from urllib.error import HTTPError
from urllib.request import Request, urlopen
MILVUS_REST_BASE_URL = "http://c-xxxx.milvus.aliyuncs.com:19530"
MILVUS_AUTH_TOKEN = "<yourUsername>:<yourPassword>"
MODEL_NAME = "qwen3-vl-plus"
INPUT_MODE = os.getenv("AIFUNC_AI_BATCH_INPUT_MODE", "image").lower()
POLL_INTERVAL_SEC = int(os.getenv("AIFUNC_AI_BATCH_POLL_INTERVAL_SEC", "5"))
MAX_POLL_ATTEMPTS = int(os.getenv("AIFUNC_AI_BATCH_MAX_POLL_ATTEMPTS", "120"))
def post_json(path: str, body: dict[str, Any], timeout: int = 120) -> tuple[int, dict[str, Any]]:
request = Request(
f"{MILVUS_REST_BASE_URL.rstrip('/')}{path}",
data=json.dumps(body, ensure_ascii=False).encode("utf-8"),
headers={"Authorization": f"Bearer {MILVUS_AUTH_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"))
def batch_request(custom_id: str, content: Any, *, enable_thinking: bool | None = None) -> dict[str, Any]:
body: dict[str, Any] = {"model": MODEL_NAME, "messages": [{"role": "user", "content": content}]}
if enable_thinking is not None:
body["enable_thinking"] = enable_thinking
return {"custom_id": custom_id, "method": "POST", "url": "/v1/chat/completions", "body": body}
def default_requests(input_mode: str) -> list[dict[str, Any]]:
if input_mode == "text":
return [
batch_request("milvus-summary-1", "For a support knowledge base, summarize in one English sentence: Milvus stores and searches vector embeddings for RAG, recommendation, and multimodal retrieval.", enable_thinking=False),
batch_request("milvus-summary-2", "Turn this incident note into an English FAQ title and one-sentence answer: after documents are updated, regenerate embeddings before users search the new content.", enable_thinking=False),
batch_request("milvus-summary-3", "Create a one-sentence English catalog description for an enterprise RAG case that retrieves policy passages before the assistant answers.", enable_thinking=False),
]
if input_mode == "image":
return [batch_request("milvus-image-1", [
{"type": "image_url", "image_url": {"url": "https://dashscope.oss-cn-beijing.aliyuncs.com/images/dog_and_girl.jpeg"}},
{"type": "text", "text": "For a pet-service media library, return a concise English accessibility caption and up to three searchable subject tags for this uploaded case photo."},
])]
if input_mode == "video":
return [batch_request("milvus-video-1", [
{"type": "video", "video": "https://help-static-aliyun-doc.aliyuncs.com/file-manage-files/zh-CN/20260409/dozxak/Wan_Video_Edit_33_1.mp4"},
{"type": "text", "text": "For a marketing asset library, return a one-sentence English scene summary, three retrieval keywords, and whether manual brand-safety review is needed."},
])]
if input_mode == "audio":
return [batch_request("milvus-audio-1", [
{"type": "input_audio", "input_audio": {"data": "https://dashscope.oss-cn-beijing.aliyuncs.com/audios/welcome.mp3", "format": "mp3"}},
{"type": "text", "text": "For a customer-service hotline greeting archive, transcribe the welcome message and assess whether its purpose, service availability, and recording or privacy notice are clear; mark details not present in the audio as unconfirmed."},
])]
print("AIFUNC_AI_BATCH_INPUT_MODE must be one of text, image, video, or audio", file=sys.stderr)
sys.exit(1)
def post_multipart_upload(input_file: str) -> tuple[int, dict[str, Any]]:
boundary = f"----milvus-ai-batch-{uuid.uuid4().hex}"
fields = {"provider": "aliyun_milvus", "model_name": MODEL_NAME, "endpoint": "/v1/chat/completions", "purpose": "batch"}
with tempfile.TemporaryFile(mode="w+b") as payload:
for name, value in fields.items():
payload.write(f"--{boundary}\r\n".encode())
payload.write(f'Content-Disposition: form-data; name="{name}"\r\n\r\n'.encode())
payload.write(value.encode())
payload.write(b"\r\n")
payload.write(f"--{boundary}\r\n".encode())
payload.write(b'Content-Disposition: form-data; name="file"; filename="input.jsonl"\r\n')
payload.write(b"Content-Type: application/jsonl\r\n\r\n")
with open(input_file, "rb") as input_handle:
shutil.copyfileobj(input_handle, payload)
payload.write(b"\r\n")
payload.write(f"--{boundary}--\r\n".encode())
payload_length = payload.tell()
payload.seek(0)
request = Request(
f"{MILVUS_REST_BASE_URL.rstrip('/')}/v2/vectordb/ai/batch/files/upload",
data=payload,
headers={"Authorization": f"Bearer {MILVUS_AUTH_TOKEN}", "Content-Type": f"multipart/form-data; boundary={boundary}", "Content-Length": str(payload_length)},
method="POST",
)
try:
with urlopen(request, timeout=600) 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"))
def download_batch_file(batch_id: str, file_type: str, output_file: Path) -> None:
request = Request(
f"{MILVUS_REST_BASE_URL.rstrip('/')}/v2/vectordb/ai/batch/files/content",
data=json.dumps({"provider": "aliyun_milvus", "batch_id": batch_id, "file_type": file_type}, ensure_ascii=False).encode("utf-8"),
headers={"Authorization": f"Bearer {MILVUS_AUTH_TOKEN}", "Content-Type": "application/json"},
method="POST",
)
with urlopen(request, timeout=600) as response, output_file.open("wb") as output:
shutil.copyfileobj(response, output)
temporary_input = None
INPUT_FILE = os.getenv("AIFUNC_AI_BATCH_INPUT_FILE")
if INPUT_FILE is None:
temporary_input = tempfile.NamedTemporaryFile(mode="w", suffix=".jsonl", delete=False, encoding="utf-8")
for request in default_requests(INPUT_MODE):
temporary_input.write(json.dumps(request, ensure_ascii=False, separators=(",", ":")) + "\n")
temporary_input.close()
INPUT_FILE = temporary_input.name
try:
status, data = post_multipart_upload(INPUT_FILE)
if status != 200 or data.get("code") != 0:
sys.exit(1)
input_file_id = data.get("data", {}).get("id")
status, data = post_json("/v2/vectordb/ai/batch/jobs/create", {
"provider": "aliyun_milvus", "input_file_id": input_file_id, "endpoint": "/v1/chat/completions",
"completion_window": "24h", "metadata": {"ds_name": "milvus-ai-function-example", "ds_description": "AI Batch example"},
})
if status != 200 or data.get("code") != 0:
sys.exit(1)
batch_id = data.get("data", {}).get("id")
for attempt in range(1, MAX_POLL_ATTEMPTS + 1):
status, data = post_json("/v2/vectordb/ai/batch/jobs/describe", {"provider": "aliyun_milvus", "batch_id": batch_id})
batch_data = data.get("data", {})
batch_status = batch_data.get("status")
if batch_status == "completed":
output_file = Path(os.getenv("AIFUNC_AI_BATCH_OUTPUT_FILE", f"ai_batch_output_{batch_id}.jsonl"))
download_batch_file(batch_id, "output", output_file)
print(f"Batch output downloaded to: {output_file}")
if batch_data.get("error_file_id"):
download_batch_file(batch_id, "error", Path(f"ai_batch_errors_{batch_id}.jsonl"))
break
if batch_status in {"failed", "expired", "cancelled"}:
print(f"Batch {batch_id} ended with status: {batch_status}", file=sys.stderr)
sys.exit(1)
if attempt < MAX_POLL_ATTEMPTS:
time.sleep(POLL_INTERVAL_SEC)
finally:
if temporary_input is not None:
os.unlink(temporary_input.name)
Beklenen sonuç: image-output.jsonl dosyası, aynı custom_id=milvus-image-1 değerini kullanarak görüntü açıklamasını ve etiketleri döndürür. Uygulama tarafı metni vaka kütüphanesi alanlarına yazabilir ve görüntü-metin getirme için görüntü vektörleriyle birleştirebilir. Görev tamamlansa bile başarısız satırlar varsa, file_type=error sonuç dosyasını indirin.
Örnek 3: Toplu video materyal özeti oluşturma (video)
Bir operasyon ekibi, video varlık getirmeyi desteklemek amacıyla geçmiş kısa videolar için tek cümlelik özetler oluşturmak ister. Her satır, bir video içerik bloğu aracılığıyla bir video URL'si iletir. Video anlamayı destekleyen çok modlu bir model kullanın.
REST API
REST API
#!/usr/bin/env bash
set -euo pipefail
MILVUS_REST_BASE_URL="http://c-xxxx.milvus.aliyuncs.com:19530"
MILVUS_AUTH_TOKEN="<yourUsername>:<yourPassword>"
post_json() {
local path="$1"
local body="$2"
curl -X POST \
"$MILVUS_REST_BASE_URL$path" \
-H "Authorization: Bearer $MILVUS_AUTH_TOKEN" \
-H "Content-Type: application/json" \
-d "$body"
}
MODEL_NAME="qwen3-vl-plus"
INPUT_FILE="${AIFUNC_AI_BATCH_INPUT_FILE:-}"
POLL_INTERVAL_SEC="${AIFUNC_AI_BATCH_POLL_INTERVAL_SEC:-5}"
MAX_POLL_ATTEMPTS="${AIFUNC_AI_BATCH_MAX_POLL_ATTEMPTS:-120}"
if [ -z "$INPUT_FILE" ]; then
echo "Save the sample JSONL requests to a file first, then set AIFUNC_AI_BATCH_INPUT_FILE to the file path." >&2
exit 1
fi
download_batch_file() {
local file_type="$1"; local output_file="$2"; local content_body
content_body="$(jq -nc --arg batch_id "$BATCH_ID" --arg file_type "$file_type" '{provider:"aliyun_milvus", batch_id:$batch_id, file_type:$file_type}')"
curl --fail --silent --show-error -X POST "$MILVUS_REST_BASE_URL/v2/vectordb/ai/batch/files/content" \
-H "Authorization: Bearer $MILVUS_AUTH_TOKEN" -H "Content-Type: application/json" \
-d "$content_body" --output "$output_file"
}
UPLOAD_RESPONSE="$(curl --fail --silent --show-error -X POST "$MILVUS_REST_BASE_URL/v2/vectordb/ai/batch/files/upload" \
-H "Authorization: Bearer $MILVUS_AUTH_TOKEN" \
-F "provider=aliyun_milvus" -F "model_name=$MODEL_NAME" -F "endpoint=/v1/chat/completions" \
-F "purpose=batch" -F "file=@$INPUT_FILE;type=application/jsonl")"
INPUT_FILE_ID="$(echo "$UPLOAD_RESPONSE" | jq -r '.data.id // empty')"
[ -n "$INPUT_FILE_ID" ] || exit 1
CREATE_BODY="$(jq -nc --arg input_file_id "$INPUT_FILE_ID" '{provider:"aliyun_milvus", input_file_id:$input_file_id, endpoint:"/v1/chat/completions", completion_window:"24h", metadata:{ds_name:"milvus-ai-function-example", ds_description:"AI Batch REST example"}}')"
CREATE_RESPONSE="$(post_json "/v2/vectordb/ai/batch/jobs/create" "$CREATE_BODY")"
BATCH_ID="$(echo "$CREATE_RESPONSE" | jq -r '.data.id // empty')"
[ -n "$BATCH_ID" ] || exit 1
for ((attempt = 1; attempt <= MAX_POLL_ATTEMPTS; attempt++)); do
DESCRIBE_BODY="$(jq -nc --arg batch_id "$BATCH_ID" '{provider:"aliyun_milvus", batch_id:$batch_id}')"
DESCRIBE_RESPONSE="$(post_json "/v2/vectordb/ai/batch/jobs/describe" "$DESCRIBE_BODY")"
BATCH_STATUS="$(echo "$DESCRIBE_RESPONSE" | jq -r '.data.status // empty')"
case "$BATCH_STATUS" in
completed)
OUTPUT_FILE="${AIFUNC_AI_BATCH_OUTPUT_FILE:-./ai_batch_output_${BATCH_ID}.jsonl}"
download_batch_file "output" "$OUTPUT_FILE"
echo "Batch output downloaded to: $OUTPUT_FILE"
exit 0 ;;
failed|expired|cancelled)
echo "Batch $BATCH_ID ended with status: $BATCH_STATUS" >&2; exit 1 ;;
validating|in_progress|finalizing|cancelling)
[ "$attempt" -lt "$MAX_POLL_ATTEMPTS" ] && sleep "$POLL_INTERVAL_SEC" ;;
*) echo "Unknown status: ${BATCH_STATUS:-empty}" >&2; exit 1 ;;
esac
done
echo "Batch $BATCH_ID did not finish after $MAX_POLL_ATTEMPTS checks." >&2
exit 1
Python
Python
from __future__ import annotations
import json
import os
import shutil
import sys
import tempfile
import time
import uuid
from pathlib import Path
from typing import Any
from urllib.error import HTTPError
from urllib.request import Request, urlopen
MILVUS_REST_BASE_URL = "http://c-xxxx.milvus.aliyuncs.com:19530"
MILVUS_AUTH_TOKEN = "<yourUsername>:<yourPassword>"
MODEL_NAME = "qwen3-vl-plus"
INPUT_MODE = os.getenv("AIFUNC_AI_BATCH_INPUT_MODE", "video").lower()
POLL_INTERVAL_SEC = int(os.getenv("AIFUNC_AI_BATCH_POLL_INTERVAL_SEC", "5"))
MAX_POLL_ATTEMPTS = int(os.getenv("AIFUNC_AI_BATCH_MAX_POLL_ATTEMPTS", "120"))
def post_json(path: str, body: dict[str, Any], timeout: int = 120) -> tuple[int, dict[str, Any]]:
request = Request(
f"{MILVUS_REST_BASE_URL.rstrip('/')}{path}",
data=json.dumps(body, ensure_ascii=False).encode("utf-8"),
headers={"Authorization": f"Bearer {MILVUS_AUTH_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"))
def batch_request(custom_id: str, content: Any, *, enable_thinking: bool | None = None) -> dict[str, Any]:
body: dict[str, Any] = {"model": MODEL_NAME, "messages": [{"role": "user", "content": content}]}
if enable_thinking is not None:
body["enable_thinking"] = enable_thinking
return {"custom_id": custom_id, "method": "POST", "url": "/v1/chat/completions", "body": body}
def default_requests(input_mode: str) -> list[dict[str, Any]]:
if input_mode == "text":
return [
batch_request("milvus-summary-1", "For a support knowledge base, summarize in one English sentence: Milvus stores and searches vector embeddings for RAG, recommendation, and multimodal retrieval.", enable_thinking=False),
batch_request("milvus-summary-2", "Turn this incident note into an English FAQ title and one-sentence answer: after documents are updated, regenerate embeddings before users search the new content.", enable_thinking=False),
batch_request("milvus-summary-3", "Create a one-sentence English catalog description for an enterprise RAG case that retrieves policy passages before the assistant answers.", enable_thinking=False),
]
if input_mode == "image":
return [batch_request("milvus-image-1", [
{"type": "image_url", "image_url": {"url": "https://dashscope.oss-cn-beijing.aliyuncs.com/images/dog_and_girl.jpeg"}},
{"type": "text", "text": "For a pet-service media library, return a concise English accessibility caption and up to three searchable subject tags for this uploaded case photo."},
])]
if input_mode == "video":
return [batch_request("milvus-video-1", [
{"type": "video", "video": "https://help-static-aliyun-doc.aliyuncs.com/file-manage-files/zh-CN/20260409/dozxak/Wan_Video_Edit_33_1.mp4"},
{"type": "text", "text": "For a marketing asset library, return a one-sentence English scene summary, three retrieval keywords, and whether manual brand-safety review is needed."},
])]
if input_mode == "audio":
return [batch_request("milvus-audio-1", [
{"type": "input_audio", "input_audio": {"data": "https://dashscope.oss-cn-beijing.aliyuncs.com/audios/welcome.mp3", "format": "mp3"}},
{"type": "text", "text": "For a customer-service hotline greeting archive, transcribe the welcome message and assess whether its purpose, service availability, and recording or privacy notice are clear; mark details not present in the audio as unconfirmed."},
])]
print("AIFUNC_AI_BATCH_INPUT_MODE must be one of text, image, video, or audio", file=sys.stderr)
sys.exit(1)
def post_multipart_upload(input_file: str) -> tuple[int, dict[str, Any]]:
boundary = f"----milvus-ai-batch-{uuid.uuid4().hex}"
fields = {"provider": "aliyun_milvus", "model_name": MODEL_NAME, "endpoint": "/v1/chat/completions", "purpose": "batch"}
with tempfile.TemporaryFile(mode="w+b") as payload:
for name, value in fields.items():
payload.write(f"--{boundary}\r\n".encode())
payload.write(f'Content-Disposition: form-data; name="{name}"\r\n\r\n'.encode())
payload.write(value.encode())
payload.write(b"\r\n")
payload.write(f"--{boundary}\r\n".encode())
payload.write(b'Content-Disposition: form-data; name="file"; filename="input.jsonl"\r\n')
payload.write(b"Content-Type: application/jsonl\r\n\r\n")
with open(input_file, "rb") as input_handle:
shutil.copyfileobj(input_handle, payload)
payload.write(b"\r\n")
payload.write(f"--{boundary}--\r\n".encode())
payload_length = payload.tell()
payload.seek(0)
request = Request(
f"{MILVUS_REST_BASE_URL.rstrip('/')}/v2/vectordb/ai/batch/files/upload",
data=payload,
headers={"Authorization": f"Bearer {MILVUS_AUTH_TOKEN}", "Content-Type": f"multipart/form-data; boundary={boundary}", "Content-Length": str(payload_length)},
method="POST",
)
try:
with urlopen(request, timeout=600) 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"))
def download_batch_file(batch_id: str, file_type: str, output_file: Path) -> None:
request = Request(
f"{MILVUS_REST_BASE_URL.rstrip('/')}/v2/vectordb/ai/batch/files/content",
data=json.dumps({"provider": "aliyun_milvus", "batch_id": batch_id, "file_type": file_type}, ensure_ascii=False).encode("utf-8"),
headers={"Authorization": f"Bearer {MILVUS_AUTH_TOKEN}", "Content-Type": "application/json"},
method="POST",
)
with urlopen(request, timeout=600) as response, output_file.open("wb") as output:
shutil.copyfileobj(response, output)
temporary_input = None
INPUT_FILE = os.getenv("AIFUNC_AI_BATCH_INPUT_FILE")
if INPUT_FILE is None:
temporary_input = tempfile.NamedTemporaryFile(mode="w", suffix=".jsonl", delete=False, encoding="utf-8")
for request in default_requests(INPUT_MODE):
temporary_input.write(json.dumps(request, ensure_ascii=False, separators=(",", ":")) + "\n")
temporary_input.close()
INPUT_FILE = temporary_input.name
try:
status, data = post_multipart_upload(INPUT_FILE)
if status != 200 or data.get("code") != 0:
sys.exit(1)
input_file_id = data.get("data", {}).get("id")
status, data = post_json("/v2/vectordb/ai/batch/jobs/create", {
"provider": "aliyun_milvus", "input_file_id": input_file_id, "endpoint": "/v1/chat/completions",
"completion_window": "24h", "metadata": {"ds_name": "milvus-ai-function-example", "ds_description": "AI Batch example"},
})
if status != 200 or data.get("code") != 0:
sys.exit(1)
batch_id = data.get("data", {}).get("id")
for attempt in range(1, MAX_POLL_ATTEMPTS + 1):
status, data = post_json("/v2/vectordb/ai/batch/jobs/describe", {"provider": "aliyun_milvus", "batch_id": batch_id})
batch_data = data.get("data", {})
batch_status = batch_data.get("status")
if batch_status == "completed":
output_file = Path(os.getenv("AIFUNC_AI_BATCH_OUTPUT_FILE", f"ai_batch_output_{batch_id}.jsonl"))
download_batch_file(batch_id, "output", output_file)
print(f"Batch output downloaded to: {output_file}")
if batch_data.get("error_file_id"):
download_batch_file(batch_id, "error", Path(f"ai_batch_errors_{batch_id}.jsonl"))
break
if batch_status in {"failed", "expired", "cancelled"}:
print(f"Batch {batch_id} ended with status: {batch_status}", file=sys.stderr)
sys.exit(1)
if attempt < MAX_POLL_ATTEMPTS:
time.sleep(POLL_INTERVAL_SEC)
finally:
if temporary_input is not None:
os.unlink(temporary_input.name)
Beklenen sonuç: video-output.jsonl dosyası, custom_id=milvus-video-1 ile video özetini döndürür. Özeti video varlık meta verilerine yazdıktan sonra, getirme için video vektörlerini ve anahtar kelimeleri birleştirebilirsiniz. Son durum bir error_file_id içeriyorsa, başarısız görevleri bulmak için hata dosyasını saklayın.
Örnek 4: Müşteri hizmetleri çağrı merkezi karşılama mesajlarının gece boyunca arşivlenmesi (ses)
Bir müşteri hizmetleri operasyon ekibi, her hizmet hattının karşılama mesajlarını periyodik olarak arşivler, içeriği transkribe eder ve çağrı merkezi amacının, hizmet saatlerinin ve kayıt veya gizlilik bildirimlerinin net olup olmadığını kontrol eder; ardından sonuçları karşılama sürüm kimliğini kullanarak yapılandırma merkezine geri yazar. Her satır, ses anlamayı destekleyen bir model kullanarak input_audio aracılığıyla bir MP3 karşılama mesajı iletir ve orijinal sürümle ilişkilendirmek için benzersiz bir custom_id kullanır.
Ses kayıtları ve transkriptler kişisel bilgiler veya dahili hizmet yapılandırmaları içerebilir. Çalıştırmadan önce toplama yetkilendirmesini ve duyarsızlaştırma gereksinimlerini onaylayın. Çıktıları yalnızca geri yazma ve nokta kontrollerini tamamlamak için gereken süre boyunca saklayın, ardından kuruluşunuzun veri saklama politikasına göre güvenli bir şekilde silin. Üretim kayıtlarını veya sonuçlarını bir kod deposuna commit etmeyin.
REST API
REST API
#!/usr/bin/env bash
set -euo pipefail
MILVUS_REST_BASE_URL="http://c-xxxx.milvus.aliyuncs.com:19530"
MILVUS_AUTH_TOKEN="<yourUsername>:<yourPassword>"
post_json() {
local path="$1"
local body="$2"
curl -X POST \
"$MILVUS_REST_BASE_URL$path" \
-H "Authorization: Bearer $MILVUS_AUTH_TOKEN" \
-H "Content-Type: application/json" \
-d "$body"
}
MODEL_NAME="qwen3.5-omni-plus"
INPUT_FILE="${AIFUNC_AI_BATCH_INPUT_FILE:-}"
POLL_INTERVAL_SEC="${AIFUNC_AI_BATCH_POLL_INTERVAL_SEC:-5}"
MAX_POLL_ATTEMPTS="${AIFUNC_AI_BATCH_MAX_POLL_ATTEMPTS:-120}"
if [ -z "$INPUT_FILE" ]; then
echo "Save the sample JSONL requests to a file first, then set AIFUNC_AI_BATCH_INPUT_FILE to the file path." >&2
exit 1
fi
download_batch_file() {
local file_type="$1"; local output_file="$2"; local content_body
content_body="$(jq -nc --arg batch_id "$BATCH_ID" --arg file_type "$file_type" '{provider:"aliyun_milvus", batch_id:$batch_id, file_type:$file_type}')"
curl --fail --silent --show-error -X POST "$MILVUS_REST_BASE_URL/v2/vectordb/ai/batch/files/content" \
-H "Authorization: Bearer $MILVUS_AUTH_TOKEN" -H "Content-Type: application/json" \
-d "$content_body" --output "$output_file"
}
UPLOAD_RESPONSE="$(curl --fail --silent --show-error -X POST "$MILVUS_REST_BASE_URL/v2/vectordb/ai/batch/files/upload" \
-H "Authorization: Bearer $MILVUS_AUTH_TOKEN" \
-F "provider=aliyun_milvus" -F "model_name=$MODEL_NAME" -F "endpoint=/v1/chat/completions" \
-F "purpose=batch" -F "file=@$INPUT_FILE;type=application/jsonl")"
INPUT_FILE_ID="$(echo "$UPLOAD_RESPONSE" | jq -r '.data.id // empty')"
[ -n "$INPUT_FILE_ID" ] || exit 1
CREATE_BODY="$(jq -nc --arg input_file_id "$INPUT_FILE_ID" '{provider:"aliyun_milvus", input_file_id:$input_file_id, endpoint:"/v1/chat/completions", completion_window:"24h", metadata:{ds_name:"milvus-ai-function-example", ds_description:"AI Batch REST example"}}')"
CREATE_RESPONSE="$(post_json "/v2/vectordb/ai/batch/jobs/create" "$CREATE_BODY")"
BATCH_ID="$(echo "$CREATE_RESPONSE" | jq -r '.data.id // empty')"
[ -n "$BATCH_ID" ] || exit 1
for ((attempt = 1; attempt <= MAX_POLL_ATTEMPTS; attempt++)); do
DESCRIBE_BODY="$(jq -nc --arg batch_id "$BATCH_ID" '{provider:"aliyun_milvus", batch_id:$batch_id}')"
DESCRIBE_RESPONSE="$(post_json "/v2/vectordb/ai/batch/jobs/describe" "$DESCRIBE_BODY")"
BATCH_STATUS="$(echo "$DESCRIBE_RESPONSE" | jq -r '.data.status // empty')"
case "$BATCH_STATUS" in
completed)
OUTPUT_FILE="${AIFUNC_AI_BATCH_OUTPUT_FILE:-./ai_batch_output_${BATCH_ID}.jsonl}"
download_batch_file "output" "$OUTPUT_FILE"
echo "Batch output downloaded to: $OUTPUT_FILE"
exit 0 ;;
failed|expired|cancelled)
echo "Batch $BATCH_ID ended with status: $BATCH_STATUS" >&2; exit 1 ;;
validating|in_progress|finalizing|cancelling)
[ "$attempt" -lt "$MAX_POLL_ATTEMPTS" ] && sleep "$POLL_INTERVAL_SEC" ;;
*) echo "Unknown status: ${BATCH_STATUS:-empty}" >&2; exit 1 ;;
esac
done
echo "Batch $BATCH_ID did not finish after $MAX_POLL_ATTEMPTS checks." >&2
exit 1
Python
Python
from __future__ import annotations
import json
import os
import shutil
import sys
import tempfile
import time
import uuid
from pathlib import Path
from typing import Any
from urllib.error import HTTPError
from urllib.request import Request, urlopen
MILVUS_REST_BASE_URL = "http://c-xxxx.milvus.aliyuncs.com:19530"
MILVUS_AUTH_TOKEN = "<yourUsername>:<yourPassword>"
MODEL_NAME = "qwen3.5-omni-plus"
INPUT_MODE = os.getenv("AIFUNC_AI_BATCH_INPUT_MODE", "audio").lower()
POLL_INTERVAL_SEC = int(os.getenv("AIFUNC_AI_BATCH_POLL_INTERVAL_SEC", "5"))
MAX_POLL_ATTEMPTS = int(os.getenv("AIFUNC_AI_BATCH_MAX_POLL_ATTEMPTS", "120"))
def post_json(path: str, body: dict[str, Any], timeout: int = 120) -> tuple[int, dict[str, Any]]:
request = Request(
f"{MILVUS_REST_BASE_URL.rstrip('/')}{path}",
data=json.dumps(body, ensure_ascii=False).encode("utf-8"),
headers={"Authorization": f"Bearer {MILVUS_AUTH_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"))
def batch_request(custom_id: str, content: Any, *, enable_thinking: bool | None = None) -> dict[str, Any]:
body: dict[str, Any] = {"model": MODEL_NAME, "messages": [{"role": "user", "content": content}]}
if enable_thinking is not None:
body["enable_thinking"] = enable_thinking
return {"custom_id": custom_id, "method": "POST", "url": "/v1/chat/completions", "body": body}
def default_requests(input_mode: str) -> list[dict[str, Any]]:
if input_mode == "text":
return [
batch_request("milvus-summary-1", "For a support knowledge base, summarize in one English sentence: Milvus stores and searches vector embeddings for RAG, recommendation, and multimodal retrieval.", enable_thinking=False),
batch_request("milvus-summary-2", "Turn this incident note into an English FAQ title and one-sentence answer: after documents are updated, regenerate embeddings before users search the new content.", enable_thinking=False),
batch_request("milvus-summary-3", "Create a one-sentence English catalog description for an enterprise RAG case that retrieves policy passages before the assistant answers.", enable_thinking=False),
]
if input_mode == "image":
return [batch_request("milvus-image-1", [
{"type": "image_url", "image_url": {"url": "https://dashscope.oss-cn-beijing.aliyuncs.com/images/dog_and_girl.jpeg"}},
{"type": "text", "text": "For a pet-service media library, return a concise English accessibility caption and up to three searchable subject tags for this uploaded case photo."},
])]
if input_mode == "video":
return [batch_request("milvus-video-1", [
{"type": "video", "video": "https://help-static-aliyun-doc.aliyuncs.com/file-manage-files/zh-CN/20260409/dozxak/Wan_Video_Edit_33_1.mp4"},
{"type": "text", "text": "For a marketing asset library, return a one-sentence English scene summary, three retrieval keywords, and whether manual brand-safety review is needed."},
])]
if input_mode == "audio":
return [batch_request("milvus-audio-1", [
{"type": "input_audio", "input_audio": {"data": "https://dashscope.oss-cn-beijing.aliyuncs.com/audios/welcome.mp3", "format": "mp3"}},
{"type": "text", "text": "For a customer-service hotline greeting archive, transcribe the welcome message and assess whether its purpose, service availability, and recording or privacy notice are clear; mark details not present in the audio as unconfirmed."},
])]
print("AIFUNC_AI_BATCH_INPUT_MODE must be one of text, image, video, or audio", file=sys.stderr)
sys.exit(1)
def post_multipart_upload(input_file: str) -> tuple[int, dict[str, Any]]:
boundary = f"----milvus-ai-batch-{uuid.uuid4().hex}"
fields = {"provider": "aliyun_milvus", "model_name": MODEL_NAME, "endpoint": "/v1/chat/completions", "purpose": "batch"}
with tempfile.TemporaryFile(mode="w+b") as payload:
for name, value in fields.items():
payload.write(f"--{boundary}\r\n".encode())
payload.write(f'Content-Disposition: form-data; name="{name}"\r\n\r\n'.encode())
payload.write(value.encode())
payload.write(b"\r\n")
payload.write(f"--{boundary}\r\n".encode())
payload.write(b'Content-Disposition: form-data; name="file"; filename="input.jsonl"\r\n')
payload.write(b"Content-Type: application/jsonl\r\n\r\n")
with open(input_file, "rb") as input_handle:
shutil.copyfileobj(input_handle, payload)
payload.write(b"\r\n")
payload.write(f"--{boundary}--\r\n".encode())
payload_length = payload.tell()
payload.seek(0)
request = Request(
f"{MILVUS_REST_BASE_URL.rstrip('/')}/v2/vectordb/ai/batch/files/upload",
data=payload,
headers={"Authorization": f"Bearer {MILVUS_AUTH_TOKEN}", "Content-Type": f"multipart/form-data; boundary={boundary}", "Content-Length": str(payload_length)},
method="POST",
)
try:
with urlopen(request, timeout=600) 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"))
def download_batch_file(batch_id: str, file_type: str, output_file: Path) -> None:
request = Request(
f"{MILVUS_REST_BASE_URL.rstrip('/')}/v2/vectordb/ai/batch/files/content",
data=json.dumps({"provider": "aliyun_milvus", "batch_id": batch_id, "file_type": file_type}, ensure_ascii=False).encode("utf-8"),
headers={"Authorization": f"Bearer {MILVUS_AUTH_TOKEN}", "Content-Type": "application/json"},
method="POST",
)
with urlopen(request, timeout=600) as response, output_file.open("wb") as output:
shutil.copyfileobj(response, output)
temporary_input = None
INPUT_FILE = os.getenv("AIFUNC_AI_BATCH_INPUT_FILE")
if INPUT_FILE is None:
temporary_input = tempfile.NamedTemporaryFile(mode="w", suffix=".jsonl", delete=False, encoding="utf-8")
for request in default_requests(INPUT_MODE):
temporary_input.write(json.dumps(request, ensure_ascii=False, separators=(",", ":")) + "\n")
temporary_input.close()
INPUT_FILE = temporary_input.name
try:
status, data = post_multipart_upload(INPUT_FILE)
if status != 200 or data.get("code") != 0:
sys.exit(1)
input_file_id = data.get("data", {}).get("id")
status, data = post_json("/v2/vectordb/ai/batch/jobs/create", {
"provider": "aliyun_milvus", "input_file_id": input_file_id, "endpoint": "/v1/chat/completions",
"completion_window": "24h", "metadata": {"ds_name": "milvus-ai-function-example", "ds_description": "AI Batch example"},
})
if status != 200 or data.get("code") != 0:
sys.exit(1)
batch_id = data.get("data", {}).get("id")
for attempt in range(1, MAX_POLL_ATTEMPTS + 1):
status, data = post_json("/v2/vectordb/ai/batch/jobs/describe", {"provider": "aliyun_milvus", "batch_id": batch_id})
batch_data = data.get("data", {})
batch_status = batch_data.get("status")
if batch_status == "completed":
output_file = Path(os.getenv("AIFUNC_AI_BATCH_OUTPUT_FILE", f"ai_batch_output_{batch_id}.jsonl"))
download_batch_file(batch_id, "output", output_file)
print(f"Batch output downloaded to: {output_file}")
if batch_data.get("error_file_id"):
download_batch_file(batch_id, "error", Path(f"ai_batch_errors_{batch_id}.jsonl"))
break
if batch_status in {"failed", "expired", "cancelled"}:
print(f"Batch {batch_id} ended with status: {batch_status}", file=sys.stderr)
sys.exit(1)
if attempt < MAX_POLL_ATTEMPTS:
time.sleep(POLL_INTERVAL_SEC)
finally:
if temporary_input is not None:
os.unlink(temporary_input.name)
Beklenen sonuç: call-greeting-output.jsonl veya call-greeting-error.jsonl dosyası, custom_id=milvus-audio-1 içeren tam olarak bir satır içerir. Sınırlı kapsamlı doğrulamadan sonra, başarılı sonuçlar müşteri hizmetleri operasyon personeli tarafından nokta kontrolü yapılması üzere yapılandırma merkezine yazılır. Başarısız satırlar orijinal custom_id değerini korur ve ses izinleri, MP3 formatı veya model parametreleri düzeltildikten sonra tek tek yeniden denenebilir.