Azure AI Search - Vector Store

Azure AI Search - Vector Store (Passthrough API)

Azure AI Search API를 네이티브 형식 그대로 사용해 개발자가 벡터 스토어를 생성하고 검색할 수 있게 하되, Azure AI 자격 증명은 노출하지 않게 해요. 이 기능은 Proxy 전용이에요.

출처: 문서

본문

관리자 흐름 (Admin Flow)

1. LiteLLM에 벡터 스토어 추가

LiteLLM에 벡터 스토어 자격 증명을 추가해요.

model_list:
  - model_name: embedding-model
    litellm_params:
      model: openai/text-embedding-3-large

vector_store_registry:
  - vector_store_name: "azure-ai-search"
    litellm_params:
      vector_store_id: "can-be-anything"  # vector store id can be anything for the purpose of passthrough api
      custom_llm_provider: "azure_ai"
      api_key: os.environ/AZURE_SEARCH_API_KEY
      api_base: https://azure-kb-search.search.windows.net
      litellm_embedding_model: "azure/text-embedding-3-large"
      litellm_embedding_config:
        api_base: https://krris-mh44uf7y-eastus2.cognitiveservices.azure.com/
        api_key: os.environ/AZURE_API_KEY
        api_version: "2025-09-01"

general_settings:
  database_url: "postgresql://user:***@host:port/database"
  master_key: "sk-<your-litellm-master-key>"

2. Proxy 시작

litellm --config /path/to/config.yaml
# RUNNING on http://0.0.0.0:4000

3. 가상 인덱스 생성

이는 개발자가 벡터 스토어를 생성하고 검색할 때 사용하는 가상 인덱스예요.

curl -L -X POST 'http://0.0.0.0:4000/v1/indexes' \
  -H 'Content-Type: application/json' \
  -H "Authorization: Bearer ***" \
  -d '{
    "index_name": "dall-e-4",
    "litellm_params": {
      "vector_store_index": "real-index-name-2",
      "vector_store_name": "azure-ai-search"
    }
  }'

4. 벡터 스토어 권한으로 키 생성

키에 가상 인덱스와 임베딩 모델에 대한 접근 권한을 부여해요.

curl -L -X POST 'http://0.0.0.0:4000/key/generate' \
  -H 'Content-Type: application/json' \
  -H "Authorization: Bearer ***" \
  -d '{
    "allowed_vector_store_indexes": [{"index_name": "dall-e-4", "index_permissions": ["write", "read"]}],
    "models": ["embedding-model"]
  }'
{ "key": "sk-…" }

개발자 흐름 (Developer Flow)

1. 문서 몇 개로 벡터 스토어 생성

참고: passthrough API에 Azure의 azure_ai 공급자를 사용하려면 /azure_ai 엔드포인트를 사용해요.

import requests
import json

# ----------------------------
# 🔐 CONFIGURATION
# ----------------------------
# Azure OpenAI (for embeddings)
AZURE_OPENAI_ENDPOINT = "http://0.0.0.0:4000"
AZURE_OPENAI_KEY = "sk-…"
EMBEDDING_DEPLOYMENT_NAME = "embedding-model"

# Azure AI Search
AZURE_AI_SEARCH_ENDPOINT = "http://0.0.0.0:4000/azure_ai"  # IMPORTANT: Use the '/azure_ai' endpoint for the passthrough api to Azure
SEARCH_API_KEY = "sk-…"
INDEX_NAME = "dall-e-4"

# Vector dimensions (text-embedding-3-large uses 3072 dimensions)
VECTOR_DIMENSIONS = 3072

# Example docs (replace with your own)
documents = [
    {"id": "1", "content": "Refunds must be requested within 30 days."},
    {"id": "2", "content": "We offer 24/7 support for all enterprise customers."},
]

def delete_index_if_exists():
    """Delete the index if it exists"""
    index_url = f"{AZURE_AI_SEARCH_ENDPOINT}/indexes/{INDEX_NAME}?api-version=2024-07-01"
    headers = {"api-key": SEARCH_API_KEY}
    response = requests.delete(index_url, headers=headers)
    if response.status_code == 204:
        print(f"Deleted existing index '{INDEX_NAME}'")
        return True
    elif response.status_code == 404:
        print(f"Index '{INDEX_NAME}' does not exist yet")
        return False
    else:
        print(f"Delete response: {response.status_code}")
        return False

def create_index():
    """Create the Azure AI Search index with proper schema"""
    index_url = f"{AZURE_AI_SEARCH_ENDPOINT}/indexes/{INDEX_NAME}?api-version=2024-07-01"
    headers = {"Content-Type": "application/json", "api-key": SEARCH_API_KEY}
    index_schema = {
        "name": INDEX_NAME,
        "fields": [
            {"name": "id", "type": "Edm.String", "key": True, "filterable": True},
            {"name": "content", "type": "Edm.String", "searchable": True, "filterable": False},
            {
                "name": "contentVector",
                "type": "Collection(Edm.Single)",
                "searchable": True,
                "dimensions": VECTOR_DIMENSIONS,
                "vectorSearchProfile": "my-vector-profile",
            },
        ],
        "vectorSearch": {
            "algorithms": [{"name": "my-vector-profile", "kind": "hnsw", "hnswParameters": {}}],
        },
    }
    response = requests.put(index_url, headers=headers, json=index_schema)
    ...

2. 쿼리 임베딩 생성 및 벡터 검색

import requests
import json

# ----------------------------
# 🔐 CONFIGURATION
# ----------------------------
AZURE_OPENAI_ENDPOINT = "http://0.0.0.0:4000"
AZURE_OPENAI_KEY = "sk-…"
EMBEDDING_DEPLOYMENT_NAME = "embedding-model"
AZURE_AI_SEARCH_ENDPOINT = "http://0.0.0.0:4000/azure_ai"
SEARCH_API_KEY = "sk-…"
INDEX_NAME = "dall-e-4"

def get_embedding(text: str):
    """Generate embedding for the query text"""
    url = f"{AZURE_OPENAI_ENDPOINT}/openai/deployments/{EMBEDDING_DEPLOYMENT_NAME}/embeddings?api-version=2024-10-21"
    headers = {"Content-Type": "application/json", "api-key": AZURE_OPENAI_KEY}
    payload = {"input": text}
    response = requests.post(url, headers=headers, json=payload)
    if response.status_code != 200:
        raise Exception(f"Embedding failed: {response.status_code}\n{response.text}")
    return response.json()["data"][0]["embedding"]

def search_knowledge_base(query: str, top_k: int = 3):
    """Search the knowledge base using vector similarity.

    Args:
        query: The search query string
        top_k: Number of top results to return (default: 3)

    Returns:
        List of search results with content and scores
    """
    print(f"Searching for: '{query}'")
    # Step 1: Generate embedding for the query
    query_vector = get_embedding(query)
    # Step 2: Perform vector search
    search_url = f"{AZURE_AI_SEARCH_ENDPOINT}/indexes/{INDEX_NAME}/docs/search?api-version=2024-07-01"
    headers = {"Content-Type": "application/json", "api-key": SEARCH_API_KEY}
    search_payload = {
        "search": "*",  # Get all documents
        "vectorQueries": [
            {
                "vector": query_vector,
                "fields": "contentVector",
                "kind": "vector",
                "k": top_k,  # Number of nearest neighbors to return
            }
        ],
        "select": "id,content",  # Fields to return
        "top": top_k,
    }
    ...

더 알아보기 (Learn more)