azure-ai-search-py
Azure AI Search SDK para Python. Crea indices vectoriales/semánticos, ingesta documentos y ejecuta búsquedas RAG sobre catálogos de productos, bases de conocimiento o contenido de marketing. Triggers: "azure search", "ai search", "busqueda semantica", "vector search", "índice azure".
azure-search-documents Python Azure AI Foundry data avanzado MIT
ID 1d50752d-ex
Version 1.0.0
Lineas SKILL.md 312
Scenarios 4
Generado 2026-06-15

📦 Instalacion

bash terminal Copiar
pip install azure-search-documents azure-identity

Requiere Python 3.9+. Incluye el cliente síncrono SearchClient, SearchIndexClient e SearchIndexerClient.

🔧 Variables de Entorno

bash .env Copiar
AZURE_SEARCH_ENDPOINT=https://<resource>.search.windows.net   # Endpoint del servicio
AZURE_SEARCH_INDEX_NAME=products-catalog                       # Nombre del índice
AZURE_TOKEN_CREDENTIALS=prod                                   # Solo si DefaultAzureCredential en produccion
AZURE_SEARCH_API_KEY=<api-key>                                 # Solo para legacy API-key auth (ver abajo)

🔑 Autenticacion & Lifecycle

🔑 Dos reglas aplican a TODOS los ejemplos de codigo:
  • Prefiere DefaultAzureCredential. Funciona localmente (Azure CLI / VS Code / Developer CLI) y en Azure (managed identity, workload identity) sin cambios de codigo. Evita connection strings y API keys — eluden el audit y la rotacion de Entra.
    • Dev local: DefaultAzureCredential funciona tal cual.
    • Produccion: establece AZURE_TOKEN_CREDENTIALS=prod para restringir la credential chain a credenciales seguras.
  • Envuelve cada cliente en un context manager para liberar HTTP transports, sockets y token caches de forma determinista:
    • Sync: with <Client>(...) as client:
    • Async: async with <Client>(...) as client: y async with DefaultAzureCredential() as credential:

Autenticacion con DefaultAzureCredential (recomendado)

python auth_example.py Copiar
import os
from azure.identity import DefaultAzureCredential, ManagedIdentityCredential
from azure.search.documents import SearchClient
from azure.search.documents.indexes import SearchIndexClient

# Dev local: DefaultAzureCredential. Prod: AZURE_TOKEN_CREDENTIALS=prod
credential = DefaultAzureCredential(require_envvar=True)
# Produccion con credencial explicita:
# credential = ManagedIdentityCredential()

endpoint = os.environ["AZURE_SEARCH_ENDPOINT"]
index_name = os.environ["AZURE_SEARCH_INDEX_NAME"]

with SearchIndexClient(endpoint=endpoint, credential=credential) as index_client:
    names = [idx.name for idx in index_client.list_index_names()]
    print(f"Indices disponibles: {names}")

with SearchClient(endpoint=endpoint, index_name=index_name, credential=credential) as client:
    result = client.search(search_text="*", top=1)
    print("Conexion OK — primer doc:", next(iter(result), None))

Legacy: API Key (deployments existentes)

⚠️ Solo para deployments existentes
El nuevo codigo debe usar DefaultAzureCredential. La ruta de API key elude el audit de Entra y la rotacion automatica. Usar solo mientras se completa la migracion.
python legacy_key_auth.py Copiar
from azure.core.credentials import AzureKeyCredential
from azure.search.documents import SearchClient

credential = AzureKeyCredential(os.environ["AZURE_SEARCH_API_KEY"])
with SearchClient(endpoint=endpoint, index_name=index_name, credential=credential) as client:
    ...

▶️ Core Workflow

Flujo completo: crear índice vectorial + ingestar productos + búsqueda semántica/vectorial.

1. Definir y crear el índice

python create_index.py Copiar
from azure.search.documents.indexes.models import (
    SearchIndex, SearchField, SearchFieldDataType,
    SimpleField, SearchableField, VectorSearch,
    HnswAlgorithmConfiguration, VectorSearchProfile,
    SemanticConfiguration, SemanticSearch, SemanticPrioritizedFields,
    SemanticField
)

