Copilot Studioで会議室予約エージェントを作ってみた

この記事はMTI Blog Summer 2025の8月7日分の記事です。こんにちは、スマートコンテンツ事業部に所属している友末です。

※この記事の画像や文章は一部生成AIを使用して作成しています。

はじめに

弊社ではフルリモートで仕事をしているのですが、たまに出社した際、会議室を予約するのが面倒だなと感じることがあります。
会議室の予約は会議室を出席者に含めた会議を作成すると、自動で予約できる仕組みになっていますが、会議室の空き状況を確認するのが手間となっています。
今回はCopilot Studioを用いて、会議室の空き確認から予約まで一連の作業を自動化するエージェントを作成してみました。

目次

  1. 作成するエージェント全体フロー
  2. エージェントの設定
  3. ツール1: 予定取得
  4. ツール2: 会議室検索
  5. ツール3: 会議室予約
  6. まとめ

作成するエージェント全体フロー

エージェントの設定

基本設定

今回作成するエージェントは生成オーケストレーションを使用して構築します。 エージェントの作成方法については、私は以下の記事を参考にしました。

参考記事:【Copilot studio】- 生成オーケストレーションモードのエージェントを開発する

エージェントへの指示

エージェントの動作を制御するため、以下の指示を設定しました。 Outlook APIのレスポンスがUTCで返されるため、JSTへの変換指示も含めています。

・ユーザー本人の予定一覧を取得し、分かりやすく提示する。UTCではなくJSTに変換してください(+9時間する)
・指定された日時や会議に対して空いている会議室を検索し、候補を提示する
・ユーザーの希望に応じて会議室の予約手続きを案内・実行する
・常に丁寧かつ迅速に対応し、ユーザーの会議予約体験をサポートする

ツール1: 予定取得

今日の自分の予約一覧を取得するためのツールを作成します。 Copilot Studioで事前に準備されている、Office 365 Outlookの「イベントのカレンダー ビューの取得(V3)」ツールを使用します。

設定項目

  • ツール名: 「自分の予定取得」
  • 説明: 「指定した日時の自分のカレンダーの予定を取得し、会議室予約の際に時間の重複がないかチェックするためのツール」

💡 設定のポイント: ツール名と説明はAIエージェントがツールの使用時に読み込むため、詳細にしましょう。

入力パラメータ

パラメータ 設定方法 設定値・説明
Calendar Id カスタム値 Calendar(自分のカレンダーID)
Start Time カスタム値 Text(DateAdd(Today(), -9, TimeUnit.Hours),"yyyy-mm-ddThh:mm:ss")
End Time カスタム値 Text(DateAdd(DateAdd(Today(), 1, TimeUnit.Days),-9, TimeUnit.Hours),"yyyy-mm-ddThh:mm:ss")

💡 パラメータ設定のポイント: 今回は「カスタム値」で設定していますが、柔軟なツールにしたい場合や他の人にエージェントを共有したい場合は「AIで動的に入力する」にするとよいです。 現在の設定では作成者のCalendar Idが固定で入力されるため、他の人は使えません。

動作確認

Copilot Studioでテストを実行すると、今日の予定一覧が取得できることを確認できます。

ツール2: 会議室検索

空いている会議室を検索するためのツールを作成します。 複雑な要件となるので独自のエージェント フローを新たに構築し、ツールとして登録します。

エージェント フローはツールの追加から作成できます。

設定項目

  • ツール名: 「会議室検索」
  • 説明: 「指定した日時に空いている会議室を検索し、各会議室の状態を詳細に返すツール」

入力パラメータ

パラメータ 設定方法 設定値・説明
会議の開始時間 AIで動的に入力 「2025-08-05T10:00」のような形で入力
会議時間(分) AIで動的に入力 会議時間を分単位で入力(例:1時間の場合は「60」)

💡 パラメータ設定のポイント: AIで動的に入力する場合、「カスタマイズ > 説明」から入力値の説明を設定できます。この値も設定することでエージェントがツールを使いやすくなります。

実装詳細

作成するフローの全体図

入力の受け取り

