Inicio / Artículos / APIM frente a las llamadas de la fundición de agentes con RBAC de menor privilegio

APIM frente a las llamadas de la fundición de agentes con RBAC de menor privilegio

Invocaciones de modelos frontales con API Management, identidad gestionada, roles personalizados en el plano de datos y backends de Function App.

2858 palabras

Qué estamos desarrollando

AI Foundry puede alojar muchas familias de modelos. Crear agentes sobre ellos se vuelve interesante —y complicado— cuando cada modelo tiene diferentes métodos de autenticación y cuando aplican reglas relacionadas con PII/PCI. Azure API Management (APIM) junto con identidades de mínimo privilegio proporciona una única puerta de entrada.

Parte 1: La base

Aprovisione los recursos de Foundry/OpenAI, una Function App que llamará a los modelos y APIM que servirá como interfaz para dicha función. Decida de antemano las regiones y la red privada a utilizar.

Parte 2: Despliegue de los modelos

Despliegue solo los modelos de chat que realmente necesite. Registre los nombres de despliegue por separado de los nombres de los modelos base.

PARTE 2: RBAC personalizado con mínimo privilegio

OpenAI: solo completaciones de chat

Los roles personalizados que permiten únicamente las acciones con datos que necesita su función son mejores que las asignaciones generales de Cognitive Services Contributor.

Anthropic: roles integrados para acciones de datos que pueden faltar

Algunas rutas de modelos de terceros necesitan acciones de datos adicionales; verifique con el cuerpo del error de una llamada fallida y luego modifique el rol, no al revés.

RG="rg-foundry-func-demo"
LOCATION="eastus2"
UMI_NAME="umi-foundry-func"
FOUNDRY_NAME="foundry-func-demo"
STORAGE_NAME="stfoundryfuncdemo"
FUNC_NAME="func-foundry-py-$RANDOM"
SUBSCRIPTION_ID=$(az account show --query id -o tsv)

az group create --name $RG --location $LOCATION
az identity create --name $UMI_NAME --resource-group $RG

UMI_CLIENT_ID=$(az identity show --name $UMI_NAME --resource-group $RG --query clientId -o tsv)
UMI_PRINCIPAL_ID=$(az identity show --name $UMI_NAME --resource-group $RG --query principalId -o tsv)
UMI_RESOURCE_ID=$(az identity show --name $UMI_NAME --resource-group $RG --query id -o tsv)

az cognitiveservices account create \
  --name $FOUNDRY_NAME \
  --resource-group $RG \
  --location $LOCATION \
  --kind AIServices \
  --sku S0 \
  --custom-domain $FOUNDRY_NAME \
  --yes

FOUNDRY_ENDPOINT=$(az cognitiveservices account show \
  --name $FOUNDRY_NAME --resource-group $RG \
  --query properties.endpoint -o tsv)

FOUNDRY_ID=$(az cognitiveservices account show \
  --name $FOUNDRY_NAME --resource-group $RG --query id -o tsv)
az cognitiveservices account deployment create \
  --name $FOUNDRY_NAME \
  --resource-group $RG \
  --deployment-name gpt-5-mini \
  --model-name gpt-5-mini \
  --model-version "2025-08-07" \
  --model-format OpenAI \
  --sku-name GlobalStandard \
  --sku-capacity 10