fields = [
    SimpleField(name="id", type=SearchFieldDataType.String, key=True),
    SearchableField(name="nombre", type=SearchFieldDataType.String, analyzer_name="es.microsoft"),
    SearchableField(name="descripcion", type=SearchFieldDataType.String, analyzer_name="es.microsoft"),
    SimpleField(name="categoria", type=SearchFieldDataType.String, filterable=True, facetable=True),
    SimpleField(name="precio", type=SearchFieldDataType.Double, filterable=True, sortable=True),
    SearchField(
        name="vector_descripcion",
        type=SearchFieldDataType.Collection(SearchFieldDataType.Single),
        searchable=True, vector_search_dimensions=1536,
        vector_search_profile_name="my-hnsw-profile"
    ),
]

vector_search = VectorSearch(
    algorithms=[HnswAlgorithmConfiguration(name="my-hnsw")],
    profiles=[VectorSearchProfile(name="my-hnsw-profile", algorithm_configuration_name="my-hnsw")]
)
semantic_config = SemanticConfiguration(
    name="my-semantic-config",
    prioritized_fields=SemanticPrioritizedFields(
        title_field=SemanticField(field_name="nombre"),
        content_fields=[SemanticField(field_name="descripcion")]
    )
)
index = SearchIndex(
    name=index_name,
    fields=fields,
    vector_search=vector_search,
    semantic_search=SemanticSearch(configurations=[semantic_config])
)

with SearchIndexClient(endpoint=endpoint, credential=credential) as index_client:
    created = index_client.create_or_update_index(index)
    print(f"Indice '{created.name}' listo — {len(created.fields)} campos")

2. Ingestar documentos

python upload_docs.py Copiar
from azure.search.documents.models import IndexDocumentsBatch

productos = [
    {
        "id": "001", "nombre": "Cafe de Especialidad Guatemala",
        "descripcion": "Granos arábica de altura, notas de chocolate y naranja",
        "categoria": "cafe", "precio": 18.50,
        "vector_descripcion": generar_embedding("Granos arábica de altura...")
    },
    {
        "id": "002", "nombre": "Kit Inicio Cultivo Microgreens",
        "descripcion": "Todo lo necesario para cultivar microgreens en casa en 10 dias",
        "categoria": "cultivo", "precio": 34.90,
        "vector_descripcion": generar_embedding("Todo lo necesario para cultivar...")
    },
]

with SearchClient(endpoint=endpoint, index_name=index_name, credential=credential) as client:
    results = client.upload_documents(documents=productos)
    success = [r for r in results if r.succeeded]
    print(f"Cargados: {len(success)}/{len(productos)}")

3. Busqueda semantica + vectorial (RAG)

python search_rag.py Copiar
from azure.search.documents.models import VectorizedQuery, QueryType

query = "regalo especial para amante del cafe con notas frutales"
query_vector = generar_embedding(query)

with SearchClient(endpoint=endpoint, index_name=index_name, credential=credential) as client:
    results = client.search(
        search_text=query,
        query_type=QueryType.SEMANTIC,
        semantic_configuration_name="my-semantic-config",
        query_caption="extractive",
        vector_queries=[
            VectorizedQuery(
                vector=query_vector,
                k_nearest_neighbors=5,
                fields="vector_descripcion"
            )
        ],
        select=["id", "nombre", "descripcion", "precio"],
        top=3
    )
    for doc in results:
        score = doc.get("@search.score", 0)
        print(f"[{score:.3f}] {doc['nombre']} — €{doc['precio']}")

📋 Clientes y Metodos Principales

SearchIndexClient — Gestion de indices

MetodoComportamientoDevuelve
create_index(index)Crea nuevo; falla si existeSearchIndex
create_or_update_index(index)Upsert del indiceSearchIndex
get_index(name)Obtiene; error si no existeSearchIndex
list_indexes()Itera todos los indicesItemPaged[SearchIndex]
delete_index(name)Elimina; ok si no existeNone
get_index_statistics(name)Doc count, storage sizeSearchIndexStatistics

SearchClient — Operaciones sobre documentos

