清水理史の「イニシャルB」

ChatGPTとGoogle Calendar APIを組み合わせ、自分のスケジュール調整に使えるカスタムGPTを作る

Google Calendar APIと連携するカスタムGPT

 ChatGPTのカスタムGPTを利用して、Google CalendarとのAPI連携を実装してみた。認証周りなど、若干、工夫が必要だが、自然言語で「今週の予定は?」と聞いたり、「会議の予定を明日15:00〜16:00で入れて」とお願いしたりできる。

GoogleとつながるChatGPTを

 昨年末、GoogleのBardでGmailやGoogleマップと連携できる拡張機能が紹介された。自然言語でメールの内容を質問したり、宿泊先情報を検索したりと、なかなか便利な機能となっている。

▼Bardの拡張機能の紹介記事(Google Japan Blog)
BardがマップやGmail、YouTubeなどのGoogleのサービスと連携

 Copilot for Microsoft 365などのサービスもそうだが、生成AIを利用したサービスは、データやサービスをいかに活用するかが鍵となっている。インターネット上で検索するデータもその1つで、そのほか、組織内に蓄えられた文書やデータベース、個人ユーザーが持つメールやカレンダーなどの情報を組み合わせてこそ、実用性の高いサービスが実現できるわけだ。

 そこで、筆者も自分のデータを活用できるカスタムGPTを作ってみることにした。

 具体的には、Google Calendar APIを利用するカスタムGPTだ。と言っても、提供されている全ての機能を利用可能にするのは、なかなか手間がかかるので、今回は「Events: list」と「Events: insert」の2つを使って、現在登録されている予定の検索と新しい予定の登録の2つを実現することにした。

 それぞれGETとPOSTの代表的な使い方となるので、そのほかの機能にも応用できるはずだ。なお、カスタムGPTの作成には、ChatGPTの有料プラン契約が必要となる。

▼Events: list、Events: insertの解説
Google Calendar API(Events: list)
Google Calendar API(Events: insert)

カスタムGPTからGoogle Calendarの基本的なAPIを利用する

Google Cloud ConsoleでAPIを利用可能にする

 ここから手順を紹介していく。まずは、事前準備として、Google Calendar APIを有効化し、OAuthで認証できるようにしておこう。

STEP 1:Google Calendar APIを有効化する

 Google Cloud Consoleにアクセスし、新しいプロジェクトを作成する。続けて[APIとサービス]から[APIとサービスの有効化]をクリックし、[Google Calendar API]を検索して有効化しておく。

Google Calendar APIを有効化する

STEP 2:認証情報を追加する

 作成できたら、[認証情報を作成]をクリックして、以下のように認証に必要な設定をする。

[認証情報を作成]から、以下の解説する設定を行う

[認証情報の種類]

 ユーザー個人が所有するカレンダーデータを参照したいので[ユーザーデータ]を選択する。

[OAuth同意画面]

 認証時に表示される画面を設定する。アプリ名(識別できればなんでもいい)と連絡先などとして表示されるメールアドレスを設定しておく。

[スコープ]

 ユーザーに許可する権限を設定する。今回は、カレンダーの予定を参照・追加するので全てのカレンダーの予定の表示と編集が可能な「https://www.googleapis.com/auth/calendar.events」の権限を追加する。[スコープを追加または削除]から、このスコープを追加しておく(機密性の高いスコープに追加される)。

スコープの追加を行う画面

[OAuthクライアントID]

 アプリを識別ためのIDを設定する。[アプリケーションの種類]で[ウェブアプリケーション]を選択し、[CustomGPT]などの名前を付けておく。この画面では、[承認済みのリダイレクトURI]の設定も必要だが、これはChatGPTでカスタムGPTのアドレスになるので、作成後に再度設定する。

STEP 3:接続情報をメモする

 APIの設定が完了すると、カスタムGPTから接続するためのクライアントIDとクライアントシークレットが発行される。[認証情報]画面で、作成した[OAuth 2.0クライアントID]を選択して、これらをメモしておく。

クライアントID、クライアントシークレットをメモ(コピー)しておく

 以下は、クライアントID、クライアントシークレットの例だ。実際にはユーザーにより異なるので、自分に発行されたものをメモしておくこと。

クライアントID:486326492413-8tm2pmsrcq4vqq2c7qf4tfikbjurugdp.apps.googleusercontent.com
クライアントシークレット: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