{
  "etag": "\"c39e7d0e-7c32-48a2-ba62-8a897265efbb\"",
  "id": "/subscriptions/a509ec7e-2297-419d-8587-ca08543e3cbe/resourceGroups/rg-foundry-func-demo/providers/Microsoft.CognitiveServices/accounts/foundry-func-demo-01/deployments/gpt-5-mini",
  "name": "gpt-5-mini",
  "properties": {
    "callRateLimit": null,
    "capabilities": {
      "agentsV2": "true",
      "area": "US",
      "assistants": "true",
      "chatCompletion": "true",
      "responses": "true"
    },
    "capacitySettings": null,
    "currentCapacity": 10,
    "deploymentState": "Running",
    "dynamicThrottlingEnabled": null,
    "model": {
      "callRateLimit": null,
      "format": "OpenAI",
      "name": "gpt-5-mini",
      "publisher": null,
      "source": null,
      "sourceAccount": null,
      "version": "2025-08-07"
    },
    "parentDeploymentName": null,
    "provisioningState": "Succeeded",
    "raiPolicyName": "Microsoft.DefaultV2",
    "rateLimits": [
      {
        "count": 10.0,
        "dynamicThrottlingEnabled": null,
        "key": "request",
        "matchPatterns": null,
        "minCount": null,
        "renewalPeriod": 60.0
      },
      {
        "count": 10000.0,
        "dynamicThrottlingEnabled": null,
        "key": "token",
        "matchPatterns": null,
        "minCount": null,
        "renewalPeriod": 60.0
      }
    ],
    "routing": null,
    "scaleSettings": null,
    "serviceTier": null,
    "spilloverDeploymentName": null,
    "versionUpgradeOption": "OnceNewDefaultVersionAvailable"
  },
  "resourceGroup": "rg-foundry-func-demo",
  "sku": {
    "capacity": 10,
    "family": null,
    "name": "GlobalStandard",
    "size": null,
    "tier": null
  },
  "systemData": {
    "createdAt": "2026-07-05T18:03:12.346795+00:00",
    "createdBy": "krishnan.sriram@hotmail.com",
    "createdByType": "User",
    "lastModifiedAt": "2026-07-05T18:03:12.346795+00:00",
    "lastModifiedBy": "krishnan.sriram@hotmail.com",
    "lastModifiedByType": "User"
  },
  "tags": null,
  "type": "Microsoft.CognitiveServices/accounts/deployments"
}
FOUNDRY_ID=$(az cognitiveservices account show \
  --name $FOUNDRY_NAME --resource-group $RG --query id -o tsv)

az resource create \
  --id "$FOUNDRY_ID/deployments/claude-opus-4-6" \
  --api-version 2025-10-01-preview \
  --is-full-object \
  --properties '{
    "location": "eastus2",
    "properties": {
      "model": {
        "format": "Anthropic",
        "name": "claude-opus-4-6",
        "version": "1"
      },
      "modelProviderData": {
        "organizationName": "SRK Tech",
        "countryCode": "US",
        "industry": "technology"
      }
    },
    "sku": {
      "name": "GlobalStandard",
      "capacity": 10
    }
  }'
# execution Results
{
  "etag": "\"a5b65a53-a65a-494b-9059-b16dc700b743\"",
  "extendedLocation": null,
  "id": "/subscriptions/a509ec7e-2297-419d-8587-ca08543e3cbe/resourceGroups/rg-foundry-func-demo/providers/Microsoft.CognitiveServices/accounts/foundry-func-demo-01/deployments/claude-opus-4-6",
  "identity": null,
  "kind": null,
  "location": null,
  "managedBy": null,
  "name": "claude-opus-4-6",
  "plan": null,
  "properties": {
    "capabilities": {
      "agentsV2": "true",
      "chatCompletion": "true"
    },
    "currentCapacity": 10,
    "deploymentState": "Running",
    "model": {
      "format": "Anthropic",
      "name": "claude-opus-4-6",
      "version": "1"
    },
    "modelProviderData": {
      "countryCode": "US",
      "industry": "technology",
      "organizationName": "SRK Tech"
    },
    "provisioningState": "Succeeded",
    "raiPolicyName": "Microsoft.DefaultV2",
    "rateLimits": [
      {
        "count": 10,
        "key": "request",
        "renewalPeriod": 60
      },
      {
        "count": 10000,
        "key": "token",
        "renewalPeriod": 60
      }
    ],
    "versionUpgradeOption": "OnceNewDefaultVersionAvailable"
  },
  "resourceGroup": "rg-foundry-func-demo",
  "sku": {
    "capacity": 10,
    "family": null,
    "model": null,
    "name": "GlobalStandard",
    "size": null,
    "tier": null
  },
  "systemData": {
    "createdAt": "2026-07-05T18:06:43.6897129Z",
    "createdBy": "krishnan.sriram@hotmail.com",
    "createdByType": "User",
    "lastModifiedAt": "2026-07-05T18:06:43.6897129Z",
    "lastModifiedBy": "krishnan.sriram@hotmail.com",
    "lastModifiedByType": "User"
  },
  "tags": null,
  "type": "Microsoft.CognitiveServices/accounts/deployments"
}
az cognitiveservices account deployment list \
  --name $FOUNDRY_NAME --resource-group $RG -o table
