Главная / Статьи / APIM перед вызовами агентской Foundry с использованием модели RBAC с минимальными привилегиями

APIM перед вызовами агентской Foundry с использованием модели RBAC с минимальными привилегиями

Использование моделей фронт-энда с API Management, управляемыми идентификаторами, пользовательскими ролями в слое данных и бэкендами Function App.

2858 слов

Что мы создаём

AI Foundry может хранить множество семей моделей. Разработка агентов на их основе становится интересной, но сложной, когда у каждой модели свои механизмы аутентификации и действуют правила, касающиеся персональных данных и PCI. Azure API Management (APIM) в сочетании с принципом минимальных привилегий обеспечивает единый входной пункт.

Часть 1 — Основы

Настройте ресурсы Foundry/OpenAI, приложение Function App для вызова моделей и APIM для обслуживания этих функций. Заранее определите регионы и структуру частной сети.

Часть 2 — Развертывание моделей

Разверните те чат-модели, которые вам действительно нужны. Записывайте названия развертываний отдельно от названий базовых моделей.

ЧАСТЬ 2: Персонализированная система RBAC с принципом минимальных привилегий

OpenAI — только дополнение к чату

Персонализированные роли, разрешающие выполнять только те действия с данными, которые необходимы вашей функции, лучше, чем широкие права типа Cognitive Services Contributor.

Anthropic — встроенные роли могут не учитывать действия с данными

Некоторые маршруты сторонних моделей требуют дополнительных действий с данными; проверьте содержимое сообщения об ошибке неудачного вызова, затем внесите изменения в роль, а не наоборот.

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"

ЧАСТЬ 3 — Azure Function

1–2. Хранилище + приложение функции на Python

3. Прикрепление управляемой идентичности, назначенной пользователем

4. Настройки приложения

URL-адреса конечных точек, имена развертываний, ключи APIM или настройки аудитории — никогда не сохраняйте секреты.

5–7. Структура проекта, файл requirements.txt, файл function_app.py, развертывание

8. Тестирование через APIM

Необходимо вызывать маршрут APIM, а не URL функции, чтобы политики (ограничение скорости, JWT, фильтр IP) действительно применялись.

Справочник по устранению неполадок

Коды 401/403 обычно означают проблемы с RBAC или неверное имя развертывания. Код 404 часто свидетельствует о несоответствии региона и модели. Проблемы с таймаутами часто возникают из-за холодного запуска функции при слишком низком таймауте бэкенда APIM.

Извлеченные уроки

Централизуйте доступ к моделям через APIM; используйте управляемые идентификаторы для доступа к моделям; строго контролируйте настройки RBAC; тестируйте действия с данными для каждой новой группы моделей; ведите журналы с идентификаторами корреляции от APIM до функции и затем до вызова модели.

Разработка с использованием агентов без API-фронтенда приводит к распылению ключей. Именно APIM позволяет сделать трафик агентов управляемым.

Совет по настройкам политики: разместите лимиты токенов и ограничения скорости для каждого пользователя в APIM, чтобы циклы некорректно работающих агентов не могли обойти ограничения на уровне приложения путем прямого вызова функции — заблокируйте прямой вход в функцию в производственных сетях.

Совет по настройкам политики: разместите лимиты токенов и ограничения скорости для каждого пользователя в APIM, чтобы циклы некорректно работающих агентов не могли обойти ограничения на уровне приложения путем прямого вызова функции — заблокируйте прямой вход в функцию в производственных сетях.

Совет по настройкам политики: разместите лимиты токенов и ограничения скорости для каждого пользователя в APIM, чтобы циклы некорректно работающих агентов не могли обойти ограничения на уровне приложения путем прямого вызова функции — заблокируйте прямой вход в функцию в производственных сетях.

Совет по настройкам политики: разместите лимиты токенов и ограничения скорости для каждого пользователя в APIM, чтобы циклы некорректно работающих агентов не могли обойти ограничения на уровне приложения путем прямого вызова функции — заблокируйте прямой вход в функцию в производственных сетях.

Совет по настройкам политики: разместите лимиты токенов и ограничения скорости для каждого пользователя в APIM, чтобы циклы некорректно работающих агентов не могли обойти ограничения на уровне приложения путем прямого вызова функции — заблокируйте прямой вход в функцию в производственных сетях.

Совет по настройкам политики: разместите лимиты токенов и ограничения скорости для каждого пользователя в APIM, чтобы циклы некорректно работающих агентов не могли обойти ограничения на уровне приложения путем прямого вызова функции — заблокируйте прямой вход в функцию в производственных сетях.

Совет по настройкам политики: разместите лимиты токенов и ограничения скорости для каждого пользователя в APIM, чтобы циклы некорректно работающих агентов не могли обойти ограничения на уровне приложения путем прямого вызова функции — заблокируйте прямой вход в функцию в производственных сетях.

Совет по настройкам политики: разместите лимиты токенов и ограничения скорости для каждого пользователя в APIM, чтобы циклы некорректно работающих агентов не могли обойти ограничения на уровне приложения путем прямого вызова функции — заблокируйте прямой вход в функцию в производственных сетях.

Совет по настройкам политики: разместите лимиты токенов и ограничения скорости для каждого пользователя в APIM, чтобы циклы некорректно работающих агентов не могли обойти ограничения на уровне приложения путем прямого вызова функции — заблокируйте прямой вход в функцию в производственных сетях.

Совет по настройкам политики: разместите лимиты токенов и ограничения скорости для каждого пользователя в APIM, чтобы циклы некорректно работающих агентов не могли обойти ограничения на уровне приложения путем прямого вызова функции — заблокируйте прямой вход в функцию в производственных сетях.