MetodoComportamientoDevuelve
upload_documents(documents)Inserta o reemplazaList[IndexingResult]
merge_documents(documents)Actualiza campos; falla si no existeList[IndexingResult]
merge_or_upload_documents(documents)Upsert de documentosList[IndexingResult]
delete_documents(documents)Elimina por key; ok si no existeList[IndexingResult]
search(search_text, **kwargs)Busqueda full-text / semantica / vectorItemPaged[dict]
get_document(key)Obtiene doc por clave; error si no existedict
suggest(search_text, suggester_name)AutocompletadoSuggestResults

Verbos Azure SDK estandar

VerboComportamiento
createCrea nuevo; falla si ya existe
upsert / create_or_updateCrea o actualiza
getRecupera; error si no existe
listDevuelve coleccion paginada (ItemPaged)
deleteElimina; exito incluso si no existe

Best Practices

✅ Reglas obligatorias para Python — incluir en toda skill generada
Las dos primeras reglas deben aparecer VERBATIM en el SKILL.md final para que los usuarios las apliquen en su propio codigo.
  1. Elige sync O async y manten la consistencia. No mezcles azure.search.documents (sync) con azure.search.documents.aio (async) en el mismo call path. Elige un modo por modulo.
  2. Usa siempre context managers para clientes y credenciales async. Envuelve cada cliente en with SearchClient(...) as client: (sync) o async with SearchClient(...) as client: (async). Para DefaultAzureCredential async de azure.identity.aio, usa tambien async with credential:.
  3. Usa DefaultAzureCredential para codigo que corre localmente. Usa una credencial especifica (ManagedIdentityCredential, WorkloadIdentityCredential) para codigo en Azure en produccion.
  4. Pagina siempre con ItemPaged — no consumas toda la coleccion en memoria. Itera directamente sobre el resultado de search() o list_indexes().
  5. Filtra con OData en el server — usa el parametro filter en lugar de filtrar en Python. Ejemplo: filter="categoria eq 'cafe' and precio lt 25".
  6. Separa SearchIndexClient de SearchClient — el primero gestiona el esquema del indice; el segundo opera sobre documentos. No uses el de indice para buscar.
  7. Limpia el indice en ejemplos y tests — llama a delete_index() al final de scripts de demostracion para no acumular indices de prueba.

🧪 Tests — Acceptance Criteria & Scenarios

Acceptance Criteria: patrones correctos vs. incorrectos

EstadoPatronMotivo
✅ CORRECTOfrom azure.search.documents import SearchClientRuta de importacion correcta
✅ CORRECTOwith SearchClient(...) as client:Context manager — limpia HTTP transport
✅ CORRECTODefaultAzureCredential(require_envvar=True)Auth Entra; configurable en produccion
❌ INCORRECTOAzureKeyCredential("hardcoded-key")Credencial hardcoded — riesgo de seguridad
❌ INCORRECTOclient = SearchClient(...) sin withFuga de HTTP transport al salir de scope
❌ INCORRECTOfrom azure.search.documents.models import SearchClientRuta de modulo incorrecta
❌ INCORRECTOMezclar .documents (sync) + .aio (async)Comportamiento impredecible en el event loop

Scenarios YAML

basic_client_creation
basic auth
Expected patterns
DefaultAzureCredential
SearchClient
with SearchClient
azure.search.documents
Forbidden patterns
api_key=
hardcoded
AzureKeyCredential("
create_index_with_vector
index vector
Expected patterns
SearchIndexClient
VectorSearch
HnswAlgorithmConfiguration
create_or_update_index
Forbidden patterns
create_index (sin upsert)
ExhaustiveKnn para escala
semantic_hybrid_search
search semantic vector
Expected patterns
QueryType.SEMANTIC
VectorizedQuery
k_nearest_neighbors
semantic_configuration_name
Forbidden patterns
✗ Cargar todos los resultados en lista
✗ Filtrar en Python en lugar de OData
async_variant
async avanzado
Expected patterns
from azure.search.documents.aio
async with SearchClient
async with credential
from azure.identity.aio
Forbidden patterns
✗ Mezcla sync + async
from azure.search.documents import en path async

☑️ Checklist de Entrega

Prerequisitos

Creacion del Skill

Categorizacion

Testing

Documentacion