Name             ResourceGroup
---------------  --------------------
gpt-5-mini       rg-foundry-func-demo
claude-opus-4-6  rg-foundry-func-demo
cat > /tmp/foundry-openai-role.json << EOF
{
  "Name": "Foundry OpenAI Chat Inference User",
  "IsCustom": true,
  "Description": "Minimal data-plane access to call chat completions on a Microsoft Foundry OpenAI deployment.",
  "Actions": [],
  "DataActions": [
    "Microsoft.CognitiveServices/accounts/OpenAI/deployments/chat/completions/action"
  ],
  "AssignableScopes": ["/subscriptions/$SUBSCRIPTION_ID"]
}
EOF
az role definition create --role-definition /tmp/foundry-openai-role.json

# Execution results
{
  "assignableScopes": [
    "/subscriptions/a509ec7e-2297-419d-8587-ca08543e3cbe"
  ],
  "createdBy": null,
  "createdOn": "2026-07-05T18:22:39.111105+00:00",
  "description": "Minimal data-plane access to call chat completions on a Microsoft Foundry OpenAI deployment.",
  "id": "/subscriptions/a509ec7e-2297-419d-8587-ca08543e3cbe/providers/Microsoft.Authorization/roleDefinitions/c7035712-c66d-482d-9fd5-0fc116a2d9f4",
  "name": "c7035712-c66d-482d-9fd5-0fc116a2d9f4",
  "permissions": [
    {
      "actions": [],
      "condition": null,
      "conditionVersion": null,
      "dataActions": [
        "Microsoft.CognitiveServices/accounts/OpenAI/deployments/chat/completions/action"
      ],
      "notActions": [],
      "notDataActions": []
    }
  ],
  "roleName": "Foundry OpenAI Chat Inference User",
  "roleType": "CustomRole",
  "type": "Microsoft.Authorization/roleDefinitions",
  "updatedBy": "28567f42-a049-464e-9211-8f1b270cdf6a",
  "updatedOn": "2026-07-05T18:22:39.111105+00:00"
}

az role assignment create \
  --assignee-object-id $UMI_PRINCIPAL_ID \
  --assignee-principal-type ServicePrincipal \
  --role "Foundry OpenAI Chat Inference User" \
  --scope $FOUNDRY_ID

# Execution results
{
  "condition": null,
  "conditionVersion": null,
  "createdBy": null,
  "createdOn": "2026-07-05T18:23:42.642379+00:00",
  "delegatedManagedIdentityResourceId": null,
  "description": null,
  "id": "/subscriptions/a509ec7e-2297-419d-8587-ca08543e3cbe/resourceGroups/rg-foundry-func-demo/providers/Microsoft.CognitiveServices/accounts/foundry-func-demo-01/providers/Microsoft.Authorization/roleAssignments/580a3b61-aaad-456e-9b3e-6ced977dd378",
  "name": "580a3b61-aaad-456e-9b3e-6ced977dd378",
  "principalId": "a114ced5-b53e-4c42-8949-bddd053610cc",
  "principalType": "ServicePrincipal",
  "resourceGroup": "rg-foundry-func-demo",
  "roleDefinitionId": "/subscriptions/a509ec7e-2297-419d-8587-ca08543e3cbe/providers/Microsoft.Authorization/roleDefinitions/c7035712-c66d-482d-9fd5-0fc116a2d9f4",
  "scope": "/subscriptions/a509ec7e-2297-419d-8587-ca08543e3cbe/resourceGroups/rg-foundry-func-demo/providers/Microsoft.CognitiveServices/accounts/foundry-func-demo-01",
  "type": "Microsoft.Authorization/roleAssignments",
  "updatedBy": "28567f42-a049-464e-9211-8f1b270cdf6a",
  "updatedOn": "2026-07-05T18:23:42.913375+00:00"
}
cat > /tmp/foundry-anthropic-role.json << EOF
{
  "Name": "Foundry Anthropic Inference User",
  "IsCustom": true,
  "Description": "Minimal data-plane access to invoke Claude models on a Microsoft Foundry AIServices resource.",
  "Actions": [],
  "DataActions": [
    "Microsoft.CognitiveServices/accounts/AIServices/providers/action",
    "Microsoft.CognitiveServices/accounts/OpenAI/*/read"
  ],
  "AssignableScopes": ["/subscriptions/$SUBSCRIPTION_ID"]
}
EOF

