Bing Grounding API

一个基于FastAPI的REST API和模型上下文协议(MCP)服务器,为Azure AI Agent服务提供Bing grounding功能,支持通过REST和MCP接口提供带自动引用提取的AI响应。

  • by drewelewis
  • Python
  • 本地部署
  • 更新于
在 GitHub 查看 drewelewis/ai-bing-grounding-mcp

使用说明

必应接地API

用于Azure AI代理服务的基于FastAPI的REST API和模型上下文协议(MCP)服务器,具有Bing基础功能。通过REST和MCP接口提供基于人工智能的自动引文提取响应。

特性

✅ REST API 基于Bing的Azure AI代理包装器
✅ 模型上下文协议(MCP)服务器 通过Azure API管理
✅ 带引用的结构化JSON响应
✅ 用于多区域部署的区域跟踪元数据
✅ Docker容器化,易于部署
✅ 健康检查端点
✅ Azure容器应用程序就绪
✅ 线程管理和清理
✅ APIM负载平衡与断路器模式
✅ 会话关联性(粘性会话)
✅ 自动故障转移和恢复
✅ 使用Azure Developer CLI(azd)自动部署
✅ 在配置过程中自动创建的12个AI代理
✅ 用于标准化AI工具集成的MCP端点


快速入门:配置并部署到Azure

最快的入门方法是使用Azure Developer CLI:

# 1. Login to Azure
azd auth login

# 2. Create environment (first time only)
azd env new <environment-name>

# 3. Provision and deploy everything
azd up

就是这样! 一个命令可以完成所有事情:

  • ✅ 提供所有Azure资源(容器应用程序、AI Foundry、APIM等)
  • ✅ 自动创建12个基于Bing的GPT-4o AI代理(新API)
  • ✅ 构建和部署Docker容器
  • ✅ 配置托管身份和RBAC
  • ✅ 使用负载平衡设置API管理

整个过程大约需要8-15分钟。

您的API将在输出中显示的端点处可用。

常用命令

命令 它的作用 何时使用
azd up 提供+部署一切 用于初始设置和更新
azd deploy 仅部署代码(跳过配置) 快速更新现有资源的代码
azd down 删除所有Azure资源 清理/拆除环境
azd env list 显示可用环境 检查存在哪些环境

📚 有关详细的配置步骤,请参阅 部署到Azure 在......下面

💡 通过APIM连接MCP服务器:Azure API管理本机将REST API转换为模型上下文协议(MCP)服务器。MCP客户端(如GitHub Copilot、Semantic Kernel或Azure OpenAI Responses API)通过HTTP/SSE传输连接到APIM的MCP端点,以作为标准化工具访问您的API。看 APIM作为MCP服务器 了解详情。

⚠️ 部署后需要手动执行步骤:The azd deploy postdeploy钩子将显示在APIM门户中创建MCP服务器的说明(1-2分钟)。此步骤目前是手动的,因为ARM/Diber模板中尚未提供MCP服务器资源。


建筑

当前架构:具有代理池的单个项目

graph TB
    subgraph External["External Clients"]
        Client[LLM Suite / MCP Client]
    end
    
    subgraph APIM["Azure API Management"]
        Gateway[API Gateway<br/>• Circuit Breaker<br/>• Rate Limiting<br/>• Session Affinity]
    end
    
    subgraph ContainerApps["Container Apps Environment"]
        CA1[Container App 1<br/>12 Agent Endpoints]
        CA2[Container App 2<br/>12 Agent Endpoints]
        CA3[Container App 3<br/>12 Agent Endpoints]
    end
    
    subgraph Foundry["Azure AI Foundry (Single Project)"]
        Project[AI Project]
        GPT4O[GPT-4o Deployment<br/>10K TPM Capacity]
        subgraph Agents["Agent Pool (12 Agents)"]
            Agent1[Agent 1]
            Agent2[Agent 2]
            Agent12[Agent 12]
        end
        Bing[Bing Grounding<br/>Connection]
    end
    
    Client -->|HTTPS Requests| Gateway
    Gateway -->|Load Balance| CA1
    Gateway -->|Load Balance| CA2
    Gateway -->|Load Balance| CA3
    
    CA1 & CA2 & CA3 -->|Managed Identity| Project
    Project --> Agents
    Agent1 & Agent2 & Agent12 -->|Use| GPT4O
    Agent1 & Agent2 & Agent12 -->|Search| Bing
    
    style Gateway fill:#0078d4,color:#fff
    style CA1 fill:#00bcf2,color:#000
    style CA2 fill:#00bcf2,color:#000
    style CA3 fill:#00bcf2,color:#000
    style Project fill:#50e6ff,color:#000
    style GPT4O fill:#ff6b6b,color:#fff
    style Bing fill:#00b294,color:#fff

特点:

  • ✅ TPM容量: 10K TPM(在所有代理之间共享)
  • ✅ 代理池: 12个负载分配代理
  • ✅ 高可用性: 3个容器应用程序实例
  • ✅ APIM负载平衡: 仅跨容器应用程序
  • ⚠️ 单个TPM配额: 所有代理共享相同的GPT-4o部署

用例: 开发、试点项目、中等生产工作量(高达约30万次查询/月)