STEP 4:テストユーザーを登録しておく

 今回は、APIをテストとしてのみ有効化する。このため、APIを利用できるユーザーを設定する必要がある。[OAuth同意画面]で[テストユーザー]に利用を許可するGoogleアカウントを登録しておく。

[テストユーザー]に登録したテストユーザーの情報が表示される

 ここまでで、Google Calendar APIを有効化は完了だ。

カスタムGPTを作成する

 続けて、ChatGPTでカスタムGPTを作成する手順に入る。

 ただし、注意点が1つある。本稿執筆時点(2023年12月21日)では、OAuthの連携に不具合があり、ChatGPTの言語設定が「Ja-JP」や「自動選択」の場合、認証後のリダイレクトでエラーが発生する。このため、あらかじめ[設定]の[言語]で[en-US]を選択しておく必要がある。

ChatGPTの[設定]の[言語]で[en-US]を選択しておく

STEP 5:カスタムGPTの基本設定をする

 準備ができたら、GPTsでカスタムGPTの作成を開始し、基本情報を登録しておく。今回は下図のように設定した。APIのみを利用するので、Knowledgeに登録するファイルはなし、Capabilitiesも全てオフにしておく。

 なお、カスタムGPTの作成からAction(s)の登録に関しては、以前の記事で詳しく解説しているので、今回は簡単に触れるのみとする。初めて触れる人は以下の記事も参考にしてほしい。

▼GPTsとActionの解説
カスタムChatGPT開発例4選、新機能「GPTs」で書籍情報や天気情報を調べるAIチャットを作る

カスタムGPTの[Configure]で、基本的な設定をしておく。今回はActionのみを利用する

STEP 6:Actionを登録する

 そして、本題のActionを作成する。少々長いが、以下のスキーマをコピーしてそのまま登録すればいい。これは、ChatGPTに依頼して生成してもらったものを、OpenAIの形式に合わせて「servers」や「operationId」などの項目を追加するなどして、多少手直ししたものだ。

APIのドキュメントをもとにChatGPTにスキーマの作成を依頼して作成した。この画面で見えているのはEvents: listの部分のみ。Events: Insert用は別途依頼して作った
スキーマを登録する画面
{
  "openapi": "3.0.0",
  "info": {
    "title": "Google Calendar Events API",
    "version": "v3"
  },
  "servers": [
    {
      "url": "https://www.googleapis.com/calendar/v3"
    }
  ],
  "paths": {
    "/calendars/{calendarId}/events": {
      "get": {
        "summary": "Retrieve events from a specified calendar",
        "operationId": "getEvents",
        "parameters": [
          {
            "name": "calendarId",
            "in": "path",
            "required": true,
            "description": "Calander ID. Default to primary calender(primary) if not specified.",
            "schema": {
              "type": "string",
              "default": "primary"
            }
          },
          {
            "name": "timeMin",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "timeMax",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EventsResponse"
                }
              }
            }
          }
        }
      },
      "post": {
        "summary": "Insert a new event",
        "description": "Insert new event to the {calendarId} calendar with the given summary and time.",
        "operationId": "insertEvents",
        "parameters": [
          {
            "name": "calendarId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "summary": {
                   "type": "string",
                   "description": "Summary or title of the event."
                 },
                 "start": {
                   "type": "object",
                   "properties": {
                     "dateTime": {
                       "type": "string",
                       "format": "date-time",
                       "description": "The start date and time of the event in ISO 8601 format. Add '+09:00' to end of format."
                     }
                   },
                   "required": ["dateTime"]
                 },
                 "end": {
                   "type": "object",
                   "properties": {
                     "dateTime": {
                       "type": "string",
                       "format": "date-time",
                       "description": "The end date and time of the event in ISO 8601 format. Add '+09:00' to end of format."
                      }
                   },
                   "required": ["dateTime"]
                 }
                },
                "required": ["summary", "start", "end"]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Event created",
            "content": {
              "application/json": {
                "schema": {
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "EventsResponse": {
        "type": "object",
        "properties": {
          "kind": {
            "type": "string"
          },
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Event"
            }
          }
        }
      },
      "Event": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "summary": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "start": {
            "type": "string",
            "format": "date-time"
          },
          "end": {
            "type": "string",
            "format": "date-time"
          }
        }
      }
    }
  }
}