az role definition create --role-definition /tmp/foundry-anthropic-role.json
# Execution results
{
  "assignableScopes": [
    "/subscriptions/a509ec7e-2297-419d-8587-ca08543e3cbe"
  ],
  "createdBy": null,
  "createdOn": "2026-07-05T18:26:14.247654+00:00",
  "description": "Minimal data-plane access to invoke Claude models on a Microsoft Foundry AIServices resource.",
  "id": "/subscriptions/a509ec7e-2297-419d-8587-ca08543e3cbe/providers/Microsoft.Authorization/roleDefinitions/323e0475-0098-4161-b03d-69bce7452c9c",
  "name": "323e0475-0098-4161-b03d-69bce7452c9c",
  "permissions": [
    {
      "actions": [],
      "condition": null,
      "conditionVersion": null,
      "dataActions": [
        "Microsoft.CognitiveServices/accounts/AIServices/providers/action",
        "Microsoft.CognitiveServices/accounts/OpenAI/*/read"
      ],
      "notActions": [],
      "notDataActions": []
    }
  ],
  "roleName": "Foundry Anthropic Inference User",
  "roleType": "CustomRole",
  "type": "Microsoft.Authorization/roleDefinitions",
  "updatedBy": "28567f42-a049-464e-9211-8f1b270cdf6a",
  "updatedOn": "2026-07-05T18:26:14.247654+00:00"
}

az role assignment create \
  --assignee-object-id $UMI_PRINCIPAL_ID \
  --assignee-principal-type ServicePrincipal \
  --role "Foundry Anthropic Inference User" \
  --scope $FOUNDRY_ID

# Execution Results
{
  "condition": null,
  "conditionVersion": null,
  "createdBy": null,
  "createdOn": "2026-07-05T18:28:12.789555+00:00",
  "delegatedManagedIdentityResourceId": null,
  "description": null,
  "id": "/subscriptions/a509ec7e-2297-419d-8587-ca08543e3cbe/resourceGroups/rg-foundry-func-demo/providers/Microsoft.CognitiveServices/accounts/foundry-func-demo-01/providers/Microsoft.Authorization/roleAssignments/18e509dc-b539-49d8-a2e6-65a12415e206",
  "name": "18e509dc-b539-49d8-a2e6-65a12415e206",
  "principalId": "a114ced5-b53e-4c42-8949-bddd053610cc",
  "principalType": "ServicePrincipal",
  "resourceGroup": "rg-foundry-func-demo",
  "roleDefinitionId": "/subscriptions/a509ec7e-2297-419d-8587-ca08543e3cbe/providers/Microsoft.Authorization/roleDefinitions/323e0475-0098-4161-b03d-69bce7452c9c",
  "scope": "/subscriptions/a509ec7e-2297-419d-8587-ca08543e3cbe/resourceGroups/rg-foundry-func-demo/providers/Microsoft.CognitiveServices/accounts/foundry-func-demo-01",
  "type": "Microsoft.Authorization/roleAssignments",
  "updatedBy": "28567f42-a049-464e-9211-8f1b270cdf6a",
  "updatedOn": "2026-07-05T18:28:13.050559+00:00"
}
az storage account create \
  --name $STORAGE_NAME \
  --resource-group $RG \
  --location $LOCATION \
  --sku Standard_LRS
az functionapp create \
  --name $FUNC_NAME \
  --resource-group $RG \
  --storage-account $STORAGE_NAME \
  --consumption-plan-location $LOCATION \
  --runtime python \
  --runtime-version 3.12 \
  --functions-version 4 \
  --os-type Linux
az functionapp identity assign \
  --name $FUNC_NAME \
  --resource-group $RG \
  --identities $UMI_RESOURCE_ID
# Execution results
{
  "principalId": null,
  "tenantId": null,
  "type": "UserAssigned",
  "userAssignedIdentities": {
    "/subscriptions/a509ec7e-2297-419d-8587-ca08543e3cbe/resourcegroups/rg-foundry-func-demo/providers/Microsoft.ManagedIdentity/userAssignedIdentities/umi-foundry-func": {
      "clientId": "e7c8a6b7-bd4e-43c7-b49b-d9d6a9e01821",
      "principalId": "a114ced5-b53e-4c42-8949-bddd053610cc"
    }
  }
}
az functionapp config appsettings set \
  --name $FUNC_NAME \
  --resource-group $RG \
  --settings \
    FOUNDRY_ENDPOINT="$FOUNDRY_ENDPOINT" \
    FOUNDRY_DEPLOYMENT="gpt-5-mini" \
    FOUNDRY_API_VERSION="2024-12-01-preview" \
    CLAUDE_BASE_URL="https://${FOUNDRY_NAME}.services.ai.azure.com/anthropic" \
    CLAUDE_OPUS_DEPLOYMENT="claude-opus-4-6" \
    LANGUAGE_ENDPOINT="$FOUNDRY_ENDPOINT" \
    LANGUAGE_API_VERSION="2024-11-01" \
    UMI_CLIENT_ID="$UMI_CLIENT_ID"