フローの開始時に、エージェントからの入力を受け取ります。

  • エージェントがフローを呼び出したときトリガーを使用
  • 以下の入力パラメータを設定
    • 会議の開始時間: String型
    • 会議時間(分): Number型

会議時間の計算

会議の開始時間と会議時間から会議の終了時間を計算します。

会議の終了時間を入力に含めないことにより、このツールを単体で使いやすくします。 (エージェントが会議の終了時間をユーザーに求めないようにするため)

アクション: 作成(Compose)
入力: `formatDateTime(addToTime("エージェントから入力された会議の開始時間", "エージェントから入力された会議時間(分)", 'Minute'), 'yyyy-MM-ddTHH:mm')`

会議室状態の取得

会議室の状態を取得するのにGraph APIを利用します。 会議室の状態取得には、「GetSchedule」Graph APIを使用します。

アクション: HTTP要求を送信します(Office 365 Outlook)
URI: https://graph.microsoft.com/v1.0/me/calendar/getSchedule
Method: POST
Headers: 
  Content-Type: application/json
  Prefer: outlook.timezone="Tokyo Standard Time"
Body:
{
  "schedules": ["room-A@company.com", "room-B@company.com"],
  "startTime": {
    "dateTime": "エージェントから入力された会議の開始時間",
    "timeZone": "Tokyo Standard Time"
  },
  "endTime": {
    "dateTime": "計算された会議の終了時間", 
    "timeZone": "Tokyo Standard Time"
  },
  "availabilityViewInterval": "エージェントから入力された会議時間(分)"
}

主要パラメータの説明: - schedules: 会議室のメールアドレスを指定します。 - availabilityViewInterval: 会議時間を分単位で指定します。これを指定しないと30分未満の会議は取得できません

補足: Graph APIをテストするにはGraph Explorerを利用すると簡単にテストできます。

データの初期化

APIのレスポンス処理に必要な変数を初期化します。

空き会議室詳細

アクション: 変数を初期化する(Initialize variable)
種類: Array
値: []

使用中会議室詳細

アクション: 変数を初期化する(Initialize variable)
種類: Array
値: []

空き会議室一覧

アクション: 変数を初期化する(Initialize variable)
種類: Array
値: []

会議室ごとの状態判定

取得した各会議室の状態を確認し、空いているか使用中かを判定します:

アクション: それぞれに適用する(Apply to each)
選択する出力: triggerBody()['body']['value']

条件(Condition)アクション

条件: length(item()['scheduleItems'])
演算子: is equal to
値: 0

空いている会議室の場合

空き会議室詳細に追加:

アクション: 変数の値を増やす(Append to array variable)
名前: 空き会議室詳細
値: 
{
  "room_name": "@{replace(replace(item()['scheduleId'], '@company.com', ''), 'room', '会議室-')}",
  "room_id": "@{item()['scheduleId']}",
  "status": "利用可能",
  "status_en": "available",
  "description": "現在空いており、すぐに利用できます",
  "next_booking": null
}

空き会議室一覧に追加:

アクション: 変数の値を増やす(Append to array variable)
名前: 空き会議室一覧
値: "@{replace(replace(item()['scheduleId'], '@company.com', ''), 'room', '会議室-')}"

使用中の会議室の場合

使用中会議室詳細に追加:

アクション: 変数の値を増やす(Append to array variable)
名前: 使用中会議室詳細
値:
{
  "room_name": "@{replace(replace(item()['scheduleId'], '@company.com', ''), 'room', '会議室-')}",
  "room_id": "@{item()['scheduleId']}",
  "status": "使用中",
  "status_en": "busy",
  "description": "@{trim(item()['scheduleItems'][0]['subject'])}さんが@{formatDateTime(item()['scheduleItems'][0]['start']['dateTime'], 'HH:mm')}-@{formatDateTime(item()['scheduleItems'][0]['end']['dateTime'], 'HH:mm')}まで使用中です",
  "current_booking": {
    "user": "@{trim(item()['scheduleItems'][0]['subject'])}",
    "start": "@{formatDateTime(item()['scheduleItems'][0]['start']['dateTime'], 'HH:mm')}",
    "end": "@{formatDateTime(item()['scheduleItems'][0]['end']['dateTime'], 'HH:mm')}",
    "location": "@{item()['scheduleItems'][0]['location']}"
  }
}