Совет по настройкам политики: разместите лимиты токенов и ограничения скорости для каждого пользователя в APIM, чтобы циклы некорректно работающих агентов не могли обойти ограничения на уровне приложения путем прямого вызова функции — заблокируйте прямой вход в функцию в производственных сетях.

Совет по настройкам политики: разместите лимиты токенов и ограничения скорости для каждого пользователя в APIM, чтобы циклы некорректно работающих агентов не могли обойти ограничения на уровне приложения путем прямого вызова функции — заблокируйте прямой вход в функцию в производственных сетях.

Совет по настройкам политики: разместите лимиты токенов и ограничения скорости для каждого пользователя в APIM, чтобы циклы некорректно работающих агентов не могли обойти ограничения на уровне приложения путем прямого вызова функции — заблокируйте прямой вход в функцию в производственных сетях.

Совет по настройкам политики: разместите лимиты токенов и ограничения скорости для каждого пользователя в APIM, чтобы циклы некорректно работающих агентов не могли обойти ограничения на уровне приложения путем прямого вызова функции — заблокируйте прямой вход в функцию в производственных сетях.

Совет по настройкам политики: разместите лимиты токенов и ограничения скорости для каждого пользователя в APIM, чтобы циклы некорректно работающих агентов не могли обойти ограничения на уровне приложения путем прямого вызова функции — заблокируйте прямой вход в функцию в производственных сетях.

Совет по настройкам политики: разместите лимиты токенов и ограничения скорости для каждого пользователя в APIM, чтобы циклы некорректно работающих агентов не могли обойти ограничения на уровне приложения путем прямого вызова функции — заблокируйте прямой вход в функцию в производственных сетях.

Совет по настройкам политики: разместите лимиты токенов и ограничения скорости для каждого пользователя в APIM, чтобы циклы некорректно работающих агентов не могли обойти ограничения на уровне приложения путем прямого вызова функции — заблокируйте прямой вход в функцию в производственных сетях.

Совет по настройкам политики: разместите лимиты токенов и ограничения скорости для каждого пользователя в APIM, чтобы циклы некорректно работающих агентов не могли обойти ограничения на уровне приложения путем прямого вызова функции — заблокируйте прямой вход в функцию в производственных сетях.

Совет по настройкам политики: разместите лимиты токенов и ограничения скорости для отдельных пользователей в APIM, чтобы некорректно работающие агенты не могли обойти ограничения на уровне приложения путем прямого вызова функций — заблокируйте прямой вход в функции в производственных сетях.

Совет по настройкам политики: разместите лимиты токенов и ограничения скорости для отдельных пользователей в APIM, чтобы некорректно работающие агенты не могли обойти ограничения на уровне приложения путем прямого вызова функций — заблокируйте прямой вход в функции в производственных сетях.

Для разных сред требуются отдельные продукты APIM и учетные записи. Использование одной и той же конфигурации Foundry как в разработке, так и в производстве с целью «экономии денег» обычно приводит к утечке данных и нарушению изоляции сред.

Задокументируйте, какие пользователи могут создавать развертывания моделей, а какие могут их только вызывать. Обычно разумным подходом является наличие прав на вызов без прав на развертывание.

При добавлении новой семьи моделей необходимо обновить пользовательскую роль, бэкенд APIM, настройки функции и выполнить тест на работоспособность в рамках одного PR. Именно частичные развертывания приводят к тому, что ошибки 403 появляются только по пятницам.

Рекомендуется использовать заголовки корреляции от APIM, проходящие через функцию до логов SDK модели, чтобы один заявок на поддержку позволяла восстановить полный путь.

Для разных сред требуются отдельные продукты APIM и учетные записи. Общее использование одного развертывания Foundry между средами разработки и производства с целью «экономии денег» обычно приводит к утечке информации и нарушению изоляции сред.

Необходимо задокументировать, какие пользователи могут создавать развертывания моделей, а какие могут их только вызывать. Обычно разумным подходом является наличие прав на вызов без прав на развертывание.

При добавлении новой семьи моделей необходимо обновить пользовательскую роль, бэкенд APIM, настройки функции и выполнить тест на работоспособность в рамках одного PR. Именно частичные развертывания приводят к тому, что ошибки 403 появляются только по пятницам.

Рекомендуется использовать заголовки корреляции от APIM, проходящие через функцию в логи SDK модели, чтобы один заявок на поддержку могла восстановить полный путь.

Для разных сред требуются отдельные продукты APIM и учетные записи. Общее использование одной конфигурации Foundry между средами разработки и производства с целью «сохранения денег» обычно приводит к утечке информации и нарушению изоляции сред.

Должны быть задокументированы те пользователи, которые могут создавать развертывания моделей, и те, кто может их только вызывать. Обычно разумным подходом является наличие прав на вызов без прав на развертывание.

При добавлении новой семьи моделей необходимо обновить пользовательскую роль, серверную часть APIM, настройки функции и выполнить тест в рамках одного PR. Частичные развертывания — причина, по которой ошибки 403 возникают только по пятницам.

Рекомендуется использовать заголовки корреляции от APIM, проходящие через функцию в логи SDK модели, чтобы один заявок на поддержку могла восстановить полный путь.

Для разных сред требуются отдельные продукты APIM и учетные записи. Использование одной конфигурации Foundry как в среде разработки, так и в производственной для «экономии средств» обычно приводит к утечке запросов и нарушению изоляции между средами.