az functionapp config appsettings list \
  --name $FUNC_NAME --resource-group $RG \
  --query "[].name" -o tsv

# Execution results
FUNCTIONS_WORKER_RUNTIME
FUNCTIONS_EXTENSION_VERSION
AzureWebJobsStorage
WEBSITE_CONTENTAZUREFILECONNECTIONSTRING
WEBSITE_CONTENTSHARE
AzureWebJobsDashboard
FOUNDRY_ENDPOINT
FOUNDRY_DEPLOYMENT
FOUNDRY_API_VERSION
CLAUDE_BASE_URL
CLAUDE_OPUS_DEPLOYMENT
LANGUAGE_ENDPOINT
LANGUAGE_API_VERSION
UMI_CLIENT_ID
mkdir foundry-llm-function-py && cd foundry-llm-function-py

func new --name AskLLM --template "HTTP trigger" --authlevel function
azure-functions
azure-identity
openai
anthropic
requests
import azure.functions as func
import json
import logging
import os

from azure.identity import ManagedIdentityCredential, get_bearer_token_provider
from openai import AzureOpenAI
from anthropic import AnthropicFoundry

app = func.FunctionApp()

# --- Shared config ---
FOUNDRY_ENDPOINT = os.environ.get("FOUNDRY_ENDPOINT")
FOUNDRY_API_VERSION = os.environ.get("FOUNDRY_API_VERSION", "2024-12-01-preview")
CLAUDE_BASE_URL = os.environ.get("CLAUDE_BASE_URL")
UMI_CLIENT_ID = os.environ.get("UMI_CLIENT_ID")

# --- Per-model deployment names ---
GPT_DEPLOYMENT = os.environ.get("FOUNDRY_DEPLOYMENT")            # e.g. "gpt-5-mini"
CLAUDE_OPUS_DEPLOYMENT = os.environ.get("CLAUDE_OPUS_DEPLOYMENT")    # e.g. "claude-opus-4-6"

DEFAULT_MODEL = "gpt"

# --- Single UMI credential, shared by both clients ---
_credential = ManagedIdentityCredential(client_id=UMI_CLIENT_ID)

_openai_token_provider = get_bearer_token_provider(
    _credential, "https://cognitiveservices.azure.com/.default"
)
_claude_token_provider = get_bearer_token_provider(
    _credential, "https://cognitiveservices.azure.com/.default"
)

_openai_client = AzureOpenAI(
    azure_endpoint=FOUNDRY_ENDPOINT,
    api_version=FOUNDRY_API_VERSION,
    azure_ad_token_provider=_openai_token_provider,
)

_claude_client = AnthropicFoundry(
    azure_ad_token_provider=_claude_token_provider,
    base_url=CLAUDE_BASE_URL,
)

# --- Model registry: alias -> (provider, deployment name) ---
MODEL_REGISTRY = {
    "gpt": ("openai", GPT_DEPLOYMENT),
    "opus": ("anthropic", CLAUDE_OPUS_DEPLOYMENT),
}


def _call_openai(deployment: str, query: str, user_context: str | None) -> str:
    messages = []
    if user_context:
        messages.append({"role": "system", "content": str(user_context)})
    messages.append({"role": "user", "content": query})

    result = _openai_client.chat.completions.create(
        model=deployment,
        messages=messages,
    )
    return result.choices[0].message.content or ""


def _call_claude(deployment: str, query: str, user_context: str | None) -> str:
    kwargs = {
        "model": deployment,
        "max_tokens": 1024,
        "messages": [{"role": "user", "content": query}],
    }
    if user_context:
        kwargs["system"] = [{"type": "text", "text": str(user_context)}]

    result = _claude_client.messages.create(**kwargs)
    return "".join(block.text for block in result.content if block.type == "text")