STEP 7:Authenticationを設定する

 認証情報は、Google Cloud Consoleで登録したOAuthの設定となる。STEP 3でメモしておいたクライアントIDとクライアントシークレットに加え、以下の認証先URLとスコープを登録しておく。

認証情報を登録する
Authorization URL:https://accounts.google.com/o/oauth2/v2/auth
Token URL:https://oauth2.googleapis.com/token
Scope:https://www.googleapis.com/auth/calendar.events

STEP 8:コールバックURLを生成する

 ここまでの設定ができたら、ひとまずカスタムGPTを保存する。すると[Callback URL]として以下のようなアドレスが生成されるので、これをメモしておく(ユーザーにより異なるので、自分に表示されたものをメモしておくこと)。

Callback URL:https://chat.openai.com/aip/g-f73d2ea5e39cb92631a5d4957e5b64d70d5820b2/oauth/callback
Callback URLを確認する

STEP 9:Google Cloud ConsoleにCallback URLを登録する

 Google Cloud Consoleに戻って、[認証情報]から作成済みのOAuth 2.0 クライアント IDを開き、[承認済みリダイレクトURL]に、STEP 8で確認したCallback URLを登録する。なお、Callback URLはカスタムGPTの設定を変更すると変化するので、保存の度に確認し、Google Cloud Console側でも修正しておく必要がある。

Callback URLを承認済みリダイレクトURLに登録する

カレンダーGPTが完成した

 これで設定は完了だ。Google Cloud Consoleの設定が反映されるまで時間がかかる場合があるので、しばらくしてからアクセスすると、カスタムGPTからGoogle Calendar APIを利用できるようになる。

 「今週の予定を教えて」のように入力すると、まずOAuthの認証が実行されるので、Googleアカウントでサインインしてアクセスを許可する。初回は認証後のリダイレクトで質問がクリアされてしまうので、もう一度、質問してAPIアクセスを許可すると、カレンダーの情報に答えてくれるようになる。

シンプルに予定を聞く

 もちろん、もう少し複雑な会話も可能で、「12月22日に3時間会議をしたいんだけど、空いている時間の候補を挙げて」のように入力すると、予定を確認して開いている時間から会議の候補時間を挙げてくれる。

空き時間の候補を聞く

 そのまま予定の登録も可能で、表示された候補から「1で『選考会議』という予定を入れて」のようにすれば、候補の時間帯に予定を登録できる。

カレンダーに登録する

API利用は著作権に注意する必要もありそう

 以上、今回は、ChatGPTのカスタムGPTを利用して、Googleカレンダーを利用する方法を紹介した。少々、手間はかかるが、予定の調整などがなかなか便利だ。

 ところで、昨年末(本稿執筆しているちょうど前日の12月20日)、文化庁の文化審議会著作権分科会の法制度小委員会で、生成AI(人工知能)によるコンテンツの無断学習は、著作権法で著作権者の許諾が不要とされる「非享受目的」(第30条の4)にあたらない場合があるとする「AIと著作権に関する考え方」の素案が示された。

▼文化庁による「AIと著作権に関する考え方」の素案ほかの資料
文化審議会著作権分科会法制度小委員会(第5回)

 この素案では、RAG(Retrieval Augmented Generation:外部の情報をプロンプトに追加して生成AIを使用すること。今回のGoogleカレンダーの情報も外部の情報にあたる)について、「生成に際して既存の著作物の一部を出力するものであることから、その開発のために行う著作物の複製等は、非享受目的の利用行為とはいえず、法第30条の4は適用されないと考えられる。」という骨子が示されている。

 骨子なので、これから変更される可能性もあるが、つまり、RAGで入力する文書やウェブ検索結果などのデータは、元データの「思想又は感情を自ら享受し又は他人に享受させることを目的としている=享受目的」で使われているので、利用する場合は著作権者の許可が必要という話だ。

 今回のように自分のデータを扱うのであれば心配はいらないが、カスタムGPTで、外部のPDFやAPIなどを使う場合は、元データの著作権を慎重に考慮する必要がありそうだ。

※本稿では、クライアントIDとクライアントシークレットがどのような値なのかを実際に確認できるようにするために、例として公開しています。実際の自分の値を第三者に公開することは避けてください

清水 理史

製品レビューなど幅広く執筆しているが、実際に大手企業でネットワーク管理者をしていたこともあり、Windowsのネットワーク全般が得意ジャンル。最新刊「できるWindows 11」ほか多数の著書がある。