Teams ボット入門 — Bot Framework と Graph API の役割分担
実務入門 #6 で Teams に通知を送る 3 つの経路を扱ったとき、本格的なボットは別のテーマとして先送りしました。今回がそのテーマです。結論を先に書くと、Teams で「ボット」というアイデンティティで対話するには、Graph API だけでは足りず Bot Framework(Azure Bot)が必要です。 この記事では、なぜそうなのか、ボットはどんな構造で動くのか、そして Graph がボットのどこを補うのかを整理します。
なぜ Graph だけではボットにならないのか #
Graph にもチャットメッセージを送る API があるので、「これでボットを作ればいいのでは」と考えるのは自然です。壁が 2 つあります。
- アイデンティティ: Graph で送るメッセージは、トークンの持ち主(ユーザーまたは自動化アカウント)の名前で届きます。ボットの名前とアバターで、人と区別される主体として話すのは、Bot Framework を通す必要があります。
- アプリ権限の制約: チャンネル・チャットに通常のメッセージを送る Graph 呼び出しは、アプリケーション権限では事実上閉じられています(マイグレーション専用権限を除く — #6 で扱ったあの制約です)。無人のサービスが自分の名前で会話する構造そのものが、Graph の設計の外にあります。
だから役割が分かれます。会話(受信、応答、カード、ボタン)は Bot Framework、組織データ(ユーザー照会、ファイル、予定)はボットのバックエンドからの Graph 呼び出し。 2 つの技術は競争ではなく、ひとつのアプリの中で分業します。
ボットの動作構造 — Webhook を受ける Web サービスです #
ボットの実体は神秘的なものではありません。HTTPS エンドポイントをひとつ持つ Web サービスです。
- Azure Bot リソースの登録: ボットの身元(アプリ ID)とメッセージングエンドポイントの URL を登録します。
- メッセージの受信: ユーザーがボットに話しかけると、Bot Framework のコネクタサービスがそのアクティビティ(activity)を JSON で自分のエンドポイントに POST してきます。
- 応答: 自分のサービスが SDK(C#、JavaScript、Python をサポート)で応答アクティビティを返します。
- Teams アプリマニフェスト: ボットを Teams アプリとしてパッケージし、組織のカタログに載せればユーザーがインストールできるようになります。
SDK が便利ですが、コネクタサービスの REST API を直接呼ぶこともできるので、言語に縛られはしません。ローカル開発は、トンネリング(開発用トンネル)でエンドポイントを公開してテストするのが標準の流れです。
プロアクティブメッセージ — ボットが先に話しかける方法 #
通知ボットの核心的な要求は「ユーザーが話しかける前にボットが先に送る」です。ここに Bot Framework の重要なルールがあります。プロアクティブメッセージ(proactive message)を送るには会話参照(conversation reference)が必要です。 会話参照はボットがそのユーザー・チャンネルとのアクティビティから得るアドレス情報で、普通はユーザーが一度はボットとやり取りする(またはボットのインストールイベントを受け取る)ことで確保されます。
すると「インストールもしていないユーザーに初めて送るには?」が残りますが、ここで Graph がボットを補完します。Graph のプロアクティブインストール API(userteamwork/installedApps、アプリ権限)でボットアプリを対象ユーザーにインストールすると、インストールイベント(conversationUpdate)がボットのエンドポイントに届き、そこで会話参照を確保して最初のメッセージを送れます。全社告知型ボットが使う標準パターンです。
運用上の注意は頻度です。短時間にプロアクティブメッセージを送りすぎるとチャンネルのポリシーに引っかかることがあり、Teams 側の送信レート制限もあるので、大量送信はキューで緩やかに流す設計が必要です。
読み取り — メンションなしでチャンネルメッセージを受け取る RSC #
デフォルトでは、チャンネルに追加されたボットは @メンションされたメッセージだけを受け取ります。チャンネルのすべてのメッセージを聞く必要のあるボット(モデレーション、自動タグ付け、AI アシスタント)は、RSC(Resource-Specific Consent、リソース固有の同意)権限をアプリマニフェストに宣言します。ChannelMessage.Read.Group のような権限をチーム所有者がインストール時に同意する方式で、テナント全体ではなくそのチームひとつに限定された同意だというのが名前の意味です。全メッセージの受信は強力な分、何を保存するのかについての社内周知が伴うべきです。
アダプティブカード — 通知から対話へ #
ボットがテキストしか送らないなら、Webhook と変わりません。ボットの本当の力はアダプティブカード(Adaptive Card)です。JSON で宣言するカード UI に入力フィールドとボタンを入れられ、ユーザーがボタンを押すとそのアクションがボットのエンドポイントに返ってきます。「承認/却下ボタン付きの決裁通知」「フォームに記入して提出する申請カード」がこれで作られます。通知にボタンがひとつ付いた瞬間から、Webhook や Graph の送信では処理できずボットが必要になる — それが道具選択の境界線です。
では何から始めるか — 要求レベル別のはしご #
Teams メッセージングの要求を低い段から積み上げるとこうなります。
- チャンネルへの一方向通知だけ: Workflows の受信 Webhook で十分です(実務入門 #6)。
- アクティビティフィード通知: Graph の activity feed notification API でバッジ型の通知を送れます。
- ボットの名前で送る通知、最小限のインタラクション: 通知専用ボット。受信ロジックなしで、プロアクティブメッセージ + カードだけで構成されます。
- 会話・ボタン・フォーム: 本格的な Bot Framework ボット + バックエンドで Graph による組織データ照会。
一段下で足りる要求に上の段を導入するのが、Teams ボットプロジェクトが重くなる最もよくある原因です。はしごのどの段が必要かをまず決めるのが、設計の最初の問いです。
まとめ #
- Graph だけではボットになりません。ボットのアイデンティティと会話は Bot Framework の仕事で、通常メッセージ送信のアプリ権限制約もそのままです。
- ボットの実体は Azure Bot リソースに登録された HTTPS エンドポイントです。アクティビティを JSON で受け取り、SDK で応答します。
- プロアクティブメッセージには会話参照が必要で、未インストールのユーザーには Graph のプロアクティブインストール API でインストールイベントを作って確保します。大量送信はレート制限を前提に設計します。
- メンションなしでチャンネル全体を読むには、RSC 権限をマニフェストに宣言してチーム単位の同意を得ます。
- ボタン・フォームが必要になる瞬間がボット導入の境界線です。その手前の段は Webhook・アクティビティフィード通知で軽く解決します。