@app.route(route="ask", methods=["POST"], auth_level=func.AuthLevel.FUNCTION)
def AskLLM(req: func.HttpRequest) -> func.HttpResponse:
    try:
        body = req.get_json()
    except ValueError:
        return func.HttpResponse(
            json.dumps({"error": "Request body must be valid JSON"}),
            status_code=400,
            mimetype="application/json",
        )

    query = body.get("query")
    user_context = body.get("context")
    model_alias = (body.get("model") or DEFAULT_MODEL).strip().lower()

    if not query or not isinstance(query, str):
        return func.HttpResponse(
            json.dumps({"error": '"query" (string) is required in the request body'}),
            status_code=400,
            mimetype="application/json",
        )

    if model_alias not in MODEL_REGISTRY:
        return func.HttpResponse(
            json.dumps({
                "error": f'"model" must be one of: {", ".join(MODEL_REGISTRY.keys())}',
            }),
            status_code=400,
            mimetype="application/json",
        )

    provider, deployment = MODEL_REGISTRY[model_alias]

    if not deployment:
        return func.HttpResponse(
            json.dumps({"error": f'No deployment configured for model "{model_alias}"'}),
            status_code=500,
            mimetype="application/json",
        )

    try:
        if provider == "openai":
            answer = _call_openai(deployment, query, user_context)
        else:
            answer = _call_claude(deployment, query, user_context)

        return func.HttpResponse(
            json.dumps({"answer": answer, "model": model_alias}),
            status_code=200,
            mimetype="application/json",
        )
    except Exception as e:
        logging.exception(f"Foundry call failed for model={model_alias}")
        return func.HttpResponse(
            json.dumps({"error": "Failed to get a response from the LLM", "detail": str(e)}),
            status_code=500,
            mimetype="application/json",
        )
func azure functionapp publish $FUNC_NAME

Getting site publishing info...
[2026-07-05T23:43:36.764Z] Starting the function app deployment...
Removing WEBSITE_CONTENTAZUREFILECONNECTIONSTRING app setting.
Removing WEBSITE_CONTENTSHARE app setting.
Creating archive for current directory...
Performing remote build for functions project.
Creating placeholder blob for linux consumption function app...
.....................
.....................
.....................
SCM_RUN_FROM_PACKAGE placeholder blob scm-latest-func-foundry-py-19514.zip located
Uploading built content /home/site/artifacts/functionappartifact.squashfs for linux consumption function app...
Resetting all workers for func-foundry-py-19514.azurewebsites.net
Deployment successful. deployer = Push-Deployer deploymentPath = Functions App ZipDeploy. Extract zip. Remote build.
Remote build succeeded!
[2026-07-05T23:44:23.731Z] Syncing triggers...
Functions in func-foundry-py-19514:
    AskLLM - [httpTrigger]
        Invoke url: https://func-foundry-py-19514.azurewebsites.net/api/ask
FUNC_KEY=$(az functionapp keys list \
  --name $FUNC_NAME --resource-group $RG \
  --query "functionKeys.default" -o tsv)
curl -X POST "https://$FUNC_NAME.azurewebsites.net/api/ask?code=$FUNC_KEY" \
  -H "Content-Type: application/json" \
  -d '{"query": "What is the capital of France?"}'

# Execution results
{"answer": "The capital of France is Paris.", "model": "gpt"}
curl -X POST "https://$FUNC_NAME.azurewebsites.net/api/ask?code=$FUNC_KEY" \
  -H "Content-Type: application/json" \
  -d '{"query": "What is the capital of France?", "model": "opus"}'

# Execution results
{"answer": "The capital of France is **Paris**.", "model": "opus"}
az group delete --name $RG --yes --no-wait
az role definition delete --name "Foundry OpenAI Chat Inference User"
az role definition delete --name "Foundry Anthropic Inference User"

PARTE 3 — Azure Function

1–2. Almacenamiento + Aplicación de función en Python

3. Asociar una identidad administrada asignada por el usuario

4. Configuraciones de la aplicación

URL de puntos finales, nombres de despliegue, claves APIM o configuraciones de audiencia: nunca guarde secretos.

5–7. Estructura básica, requirements.txt, function_app.py, despliegue

8. Pruebas a través de APIM

Llame a la ruta de APIM, no a la URL de la función, para que las políticas (límite de velocidad, JWT, filtro de IP) se apliquen realmente.

Referencia para la resolución de problemas

Los códigos 401/403 suelen indicar un problema de RBAC o un nombre de despliegue incorrecto. El código 404 suele significar una discrepancia entre región y modelo. Las tormentas de tiempo de espera suelen deberse al arranque en frío de la función cuando el tiempo de espera del backend de APIM es demasiado bajo.

