End-to-end 串接整個流程往往是最具挑戰性的部分。本文記錄了在串接 Azure Bot 過程中容易忽略的細節和文件中的關鍵點,避免造成踩雷或撞牆的點。當然也可以通過深入研讀官方文件,了解如何統一管理整個組織的應用程式,並確保資訊安全性,同時確保 App 具有適當的權限分配(確時是要花時間理解並實際作業)。
前置準備作業
花時間看官方文檔以達事半功倍
雖然官方文檔寫的像百科全書,不過先啃一次,比較知道有一些屬於 Azure Platform 的要求與限制!文檔寫的文向出發點會偏 Platform Engineering 的大戰略也可以學到更多 Azure 在設計的毛。
- 📙 Provision and publish a Bot
- 先選擇你想要的 Chatbot solution,此次選擇偏好程式碼設計的 Bot Framework SDK 作說明
- 使用 🐍Python 的 Developer 應知此資訊 — Python 需選擇
MultiTenant
至於身份的認證可以在config.py
以環境變數注入來取得
- 應用程式註冊權限限制: 在許多組織中,普通用戶沒有權限在 Azure AD中創建應用程式註冊,這是保留給予租戶管理員的特權。因此,對於開發人員來說,在創建機器人時需要指定預定義的應用程式 ID 和密碼要與管理員合作,以獲取所需的應用程式 ID 和密碼,若是個人戶使用就到 Microsoft Entra ID 搞定權限吧!
開發工具推坑
- Visual Studio Code:Azure Account + Azure App Service
- 模擬器下載
輸入 Bot 的 URL,通常為 http://localhost:3978/api/messages,若要然載Bot Framework V4 Emulator 可至 GitHub releases page.
範例程式碼及 Local 端測試準備
- 微軟提供的 Bot open source code 🐱 microsoft / BotBuilder-Samples
先準備好要建立 Bot 的目錄並使用虛擬環境來處理相依性套件,會讓你的環境比較乾淨!
# 建立虛擬環境(Python)
python3 -m venv venv
source venv/bin/activate # 啟用虛擬環境
# 下載範本,指向 Microsoft 的 BotBuilder-Samples 儲存庫中的一個 ZIP 檔案
# 而 "cookiecutter" 這個詞通常是指一種模板化的工具
# 可以根據預定義的模板來生成專案的骨架或樣板
# 在這個情況下,它可能指的是該 ZIP 檔案中包含的一個 BotBuilder-Samples 專案的模板
cookiecutter https://github.com/microsoft/BotBuilder-Samples/releases/download/Templates/echo.zip
# 安裝相依性(Python)
pip install -r requirements.txt
# 啟動 Bot(Python)
python app.py
介紹此次使用到的 Azure 資源
- Bot Service:用於建置和部署聊天機器人的託管服務,託管帶來方便並直接提供整套工具和功能,簡化聊天機器人的開發、部署和管理。並支援多種平台和管道,包括微軟的聊天平台(如 Microsoft Teams、Skype、微信等)以及Web、行動應用程式等。開發者更可以透過 Bot Builder SDK、Azure Bot Framework 和 AI 功能等,快速建立聊天機器人
- Web App:Azure 提供的一種託管式雲端服務(PaaS),用於託管和運行 Web 應用程式,底層是使用 Docker Container 作為基礎架構,也提供了與其他 Azure 服務的整合,如 Azure SQL 資料庫、Azure Cosmos DB、Azure Functions 等,以及用於 CI/CD、監控等
本次架構示意圖如下(先忽略後端的 Storage Accounts 們)
示意關係,以下的 Miscrosoft 相關的 App ID ClientID, Secret, 都已經移除了
建立資源群組及 Bot Service
👣Step01. Create a resource group
- 建立一個全新的
resource group
- 因為是實驗性質,之後要砍資源的時候會很方便
👣Step02. 向 Azure 註冊機器人 建立與設定 Azure Bot
- 在 EchoBotDemo 的
resource group
中建立Azure Bot
- 於
Marketplace
搜尋框輸入Azure Bot
然後開始設定 Bot 的資訊
Azure Bot Build 結果
👣Step04. 取得 Bot Type + App ID(ClientID)+ Client Secret
- 回到 Bot 的 menu,並點選 Configuration 有一個應用程式 ID 為
Miscrosoft App ID
(a.k.a ClientID)> 找地方 📝 起來 - Bot Type 為
MultiTenant
> 找地方 📝 起來
- 選左側的
Configuration
> 再點選Manage Password
會被跳轉到 App Registration 的頁面,這邊先刪除掉既有的再次建立一個Client Secret
🚧 排障點 — 驗證機器人的應用程式 ID 和密碼
先確認 Bot 配發的 ClietnID 與 Secret 是有效的🐞🐛,建議花時間確認權限是否正常,否則也不用再往下執行了
- 若要驗證機器人的 ClietnID 和密碼是否有效,請使用cURL發出以下請求,並將
APP_ID
和替換APP_PASSWORD
為您的機器人的 ClietnID 和密碼
建立與設定 Azure App Service
👣Step05. Create App Service
- 先至 resource group 下,開始建立一個 Web App
可以查詢 resource group 項下的資源
📙 值得一提的是,在 Azure App 背後都會有 Service Plan 是 Azure App Service的資源模型,它定義了應用程式運行的基礎架構,例如根據負載和資源需求,在多個虛擬機器實例之間自動進行擴展和縮減。
👣Step06. Find the Web App Default domain
- 到 Web App 把
Default domain
找地方 📝 起來
👣Step07. 設定 Web App Configuration
這邊的用途是將相關的環境變數放至在 Web App 上,來讓之後 Bot.py 的程式碼可以引入相關 ClientID, Password 等環境變數
- 點選左側的
Configuration
> 再點選+ New application setting
需建立以下幾個 Application setting
MicrosoftAppId
: Step04MicrosoftAppPassword
: Step04MicrosoftAppTenant
: 官方文件有提到 Python Develpoer 這邊留空即可MicrosoftAppType
:MultiTenant
WEBSITES_PORT
:8000
對應一下官方文件
👣Step08. 設定 Azure Bot 與取得 Webhook URL
設定 Azure Bot 的 Messaging endpoint
- 於 Azure Bot 中設定 Messaging endpoint 為
https://Default-domain/api/messages
- 將
Default-domain
改為建立與設定 Azure App Service
段落中的Step6
所取得的Default domain
https://echobotdemo-multitenant.azurewebsites.net/api/messages
👣Step09. 引入官網 Echo Bot demo
- 調整
app.py
主要是調整init_func()
函數會傳回已建立的APP
物件,以便在應用程式啟動時使用。透過將應用程式程式碼封裝在init_func()
函數,組織和管理應用程式的初始化操作,提高程式碼的可執行性和可維護性,使應用程式的啟動過程更加清晰。 - MS Bot SDK Framework 使用 AIOHTTP ,該套件不僅提供與
requests
套件相同的功能,更支援 asyncio,可以在不使用 multiprocessing 或 threading 模組,就能夠達到相當高的執行效率,因為只是示例,建議可以使用 Django 或 FastAPI 等框架又更完整,來開發 server 端應用程式 - 🐍MS Bot SDK Framework 程式碼結構
app.py
import sys
import traceback
from datetime import datetime
from http import HTTPStatus
from aiohttp import web
from aiohttp.web import Request, Response, json_response
from botbuilder.core import (
BotFrameworkAdapterSettings,
TurnContext,
BotFrameworkAdapter,
)
from botbuilder.core.integration import aiohttp_error_middleware
from botbuilder.schema import Activity, ActivityTypes
from bots import EchoBot
from config import DefaultConfig
CONFIG = DefaultConfig()
# Create adapter.
# See https://aka.ms/about-bot-adapter to learn more about how bots work.
SETTINGS = BotFrameworkAdapterSettings(CONFIG.APP_ID, CONFIG.APP_PASSWORD)
ADAPTER = BotFrameworkAdapter(SETTINGS)
# Catch-all for errors.
async def on_error(context: TurnContext, error: Exception):
# This check writes out errors to console log .vs. app insights.
# NOTE: In production environment, you should consider logging this to Azure
# application insights.
print(f"\n [on_turn_error] unhandled error: {error}", file=sys.stderr)
traceback.print_exc()
# Send a message to the user
await context.send_activity("The bot encountered an error or bug.")
await context.send_activity(
"To continue to run this bot, please fix the bot source code.")
# Send a trace activity if we're talking to the Bot Framework Emulator
if context.activity.channel_id == "emulator":
# Create a trace activity that contains the error object
trace_activity = Activity(
label="TurnError",
name="on_turn_error Trace",
timestamp=datetime.utcnow(),
type=ActivityTypes.trace,
value=f"{error}",
value_type="https://www.botframework.com/schemas/error",
)
# Send a trace activity, which will be displayed in Bot Framework Emulator
await context.send_activity(trace_activity)
ADAPTER.on_turn_error = on_error
# Create the Bot
BOT = EchoBot()
# Listen for incoming requests on /api/messages
async def messages(req: Request) -> Response:
# Main bot message handler.
if "application/json" in req.headers["Content-Type"]:
body = await req.json()
else:
return Response(status=HTTPStatus.UNSUPPORTED_MEDIA_TYPE)
activity = Activity().deserialize(body)
auth_header = req.headers["Authorization"] if "Authorization" in req.headers else ""
response = await ADAPTER.process_activity(activity, auth_header, BOT.on_turn)
if response:
return json_response(data=response.body, status=response.status)
return Response(status=HTTPStatus.OK)
# 這邊開始調整如下
def init_func(argv):
APP = web.Application(middlewares=[aiohttp_error_middleware])
APP.router.add_post("/api/messages", messages)
return APP
if __name__ == "__main__":
APP = init_func(None)
try:
web.run_app(APP, host="localhost", port=CONFIG.PORT)
except Exception as error:
raise error
echo_bot.py
它只會將收到的訊息原封不動地回傳給使用者,並在有新成員加入對話時發送歡迎訊息。
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License.
from botbuilder.core import ActivityHandler, MessageFactory, TurnContext
from botbuilder.schema import ChannelAccount
class EchoBot(ActivityHandler):
async def on_members_added_activity(
self, members_added: [ChannelAccount], turn_context: TurnContext
):
for member in members_added:
if member.id != turn_context.activity.recipient.id:
await turn_context.send_activity("Hello and welcome!")
async def on_message_activity(self, turn_context: TurnContext):
return await turn_context.send_activity(
MessageFactory.text(f"Echo: {turn_context.activity.text}")
)
Bot Framework Service 是 Azure AI Bot Service 的元件,會在使用者 Bot 連線的應用程式與 Bot 之間傳送資訊。 每個通道都可以在他們傳送的活動中包含其他資訊。
👣Step10. Web App 新增 startup script
- 因之後會把
app.py
佈署在 Web App 上方
python3 -m aiohttp.web
:使用 Python 3 執行aiohttp.web
模組,這是aiohttp 框架中用於啟動 Web 伺服器的模組-H 0.0.0.0
:設定 Web 伺服器監聽的IP位址為0.0.0.0
,這表示允許從所有網路介面接受確定的請求-P 8000
:設定 Web 伺服器監聽的連接埠為8000
app:init_func
:指定要執行的初始化函數,即init_func
🚧 排障點
- 這邊有過發生異常+1,後來藉助 Web App
Diagnose and solve problem
因為是在 Docker 容器中啟動您的應用程序,該容器會監聽來自主機的請求,並使用指定的初始化函數來處理和回應這些請求,看 docker run 的 port 號是 8000 因此都先調整成 8000 port
各個參數的說明:
-d
:將 Container 設定為在背景運行--expose=8000
:將容器內部的連接埠 8000 暴露給主機-e WEBSITES_PORT=8000
:指定環境變數WEBSITES_PORT
的值為 8000,表示應用程式將在該連接埠上監聽建立的請求,這個也在 Step07 定好環境變數python3 -m aiohttp.web -H 0.0.0.0 -P 8000 app:init_func
:指定要執行的啟動命令,其中aiohttp.web
表示使用 aiohttp 框架啟動 Web 伺服器,-H 0.0.0.0 -P 8000
指定要監聽的 IP 位址和端口,app:init_func
指定要執行的初始化函數
部署 Bot Framework 到 App Service
👣Step11. Deploy to Web App
- 選擇所建立的 Azure App Service > 選擇好資料夾後
Deploy to Web App
其他佈署選項 — 透過 Azure Cli 方式
亦可將您的 Bot 應用程式檔封裝在 zip 封存中,然後使用 az webapp deployment source config-zip
命令,將 Bot 程式碼部署至您先前建立的 Azure Web App 之中。
# 切換至要佈署的資料夾中,並將程式碼壓縮至 zip file
$ zip -r bot_code.zip app.py bots config.py requirements.txt
adding: app.py (deflated 57%)
adding: bots/ (stored 0%)
adding: bots/__init__.py (deflated 16%)
adding: bots/__pycache__/ (stored 0%)
adding: bots/__pycache__/__init__.cpython-310.pyc (deflated 20%)
adding: bots/__pycache__/echo_bot.cpython-310.pyc (deflated 40%)
adding: bots/echo_bot.py (deflated 55%)
adding: config.py (deflated 37%)
adding: requirements.txt (stored 0%)
# Azure webapp Deploy
az webapp deployment source config-zip --resource-group "<resource-group-name>" \
--name "<name-of-web-app>" \
--src "python-echo-bot.zip"
# Terminal Log
Getting scm site credentials for zip deployment
Starting zip deployment. This operation can take a while to complete ...
Deployment endpoint responded with status code 202
{
"active": true,
"author": "N/A",
"author_email": "N/A",
"build_summary": {
"errors": [],
"warnings": []
},
"complete": true,
"deployer": "Push-Deployer",
"end_time": "2024-03-18T11:25:42.0943268Z",
"id": "xxxx-9f41-4775-9801-xxxxx",
"is_readonly": true,
"is_temp": false,
"last_success_end_time": "2024-03-18T11:25:42.0943268Z",
"log_url": "https://xxxxxxx.scm.azurewebsites.net/api/deployments/xxxxx/log",
"message": "Created via a push deployment",
"progress": "",
"received_time": "2024-03-18T11:24:14.8308504Z",
"site_name": "xxxxxxx",
"start_time": "2024-03-18T11:24:16.6715418Z",
"status": 4,
"status_text": "",
"url": "https://xxxxxx.scm.azurewebsites.net/api/deployments/xxxxxxx"
}
🚧 排障點
- 過程中也是把程式碼打包成鏡像並佈署至 Web App 上,排障上若有問題可以從容器打包有否成功、容器的端口映射到主機的端口,底下的兩個地方是可以查找 log、問 Genie、SSH 進入到容器排障
👣Step12. 測試 Test in Web Chat
🐱🏍環境設定完成
👣Step13. 投放機器人至指定 Channel — LINE
- LINE 開發者設定,透過瀏覽器開啟 https://developers.line.biz/
- Create new Provider
- Create a Messaging API channel
- 取得 Channel Secret > 點選
Basic setting
>Channel Secret
> 找地方 📝 起來
- 取得 Channel Access Token,到
Messaging API
> 到Channel access token
> 點選Issue
> 找地方 📝 起來
👣Step14. 設定 Azure Bot 並取得 Webhook URL
- 加入 LINE 到 Channel 並取得 Webhook URL 並注入 Step13 中的 Channel Secret + Channel Access Token
👣Step15. Line 加入 Bot 後測試
👣Step16. 搭建的 Azure Web App 的 CI/CD 流程
因為不是一次性的作業,為了之後還要再 echo_bot.py
加入元素以便可以提高整個的佈署節奏,
總結
花了好幾個步驟總算上架 Bot 完成,雖然微軟後來倡導可以從 Power Virtual Agents(PVA)上手,讓偏好無程式碼體驗的公民都可以從 Bot Framework SDK 和工具開始建置機器人,不過 Bot 的調教有滿多工作要處理,準備上架時還是依循以下的程序
- 計畫: 在進行任何程式開發之前,先確定 Bot 的目標、功能和使用者需求。檢視 Bot 設計指南以了解最佳實踐,確定 Bot 的需求
- 組建: 開始編寫 Bot 程式碼。您可以使用 Azure AI Bot 服務和 Microsoft Bot Framework 提供的 SDK 來開發 Bot,並搭配 CLI 工具來協助建立、管理和測試 Bot
- 測試: 在發布之前,確保測試 Bot 的正常運作。您可以使用 Bot Framework 模擬器在本機測試 Bot,也可以在網路上測試 Bot
- 發佈:當 Bot 已經準備好在 Web 上使用時,將其部署至 Azure 或自己的 Web 服務。對於有導入 M365 及 Teams 的公司也可以使用 PVA + Power Automate + Azure Bot 來使用。
- 連線: 將 Bot 連接到不同的通道,如 Messenger、Slack、Microsoft Teams。Bot Framework 會處理這些通道傳送和接收訊息大部分工作
- 評估: 使用 Azure 入口網站中收集的數據來評估 Bot 的效能和功能。這包括服務等級、流量、延遲、整合等數據,以及使用者、訊息和頻道數據的交談層級報告
Reference
- 【 NLP 】使用 Bot Framework 與 Azure OpenAI ChatGPT 建立 Line AI 問答機器人 ( JavaScript )
- 使用 Azure Container Apps 和 PostgreSQL 建置和部署 Python Web 應用程式
- Using Python to create Azure Chatbot and connect with Teams
- 教你學會使用 Python AIOHTTP
- 【Debug】How to create an app registration in Azure AD for a bot solution
- Azure Bot throws service unavailable error when testing in web chat
- Registering the Microsoft Teams Bot in Azure