首页 / 文章 / 在代理型Foundry调用场景中,APIM采用最小权限RBAC机制

在代理型Foundry调用场景中,APIM采用最小权限RBAC机制

使用 API 管理、托管身份、自定义数据平面角色以及函数应用后端来实现前端模型的调用。

2858 词

我们正在构建什么

AI Foundry能够托管多种模型系列。当每个模型拥有不同的认证方式且需遵循PII/PCI规则时,在其之上构建代理会变得既有趣又复杂。Azure API管理(APIM)结合最小权限身份机制可提供一个统一入口。

第一部分 — 基础设施

准备Foundry/OpenAI资源、用于调用模型的函数应用,以及负责处理请求的APIM。需提前确定部署区域和私有网络设置。

第二部分 — 部署模型

部署你实际需要的聊天模型。请将部署名称与基础模型名称分开记录。

第二部分:最小权限自定义RBAC

OpenAI — 仅限聊天补全功能

定制仅允许函数执行所需数据操作的权限角色,比使用通用的Cognitive Services贡献者权限更高效。

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"

第三部分 — 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到函数再到模型调用的关联ID。

若在没有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后端、函数设置以及相应的冒烟测试。部分部署正是导致错误403仅在周五出现的原因。

建议让APIM传递的相关性标头进入函数,再记录到模型SDK的日志中,这样就能通过一个支持工单追溯完整路径。

不同的环境需要独立的APIM产品与身份凭证。为“节省成本”而将同一个Foundry部署同时用于开发环境和生产环境,通常会导致提示信息泄露,并破坏隔离机制。

应明确记录哪些用户可以创建模型部署,哪些用户仅能调用它们。通常情况下,拥有调用权限而不具备部署权限是较为合理的划分方式。

在添加新的模型系列时,应在同一个拉取请求中更新自定义角色、APIM后端、函数设置以及相应的冒烟测试。部分部署正是导致错误403仅在周五出现的原因。

建议让关联头从 APIM 经由函数传递到模型 SDK 的日志中,这样通过一个支持工单就能还原完整路径。

不同的环境需要独立的 APIM 产品及身份凭证。为“节省成本”而在开发环境和生产环境之间共享同一个 Foundry 部署,通常会导致提示信息泄露,并破坏隔离机制。

应明确记录哪些主体可以创建模型部署,哪些主体仅能调用它们。通常情况下,拥有调用权限而没有部署权限是较为合理的划分方式。

在添加新的模型系列时,应在同一个 PR 中同步更新自定义角色、APIM 后端、函数设置以及相应的测试用例。部分部署会导致仅在周五出现 403 错误。

建议让关联头从 APIM 经由函数传递到模型 SDK 的日志中,这样通过一个支持工单就能还原完整路径。

不同的环境需要独立的 APIM 产品与身份标识。为“节省成本”而将同一个 Foundry 部署同时用于开发环境和生产环境,通常会导致提示信息泄露,并破坏隔离机制。