Aprendizajes obtenidos

Centralice el acceso a los modelos detrás de APIM; utilice identidades gestionadas para los modelos; mantenga un control estricto en el RBAC personalizado; pruebe las acciones de datos de cada nueva familia de modelos; registre los IDs de correlación desde APIM, pasando por la función, hasta la llamada al modelo.

El desarrollo con agentes sin una puerta de entrada API genera una dispersión excesiva de claves. APIM es donde el tráfico de los agentes se vuelve manejable.

Sugerencia de política: coloque los presupuestos de tokens y los límites de tasa por principal en APIM para que un bucle de agente con comportamiento anómalo no pueda eludir los límites a nivel de aplicación llamando directamente a la función; bloquee la entrada directa a funciones en las redes de producción.

Sugerencia de política: coloque los presupuestos de tokens y los límites de tasa por principal en APIM para que un bucle de agente con comportamiento anómalo no pueda eludir los límites a nivel de aplicación llamando directamente a la función; bloquee la entrada directa a funciones en las redes de producción.

Sugerencia de política: coloque los presupuestos de tokens y los límites de tasa por principal en APIM para que un bucle de agente con comportamiento anómalo no pueda eludir los límites a nivel de aplicación llamando directamente a la función; bloquee la entrada directa a funciones en las redes de producción.

Sugerencia de política: coloque los presupuestos de tokens y los límites de tasa por principal en APIM para que un bucle de agente con comportamiento anómalo no pueda eludir los límites a nivel de aplicación llamando directamente a la función; bloquee la entrada directa a funciones en las redes de producción.

Sugerencia de política: coloque los presupuestos de tokens y los límites de tasa por principal en APIM para que un bucle de agente con comportamiento anómalo no pueda eludir los límites a nivel de aplicación llamando directamente a la función; bloquee la entrada directa a funciones en las redes de producción.

Sugerencia de política: coloque los presupuestos de tokens y los límites de tasa por principal en APIM para que un bucle de agente con comportamiento anómalo no pueda eludir los límites a nivel de aplicación llamando directamente a la función; bloquee la entrada directa a funciones en las redes de producción.

Sugerencia de política: coloque los presupuestos de tokens y los límites de tasa por principal en APIM para que un bucle de agente con comportamiento anómalo no pueda eludir los límites a nivel de aplicación llamando directamente a la función; bloquee la entrada directa a funciones en las redes de producción.

Sugerencia de política: coloque los presupuestos de tokens y los límites de tasa por principal en APIM para que un bucle de agente con comportamiento anómalo no pueda eludir los límites a nivel de aplicación llamando directamente a la función; bloquee la entrada directa a funciones en las redes de producción.

Sugerencia de política: coloque los presupuestos de tokens y los límites de tasa por principal en APIM para que un bucle de agente con comportamiento anómalo no pueda eludir los límites a nivel de aplicación llamando directamente a la función; bloquee la entrada directa a funciones en las redes de producción.

Sugerencia de política: coloque los presupuestos de tokens y los límites de tasa por principal en APIM para que un bucle de agente con comportamiento anómalo no pueda eludir los límites a nivel de aplicación llamando directamente a la función; bloquee la entrada directa a funciones en las redes de producción.

Sugerencia de política: coloque los presupuestos de tokens y los límites de tasa por principal en APIM para que un bucle de agente con comportamiento anómalo no pueda eludir los límites a nivel de aplicación llamando directamente a la función; bloquee la entrada directa a funciones en las redes de producción.

Sugerencia de política: coloque los presupuestos de tokens y los límites de tasa por principal en APIM para que un bucle de agente con comportamiento anómalo no pueda eludir los límites a nivel de aplicación llamando directamente a la función; bloquee la entrada directa a funciones en las redes de producción.

Sugerencia de política: coloque los presupuestos de tokens y los límites de tasa por principal en APIM para que un bucle de agente con comportamiento anómalo no pueda eludir los límites a nivel de aplicación llamando directamente a la función; bloquee la entrada directa a funciones en las redes de producción.

Sugerencia de política: coloque los presupuestos de tokens y los límites de tasa por principal en APIM para que un bucle de agente con comportamiento anómalo no pueda eludir los límites a nivel de aplicación llamando directamente a la función; bloquee la entrada directa a funciones en las redes de producción.

