跳转至

用 Azure 打造家庭 AI 聊天機器人:從零到上線的實作記錄

OpenClaw 是一個開源的多通道聊天 agent 閘道;整套服務跑在一台 Ubuntu 24.04 Azure VM 上,前面用 Nginx 反向代理,部署用 Bicep,密鑰收進 Azure Key Vault,日誌進 Log Analytics,模型呼叫交給 Azure AI Foundry。基礎設施月費大約 $40–60 USD,再加上 token 用量;訂閱層級預算警報讓帳單持續在視線內。

這不是一篇產品介紹,而是一份實際把 OpenClaw 架到 Azure 上時留下來的工程筆記。


前言

這套系統的起點其實很單純:我想要一個家裡真的會用到的 AI 聊天機器人,不只是能回答問題,還要能長期穩定地跑著,接 Telegram、LINE,後端模型走 Azure AI Foundry,出了問題也要知道去哪裡查。

真正開始動手之後,問題就不再只是「把服務跑起來」而已,而是:怎麼部署才可重複、怎麼把密鑰收好、怎麼不要一不小心把 Dashboard 直接暴露到公網上、怎麼控制 GPT 成本不要一路往上飆。

所以這篇文章會聚焦在幾件最實際的事:

  1. 為什麼我最後選了現在這套 Azure 架構
  2. 怎麼用 Bicep 把整套基礎設施寫成可以重複部署的程式碼
  3. 安全性有哪些地方是一定要先想清楚的
  4. 怎麼把 OpenClaw 接到 Azure AI Foundry 的 GPT 模型上
  5. 上線後怎麼看成本、怎麼避免花費失控

想先掌握整個系列的脈絡,可以從 OpenClaw on Azure 系列總覽 開始。如果你想在這套基礎上繼續擴充,可以再讀 讓 OpenClaw 聽懂語音:整合 MAI-Transcribe-1 語音轉文字完整教學,了解 Azure Speech 與語音訊息轉錄流程;如果你想處理圖片生成與投遞,則可以接著看 讓 OpenClaw 學會畫圖:整合 MAI-Image-2 與 Telegram、LINE、WhatsApp


一、整體架構

先講結論:我最後沒有把所有東西拆成很多雲端服務,而是選擇用一台 Azure Linux VM 承載 OpenClaw,前面放 Nginx 做反向代理,旁邊再接 Key Vault、Log Analytics 和 Azure AI Foundry。對家庭規模的使用來說,這個架構夠簡單,維運成本也低很多。

使用者(Telegram / LINE / WhatsApp)
         │
         ▼ HTTPS :443
  [Azure 公開 IP + NSG]
         │
         ▼
  [Nginx 反向代理 :443]  ← TLS 終端、Let's Encrypt 憑證
         │
         ▼
  [OpenClaw Gateway :18789]  ← 只綁定 loopback,外部完全無法存取
         │
    ┌────┴──────────────────┐
    │                       │
    ▼                       ▼
[Azure Key Vault]    [Azure AI Foundry]
 所有密鑰集中管理      GPT-5.2-chat 模型
         │
         ▼
  [Log Analytics]
   日誌 30 天保留

這樣設計的好處是邊界很清楚。對外只有 443、80、22 幾個必要端口;真正的 OpenClaw Gateway 只留在 VM 內部 loopback;模型能力交給 Azure AI Foundry;機密資料則統一放進 Key Vault。換句話說,流量、密鑰、模型和主機責任是分開的,出問題時也比較容易定位。

關鍵設計原則

原則 做法
Gateway 不對外暴露 綁定 127.0.0.1:18789,NSG 明確 Deny
所有密鑰不落地 存在 Azure Key Vault,程式動態取用
基礎設施即程式碼 所有 Azure 資源 100% 用 Bicep 定義
最小化開放端口 只開 443(HTTPS)、80(ACME)、22(SSH)

二、Bicep IaC 實作

如果只是手動在 Portal 裡點一遍,當下當然也能把環境建起來,但第二次、第三次,或幾個月後要重建時,成本就會高很多。這也是我一開始就決定用 Bicep 的原因:把整套 Azure 資源寫成程式碼,之後每次部署都走同一條路。