レスポンスの構築

収集したデータをエージェントが理解しやすい形式に整理します:

アクション: 作成(Compose)
入力:
{
  "summary": {
    "total_rooms": @{length(triggerBody()['body']['value'])},
    "available_rooms": @{length(variables('空き会議室一覧'))},
    "busy_rooms": @{sub(length(triggerBody()['body']['value']), length(variables('空き会議室一覧')))}
  },
  "room_status": @{union(variables('空き会議室詳細'), variables('使用中会議室詳細'))},
  "available_rooms_list": @{variables('空き会議室一覧')},
  "quick_answer": "@{if(greater(length(variables('空き会議室一覧')), 0), concat('現在、', join(variables('空き会議室一覧'), '、'), 'が利用可能です。'), '現在、すべての会議室が使用中です。')}"
}

エージェントへの応答

構築したレスポンスをエージェントに返します:

アクション: エージェントに応答する(Respond to the agent)
テキスト: "上記で作成した最終レスポンスをここに挿入"

最終的なレスポンス形式(例)

{
  "summary": {
    "total_rooms": 2,
    "available_rooms": 1,
    "busy_rooms": 1
  },
  "room_status": [
    {
      "room_name": "会議室-A",
      "room_id": "room-A@company.com",
      "status": "利用可能",
      "status_en": "available",
      "description": "現在空いており、すぐに利用できます",
      "next_booking": null
    },
    {
      "room_name": "会議室-B",
      "room_id": "room-B@company.com",
      "status": "使用中",
      "status_en": "busy",
      "description": "テスト太郎さんが14:00-15:00まで使用中です",
      "current_booking": {
        "user": "テスト太郎",
        "start": "14:00",
        "end": "15:00",
        "location": "会議室-B"
      }
    }
  ],
  "available_rooms_list": [
    "会議室-A"
  ],
  "quick_answer": "現在、会議室-Aが利用可能です。"
}

動作確認

エージェントフローのテストを実行し、最終的なレスポンスを確認します。

Copilot Studioのテストも実行し、空いている会議室が検索できるか確認します。

ツール3: 会議室予約

会議室の予約を行うためのツールを作成します。 Copilot Studioで事前に準備されている、Office 365 Outlookの「イベントの作成(V3)」ツールを使用します。

設定項目

  • ツール名: 「会議室予約のための会議を作成する」
  • 説明: 「会議室予約のため、必須参加者に会議室のアドレスを含めた会議を作成する」

入力パラメータ

パラメータ 設定方法 設定値・説明
Calendar Id カスタム値 Calendar(自分のカレンダーID)
Subject AIで動的に入力 会議のタイトル
Start Time AIで動的に入力 「2025-08-05T10:00」のような形式で入力
End Time AIで動的に入力 「2025-08-05T15:00」のような形式で入力
Attendees AIで動的に入力 会議室のメールアドレス(例:room-A@company.com)

動作確認

Copilot Studioでテストを実行した結果、会議室の予約まで完了しました。

まとめ

今回、Copilot Studioを使って会議室予約エージェントを作成してみましたが、想像以上に実用的なものができあがりました。
特にエージェント フローとGraph APIと組み合わせることで複雑な要件も達成でき、会議室の詳細な空き状況から使用中の予定まで一括で取得・整理できる点は、単純なツール連携では実現が難しかった部分だと感じました。

実際に使ってみると、「この会議で会議室を使いたい」と話しかけるだけで、空いている部屋を探して予約まで完了してくれるのは非常に便利です。

今後は複数日にまたがる検索や、Calendar Idを動的に取得し、他の人も使えるようにしていきたいと考えています。 ※Calendar Idの動的取得には、「Calendar」Graph APIを使用するとよいでしょう。

Copilot Studioは豊富なコネクタとAI機能を活用することで、日常の業務を効率化できる可能性を秘めていると感じました。

同じような課題を抱えている方は、ぜひ一度試してみてください!