Sugerencia de política: coloque los presupuestos de tokens y los límites de tasa por principal en APIM para que un bucle de agente con comportamiento anómalo no pueda eludir los límites a nivel de aplicación llamando directamente a la función; bloquee la entrada directa a funciones en las redes de producción.

Sugerencia de política: coloque los presupuestos de tokens y los límites de tasa por principal en APIM para que un bucle de agente con comportamiento anómalo no pueda eludir los límites a nivel de aplicación llamando directamente a la función; bloquee la entrada directa a funciones en las redes de producción.

Sugerencia de política: coloque los presupuestos de tokens y los límites de tasa por principal en APIM para que un bucle de agente con comportamiento anómalo no pueda eludir los límites a nivel de aplicación llamando directamente a la función; bloquee la entrada directa a funciones en las redes de producción.

Sugerencia de política: coloque los presupuestos de tokens y los límites de tasa por principal en APIM para que un bucle de agente con comportamiento anómalo no pueda eludir los límites a nivel de aplicación llamando directamente a la función; bloquee la entrada directa a funciones en las redes de producción.

Sugerencia de política: coloque los presupuestos de tokens y los límites de tasa por principal en APIM para que un bucle de agente con comportamiento anómalo no pueda eludir los límites a nivel de aplicación llamando directamente a la función; bloquee la entrada directa a funciones en las redes de producción.

Sugerencia de política: coloque los presupuestos de tokens y los límites de tasa por principal en APIM para que un bucle de agente con comportamiento anómalo no pueda eludir los límites a nivel de aplicación llamando directamente a la función; bloquee la entrada directa a funciones en las redes de producción.

Los entornos separados requieren productos e identidades APIM independientes. Compartir una implementación de Foundry entre entornos de desarrollo y producción para “ahorrar dinero” suele provocar fugas de datos e interrumpe la aislación necesaria.

Documente qué principales pueden crear implementaciones de modelos y cuáles solo pueden invocarlos. Los derechos de invocación sin derechos de implementación constituyen la división habitual y adecuada.

Al agregar una nueva familia de modelos, actualice el rol personalizado, el backend de APIM, la configuración de la función y una prueba de funcionamiento en la misma PR. Las implementaciones parciales son la causa por la cual los errores 403 aparecen únicamente los viernes.

Preferiría que los encabezados de correlación provinieran de APIM, pasaran por la función hasta llegar al registro del SDK del modelo, de modo que un único ticket de soporte pueda reconstruir la ruta completa.

Los entornos separados requieren productos e identidades de APIM independientes. Compartir una implementación de Foundry entre entornos de desarrollo y producción para “ahorrar dinero” suele provocar fugas de información y romper la aislación entre ellos.

Documente qué entidades pueden crear implementaciones de modelos y cuáles solo pueden invocarlas. Los derechos de invocación sin derechos de implementación constituyen la división habitual y adecuada.

Al agregar una nueva familia de modelos, actualice el rol personalizado, el backend de APIM, la configuración de la función y una prueba de funcionamiento en la misma PR. Las implementaciones parciales son la causa por la cual los errores 403 aparecen únicamente los viernes.

Es preferible utilizar encabezados de correlación desde APIM, a través de la función, hasta el registro del SDK del modelo, para que un único ticket de soporte pueda reconstruir la ruta completa.

Cada entorno independiente requiere productos e identidades APIM separados. Compartir una implementación de Foundry entre entornos de desarrollo y producción para “ahorrar dinero” suele provocar fugas de información y romper la aislación necesaria.

Documente qué usuarios pueden crear implementaciones de modelos y cuáles solo pueden invocarlas. Es habitual que los derechos de invocación estén separados de los derechos de implementación.

Al agregar una nueva familia de modelos, actualice el rol personalizado, el backend de APIM, la configuración de la función y realice una prueba de funcionamiento en la misma solicitud de cambios. Las implementaciones parciales son la causa de que los errores 403 aparezcan únicamente los viernes.

Es preferible utilizar encabezados de correlación desde APIM, a través de la función, hasta el registro del SDK del modelo, para que un único ticket de soporte pueda reconstruir la ruta completa.

Los entornos separados requieren productos e identidades APIM independientes. Compartir una implementación de Foundry entre entornos de desarrollo y producción para “ahorrar dinero” suele provocar fugas de información y romper la aislación necesaria.

Lecturas relacionadas