我把不同責任切成獨立模組,最後由 main.bicep 統一組裝。

2.1 模組地圖

infra/bicep/
├── main.bicep                  ← 入口點,呼叫所有模組
└── modules/
    ├── vnet.bicep              ← VNet + Subnet
    ├── nsg.bicep               ← NSG 安全規則
    ├── vm-linux.bicep          ← Ubuntu 24.04 VM + 公開 IP + NIC
    ├── keyvault.bicep          ← Key Vault(RBAC 授權)
    ├── loganalytics.bicep      ← Log Analytics(30 天保留)
    ├── openai.bicep            ← Azure AI Foundry + GPT 模型部署
    └── budget.subscription.bicep ← 訂閱層級費用預算警報

2.2 VNet 模組(vnet.bicep

網路層沒有做得太複雜,就是一個 VNet 搭一個專門給 VM 用的子網。對這個案例來說,重點不是炫技,而是先把私有網段劃清楚,讓後續 NSG 和 NIC 掛載都有固定位置可依附。

resource vnet 'Microsoft.Network/virtualNetworks@2023-11-01' = {
  name: '${prefix}-vnet'
  location: location
  properties: {
    addressSpace: {
      addressPrefixes: ['10.20.0.0/16']
    }
    subnets: [
      {
        name: '${prefix}-subnet-vm'
        properties: {
          addressPrefix: '10.20.1.0/24'
        }
      }
    ]
  }
}

2.3 NSG 模組(nsg.bicep

NSG 是整套設計裡很關鍵的一層,因為它決定了外面到底看得到什麼。這裡的原則很簡單:只開真的需要的,其他一律關掉。443 給 webhook,80 給憑證更新,22 留給管理;3389 和 18789 這種不該被外部碰到的,就直接拒絕。

// 允許 HTTPS(Telegram / LINE webhook)
{ name: 'allow-https-443', priority: 100, destinationPortRange: '443', access: 'Allow' }

// 允許 HTTP(Let's Encrypt ACME 驗證)
{ name: 'allow-http-80',   priority: 110, destinationPortRange: '80',  access: 'Allow' }

// 允許 SSH(強烈建議縮限來源 IP,不要用 * 開放全網際網路)
// 正式環境:把 sourceAddressPrefix 改成你的固定 IP,例如 '203.0.113.10/32'
{ name: 'allow-ssh-22',    priority: 120, destinationPortRange: '22',  access: 'Allow', sourceAddressPrefix: '<your-admin-ip>/32' }

// 明確拒絕 RDP
{ name: 'deny-rdp-3389',   priority: 200, destinationPortRange: '3389', access: 'Deny' }

// 明確拒絕 Gateway Dashboard 從外部存取
{ name: 'deny-gateway-18789', priority: 210, destinationPortRange: '18789', access: 'Deny' }

2.4 VM 模組(vm-linux.bicep

運算層我最後選擇 Azure Ubuntu 24.04 VM。原因也很務實:OpenClaw 跑在原生 Linux 上最直接,除錯、SSH、systemd、Nginx 這些都很熟,出了問題比較容易查。機器規格則依使用量在 Standard_B2sStandard_D2s_v5 間選擇,前者省錢,後者比較有餘裕。

resource vm 'Microsoft.Compute/virtualMachines@2023-09-01' = {
  name: '${prefix}-vm'
  location: location
  properties: {
    hardwareProfile: { vmSize: vmSize }
    osProfile: {
      computerName: '${prefix}-vm'
      adminUsername: adminUsername
      // 只用 SSH 金鑰,完全禁用密碼登入
      linuxConfiguration: {
        disablePasswordAuthentication: true
        ssh: {
          publicKeys: [
            {
              path: '/home/${adminUsername}/.ssh/authorized_keys'
              keyData: sshPublicKey
            }
          ]
        }
      }
    }
    storageProfile: {
      imageReference: {
        publisher: 'Canonical'
        offer: '0001-com-ubuntu-server-noble'
        sku: '24_04-lts-gen2'
        version: 'latest'
      }
    }
  }
}

2.5 Key Vault 模組(keyvault.bicep

Key Vault 的角色很單純,但非常重要:所有真正敏感的東西都不應該散落在 VM、repo 或 .env 裡。這裡我選 RBAC 模式而不是舊式 Access Policy,因為權限模型比較一致,也比較符合現在 Azure 的做法。

resource kv 'Microsoft.KeyVault/vaults@2023-07-01' = {
  name: '${prefix}kv${uniqueString(resourceGroup().id)}'
  location: location
  properties: {
    sku: { family: 'A', name: 'standard' }
    tenantId: subscription().tenantId
    enableRbacAuthorization: true   // 用 RBAC,不用 Access Policy
    enableSoftDelete: true
    softDeleteRetentionInDays: 7
    publicNetworkAccess: 'Enabled'
  }
}

2.6 部署指令

當模組都準備好之後,實際部署反而很單純。從 devcontainer 執行下面這條指令,就能把整套環境建起來:

source .env.local
export VM_SSH_PUBLIC_KEY="$(cat ~/.ssh/id_ed25519.pub)"

az deployment group create \
  --resource-group "$AZURE_RESOURCE_GROUP" \
  --template-file infra/bicep/main.bicep \
  --parameters infra/bicep/params/prod.bicepparam \
  --parameters sshPublicKey="$VM_SSH_PUBLIC_KEY"

它背後會依序建立 VNet、NSG、VM、Key Vault、Log Analytics、AI Foundry 和 Budget。也因為全部都是宣告式資源,之後要調整 VM 規格、模型 deployment 或預算門檻,都是回到 Bicep 改完再重跑,而不是進 Portal 手動修。


三、安全性設計

這一段是我最不想妥協的地方。很多 AI side project 一開始都先求「能跑」,安全性等之後再說;但如果一開始就把 Dashboard、SSH、API Key 這些邊界放鬆,後面只會越補越痛苦。

3.1 Gateway 完全不對外暴露

最重要的一條規則是:OpenClaw Gateway 不直接對外。外部請求只能打到 Nginx,Nginx 再轉發到 VM 內部的 loopback 服務。這樣做的好處很直接,外網永遠碰不到真正的 Dashboard port。

[外部網路]  ─── NSG Deny :18789 ──→ ✗ 阻擋
[Nginx]    ─── proxy :18789 ──→ ✔ 只有 localhost 能到 Gateway
[管理員]   ─── SSH Tunnel ──→ ✔ 本機 port-forward 存取 Dashboard

管理員要存取 Dashboard,必須先建立 SSH Tunnel:

ssh -N -L 18789:127.0.0.1:18789 <username>@<your-vm-domain>
# 然後在瀏覽器開 http://127.0.0.1:18789/

3.2 NSG 規則層次

優先權 100 — 允許 :443  (Telegram/LINE webhook)
優先權 110 — 允許 :80   (Let's Encrypt 憑證更新)
優先權 120 — 允許 :22   (SSH,**必須縮限來源 IP**,勿用 * 開放全網)
優先權 200 — 拒絕 :3389 (RDP,徹底關閉)
優先權 210 — 拒絕 :18789 (Gateway Dashboard,loopback only)
優先權 65500 — 拒絕所有其他 (Azure 預設)

⚠️ 安全提醒: SSH(port 22)若來源設為 *,VM 會持續被全球掃描機器人嘗試連線。雖然金鑰認證讓暴力破解無效,但仍會暴露在未來 SSH 漏洞(CVE)的攻擊面下。有固定 IP 的場合,務必把來源縮限到 <your-ip>/32

3.3 所有密鑰存在 Azure Key Vault

密鑰管理也是同樣的思路:不要讓祕密散落。Telegram token、LINE secret、Azure AI Foundry API Key,這些資料如果一份在 repo、一份在 shell history、一份在 VM 上,總有一天會出事。集中進 Key Vault 雖然前期多做一點工,但後面會輕鬆很多。

# 部署後,用腳本把 AI Foundry API Key 存入 Key Vault
uv run oc store-key
# 等同於:
az keyvault secret set \
  --vault-name "$KV_NAME" \
  --name "azure-openai-api-key" \
  --value "$OPENAI_KEY"

3.4 OpenClaw 應用層硬化

除了 Azure 層的網路規則,我也把 OpenClaw 本身收緊。這些設定看起來像小事,但其實都是實際避免誤用或濫用的關鍵。

{
  "gateway": {
    "bind": "loopback"       // Dashboard 只綁 127.0.0.1
  },
  "channels": {
    "telegram": {
      "configWrites": false,  // 禁止透過聊天更改設定
      "dmPolicy": "pairing",  // 新用戶必須先審核
      "groupPolicy": "allowlist",  // 只有白名單群組能使用
      "groupAllowFrom": ["<家庭群組ID-1>", "<家庭群組ID-2>"]
    }
  }
}
設定 作用
configWrites: false 防止有人透過聊天室改掉 bot 的設定
dmPolicy: pairing 陌生人第一則訊息會被靜默丟棄,需管理員審核
groupPolicy: allowlist 只有列出的家庭群組能收到回覆

3.5 SSH 金鑰認證,完全禁用密碼

最後是 SSH。這邊沒有什麼花樣,就是徹底禁用密碼登入,只接受 SSH 金鑰。這不是高級防禦,而是基礎 hygiene;但少了這一步,前面很多保護都會變得沒有意義。


四、成本控制

把服務跑起來不難,讓它每個月帳單還能接受,才是真正的長期題目。這套系統本來就不是高流量產品,所以成本控制的策略不是極致壓榨,而是把幾個最主要的開銷點看住:VM、儲存、日誌,以及 GPT token。

4.1 各資源月費估算(UAE North)

資源 規格 月費估算
Linux VM Standard_B2s(2 vCPU, 4 GB RAM) ~$30–40
OS Disk Premium SSD P4 (32 GB) ~$5
公開 IP Standard SKU,靜態 ~$3
Key Vault Standard,操作量不多 幾乎忽略
Log Analytics 30 天保留,家庭用量 < $1/GB
Azure AI Foundry gpt-5.2-chat,按 token 計費 視用量而定
合計基礎設施 ~$38–48/月

LLM 費用視使用量,家庭輕量使用通常每月幾美元到十幾美元。

4.2 三大省錢策略

① 訂閱層級預算警報(Bicep 自動部署)

// budget.subscription.bicep
resource budget 'Microsoft.Consumption/budgets@2023-11-01' = {
  name: '${prefix}-monthly-budget'
  properties: {
    amount: 150          // 設定上限 $150/月
    timeGrain: 'Monthly'
    notifications: {
      // 80% 時發 Email 警告
      eighty_percent: {
        threshold: 80
        contactEmails: [notificationEmail]
      }
      // 100% 時立即通知
      one_hundred_percent: {
        threshold: 100
        contactEmails: [notificationEmail]
      }
    }
  }
}

② VM 定時自動啟動(Automation Account Runbook)

透過 Automation Account + PowerShell Runbook,每天 08:00 UTC 自動啟動 VM,減少非必要的運行時間:

# runbook-vm-restart.ps1 片段
Connect-AzAccount -Identity
Start-AzVM -ResourceGroupName $ResourceGroup -Name $VMName

③ GPT Token 用量監控

有兩種方式可以追蹤 token 用量:

方法一:透過 OpenClaw CLI(即時查看)

# 在 VM 上查看 LLM 用量
ssh <username>@<your-vm-domain> 'openclaw status --usage'

方法二:Azure AI Foundry Portal(圖表 + 歷史趨勢)

Azure AI Foundry Portal 裡,進入你的 Hub → Metrics 頁面,可以看到:

指標 意義
TokenTransaction 每次請求消耗的 token 數
ProcessedPromptTokens Input tokens(含 system prompt + tools)
GeneratedCompletionTokens Output tokens
TotalTokens 兩者合計,對應帳單金額

也可以在 Azure Portal → Cost Analysis 裡按資源篩選 Cognitive Services,看實際產生的費用,設定時間範圍觀察用量趨勢。

以下是最近一週(Mar 8–14)的實際費用截圖,整週累計 £17.64,遠低於 £150/月的預算上限:

Azure Cost Analysis — Mar 8–14,累計 £17.64,預算 £150/mo

這種圖表很好用,因為它不是抽象的「應該很省」,而是真的能看到成本曲線怎麼走。圖中可以看到費用在後段增長放緩,這和 VM 的排程啟停、整體使用量維持在家庭場景有直接關係。

如果要更細地看模型實際吃掉多少 token,Azure AI Foundry 的 Resource usage 頁面也很有幫助。下面這張圖是最近 7 天 gpt-5dot2-chat deployment 的使用情況:

Azure AI Foundry Resource Usage — gpt-5dot2-chat, last 7 days

從這張圖可以直接讀到幾個重點:7 天內總共處理了 453 次 requests,總 token 數約 12.52M,其中 12.46M 幾乎都來自 prompt tokens,completion tokens 只有 58.71K。這代表目前這套 OpenClaw 工作負載的成本主要不是出在模型回覆太長,而是 system prompt、tool schema、上下文和多工具呼叫本身就相對重。

另一個值得注意的數字是平均每次 request 約 27,640 tokens,而平均 completion 只有 130 tokens。這很符合 agent 型工作流的特徵:前置上下文很厚,真正模型吐出的最終文字反而不長。對成本優化來說,後續如果要再省,通常優先檢查的會是 prompt 結構、tool 數量和上下文裁剪,而不是先去縮短最終回答。

每個帶 21 個 tool 的請求約消耗 11–13K input tokens。Token 計費在量少時很便宜(家庭用途通常每月 $3–15 USD)。

如果想把每一次模型呼叫對應回觸發它的 agent 步驟,可以參考 為 AI Foundry 呼叫加上 OpenTelemetry 追蹤


五、串接 Azure AI Foundry GPT 模型

模型整合若不穩,整個系統就只是個轉發器。這一段就是怎麼把 OpenClaw 接上 Azure AI Foundry 的 GPT 模型。

5.1 用 Bicep 部署 AI Foundry 資源

這裡我沒有走舊式 kind: OpenAI 資源,而是直接用 kind: AIServices。這樣比較符合現在 Azure AI Foundry 的路徑,也比較容易和 Hub / Project 那套管理方式接起來。Azure AI Foundry 是現在 Azure 上統一管理 OpenAI 等模型部署的平台;舊的 Azure OpenAI Service(kind: OpenAI)正在被收攏進 Foundry,新環境應該直接走 Foundry 路徑。

openai.bicep 主要建立三類資源:

// 1. AIServices 帳號(不是舊的 kind: OpenAI)
resource openai 'Microsoft.CognitiveServices/accounts@2024-10-01' = {
  name: '${prefix}-foundry-${unique}'
  location: location
  kind: 'AIServices'           // 關鍵!要用 AIServices,不是 OpenAI
  sku: { name: 'S0' }
  properties: {
    customSubDomainName: '${prefix}-foundry-${unique}'
    publicNetworkAccess: 'Enabled'
    disableLocalAuth: false    // 允許 API Key 認證
  }
}

// 2. 模型部署(gpt-5.2-chat)
resource deployments 'Microsoft.CognitiveServices/accounts/deployments@2024-10-01' = [
  for deployment in modelDeployments: {
    parent: openai
    name: deployment.name      // 例如 'gpt-5dot2-chat'
    sku: {
      name: 'GlobalStandard'   // GlobalStandard = 全球 routing,較便宜
      capacity: 10             // 100K TPM 容量
    }
    properties: {
      model: {
        format: 'OpenAI'
        name: 'gpt-5.2-chat'
        version: '2026-02-01'
      }
    }
  }
]

// 3. AI Foundry Hub(讓 Azure Portal 有追蹤 UI)
resource hub 'Microsoft.MachineLearningServices/workspaces@2024-10-01' = {
  name: '${prefix}-hub-${unique}'
  kind: 'Hub'
  identity: { type: 'SystemAssigned' }
  properties: {
    storageAccount: storage.id
    publicNetworkAccess: 'Enabled'
  }
}

5.2 部署流程

模型部署上,我沒有只放一個主模型,而是同時準備了 primary 和 failover。主力是 gpt-5.2-chat,備援是 gpt-5.3-chat。這個設計不是為了炫耀多模型,而是很實際地降低單一 deployment 抽風時整個服務停掉的機率。

// prod.bicepparam — 同時部署 primary 與 failover 模型
param openaiLocation = 'swedencentral'
param openaiModelDeployments = [
  {
    name: 'gpt-5dot2-chat'
    modelName: 'gpt-5.2-chat'
    modelVersion: '2026-02-10'
    skuName: 'GlobalStandard'
    skuCapacity: 100           // Primary:100K TPM
  }
  {
    name: 'gpt-5.3-chat'
    modelName: 'gpt-5.3-chat'
    modelVersion: '2026-03-03'
    skuName: 'GlobalStandard'
    skuCapacity: 150           // Failover:150K TPM,容量更大
  }
]

部署完成後,Azure AI Foundry 端會同時看到兩個 deployment,都已經可以被同一組 provider 設定引用:

az deployment group create \
  --resource-group "$AZURE_RESOURCE_GROUP" \
  --template-file infra/bicep/main.bicep \
  --parameters infra/bicep/params/prod.bicepparam

5.3 把 API Key 存入 Key Vault

資源建立好之後,下一步不是立刻去改 OpenClaw 設定,而是先把 API Key 正規地收進 Key Vault。這樣後續不管是輪替、查詢,還是重新配置 VM,都比較有秩序。

# 用腳本自動取得 API Key 並存進 Key Vault
uv run oc store-key

# 等同於以下 az CLI 操作:
OPENAI_KEY=$(az cognitiveservices account keys list \
  -g "$AZURE_RESOURCE_GROUP" -n "$OPENAI_ACCOUNT_NAME" \
  --query key1 -o tsv)

az keyvault secret set \
  --vault-name "$KV_NAME" \
  --name "azure-openai-api-key" \
  --value "$OPENAI_KEY"

5.4 在 OpenClaw 設定 Provider

真正容易踩坑的地方在這裡。Azure AI Foundry 雖然提供的是 OpenAI 相容介面,但認證細節和一般 OpenAI API 並不完全一樣。如果照很多網路範例直接用 Bearer token,最後多半會卡住。

{
  "models": {
    "providers": {
      "azure-openai-responses": {
        "api": "openai-responses",
        "baseURL": "https://<資源名稱>.openai.azure.com/openai/v1",
        "apiKey": "<API_KEY>",
        "authHeader": false,         // 關鍵!停用預設 Bearer token
        "headers": {
          "api-key": "<API_KEY>"     // Azure 用自己的 header
        },
        "compat": {
          "supportsStore": false     // Azure 不支援 store 參數
        }
      }
    }
  },
  "agents": {
    "defaults": {
      "models": {
        "primary": "azure-openai-responses/gpt-5.2-chat",
        "failover": [
          "azure-openai-responses/gpt-5.3-chat"  // 自動 failover 到 gpt-5.3-chat
        ],
        "streaming": false           // Azure Responses API 有 bug,關掉 streaming
      }
    }
  }
}

Failover 的運作邏輯也很直白:當 gpt-5.2-chat 回傳錯誤,例如 429 超過 TPM 限制,或 5xx 代表服務短暫異常時,OpenClaw 就改試 gpt-5.3-chat。對使用者來說,這件事通常是無感的;對維運者來說,則多了一層緩衝。兩個模型同屬同一個 AI Foundry 帳號,共用同一個 endpoint 和 API Key,不需要額外維護第二套認證。

重要: authHeader: false + headers.api-key 這個組合是必要的。 Azure AI Foundry 不接受 Authorization: Bearer <key> 這種格式, 必須改用 api-key: <key> header。API Key 要同時出現在 apiKey 欄位和 headers.api-key

5.5 用腳本自動設定 VM

這一步我也盡量避免手工操作。直接 SSH 進 VM 改 JSON 當然可以,但這種流程最容易在下次重建時忘記。比較穩的做法,是把它收斂成固定腳本:

uv run oc configure-openai \
  "$VM_IP" \
  "$OPENAI_ENDPOINT" \
  "$OPENAI_KEY" \
  "gpt-5.2-chat"

腳本會: 1. 備份目前的 openclaw.json 2. 透過 SSH 將 provider 設定 merge 進去 3. 重啟 Gateway 讓設定生效

5.6 已知問題:Streaming 必須關掉

這次整合過程裡,最煩的一個坑是 Azure OpenAI Responses API 的 streaming 問題。當 stream: true,而請求裡又帶有 function_call_output items 時,API 會報錯;改成 stream: false 就正常。

設定方式:

{
  "agents": {
    "defaults": {
      "models": {
        "streaming": false
      }
    }
  }
}

所以我最後的選擇很保守:先關掉 streaming,優先換取穩定性。代價只是使用者不會看到逐字輸出的效果,但整體功能和正確性不受影響。


六、完整架構圖

一句話總結:外部流量先進 Nginx,內部服務留在 loopback,祕密資料交給 Key Vault,模型能力交給 Azure AI Foundry。

graph TB
  Telegram[Telegram]
  LINE[LINE]
  WhatsApp[WhatsApp]
  Admin[Admin Devcontainer]

  subgraph SUB[Azure Subscription]
    Budget[Budget 150 per month]

    subgraph RG[Resource Group UAE North]
      NSG[NSG Allow 443 80 22 Deny 3389 18789]
      PIP[Public IP Static]
      subgraph VM[Ubuntu VM Standard B2s]
        Nginx[Nginx 443 TLS]
        Gateway[OpenClaw Gateway 18789 loopback only]
      end
      KV[Key Vault RBAC]
      LA[Log Analytics 30 days]
    end

    subgraph FOUNDRY[Azure AI Foundry Sweden Central]
      GPT[gpt 5.2 chat GlobalStandard 10K TPM]
    end
  end

  Telegram -->|HTTPS 443| PIP
  LINE -->|HTTPS 443| PIP
    PIP --> NSG --> VM
    Nginx --> Gateway
  WhatsApp -.->|Baileys Protocol| Gateway
  Gateway -->|Secrets| KV
  Gateway -->|Logs| LA
  Gateway -->|API Key| GPT
  Admin -->|Bicep deploy| RG
  Admin -.->|SSH Tunnel 22| VM

七、部署驗證

部署完成後,我不會只看 Azure Portal 顯示成功就收工,還會跑一次 smoke test,確認 Gateway、channel 和 reminder plugin 都真的活著。

uv run oc smoke-test <your-vm-domain>

預期輸出:

=== 1/3  Gateway /health ===
{"ok":true,"status":"live"}
gateway: OK

=== 2/3  Channel probes ===
channels OK: telegram, line

=== 3/3  Reminder plugin ===
reminder plugin: loaded

smoke-test: PASS

那一行 reminder plugin: loaded 背後的實作細節,可以參考 Reminder plugin 的設計細節


總結

面向 做法
基礎設施 100% Bicep IaC,一條指令部署所有資源
安全性 Gateway loopback-only、Key Vault 存密鑰、NSG 最小開放、SSH key-only
成本 Budget 警報 + VM 定時排程 + Token 用量監控
AI 模型 Azure AI Foundry (AIServices) + azure-openai-responses provider,注意 authHeader: false 和關掉 streaming

回頭看,這套架構其實沒有什麼特別花俏的地方。它比較像是一套刻意收斂過的選擇:不用過度複雜的雲端拓撲、不把安全性留到最後、不把模型設定藏在手工操作裡。Bicep 負責讓部署可重複,Key Vault 和 NSG 把邊界守住,Azure AI Foundry 提供模型能力,Budget 和監控把成本壓在看得見的範圍內。

如果使用場景和我差不多,想做的是一套家庭規模、可長期運行、能自己掌握部署與成本的 AI 機器人,那這套做法是很務實的一條路。家庭用途下,整體基礎設施大致落在每月 $40–60 USD,再加上依使用量變動的 GPT 費用,通常是可以接受的範圍。


參考資料