每月费用: 约2000美元(见 成本分析 在......下面


横向扩展策略

随着您的工作负载增长到超过10K TPM容量,您有几个扩展选项。每种策略在复杂性、成本和LLM Suite集成方面都有不同的权衡。

策略1:垂直规模(每个项目增加TPM)

graph TB
    subgraph External["External Clients"]
        Client[LLM Suite / MCP Client]
    end
    
    subgraph APIM["Azure API Management"]
        Gateway[API Gateway]
    end
    
    subgraph ContainerApps["Container Apps (3 instances)"]
        CA[Container Apps<br/>12 Agent Endpoints]
    end
    
    subgraph Foundry["Azure AI Foundry (Single Project)"]
        Project[AI Project]
        GPT4O[GPT-4o Deployment<br/>⬆️ 100K TPM<br/>Provisioned Throughput]
        Agents[Agent Pool<br/>12 Agents]
        Bing[Bing Grounding]
    end
    
    Client -->|HTTPS| Gateway
    Gateway -->|Load Balance| CA
    CA -->|Managed Identity| Project
    Project --> Agents
    Agents -->|Use| GPT4O
    Agents -->|Search| Bing
    
    style Gateway fill:#0078d4,color:#fff
    style CA fill:#00bcf2,color:#000
    style GPT4O fill:#ff6b6b,color:#fff
    style Project fill:#50e6ff,color:#000

实施:

  • 将GPT-4o部署从10K增加到100K TPM(或更高)
  • 使用预留吞吐量单位(PTU)保证容量
  • 无需更改架构

特点:

  • ✅ 最简单的方法 -无代码更改
  • ✅ 单端点 LLM套房
  • ✅ 高达1M TPM 带PTU
  • ⚠️ 成本更高 -PTU定价(约540美元/PTU/月)
  • ⚠️ 单点故障 (一个项目)

LLM套件集成:

  • 无需更改 -相同的端点结构
  • 继续使用 /bing-grounding/gpt4o_{1-12} 端点

每月费用: 根据PTU分配,约为5K-50K美元

何时使用: 当您需要快速扩展而无需更改架构时


策略2:多项目横向规模(APIM管理)

graph TB
    subgraph External["External Clients"]
        Client[LLM Suite / MCP Client<br/>Single Endpoint]
    end
    
    subgraph APIM["Azure API Management - Backend Pool"]
        Gateway[API Gateway<br/>Round-Robin LB<br/>Circuit Breaker]
    end
    
    subgraph Backend1["Environment 1"]
        CA1[Container Apps<br/>12 Agents]
        Project1[AI Project 1<br/>GPT-4o: 10K TPM]
    end
    
    subgraph Backend2["Environment 2"]
        CA2[Container Apps<br/>12 Agents]
        Project2[AI Project 2<br/>GPT-4o: 10K TPM]
    end
    
    subgraph Backend3["Environment 3"]
        CA3[Container Apps<br/>12 Agents]
        Project3[AI Project 3<br/>GPT-4o: 10K TPM]
    end
    
    Client -->|HTTPS| Gateway
    Gateway -->|Route 33%| CA1
    Gateway -->|Route 33%| CA2
    Gateway -->|Route 34%| CA3
    
    CA1 --> Project1
    CA2 --> Project2
    CA3 --> Project3
    
    style Gateway fill:#0078d4,color:#fff
    style CA1 fill:#00bcf2,color:#000
    style CA2 fill:#00bcf2,color:#000
    style CA3 fill:#00bcf2,color:#000
    style Project1 fill:#50e6ff,color:#000
    style Project2 fill:#50e6ff,color:#000
    style Project3 fill:#50e6ff,color:#000

实施:

  1. 部署多个环境:

    # Create 3 separate environments
    azd env new prod-foundry-1
    azd up
    
    azd env new prod-foundry-2
    azd up
    
    azd env new prod-foundry-3
    azd up
    
  2. 配置APIM后端池:

    <backends>
      <backend id="foundry-pool">
        <backend>
          <url>https://ca-foundry1.azurecontainerapps.io</url>
        </backend>
        <backend>
          <url>https://ca-foundry2.azurecontainerapps.io</url>
        </backend>
        <backend>
          <url>https://ca-foundry3.azurecontainerapps.io</url>
        </backend>
      </backend>
    </backends>
    
    <inbound>
      <set-backend-service backend-id="foundry-pool" />
    </inbound>
    

特点:

  • ✅ 线性容量扩展 (3个项目×10K=30K TPM)
  • ✅ 容错 -项目失败不会影响他人
  • ✅ 成本效益 -按使用付费定价
  • ✅ 单端点 用于LLM套件(APIM处理路由)
  • ⚠️ 更多基础设施 管理(3个环境)
  • ⚠️ 需要APIM配置 更新

LLM套件集成:

  • 无需更改 -LLM套件看到单个APIM端点
  • APIM透明地路由到可用的Foundry项目
  • 保持不变 /bing-grounding/gpt4o_{1-12} 端点结构

每月费用: 约6000美元(3个环境×每个环境2K美元)

何时使用:

  • 需要20K-50K TPM容量
  • 希望跨项目容错
  • 相较于PTU定价,更倾向于按次付费

策略3:横向扩展与客户端负载平衡

graph TB
    subgraph External["External Clients"]
        Client[LLM Suite / MCP Client<br/>⬆️ Client-Side LB Logic]
    end
    
    subgraph APIM["Azure API Management"]
        Gateway[API Gateway<br/>Shared Policies]
    end
    
    subgraph Backend1["Environment 1"]
        CA1[Container Apps<br/>12 Agents]
        Project1[AI Project 1<br/>GPT-4o: 10K TPM]
    end
    
    subgraph Backend2["Environment 2"]
        CA2[Container Apps<br/>12 Agents]
        Project2[AI Project 2<br/>GPT-4o: 10K TPM]
    end
    
    subgraph Backend3["Environment 3"]
        CA3[Container Apps<br/>12 Agents]
        Project3[AI Project 3<br/>GPT-4o: 10K TPM]
    end
    
    Client -->|33% Traffic| Gateway
    Client -->|33% Traffic| Gateway
    Client -->|34% Traffic| Gateway
    
    Gateway -->|/project1/*| CA1
    Gateway -->|/project2/*| CA2
    Gateway -->|/project3/*| CA3
    
    CA1 --> Project1
    CA2 --> Project2
    CA3 --> Project3
    
    style Client fill:#ffa500,color:#fff
    style Gateway fill:#0078d4,color:#fff
    style CA1 fill:#00bcf2,color:#000
    style CA2 fill:#00bcf2,color:#000
    style CA3 fill:#00bcf2,color:#000
    style Project1 fill:#50e6ff,color:#000
    style Project2 fill:#50e6ff,color:#000
    style Project3 fill:#50e6ff,color:#000

实施:

  1. 使用不同的路径部署多个环境:

    azd env new prod-foundry-1
    azd up
    # Endpoint: https://apim.azure-api.net/project1/bing-grounding
    
    azd env new prod-foundry-2
    azd up
    # Endpoint: https://apim.azure-api.net/project2/bing-grounding
    
    azd env new prod-foundry-3
    azd up
    # Endpoint: https://apim.azure-api.net/project3/bing-grounding
    
  2. 配置APIM路由:

    <choose>
      <when condition="@(context.Request.Url.Path.StartsWith("/project1"))">
        <set-backend-service base-url="https://ca-foundry1.azurecontainerapps.io" />
      </when>
      <when condition="@(context.Request.Url.Path.StartsWith("/project2"))">
        <set-backend-service base-url="https://ca-foundry2.azurecontainerapps.io" />
      </when>
      <when condition="@(context.Request.Url.Path.StartsWith("/project3"))">
        <set-backend-service base-url="https://ca-foundry3.azurecontainerapps.io" />
      </when>
    </choose>
    
  3. 更新LLM套件配置:

    # LLM Suite config
    foundry_endpoints:
      - url: https://apim.azure-api.net/project1/bing-grounding
        weight: 33
        capacity: 10000  # TPM
      - url: https://apim.azure-api.net/project2/bing-grounding
        weight: 33
        capacity: 10000
      - url: https://apim.azure-api.net/project3/bing-grounding
        weight: 34
        capacity: 10000
    
    load_balancing:
      strategy: round-robin  # or weighted, least-connections
      health_check_interval: 30s
    

特点:

  • ✅ 完全控制 LLM套件中的路由逻辑
  • ✅ 项目特定路线 用于工作负载隔离
  • ✅ 自定义故障转移 逻辑可能
  • ✅ 成本可见性 每个项目端点
  • ⚠️ LLM套件需要更改 (配置+逻辑)
  • ⚠️ 复杂的 客户端实现
  • ⚠️ 手动端点管理

LLM套件集成:

  • 需要更改配置 -多个端点
  • 需要负载平衡逻辑 -客户端循环/加权
  • 建议进行健康监测 -检查端点可用性
  • 每个代理端点:
    • 项目1: /project1/bing-grounding/gpt4o_{1-12}
    • 项目2: /project2/bing-grounding/gpt4o_{1-12}
    • 项目3: /project3/bing-grounding/gpt4o_{1-12}

每月费用: 约6000美元(3个环境×每个环境2K美元)

何时使用:

  • 需要高级路由逻辑(租户隔离、工作负载优先级)
  • 希望对流量分布进行精细控制
  • LLM套件已经具有负载平衡功能
  • 每个项目的成本跟踪需求

策略4:混合动力-PTU+多项目

graph TB
    subgraph External["External Clients"]
        Client[LLM Suite / MCP Client]
    end
    
    subgraph APIM["Azure API Management - Weighted LB"]
        Gateway[API Gateway<br/>80% to PTU<br/>20% to Standard]
    end
    
    subgraph PrimaryProject["Primary Project - PTU"]
        CA_PTU[Container Apps<br/>12 Agents]
        Project_PTU[AI Project<br/>GPT-4o PTU<br/>100K TPM<br/>Guaranteed]
    end
    
    subgraph FallbackProjects["Fallback Projects - Standard"]
        CA_STD1[Container Apps<br/>12 Agents]
        Project_STD1[AI Project 1<br/>GPT-4o: 10K TPM]
        
        CA_STD2[Container Apps<br/>12 Agents]
        Project_STD2[AI Project 2<br/>GPT-4o: 10K TPM]
    end
    
    Client -->|HTTPS| Gateway
    Gateway -->|80% Primary| CA_PTU
    Gateway -->|10% Spillover| CA_STD1
    Gateway -->|10% Spillover| CA_STD2
    
    CA_PTU --> Project_PTU
    CA_STD1 --> Project_STD1
    CA_STD2 --> Project_STD2
    
    style Gateway fill:#0078d4,color:#fff
    style CA_PTU fill:#4caf50,color:#fff
    style Project_PTU fill:#ff6b6b,color:#fff
    style CA_STD1 fill:#00bcf2,color:#000
    style CA_STD2 fill:#00bcf2,color:#000

实施:

  1. 部署主PTU项目:

    # Modify infra/resources.bicep to use PTU
    param openAiDeploymentType string = 'ProvisionedThroughput'
    param openAiCapacity int = 100  # 100 PTUs
    
    azd env new prod-primary
    azd up
    
  2. 部署后备标准项目:

    azd env new prod-fallback-1
    azd up
    
    azd env new prod-fallback-2
    azd up
    
  3. 配置加权APIM路由:

    <set-variable name="backendIndex" value="@{
        var rand = new Random().Next(100);
        if (rand < 80) return "ptu";      // 80% to PTU
        if (rand < 90) return "std1";     // 10% to Standard 1
        return "std2";                     // 10% to Standard 2
    }" />
    
    <choose>
      <when condition="@(context.Variables["backendIndex"] == "ptu")">
        <set-backend-service base-url="https://ca-ptu.azurecontainerapps.io" />
      </when>
      <when condition="@(context.Variables["backendIndex"] == "std1")">
        <set-backend-service base-url="https://ca-std1.azurecontainerapps.io" />
      </when>
      <otherwise>
        <set-backend-service base-url="https://ca-std2.azurecontainerapps.io" />
      </otherwise>
    </choose>
    

特点:

  • ✅ 保证容量 (PTU提供的10万TPM)
  • ✅ 突发容量 (标准外增加20K TPM)
  • ✅ 成本优化 -PTU为基准,按使用量付费
  • ✅ 高可用性 -多个后备项目
  • ⚠️ 复杂的配置 -加权路由+健康检查
  • ⚠️ 基础成本较高 -PTU承诺

LLM套件集成:

  • 无需更改 -单个APIM端点
  • APIM自动处理加权路由
  • 透明地切换到标准项目

每月费用: 约25000美元(PTU:21000美元+2个标准项目:4K美元)

何时使用:

  • 需要有保证的基线容量(10万TPM)
  • 预计PTU分配之外的流量峰值
  • 希望通过突发功能实现成本可预测性
  • 需要SLA的关键任务工作负载

比较矩阵

策略 最大TPM HA 成本/月 LLM套件更改 复杂性 最适合
策略1:垂直(PTU) 1M+ 中等 5K-50K美元 无 低 快速扩展,可预测负载
策略2:横向(APIM LB) 50000+ 高 6000+美元 无 中等 成本效益,容错
策略3:横向(客户LB) 50K+ 高 6K+美元 配置+逻辑 高 高级路由,租户隔离
策略4:混合动力(PTU+多功能) 12万+ 非常高 2.5万+美元 无 高 关键任务,有保证的容量

按工作量推荐

试点/开发(<10K TPM):

  • 使用当前的单一项目架构
  • 费用:约2K美元/月
  • 规模策略:无需

生产-轻型(10K-30K TPM):

  • 推荐: 策略2(与APIM LB横向)
  • 部署2-3个项目
  • 成本:约4K-6K/月
  • LLM套件:无更改

生产-中等(30K-100K TPM):

  • 推荐: 策略1(垂直PTU)
  • 50-100个PTU的单个项目
  • 成本:约27K-54K/月
  • LLM套件:无更改

生产-重型(100K+TPM):

  • 推荐: 策略4(混合PTU+多项目)
  • PTU基线+标准回退
  • 成本:约25000美元/月
  • LLM套件:无更改

企业-关键任务:

  • 推荐: 战略4+多区域
  • 跨2-3个Azure区域部署
  • 成本:约7.5万美元/月
  • LLM套件:可选地理路由

多区域部署

在多个Azure区域部署Bing基础API,以实现地理负载分布、灾难恢复和降低全球用户的延迟。

好处

✅ 地理负荷分布 -将流量分散到各个地区
✅ 灾难恢复 -如果一个区域停机,则自动故障转移
✅ 减少延迟 -用户连接到最近的地区
✅ 更好的配额利用率 -每个地区单独的TPM配额
✅ 区域跟踪 -了解每个请求的服务区域

建筑

graph TB
    subgraph Clients["Global Clients"]
        USClient[US Clients]
        EUClient[EU Clients]
    end
    
    subgraph APIM["Azure API Management (Global Gateway)"]
        Gateway[API Gateway<br/>• Geo-routing<br/>• Health Checks<br/>• Circuit Breaker]
    end
    
    subgraph EastUS["East US Region"]
        CA_East[Container App<br/>eastus]
        Foundry_East[AI Foundry<br/>5 Agents]
        Bing_East[Bing Grounding]
    end
    
    subgraph WestUS["West US Region"]
        CA_West[Container App<br/>westus]
        Foundry_West[AI Foundry<br/>5 Agents]
        Bing_West[Bing Grounding]
    end
    
    subgraph WestEU["West Europe Region"]
        CA_EU[Container App<br/>westeurope]
        Foundry_EU[AI Foundry<br/>5 Agents]
        Bing_EU[Bing Grounding]
    end
    
    USClient --> Gateway
    EUClient --> Gateway
    
    Gateway -->|Geo-routing| CA_East
    Gateway -->|Load balance| CA_West
    Gateway -->|Geo-routing| CA_EU
    
    CA_East --> Foundry_East
    CA_West --> Foundry_West
    CA_EU --> Foundry_EU
    
    Foundry_East --> Bing_East
    Foundry_West --> Bing_West
    Foundry_EU --> Bing_EU
    
    style Gateway fill:#0078d4,color:#fff
    style CA_East fill:#00bcf2,color:#000
    style CA_West fill:#00bcf2,color:#000
    style CA_EU fill:#00bcf2,color:#000

区域跟踪

每个响应都包含元数据,显示哪个区域处理了请求:

{
  "content": "AI-generated response...",
  "citations": [...],
  "metadata": {
    "agent_route": "gpt4o_2",
    "model": "gpt-4o",
    "agent_id": "asst_abc123...",
    "region": "westus"  ← Region identifier
  }
}

这使得:

  • 监控 -跟踪区域交通分布
  • 调试 -识别特定地区的问题
  • 合规 -审计数据驻留要求
  • 分析 -衡量区域绩效

部署步骤

1.部署其他区域

# Create new environment for westus
azd env new westus
azd env set AZURE_LOCATION westus

# Deploy (creates full stack: Foundry, agents, Container App, etc.)
azd up

# Get the Container App URL
azd env get-values | Select-String "CONTAINER_APP"

输出: 容器应用程序URL类似 ca-abc123.westus.azurecontainerapps.io

2.配置APIM多区域后端

选项A:随机负载平衡(最简单)

编辑您的APIM API操作策略(/bing-grounding 或 /bing-grounding-mcp):

<policies>
  <inbound>
    <set-variable name="backendUrl" value="@{
        var backends = new[] {
            "https://ca-vw5lt6yc7noze.eastus.azurecontainerapps.io",
            "https://ca-abc123.westus.azurecontainerapps.io"
        };
        var random = new Random();
        return backends[random.Next(backends.Length)];
    }" />
    <set-backend-service base-url="@((string)context.Variables["backendUrl"])" />
  </inbound>
</policies>

选项B:地理路由(基于用户位置)

<policies>
  <inbound>
    <set-variable name="backendUrl" value="@{
        // Get client location from X-Forwarded-For or request headers
        string clientLocation = context.Request.Headers.GetValueOrDefault("X-Forwarded-For", "");
        
        // Route based on geography
        if (clientLocation.Contains("Europe") || clientLocation.Contains("EU"))
            return "https://ca-eu.westeurope.azurecontainerapps.io";
        else if (clientLocation.Contains("West") || clientLocation.Contains("Pacific"))
            return "https://ca-west.westus.azurecontainerapps.io";
        else
            return "https://ca-east.eastus.azurecontainerapps.io";
    }" />
    <set-backend-service base-url="@((string)context.Variables["backendUrl"])" />
  </inbound>
</policies>

选项C:使用断路器进行健康检查(生产)

<policies>
  <inbound>
    <base />
    <set-variable name="healthyBackends" value="@{
        var allBackends = new[] { 
            "https://ca-east.eastus.azurecontainerapps.io",
            "https://ca-west.westus.azurecontainerapps.io",
            "https://ca-eu.westeurope.azurecontainerapps.io"
        };
        var healthyList = new List<string>();
        
        // Check cached health status
        foreach (var backend in allBackends)
        {
            string cacheKey = "backend-health-" + backend;
            string healthStatus;
            
            if (context.Cache.TryGetValue(cacheKey, out healthStatus))
            {
                if (healthStatus == "healthy")
                    healthyList.Add(backend);
            }
            else
            {
                healthyList.Add(backend); // Assume healthy if no data
            }
        }
        
        return healthyList.Count > 0 ? healthyList : allBackends;
    }" />
    
    <!-- Random selection from healthy backends -->
    <set-backend-service base-url="@{
        var backends = (List<string>)context.Variables["healthyBackends"];
        var random = new Random();
        return backends[random.Next(0, backends.Count)];
    }" />
  </inbound>
  
  <outbound>
    <base />
    <!-- Cache health status based on response -->
    <choose>
      <when condition="@(context.Response.StatusCode >= 500)">
        <cache-store-value 
          key="@("backend-health-" + context.Request.Url.Host)" 
          value="unhealthy" 
          duration="60" />
      </when>
      <otherwise>
        <cache-store-value 
          key="@("backend-health-" + context.Request.Url.Host)" 
          value="healthy" 
          duration="300" />
      </otherwise>
    </choose>
  </outbound>
</policies>

3.测试多区域设置

运行测试套件并观察区域轮换:

python test_mcp.py

预期产量:

Test 1: Region: eastus
Test 2: Region: westus
Test 3: Region: eastus
Test 4: Region: westus
...

各地区所需资源

每个区域部署都需要:

资源 必填 备注
AI铸造中心+项目 ✅ 是 每个地区的新实例
Bing接地 ✅ 是 必须与Foundry位于同一资源组中
容器应用程序 ✅ 是 这是端点APIM路由到
容器应用程序环境 ✅ 是 容器应用程序需要
日志分析 ✅ 是 用于容器应用程序日志记录
存储帐户 推荐 更适合区域隔离
密钥库 推荐 更好的可用性
集装箱登记处 可选 可以从主区域重复使用
APIM ❌ 否 单个全球网关,跨区域重用

成本考虑

每个额外地区:

  • 人工智能铸造项目:~$0(按使用付费)
  • GPT-4o部署(10K TPM):约800美元/月
  • 容器应用程序:~50-100美元/月
  • 集装箱环境:约50美元/月
  • Bing停飞(免费版):0美元
  • 存储+密钥库+日志:约50美元/月

每个地区总计: 约950-1000美元/月

3区设置(美国东部、美国西部、西欧):

  • 总计: 约3000美元/月
  • 优点: 30000 TPM总容量+地理分布+灾难恢复

最佳实践

  1. 从两个地区开始 -主+故障转移
  2. 使用健康检查 -实施断路器模式
  3. 监控区域分布 -轨道 metadata.region 领域
  4. 设置区域特定警报 -每个区域的Azure监视器
  5. 测试故障转移 -定期验证自动故障转移是否正常工作
  6. 缓存DNS -考虑使用Azure Front Door进行高级地理路由

监控多区域

Azure监视器查询(日志分析):

ContainerAppConsoleLogs_CL
| where TimeGenerated > ago(1h)
| extend region = tostring(parse_json(Log_s).metadata.region)
| summarize count() by region, bin(TimeGenerated, 5m)
| render timechart

APIM分析:

在APIM中跟踪后端分发→ 分析→ 自定义尺寸


先决条件

促进地方发展

  • Python 3.11+ - 下载
  • Azure命令行界面 - 安装指南
  • Docker 桌面版 (可选,适用于Docker Compose)- 下载
  • Azure订阅 可以访问:
    • Azure AI 铸造厂
    • Azure容器应用
    • Azure API管理(可选,用于生产)

用于Azure部署

需要Azure权限

  • 订阅贡献者 或 所有者 角色(创建资源组和资源)
  • Azure人工智能开发人员 或 认知服务贡献者 (创建AI Foundry项目)

入门指南

本地开发

  1. 创建虚拟环境

    _env_create.bat
    
  2. 激活虚拟环境

    _env_activate.bat
    
  3. 安装依赖项

    _install.bat
    
  4. 配置环境变量

    • 复制 env.sample 到 .env
    • 填写您的Azure AI代理凭据:
      AZURE_AI_PROJECT_ENDPOINT="https://your-project.services.ai.azure.com/api/projects/yourProject"
      AZURE_AI_AGENT_ID="asst_xxxxxxxxxxxxx"
      
  5. 启动服务器

    _run_server.bat
    

API将于 http://localhost:8989

Docker开发

  1. 从Docker Compose开始

    _up.bat
    
  2. 停止Docker编写

    _down.bat
    

API终点

GET/健康

验证服务是否正在运行的健康检查终结点。

例子:

curl http://localhost:8989/health

答复:

{
  "status": "ok",
  "service": "bing-grounding-api",
  "region": "eastus",
  "agents_loaded": 5
}

POST/bing接地

Azure AI Agent包装器端点,支持Bing基础和引用。

参数:

  • query (string,必填)-要处理的用户查询

例子:

curl -X POST "http://localhost:8989/bing-grounding?query=What+happened+in+finance+today?"

成功响应:

{
  "content": "Today in finance, the U.S. stock market saw a sharp decline, with the Dow Jones Industrial Average plunging almost 800 points (down 1.6%), and both the Nasdaq and S&P 500 also posting significant losses...",
  "citations": [
    {
      "id": 1,
      "type": "url",
      "url": "https://www.marketwatch.com/...",
      "title": "Stock Market News Today"
    },
    {
      "id": 2,
      "type": "url",
      "url": "https://www.cnbc.com/...",
      "title": "Federal Reserve Commentary"
    }
  ],
  "metadata": {
    "agent_route": "gpt4o_1",
    "model": "gpt-4o",
    "agent_id": "asst_abc123...",
    "region": "eastus"
  }
}

错误响应:

{
  "error": "processing_error",
  "message": "Error details...",
  "metadata": {
    "agent_route": "gpt4o_1",
    "model": "gpt-4o",
    "agent_id": "asst_abc123...",
    "region": "eastus"
  }
}

特征:

  • ✅ 使用Bing搜索的固定响应
  • ✅ 自动引文提取和格式化
  • ✅ 干净的内容(删除内联引用标记)
  • ✅ 结构化JSON响应
  • ✅ 元数据中的区域跟踪

响应元数据(调试信息)

每个API响应都包含 metadata 带有调试信息的对象:

字段 类型 描述 示例 目的
region string 为请求提供服务的Azure区域 "eastus", "westus" 跟踪多区域负载平衡;确定区域问题
model string 用于生成的AI模型 "gpt-4o", "gpt-4o-mini" 验证模型部署是否正确;比较模型性能
agent_route string 代理池标识符 "gpt4o_1", "gpt4o_5" 负载均衡验证;识别特定代理的问题
agent_id string Azure AI代理实例ID "asst_ElHsNtK1PSFxwha7..." 跟踪特定代理人的行为;排除代理错误

为什么元数据对调试很重要:

  1. 多区域部署 -了解哪个区域处理了诊断延迟或区域中断的请求
  2. 负载平衡验证 -确认APIM正在跨代理池分发(gpt4o_1 通过 gpt4o_5)
  3. 性能分析 -比较不同地区、模型或代理的响应时间/质量
  4. 错误排除 -确定错误是否隔离到特定的代理、区域或模型
  5. 合规与审计 -根据监管要求跟踪数据驻留和模型使用情况

元数据成功响应示例:

{
  "content": "Azure AI Foundry is a comprehensive platform for building, deploying, and managing AI applications...",
  "citations": [
    {
      "id": 1,
      "type": "url",
      "url": "https://azure.microsoft.com/products/ai-studio",
      "title": "Azure AI Foundry Documentation"
    }
  ],
  "metadata": {
    "agent_route": "gpt4o_3",      // ← Agent pool #3 (load balanced)
    "model": "gpt-4o",              // ← Using GPT-4o model
    "agent_id": "asst_ElHsNtK1PSFxwha7tLWIFM7T",  // ← Specific agent instance
    "region": "eastus"              // ← Request served from East US
  }
}

元数据错误响应示例:

{
  "error": "rate_limit_exceeded",
  "message": "Model deployment TPM limit exceeded. Please retry.",
  "metadata": {
    "agent_route": "gpt4o_2",      // ← Identifies which agent hit rate limit
    "model": "gpt-4o",
    "agent_id": "asst_abc123xyz",
    "region": "westus"              // ← Regional capacity issue
  }
}

调试用例示例:

问题 检查内容 解决方案
某些请求的响应缓慢 region 字段 可能表示一个区域过载;增加容量或调整APIM路由
间歇性429错误 agent_route + agent_id 特定代理的TPM配额可能较低;增加配额或重新分配负载
质量变化 model + agent_id 比较不同代理人的反应;可能需要及时调整
区域合规违规 region 字段 审核日志显示在错误区域处理的数据;更新地理路由策略
所有请求都发送给一个代理 agent_route 分布 APIM负载平衡中断;检查后端池配置

如何添加元数据(实现):

元数据填充在 app/main.py:

# Environment variable set by Bicep deployment
AZURE_REGION = os.getenv("AZURE_REGION", "unknown")

# On successful response (line ~133)
result["metadata"] = {
    "agent_route": agent_route,      # From URL path (gpt4o_1, gpt4o_2, etc.)
    "model": model,                  # From agent configuration
    "agent_id": AGENTS[agent_route]["agent_id"],  # From agent pool
    "region": AZURE_REGION           # From container environment
}

# On error response (line ~147)
"metadata": {
    "agent_route": agent_route,
    "model": model,
    "agent_id": AGENTS.get(agent_route, {}).get("agent_id", "unknown"),
    "region": AZURE_REGION
}

这 AZURE_REGION 环境变量在部署过程中通过二头肌自动设置(infra/resources.bicep 第258行):

{
  name: 'AZURE_REGION'
  value: location  // Resolves to deployment region (eastus, westus, etc.)
}

测试API

测试API直接端点

先决条件:

  • 已部署服务(本地或Azure)
  • 来自部署的终结点URL或 http://localhost:8989 本地

基本健康检查:

# Local
curl http://localhost:8989/health

# Azure (Container App)
curl https://ca-vw5lt6yc7noze.eastus.azurecontainerapps.io/health

预期响应:

{
  "status": "ok",
  "service": "bing-grounding-api",
  "region": "eastus",
  "agents_loaded": 5
}

测试查询:

# Local
curl -X POST "http://localhost:8989/bing-grounding/gpt4o_1?query=What+is+Azure+AI+Foundry?"

# Azure (Container App)
curl -X POST "https://ca-vw5lt6yc7noze.eastus.azurecontainerapps.io/bing-grounding/gpt4o_1?query=What+is+Azure+AI+Foundry?"

预期响应:

{
  "content": "Azure AI Foundry is Microsoft's unified platform...",
  "citations": [
    {
      "id": 1,
      "type": "url",
      "url": "https://azure.microsoft.com/products/ai-studio",
      "title": "Azure AI Foundry Overview"
    }
  ],
  "metadata": {
    "agent_route": "gpt4o_1",
    "model": "gpt-4o",
    "agent_id": "asst_ElHsNtK1PSFxwha7tLWIFM7T",
    "region": "eastus"
  }
}

测试MCP端点(模型上下文协议)

MCP端点通过Azure API管理为人工智能模型消费提供标准化接口。

先决条件

  1. APIM部署 -API管理必须部署(包含在 azd up)
  2. 订阅密钥 -APIM身份验证所需

获取您的订阅密钥:

# Option 1: Azure Portal
# Navigate to: APIM → Subscriptions → "Built-in all-access subscription" → Show keys

# Option 2: Azure CLI
az apim subscription show \
  --resource-group rg-bing-grounding-mcp-dev \
  --service-name apim-xxxxxx \
  --sid master \
  --query primaryKey -o tsv

配置环境变量

将这些添加到您的 .env 文件:

# MCP Server URL (from APIM)
APIM_MCP_SERVER_URL=https://apim-vw5lt6yc7noze.azure-api.net/bing-grounding-mcp/mcp

# Subscription Key (from APIM portal or CLI)
APIM_SUBSCRIPTION_KEY=70f2c804e2ee4f749cea4b8ab3246e7e

运行MCP测试

使用测试脚本:

# Activate virtual environment
_env_activate.bat

# Run tests
python test_mcp.py

预期产量:

=== Testing MCP Endpoint ===
MCP Server URL: https://apim-vw5lt6yc7noze.azure-api.net/bing-grounding-mcp/mcp

Test 1: What are the latest developments in AI?
RESULT: OK (10.6s)
  Region: eastus                              ← Azure region that processed request
  Model: gpt-4o                               ← AI model used for generation
  Agent Route: gpt4o_2                        ← Agent pool identifier (load balanced)
  Agent ID: asst_ElHsNtK1PSFxwha7tLWIFM7T     ← Specific agent instance ID
  Citations: 3                                ← Number of Bing grounding citations
    [1] AI News December 2025: In-Depth and Concise
    [2] Recent Developments in Generative AI Research
    [3] OpenAI Announces GPT-4.5

  Response: During December 2025, several notable advancements have been made in artificial intelligence...

Test 2: What happened in the stock market today?
RESULT: OK (8.2s)
  Region: westus                              ← Different region (multi-region load balancing)
  Model: gpt-4o
  Agent Route: gpt4o_3                        ← Different agent (load balanced across 5 agents)
  Agent ID: asst_xyz789...
  Citations: 4
    [1] Market Watch - December 17, 2025
    [2] CNBC Stock Market Update
    [3] Bloomberg Markets Summary
    [4] S&P 500 Daily Close

  Response: Today's stock market showed mixed performance, with the S&P 500 closing up 0.3%...

Test 3: Explain quantum computing
RESULT: OK (12.1s)
  Region: eastus                              ← Back to eastus (round-robin balancing)
  Model: gpt-4o
  Agent Route: gpt4o_1                        ← Cycling through agent pool
  Agent ID: asst_def456...
  Citations: 5
    [1] Quantum Computing Basics - Nature
    [2] IBM Quantum - Overview
    [3] Google Quantum AI Research
    [4] Quantum Algorithms Explained
    [5] Introduction to Qubits

  Response: Quantum computing is a revolutionary approach to computation that leverages quantum mechanics...

===========================
Tests completed: 5
Success: 5 (100%)
Failed: 0 (0%)
Average response time: 9.4s
===========================

在测试输出中查找什么:

字段 它告诉你什么 很好✅ 坏❌
区域 地理分布 跨地区轮换(东、西) 始终是同一地区;“未知”
代理路线 跨代理的负载平衡 通过gpt4o_1循环到gpt4i_5 始终使用相同的代理路由
响应时间 性能一致性 5-15秒典型 >30秒;高方差
成功率 总体可靠性 90%+成功 <80%成功;频繁故障
引用 Bing接地质量 3-5次引用/响应 0次引用;通用答案

失败测试示例(用于调试):

Test 4: What are the benefits of cloud computing?
RESULT: FAILED (2.5s) - rate_limit_exceeded
  Region: westus                              ← Helps isolate regional capacity issues
  Model: gpt-4o
  Agent Route: gpt4o_4                        ← This specific agent hit rate limit
  Agent ID: asst_ghi789...
  Error: Model deployment TPM limit exceeded  ← Error message

  Action: Increase TPM quota for westus deployment or adjust load balancing

使用cURL进行手动MCP测试

使用订阅密钥标头进行测试:

curl -X POST "https://apim-vw5lt6yc7noze.azure-api.net/bing-grounding-mcp/mcp" \
  -H "Ocp-Apim-Subscription-Key: YOUR_SUBSCRIPTION_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "tools/call",
    "params": {
      "name": "bing_grounding",
      "arguments": {
        "query": "What is the weather like today?"
      }
    },
    "id": 1
  }'

预期响应:

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "content": [
      {
        "type": "text",
        "text": "Today's weather varies by location. For specific weather information, please check your local forecast. As of December 17, 2025, many regions are experiencing seasonal winter conditions..."
      }
    ],
    "metadata": {
      "agent_route": "gpt4o_1",               // ← Which agent pool handled request
      "model": "gpt-4o",                      // ← Model used
      "agent_id": "asst_ElHsNtK1PSFxwha7tLWIFM7T",  // ← Specific agent instance
      "region": "eastus"                      // ← Region that processed request
    }
  }
}

非常适合调试:

# Add -v flag to see full request/response headers
curl -v -X POST "https://apim-vw5lt6yc7noze.azure-api.net/bing-grounding-mcp/mcp" \
  -H "Ocp-Apim-Subscription-Key: YOUR_SUBSCRIPTION_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "tools/call",
    "params": {
      "name": "bing_grounding",
      "arguments": {
        "query": "What is Azure AI Foundry?"
      }
    },
    "id": 1
  }' | jq '.'

预期详细输出(含调试信息):

< HTTP/2 200
< content-type: application/json
< ocp-apim-trace-location: https://apimst...  ← APIM trace URL (if tracing enabled)
< x-backend-server: ca-vw5lt6yc7noze         ← Which Container App served request

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "content": [
      {
        "type": "text",
        "text": "Azure AI Foundry is Microsoft's comprehensive platform for building, deploying, and managing AI applications. It provides a unified experience for developing custom AI solutions, integrating with Azure OpenAI Service, and leveraging pre-built AI models..."
      }
    ],
    "citations": [
      {
        "id": 1,
        "type": "url",
        "url": "https://azure.microsoft.com/en-us/products/ai-studio",
        "title": "Azure AI Foundry - Microsoft Azure"
      },
      {
        "id": 2,
        "type": "url",
        "url": "https://learn.microsoft.com/azure/ai-studio/",
        "title": "Azure AI Foundry documentation"
      }
    ],
    "metadata": {
      "agent_route": "gpt4o_2",
      "model": "gpt-4o",
      "agent_id": "asst_xyz789abc123def456",
      "region": "eastus"
    }
  }
}

使用元数据调试清单:

✅ 地区正确 - "eastus" 或预期区域(不是 "unknown")
✅ 代理路由有效 -其中之一 gpt4o_1 通过 gpt4o_5
✅ 预计型号 -应该是 "gpt-4o" 对于所有代理
✅ 已填充代理ID -以开头的长字符串 asst_
✅ 引文存在 -Bing搜索结果数组
✅ 响应时间可接受 -通常为5-15秒

❌ 区域显示“未知” -容器应用程序缺少AZURE_REGION环境变量
❌ 每次都是同一个特工 -APIM负载平衡不起作用
❌ 无引用 -代理上未配置Bing接地
❌ 代理ID“未知” -代理池配置错误

常见测试问题

问题1:401访问被拒绝

{
  "statusCode": 401,
  "message": "Access denied due to missing subscription key"
}

解决方案: 将订阅密钥添加到请求标头中:

-H "Ocp-Apim-Subscription-Key: YOUR_KEY_HERE"

问题2:空响应/无内容

解决方案:

  1. 检查MCP服务器URL是否正确
  2. 验证订阅密钥是否有效
  3. 检查APIM后端是否正常:
    curl https://ca-vw5lt6yc7noze.eastus.azurecontainerapps.io/health
    

问题3:区域显示“未知”

解决方案: 确保 AZURE_REGION 在容器应用程序配置中设置环境变量:

azd deploy  # Redeploy to update environment variables

多区域测试

测试多区域部署时,请遵守 region 用于验证负载平衡的字段:

# Run multiple tests to see region rotation
for i in {1..10}; do
  echo "Test $i:"
  curl -s -X POST "https://apim-xxx.azure-api.net/bing-grounding-mcp/mcp" \
    -H "Ocp-Apim-Subscription-Key: YOUR_KEY" \
    -H "Content-Type: application/json" \
    -d '{"jsonrpc":"2.0","method":"tools/call","params":{"name":"bing_grounding","arguments":{"query":"test"}},"id":'$i'}' \
    | jq -r '.result.metadata.region'
done

预期产出(2个地区):

Test 1: eastus           ← First request to East US
Test 2: westus           ← Load balanced to West US
Test 3: eastus           ← Round-robin back to East US
Test 4: westus
Test 5: eastus
Test 6: westus
Test 7: eastus
Test 8: westus
Test 9: eastus
Test 10: westus

提取完整元数据进行分析:

# Get complete metadata from multiple requests
for i in {1..5}; do
  echo "=== Request $i ==="
  curl -s -X POST "https://apim-xxx.azure-api.net/bing-grounding-mcp/mcp" \
    -H "Ocp-Apim-Subscription-Key: YOUR_KEY" \
    -H "Content-Type: application/json" \
    -d '{"jsonrpc":"2.0","method":"tools/call","params":{"name":"bing_grounding","arguments":{"query":"test query"}},"id":'$i'}' \
    | jq '.result.metadata'
  echo ""
done

预期产量:

=== Request 1 ===
{
  "agent_route": "gpt4o_1",                    ← Agent pool #1
  "model": "gpt-4o",
  "agent_id": "asst_ElHsNtK1PSFxwha7tLWIFM7T",
  "region": "eastus"                           ← East US region
}

=== Request 2 ===
{
  "agent_route": "gpt4o_3",                    ← Different agent (load balanced)
  "model": "gpt-4o",
  "agent_id": "asst_Abc123Xyz789Def456Ghi",
  "region": "westus"                           ← West US region
}

=== Request 3 ===
{
  "agent_route": "gpt4o_2",                    ← Third agent
  "model": "gpt-4o",
  "agent_id": "asst_Qrs789Tuv012Wxy345Zab",
  "region": "eastus"                           ← Back to East US
}

=== Request 4 ===
{
  "agent_route": "gpt4o_5",                    ← Fifth agent
  "model": "gpt-4o",
  "agent_id": "asst_Mno456Pqr789Stu012Vwx",
  "region": "westus"                           ← Back to West US
}

=== Request 5 ===
{
  "agent_route": "gpt4o_4",                    ← Fourth agent
  "model": "gpt-4o",
  "agent_id": "asst_Cde345Fgh678Ijk901Lmn",
  "region": "eastus"                           ← East US again
}

这显示了什么:

  • ✅ 区域分布 -东部和西部的请求正在平衡
  • ✅ 代理商分布 -不同的代理路径(gpt4o_1至gpt40_5)
  • ✅ 独特的代理商 -每个agent_id都不同
  • ✅ 一致的模型 -全部使用gpt-4o

随时间变化的轨迹分布:

# Run 50 requests and count region distribution
echo "Testing 50 requests across regions..."
for i in {1..50}; do
  curl -s -X POST "https://apim-xxx.azure-api.net/bing-grounding-mcp/mcp" \
    -H "Ocp-Apim-Subscription-Key: YOUR_KEY" \
    -H "Content-Type: application/json" \
    -d '{"jsonrpc":"2.0","method":"tools/call","params":{"name":"bing_grounding","arguments":{"query":"test"}},"id":'$i'}' \
    | jq -r '.result.metadata.region'
done | sort | uniq -c

预期产量:

     25 eastus          ← 50% of requests (good distribution)
     25 westus          ← 50% of requests (good distribution)

如果分布不均匀:

     45 eastus          ← 90% to one region
      5 westus          ← Only 10% to other region

可能的原因:

  • APIM后端健康检查标记一个区域不健康
  • 断路器在一个区域跳闸
  • 后端池权重配置不均
  • DNS缓存或粘滞会话

性能测试

具有多个并发请求的负载测试:

# Install dependencies
pip install aiohttp asyncio

# Run load test (included in test_mcp.py)
python test_mcp.py --concurrent 10 --iterations 100

监视器:

  • 响应时间
  • 成功率
  • 区域分布
  • 引文质量

Azure AI代理配置

此服务包装了一个必须配置Bing基础功能的Azure AI代理。

设置Azure AI代理

  1. 创建Azure AI项目
  2. 创建启用Bing基础的AI代理
  3. 复制项目终结点和代理ID
  4. 将它们设置为环境变量

环境变量

变量 描述 必填 示例
AZURE_AI_PROJECT_ENDPOINT Azure AI项目端点 始终 https://your-project.services.ai.azure.com/api/projects/yourProject
AZURE_AI_AGENT_ID Azure AI代理ID 始终 asst_xxxxxxxxxxxxx
AZURE_CLIENT_ID 服务主体应用程序ID 仅限本地测试 xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
AZURE_CLIENT_SECRET 服务负责人机密 仅限本地测试 your-secret-value
AZURE_TENANT_ID Azure AD租户ID 仅限本地测试 xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx

身份验证设置

用于本地测试(服务负责人)

  1. 创建服务主体:

    az ad sp create-for-rbac --name "bing-grounding-api-sp" --role Contributor
    

    这将返回:

    {
      "appId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",          # AZURE_CLIENT_ID
      "password": "your-secret-here",                           # AZURE_CLIENT_SECRET
      "tenant": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"         # AZURE_TENANT_ID
    }
    
  2. 授予人工智能项目访问权限:

    # Get your subscription ID and resource group from Azure Portal
    az role assignment create \
      --assignee <appId-from-above> \
      --role "Cognitive Services User" \
      --scope "/subscriptions/<sub-id>/resourceGroups/<rg>/providers/Microsoft.CognitiveServices/accounts/<ai-project-name>"
    
  3. 更新你的 .env 文件:

    AZURE_AI_PROJECT_ENDPOINT=https://your-region.services.ai.azure.com/api/projects/your-project
    AZURE_AI_AGENT_ID=asst_xxxxxxxxxxxxx
    AZURE_CLIENT_ID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
    AZURE_CLIENT_SECRET=your-secret-here
    AZURE_TENANT_ID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
    

用于生产(管理身份)

部署到Azure容器应用程序时,请改用托管身份:

  1. 启用托管身份 在您的容器应用程序上:

    az containerapp identity assign \
      --name bing-grounding-api \
      --resource-group your-rg \
      --system-assigned
    
  2. 授予托管身份访问权限 您的AI项目:

    # Get the principal ID from the output above or:
    PRINCIPAL_ID=$(az containerapp identity show \
      --name bing-grounding-api \
      --resource-group your-rg \
      --query principalId -o tsv)
    
    # Grant access
    az role assignment create \
      --assignee $PRINCIPAL_ID \
      --role "Cognitive Services User" \
      --scope "/subscriptions/<sub-id>/resourceGroups/<rg>/providers/Microsoft.CognitiveServices/accounts/<ai-project-name>"
    
  3. 仅使用所需变量进行部署 (无客户机密):

    az containerapp create \
      --name bing-grounding-api \
      --resource-group your-rg \
      --environment your-env \
      --image your-registry.azurecr.io/bing-grounding-api:latest \
      --target-port 8989 \
      --ingress external \
      --system-assigned \
      --env-vars \
        AZURE_AI_PROJECT_ENDPOINT="your-endpoint" \
        AZURE_AI_AGENT_ID="your-agent-id"
    

重要:不设置 AZURE_CLIENT_ID, AZURE_CLIENT_SECRET,或 AZURE_TENANT_ID 在生产中。这 DefaultAzureCredential 将自动使用托管身份。

项目结构

ai-bing-grounding-mcp/
├── agents/                         # AI Agent implementations
│   ├── __init__.py
│   ├── base_agent.py              # Abstract base class
│   └── bing_grounding.py          # Bing grounding agent
├── ai/                             # (Legacy - not used)
│   ├── __init__.py
│   └── azure_openai_client.py
├── app/                            # FastAPI application
│   ├── __init__.py
│   └── main.py                    # API endpoints
├── apim-policy.xml                # Main APIM policy (load balancing + circuit breaker)
├── apim-policy-with-healthcheck.xml  # Enhanced APIM policy with active health checks
├── apim-healthcheck-monitor.xml   # Optional active health monitoring policy
├── docker-compose.yaml            # Local Docker development
├── dockerfile                     # Container image definition
├── env.sample                     # Environment variable template
├── main.py                        # Application entry point
├── requirements.txt               # Python dependencies
├── _env_activate.bat              # Activate virtual environment
├── _env_create.bat                # Create virtual environment
├── _install.bat                   # Install dependencies
├── _run_server.bat                # Run FastAPI server
├── _up.bat                        # Start Docker Compose
├── _down.bat                      # Stop Docker Compose
└── README.md                      # This file

部署到Azure

选项1:使用Azure Developer CLI自动部署(⭐ 推荐)

Azure开发者命令行界面(azd)自动化了从创建基础设施到部署应用程序的整个部署过程。 这是推荐的方法 用于开发和生产部署。

自动部署的内容:

  • 🏗️ Azure容器应用环境+3个容器应用实例
  • 🤖 Azure人工智能铸造中心和项目
  • 🤖 12个基于Bing的GPT-4o AI代理(通过编程创建!)
  • 🔐 Azure容器注册表
  • 🔐 具有受管理身份的密钥库
  • 📊 日志分析和应用洞察
  • 🌐 Azure API管理(带负载平衡和断路器)
  • 🔒 所有资源的RBAC角色分配

总部署时间:~8-15分钟 ⏱️


先决条件

在开始之前,请确保您已经:

必修的:

  • 具有贡献者或所有者角色的Azure订阅
  • Azure开发者命令行界面(azd) - 安装指南
  • Azure命令行界面(az) - 安装指南
  • Docker桌面- 下载

可选(用于当地发展):

快速安装命令:

# Windows (PowerShell)
powershell -ex AllSigned -c "Invoke-RestMethod 'https://aka.ms/install-azd.ps1' | Invoke-Expression"

# macOS/Linux
curl -fsSL https://aka.ms/install-azd.sh | bash

第一步:登录Azure

azd auth login

这将打开一个浏览器进行身份验证。一旦通过身份验证,您就可以部署了。


步骤2:部署所有内容(一个命令)

最简单的方法是使用 azd up,它一步创建环境、配置基础设施并部署应用程序:

azd up

系统将提示您:

  • 环境名称例如。, dev, staging, prod
    • 创建资源组: rg-bing-grounding-mcp-{env-name}
  • Azure订阅:从您的订阅中选择
  • Azure位置例如。, eastus2, westus2
  • 资源组确认:如果已存在,请确认继续

期间会发生什么 azd up:

  1. 预浸挂钩 (约30秒)

    • ✅ 检查资源组状态
    • ✅ 注册Microsoft。Bing资源提供者
  2. 基础设施供应 (约5-10分钟)

    • 🏗️ 创建容器注册表
    • 🏗️ 创建容器应用程序环境
    • 🏗️ 创建AI铸造中心和项目
    • 🏗️ 部署GPT-4o模型
    • 🏗️ 创建密钥库、存储、日志分析
    • 🏗️ 创建API管理
    • 🏗️ 配置托管身份和RBAC
  3. 供应后挂钩 (约2-3分钟)

    • 🤖 使用Bing基础创建12个GPT-4o AI代理
    • 📝 将代理ID保存到环境
  4. 应用程序部署 (约3-5分钟)

    • 🐳 构建Docker镜像
    • 📤 推送到Azure容器注册表
    • 🚀 部署到所有3个容器应用程序实例
  5. 部署后挂钩 (约1分钟)

    • 🔄 更新其他容器实例

完成后,您将看到:

SUCCESS: Your application was provisioned and deployed to Azure in X minutes.

You can view the application at https://ca-xxxxxx.eastus2.azurecontainerapps.io

就是这样!您的API有12个人工智能代理,随时准备为请求提供服务。


步骤2(备选方案):单独提供和部署

如果您更喜欢控制,可以将步骤分开:

A.创造环境:

azd env new <environment-name>

示例:

  • azd env new dev → 创建 rg-bing-grounding-mcp-dev
  • azd env new prod → 创建 rg-bing-grounding-mcp-prod

B.提供基础设施:

azd provision

这将创建所有Azure资源,并运行后视觉挂钩以创建AI代理。

C.部署应用程序:

azd deploy

这将构建Docker容器并将其部署到所有实例。


步骤3:测试部署

获取您的端点:

azd env get-values | grep AZURE_CONTAINER_APP_ENDPOINT
# or
azd env get-values | findstr AZURE_CONTAINER_APP_ENDPOINT  # Windows

测试API:

# Health check
curl https://ca-xxxxxx.eastus2.azurecontainerapps.io/health

# List agents
curl https://ca-xxxxxx.eastus2.azurecontainerapps.io/agents

# Query with Bing grounding
curl -X POST "https://ca-xxxxxx.eastus2.azurecontainerapps.io/bing-grounding/gpt4o_1?query=What+is+Azure+AI+Foundry?"

高级配置

预配置环境(可选)

对于CI/CD管道或脚本部署,您可以预先配置值以避免交互式提示:

# Create environment
azd env new <env-name>

# Set subscription (find with: az account list -o table)
azd env set AZURE_SUBSCRIPTION_ID "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"

# Set location
azd env set AZURE_LOCATION "eastus2"

# Now provision and deploy without prompts
azd up

何时进行预配置:

  • ✅ CI/CD管道(GitHub操作、Azure DevOps)
  • ✅ 自动化/脚本化部署
  • ✅ 执行团队标准
  • ✅ 多环境部署(开发/测试/生产)

查看环境配置

# Show all environment variables and outputs
azd env get-values

# Show deployment status and endpoints
azd show

常见工作流

更新应用程序代码

当你修改Python代码时:

azd deploy

这将重建Docker镜像,并在零停机时间(约3-5分钟)内更新所有容器应用程序实例。

更新基础架构

在中修改二头肌模板时 infra/:

azd provision

这将应用基础设施更改,而无需重新部署应用程序(约2-5分钟)。

查看日志

# Get resource group from environment
RG=$(azd env get-values | grep AZURE_RESOURCE_GROUP | cut -d'=' -f2 | tr -d '"')

# Get Container App name
CA_NAME=$(azd env get-values | grep AZURE_CONTAINER_APP_NAME | cut -d'=' -f2 | tr -d '"')

# Stream logs
az containerapp logs show --name $CA_NAME --resource-group $RG --follow

多种环境(开发/暂存/生产)

# Create and deploy dev environment
azd env new dev
azd env set AZURE_LOCATION "eastus2"
azd up

# Create and deploy prod environment
azd env new prod
azd env set AZURE_LOCATION "eastus"
azd up

# Switch between environments
azd env select dev
azd env select prod

# List all environments
azd env list

每个环境都会得到:

  • 单独的资源组: rg-bing-grounding-mcp-{env}
  • 隔离的Azure资源
  • 本地配置 .azure/{env}/

拆除资源

# Delete all Azure resources (with confirmation)
azd down

# Delete without prompts
azd down --force --purge

⚠️ 警告:这将删除整个资源组和所有资源。


什么是自动创建的

当你奔跑时 azd up,提供以下资源:

资源 目的 详细信息
资源组 逻辑容器 rg-bing-grounding-mcp-{env}
集装箱登记处 Docker镜像 应用镜像的私有注册表
容器应用程序(×3) 应用程序托管 3个负载平衡实例
AI铸造中心 人工智能基础设施 人工智能项目中心
AI铸造项目 AI代理管理 包含GPT-4o部署
12个AI代理 必应基础代理 通过API以编程方式创建
API管理 API网关 负载平衡+断路器
密钥库 机密管理 存储敏感配置
存储帐户 数据存储 用于AI Hub和日志
日志分析 监控 集中记录
应用洞察 APM 性能监控
管理身份 身份验证 安全的服务到服务身份验证

总成本估算:根据使用情况和SKU,每月约200-400美元。


自动挂钩解释

该解决方案使用 azd 钩子用于自动化设置任务:

预浸挂钩 (基础设施之前):

  1. 检查资源组 -提示RG是否已存在
  2. 注册供应商 -登记簿 Microsoft.Bing 提供者

供应后挂钩 (基础设施之后):

  1. 创建AI代理 -以Bing为基础,通过编程创建12个GPT-4o代理
  2. 保存代理ID -将ID存储为环境变量

部署后挂钩 (部署后):

  1. 更新容器实例 -使用最新映像更新其他实例

这些钩子在 azure.yaml 并自动运行- 无需人工干预.


部署故障排除

问题: azd provision 订阅失败

  • 修复:明确设置订阅: azd env set AZURE_SUBSCRIPTION_ID "your-sub-id"
  • 修复:确保您在订阅中具有参与者/所有者角色

问题:容器部署失败,显示“找不到映像”

  • 修复:确保Docker在本地运行
  • 修复:检查ACR凭据: az acr login --name <registry-name>

问题:未创建AI代理

  • 修复:检查日志: cat .azure/{env}/.env | grep AZURE_AI_AGENT
  • 修复:手动运行: python scripts/postprovision_create_agents.py

问题:访问AI项目时出现身份验证错误

  • 修复:验证托管身份是否具有“认知服务用户”角色
  • 修复:检查Azure门户中的RBAC分配

最佳实践

✅ 使用 azd up 用于首次部署和组合基础架构+代码更改
✅ 使用 azd deploy 仅用于代码更改(更快)
✅ 使用单独的环境 用于dev/ststage/prod隔离
✅ 永不承诺 .azure/ 文件夹 -它包含特定于环境的配置
✅ 审查产出 每次部署后 azd env get-values
✅ 测试在dev 部署到生产环境之前
✅ 使用托管身份 (默认)而不是服务主体
✅ 监控成本 使用Azure成本管理


📚 了解更多:


选项2:使用Azure CLI手动部署

如果您喜欢手动控制或无法使用 azd:

第一步:创建基础设施

  1. 创建资源组:

    az group create --name rg-bing-grounding --location eastus
    
  2. 创建Azure容器注册表:

    az acr create \
      --resource-group rg-bing-grounding \
      --name acrbing123 \
      --sku Basic \
      --admin-enabled true
    
  3. 创建AI铸造中心和项目 (通过Azure门户):

    • 首选https://ai.azure.com
    • 创建新中心
    • 在Hub中创建新项目
    • 注意项目端点
  4. 创建容器应用程序环境:

    az containerapp env create \
      --name cae-bing-grounding \
      --resource-group rg-bing-grounding \
      --location eastus
    

步骤2:构建并推送容器映像

  1. 登录ACR:

    az acr login --name acrbing123
    
  2. 构建和推送图像:

    docker build -t acrbing123.azurecr.io/bing-grounding-api:latest .
    docker push acrbing123.azurecr.io/bing-grounding-api:latest
    

步骤3:部署容器应用程序(多个实例)

# Get ACR credentials
ACR_USERNAME=$(az acr credential show --name acrbing123 --query username -o tsv)
ACR_PASSWORD=$(az acr credential show --name acrbing123 --query passwords[0].value -o tsv)

# Deploy instance 1
az containerapp create \
  --name bing-grounding-api-0 \
  --resource-group rg-bing-grounding \
  --environment cae-bing-grounding \
  --image acrbing123.azurecr.io/bing-grounding-api:latest \
  --target-port 8989 \
  --ingress external \
  --registry-server acrbing123.azurecr.io \
  --registry-username $ACR_USERNAME \
  --registry-password $ACR_PASSWORD \
  --system-assigned \
  --env-vars \
    AZURE_AI_PROJECT_ENDPOINT="https://eastus.services.ai.azure.com/api/projects/yourProject" \
    AZURE_AI_AGENT_ID="asst_xxxxxxxxxxxxx"

# Repeat for instances 1 and 2
az containerapp create --name bing-grounding-api-1 ... (same parameters)
az containerapp create --name bing-grounding-api-2 ... (same parameters)

步骤4:授予托管身份访问权限

# Get managed identity principal IDs
PRINCIPAL_ID_0=$(az containerapp identity show \
  --name bing-grounding-api-0 \
  --resource-group rg-bing-grounding \
  --query principalId -o tsv)

# Grant access to AI Project (repeat for each instance)
az role assignment create \
  --assignee $PRINCIPAL_ID_0 \
  --role "Cognitive Services User" \
  --scope "/subscriptions/<sub-id>/resourceGroups/rg-bing-grounding/providers/Microsoft.CognitiveServices/accounts/<ai-project-name>"

步骤5:更新容器应用程序(用于代码更改)

# Build and push new image
docker build -t acrbing123.azurecr.io/bing-grounding-api:v2 .
docker push acrbing123.azurecr.io/bing-grounding-api:v2

# Update container apps
az containerapp update \
  --name bing-grounding-api-0 \
  --resource-group rg-bing-grounding \
  --image acrbing123.azurecr.io/bing-grounding-api:v2

# Repeat for other instances

使用Docker Compose进行本地开发

对于没有Azure资源的本地测试:

# Start services
docker-compose up -d

# View logs
docker-compose logs -f

# Stop services
docker-compose down

备注:您仍然需要有效的Azure AI项目凭据 .env 文件。


Azure API管理设置

断路器负载平衡

该服务包括用于具有多个后端实例的生产部署的APIM策略。

架构特征

┌──────────────────────────────────────────┐
│      [AZURE API MANAGEMENT]              │
│                                          │
│  ┌────────────────────────────────────┐ │
│  │  Load Balancer + Circuit Breaker   │ │
│  │  • Session Affinity (Cookies)      │ │
│  │  • Health-Based Routing            │ │
│  │  • Auto Failover & Recovery        │ │
│  └─────────────────┬──────────────────┘ │
└────────────────────┼────────────────────┘
                     │
         ┌───────────┼───────────┐
         │           │           │
         ▼           ▼           ▼
┌─────────────┐ ┌─────────────┐ ... (N instances)
│ ✅ HEALTHY  │ │ ❌ UNHEALTHY│
│ Container   │ │ Container   │
│ App #1      │ │ App #2      │
│ [ACTIVE]    │ │ [REMOVED]   │
└─────────────┘ └─────────────┘

特性

  1. 会话亲和性(粘性会话) -客户端通过Cookie坚持使用相同的后端
  2. 断路器 -不健康的后端已自动从池中删除
  3. 自动恢复 -健康恢复后,后端重新加入
  4. 健康感知路由 -仅通往健康实例的路径

APIM策略文件

包括三个策略文件:

文件 描述 用例
apim-policy.xml 具有会话关联性和断路器的主要负载平衡策略 推荐 -具有多个后端的生产部署
apim-policy-with-healthcheck.xml 通过主动健康监测增强策略 需要主动健康检查的高可用性场景
apim-healthcheck-monitor.xml 独立健康检查监测器 单独的监测管道

设置步骤

  1. 更新后端URL

    在 apim-policy.xml,将占位符URL替换为容器应用程序URL:

    var backends = new System.Collections.Generic.Dictionary<string, string> {
        { "0", "https://bing-grounding-api-1.azurecontainerapps.io" },
        { "1", "https://bing-grounding-api-2.azurecontainerapps.io" },
        { "2", "https://bing-grounding-api-3.azurecontainerapps.io" },
        { "3", "https://bing-grounding-api-4.azurecontainerapps.io" },
        { "4", "https://bing-grounding-api-5.azurecontainerapps.io" }
    };
    
  2. 在Azure门户中应用策略

    • 导航到您的APIM服务
    • 转到您的API→ 设计表
    • 点击“所有操作”(或特定操作)
    • 在“入站处理”中,单击代码编辑器(</>)
    • 从粘贴策略XML apim-policy.xml
    • 点击保存
  3. 启用内部缓存 (断路器要求)

    • 导航到APIM→ 缓存
    • 启用内置缓存

断路器行为

❌ 不健康-标记背部不健康

后端已标记 [不健康] (从池中删除30秒):

  • 退货 500, 502, 503, 504 (服务器错误)
  • 退货 429 (超出费率限制)
  • 退货 401 (身份验证失败)
  • 连接超时或失败

✅ 健康-自动恢复

后端已标记 [健康] (重新加入池)当:

  • 退货 200 OK
  • 健康状态缓存过期(30秒后)

用于监视的响应标头

APIM策略添加了用于监视的标头:

标题 描述 示例
X-APIM-Correlation-Id 用于跟踪的唯一请求ID a1b2c3d4-...
X-Backend-Instance 处理请求的后端 0, 1, 2, 3, 4
X-Served-By-Instance 为响应提供服务的后端 0, 1, 2, 3, 4
X-Error-Backend-Instance 导致错误的后端(在错误时) 2

饼干:

  • APIM-Backend-Instance -会话关联cookie(24小时TTL)

测试断路器

  1. 测试会话相关性

    # First request - receives backend assignment
    curl -i https://your-apim.azure-api.net/bing-grounding
    
    # Check Set-Cookie header for: APIM-Backend-Instance=X
    
    # Subsequent requests with cookie go to same backend
    curl -i https://your-apim.azure-api.net/health \
      -H "Cookie: APIM-Backend-Instance=0"
    
  2. 测试故障转移

    # Stop one Container App instance
    # Requests automatically route to healthy instances
    for i in {1..10}; do
      curl -s https://your-apim.azure-api.net/health | jq '.status'
    done
    
  3. 测试恢复

    # Restart the instance
    # Wait 30 seconds for cache expiration
    # It automatically rejoins on first 200 response
    

生产检查表

  • []APIM中启用了内部缓存
  • []部署并运行多个容器应用程序
  • []健康端点返回200 OK
  • []APIM策略中更新了后端URL
  • []已应用和测试的政策
  • []使用Cookie测试会话相关性
  • []断路器经过模拟故障测试
  • []已配置监控/警报(应用程序洞察)
  • []安全性:已配置APIM订阅密钥

代理架构

包装器使用抽象基类(ABC)模式进行扩展:

agents/
├── base_agent.py           # Abstract base class for all agents
└── bing_grounding.py       # Bing grounding implementation

BaseAgent(ABC)

class BaseAgent(ABC):
    """Abstract base class for all agents"""
    
    def __init__(self, endpoint: str = None, agent_id: str = None):
        self.endpoint = endpoint
        self.agent_id = agent_id
    
    @abstractmethod
    def chat(self, message: str) -> str:
        """Process a message and return response"""
        pass

BingGrounding代理

具体实施:

  • 连接到Azure AI代理服务
  • 创建对话线程
  • 从基于Bing的回复中提取引文并格式化引文
  • 返回包含内容和引用的结构化JSON
  • 处理后自动清理线程

使用新代理进行扩展

要添加新的代理类型,请执行以下操作:

  1. 创建一个新的代理类,该类继承自 BaseAgent
  2. 实施 chat() 方法
  3. 将配置添加到 .env
  4. 在中添加新端点 app/main.py

例子:

class CustomAgent(BaseAgent):
    def __init__(self):
        endpoint = os.getenv("CUSTOM_AGENT_ENDPOINT")
        agent_id = os.getenv("CUSTOM_AGENT_ID")
        super().__init__(endpoint=endpoint, agent_id=agent_id)
    
    def chat(self, message: str) -> str:
        # Your custom implementation
        pass

故障排除

问题:“AZURE_AI_PROJECT_ENDPOINT未设置”错误

  • 修复:复制 env.sample 到 .env 并填写您的凭据

问题:Azure AI代理的身份验证失败

  • 修复:确保您已通过Azure CLI的身份验证: az login
  • 修复:验证DefaultAzureCredentials是否有权访问AI项目

问题:没有引用回应

  • 修复:确保您的Azure AI代理启用了Bing基础
  • 修复:检查Azure AI Studio中是否正确配置了代理

问题:线程清理失败

  • 修复:这些已记录,但不影响响应
  • 修复:检查Azure AI代理服务限制和配额

生产安全最佳实践

为了简单起见,默认部署使用公共端点。对于生产工作负载,您应该通过专用网络和其他控制来增强安全性。

当前安全配置

✅ 托管身份 -容器应用程序向AI项目进行身份验证,没有秘密
✅ 基于角色的访问控制 -对所有资源进行基于角色的访问控制
✅ 仅限HTTPS -所有在传输过程中加密的流量
✅ ACR身份验证 -安全的容器注册表访问
✅ 密钥库RBAC -基于角色的密钥库授权

⚠️ 公共端点 -可从互联网访问的资源(适用于开发/测试)

推荐的生产增强功能

1.具有虚拟网络集成的专用网络

添加ExpressRoute集成以将资源与公共互联网隔离:

// Add to resources.bicep

// Virtual Network
resource vnet 'Microsoft.Network/virtualNetworks@2023-05-01' = {
  name: 'vnet-${uniqueString(resourceGroup().id)}'
  location: location
  tags: tags
  properties: {
    addressSpace: {
      addressPrefixes: ['10.0.0.0/16']
    }
    subnets: [
      {
        name: 'subnet-containerapp'
        properties: {
          addressPrefix: '10.0.0.0/23'
          delegations: [
            {
              name: 'Microsoft.App/environments'
              properties: {
                serviceName: 'Microsoft.App/environments'
              }
            }
          ]
        }
      }
      {
        name: 'subnet-privateendpoints'
        properties: {
          addressPrefix: '10.0.2.0/24'
          privateEndpointNetworkPolicies: 'Disabled'
        }
      }
    ]
  }
}

// Update Container App Environment with VNet
resource containerAppEnv 'Microsoft.App/managedEnvironments@2023-05-01' = {
  name: containerAppEnvName
  location: location
  tags: tags
  properties: {
    vnetConfiguration: {
      infrastructureSubnetId: vnet.properties.subnets[0].id
      internal: true  // Make it internal for production
    }
    appLogsConfiguration: {
      destination: 'log-analytics'
      logAnalyticsConfiguration: {
        customerId: logAnalytics.properties.customerId
        sharedKey: logAnalytics.listKeys().primarySharedKey
      }
    }
  }
}

2.Azure服务的专用端点

使用专用端点保护后端服务:

// Private DNS Zones (add to resources.bicep)
resource privateDnsZoneStorage 'Microsoft.Network/privateDnsZones@2020-06-01' = {
  name: 'privatelink.blob.core.windows.net'
  location: 'global'
  tags: tags
}

resource privateDnsZoneKeyVault 'Microsoft.Network/privateDnsZones@2020-06-01' = {
  name: 'privatelink.vaultcore.azure.net'
  location: 'global'
  tags: tags
}

resource privateDnsZoneACR 'Microsoft.Network/privateDnsZones@2020-06-01' = {
  name: 'privatelink.azurecr.io'
  location: 'global'
  tags: tags
}

// Link DNS Zones to VNet
resource privateDnsZoneLinkStorage 'Microsoft.Network/privateDnsZones/virtualNetworkLinks@2020-06-01' = {
  parent: privateDnsZoneStorage
  name: 'link-storage'
  location: 'global'
  properties: {
    registrationEnabled: false
    virtualNetwork: {
      id: vnet.id
    }
  }
}

// Private Endpoint for Storage Account
resource privateEndpointStorage 'Microsoft.Network/privateEndpoints@2023-05-01' = {
  name: 'pe-storage-${uniqueString(resourceGroup().id)}'
  location: location
  tags: tags
  properties: {
    subnet: {
      id: vnet.properties.subnets[1].id  // Private endpoints subnet
    }
    privateLinkServiceConnections: [
      {
        name: 'storage-connection'
        properties: {
          privateLinkServiceId: storage.id
          groupIds: ['blob']
        }
      }
    ]
  }
}

// Private DNS Zone Group for Storage
resource privateDnsZoneGroupStorage 'Microsoft.Network/privateEndpoints/privateDnsZoneGroups@2023-05-01' = {
  parent: privateEndpointStorage
  name: 'default'
  properties: {
    privateDnsZoneConfigs: [
      {
        name: 'config-storage'
        properties: {
          privateDnsZoneId: privateDnsZoneStorage.id
        }
      }
    ]
  }
}

// Update Storage Account to disable public access
resource storage 'Microsoft.Storage/storageAccounts@2023-01-01' = {
  name: 'st${uniqueString(resourceGroup().id)}'
  location: location
  tags: tags
  sku: {
    name: 'Standard_LRS'
  }
  kind: 'StorageV2'
  properties: {
    accessTier: 'Hot'
    supportsHttpsTrafficOnly: true
    minimumTlsVersion: 'TLS1_2'
    publicNetworkAccess: 'Disabled'  // Changed from default
    networkAcls: {
      defaultAction: 'Deny'
      bypass: 'AzureServices'
    }
  }
}

// Repeat similar patterns for:
// - Key Vault private endpoint
// - Container Registry private endpoint
// - AI Hub/Project (if supported in your region)

3.网络安全组(NSG)

添加NSG规则以控制流量:

// Network Security Group (add to resources.bicep)
resource nsg 'Microsoft.Network/networkSecurityGroups@2023-05-01' = {
  name: 'nsg-containerapp'
  location: location
  tags: tags
  properties: {
    securityRules: [
      {
        name: 'AllowHTTPSInbound'
        properties: {
          priority: 100
          direction: 'Inbound'
          access: 'Allow'
          protocol: 'Tcp'
          sourcePortRange: '*'
          destinationPortRange: '443'
          sourceAddressPrefix: 'Internet'
          destinationAddressPrefix: '*'
        }
      }
      {
        name: 'DenyAllInbound'
        properties: {
          priority: 1000
          direction: 'Inbound'
          access: 'Deny'
          protocol: '*'
          sourcePortRange: '*'
          destinationPortRange: '*'
          sourceAddressPrefix: '*'
          destinationAddressPrefix: '*'
        }
      }
    ]
  }
}

// Associate NSG with Container App subnet
resource vnet 'Microsoft.Network/virtualNetworks@2023-05-01' = {
  // ... existing properties
  properties: {
    // ... existing properties
    subnets: [
      {
        name: 'subnet-containerapp'
        properties: {
          addressPrefix: '10.0.0.0/23'
          networkSecurityGroup: {
            id: nsg.id
          }
          delegations: [
            {
              name: 'Microsoft.App/environments'
              properties: {
                serviceName: 'Microsoft.App/environments'
              }
            }
          ]
        }
      }
    ]
  }
}

4.Azure前门或应用程序网关

对于具有WAF保护的生产级入口:

// Azure Front Door with WAF (add to resources.bicep)
resource frontDoor 'Microsoft.Cdn/profiles@2023-05-01' = {
  name: 'fd-${uniqueString(resourceGroup().id)}'
  location: 'global'
  tags: tags
  sku: {
    name: 'Premium_AzureFrontDoor'  // Premium includes WAF
  }
  properties: {
    originResponseTimeoutSeconds: 60
  }
}

// WAF Policy
resource wafPolicy 'Microsoft.Network/FrontDoorWebApplicationFirewallPolicies@2022-05-01' = {
  name: 'waf${uniqueString(resourceGroup().id)}'
  location: 'global'
  tags: tags
  sku: {
    name: 'Premium_AzureFrontDoor'
  }
  properties: {
    policySettings: {
      enabledState: 'Enabled'
      mode: 'Prevention'
      requestBodyCheck: 'Enabled'
    }
    managedRules: {
      managedRuleSets: [
        {
          ruleSetType: 'Microsoft_DefaultRuleSet'
          ruleSetVersion: '2.1'
        }
        {
          ruleSetType: 'Microsoft_BotManagerRuleSet'
          ruleSetVersion: '1.0'
        }
      ]
    }
  }
}

// Front Door Endpoint
resource fdEndpoint 'Microsoft.Cdn/profiles/afdEndpoints@2023-05-01' = {
  parent: frontDoor
  name: 'endpoint-${uniqueString(resourceGroup().id)}'
  location: 'global'
  properties: {
    enabledState: 'Enabled'
  }
}

// Origin Group (Container Apps)
resource originGroup 'Microsoft.Cdn/profiles/originGroups@2023-05-01' = {
  parent: frontDoor
  name: 'containerapp-origins'
  properties: {
    loadBalancingSettings: {
      sampleSize: 4
      successfulSamplesRequired: 3
      additionalLatencyInMilliseconds: 50
    }
    healthProbeSettings: {
      probePath: '/health'
      probeRequestType: 'GET'
      probeProtocol: 'Https'
      probeIntervalInSeconds: 30
    }
    sessionAffinityState: 'Enabled'  // Sticky sessions
  }
}

// Origins (one for each Container App instance)
resource origin0 'Microsoft.Cdn/profiles/originGroups/origins@2023-05-01' = {
  parent: originGroup
  name: 'origin-0'
  properties: {
    hostName: containerApp[0].properties.configuration.ingress.fqdn
    httpPort: 80
    httpsPort: 443
    originHostHeader: containerApp[0].properties.configuration.ingress.fqdn
    priority: 1
    weight: 1000
    enabledState: 'Enabled'
  }
}
// Repeat for other instances...

// Route
resource route 'Microsoft.Cdn/profiles/afdEndpoints/routes@2023-05-01' = {
  parent: fdEndpoint
  name: 'api-route'
  properties: {
    originGroup: {
      id: originGroup.id
    }
    supportedProtocols: ['Https']
    patternsToMatch: ['/*']
    forwardingProtocol: 'HttpsOnly'
    linkToDefaultDomain: 'Enabled'
    httpsRedirect: 'Enabled'
  }
  dependsOn: [
    origin0
  ]
}

// Associate WAF with Endpoint
resource wafAssociation 'Microsoft.Cdn/profiles/securityPolicies@2023-05-01' = {
  parent: frontDoor
  name: 'waf-policy'
  properties: {
    parameters: {
      type: 'WebApplicationFirewall'
      wafPolicy: {
        id: wafPolicy.id
      }
      associations: [
        {
          domains: [
            {
              id: fdEndpoint.id
            }
          ]
          patternsToMatch: ['/*']
        }
      ]
    }
  }
}

// Output the Front Door URL
output frontDoorUrl string = 'https://${fdEndpoint.properties.hostName}'

5.诊断设置和监测

启用全面日志记录:

// Diagnostic Settings for Container Apps (add to resources.bicep)
resource diagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = [for i in range(0, containerAppInstances): {
  name: 'diag-containerapp-${i}'
  scope: containerApp[i]
  properties: {
    workspaceId: logAnalytics.id
    logs: [
      {
        category: 'ContainerAppConsoleLogs'
        enabled: true
        retentionPolicy: {
          enabled: true
          days: 30
        }
      }
      {
        category: 'ContainerAppSystemLogs'
        enabled: true
        retentionPolicy: {
          enabled: true
          days: 30
        }
      }
    ]
    metrics: [
      {
        category: 'AllMetrics'
        enabled: true
        retentionPolicy: {
          enabled: true
          days: 30
        }
      }
    ]
  }
}]

// Alert Rules
resource cpuAlert 'Microsoft.Insights/metricAlerts@2018-03-01' = {
  name: 'alert-high-cpu'
  location: 'global'
  tags: tags
  properties: {
    description: 'Alert when CPU usage exceeds 80%'
    severity: 2
    enabled: true
    scopes: [
      containerApp[0].id
      containerApp[1].id
      containerApp[2].id
    ]
    evaluationFrequency: 'PT5M'
    windowSize: 'PT15M'
    criteria: {
      'odata.type': 'Microsoft.Azure.Monitor.SingleResourceMultipleMetricCriteria'
      allOf: [
        {
          name: 'HighCPU'
          metricName: 'UsageNanoCores'
          operator: 'GreaterThan'
          threshold: 80
          timeAggregation: 'Average'
        }
      ]
    }
    actions: []  // Add action groups here
  }
}

生产部署检查表

安全:

  • []启用了ExpressRoute集成
  • []为存储、密钥库、ACR配置了专用端点
  • []后端服务上的公共网络访问已禁用
  • []已配置并测试NSG规则
  • []已部署Azure前门或应用程序网关
  • []在预防模式下启用WAF
  • []使用管理身份(无客户端机密)
  • []审查并最小化RBAC权限
  • []强制执行TLS 1.2最低版本

监控:

  • []所有资源上都启用了诊断设置
  • []已配置日志分析工作区
  • []已连接应用程序洞察
  • []为关键指标配置了警报规则
  • []为通知创建的操作组
  • []为仪表板创建的工作簿

高可用性:

  • []多个容器应用程序实例(至少3个)
  • []已配置健康探测器
  • []会话关联已启用(如果有状态)
  • []已配置自动缩放规则
  • []跨区域部署(可选)

顺从:

  • []已启用静态数据加密
  • []强制执行传输中的数据加密
  • []已启用审核日志记录
  • []备份和灾难恢复计划
  • []已安排访问审查
  • []已应用合规标签

成本优化技巧

默认部署适用于生产环境,但可以进行优化:

  1. 容器应用程序扩展

    scale: {
      minReplicas: 1  // Lower for dev, 2-3 for prod
      maxReplicas: 10
      rules: [
        {
          name: 'http-rule'
          http: {
            metadata: {
              concurrentRequests: '100'
            }
          }
        }
      ]
    }
    
  2. API管理SKU

    • 开发者:每月50美元-仅限非生产
    • 基础:150美元/月-轻生产工作量
    • 标准:700美元/月-生产工作量
    • 高级:3000美元/月-企业,多地区
  3. 集装箱登记处

    • 基础:$5/月-小型项目
    • 标准:20美元/月-团队项目
    • 高级:每月50美元-地理复制,高吞吐量
  4. 存储帐户

    • 使用生命周期策略存档旧日志
    • 启用软删除,缩短保留时间

许可证

麻省理工学院