GPTs-Actionの使い方(複数回の外部API呼び出し)

トレンド
この記事は約38分で読めます。

2023年11月6日にOpenAIより発表されたGPTs(Actions)について試した結果を記事にします。

GPTsとは特定のタスクに特化するようにカスタマイズされたChatGPTのモデルの総称カスタマイズ可能なGPTです。

GPTsを使うと以下のようなことができます。

・プログラミングせずにカスタマイズなチャットボットが作成可能。
・作成したチャットボットを一般公開し収益化することが可能。
・アップロードした知識ベースでの回答が可能。
・外部APIと連携(Actions)した回答が可能。

中でも興味深いのが【 Actions 】

Actionsを使うことで、AIの回答精度の向上(ハルシネーション低減)を図ることができます。
なぜなら、AIが回答に使用する中核情報をActionで提供できるようになるからです。
つまりは、AIの回答をActionでコントロールできるようになります。
このようなプロセスをReAct(Reasoning and Acting)と呼ぶそうです。
ReActの詳細はMicroSoftの中の人の記事を参照して下さい。
https://logmi.jp/tech/articles/329010

MicroSoftは知っているがOpenAIは知らない方、秒でわかる記事はこちら。

この記事ではActionsを使った事例(文字通り複数回のActionを実行)を解説します。(サイトAから取得した情報をサイトBに渡して情報を得ると言うことができます)

実装ではGPT Builder(ChatGPTのウェブアプリ上で、カスタマイズ可能なGPTを構築するためのツール)を使用します(GPT Builderの仕様は2023/11/16時点のもの)

2023/11/16時点において、GPTsは有料プラン加入者向けのサービスになるので、ChatGPT Plus等の有料プラン未加入の場合、GPT作成及び公開GPTの利用ができません。

GPT(Actions)作成

GPT Builderを利用するには今回新しく追加されたExploreをクリックします。

次に十字マークをクリックします。

GPT Builderは「Create」と「Configure」を切り替え可能なレイアウトになっています。

「Create」・・・途中まで対話方式で作成、「Configure」・・・最初から手動で作成

作りたいものが決まっていれば、直接入力する「Configure」画面で作成するのが良いです。

仮に「Create」モードで対話で進める場合、左下の入力欄に「以降は日本語で表示して下さい」と入れておけば、日本語での対話になって進めやすいです。

以降は「Configure」モードで始める手順(全て空欄状態)になります。
(Configure選択時の画面をビルド画面と言うことにします)

ビルド画面は、①②③⑤は表示(ユーザインターフェース)に関わる項目
④⑥⑦⑧は制御(プログラム)に関わる項目からなり、
表示に関わる項目を入力すると即座にPreviewに反映されます。

各項目は以下の役割を持ちます。
① GPTのアイコン。
② GPTの名前。
③ GPTがどのような機能を持つのかの説明。
④ GPTが何をするのかどのように作動するのかを指示する内容。
⑤ ユーザーがGPTにどのような入力を行えば良いかの例。
⑥ GPTが参照する文書やファイルをアップロード。
⑦ GPTが利用する機能の選択。
⑧ GPTを外部のAPIと連携して動作させる為の設定。

今回、Actionsの使用例として、指定住所の日の出、日の入り、月の出、月の入り時刻を算出するGPTを作成します。

目的は複数アクションの実行です。
Action1)指定住所の緯度経度情報が得られるサイトに問い合わせ。
Action2)指定日付と緯度経度を元に日の出(入り)、月の出(入り)情報が得られるサイトに問い合わせ。

事例におけるビルド画面の各項目の入力内容は以下になります。

項番項目入力内容
十字マーク「Use Dall-e」選択(②を入力後に実行すると内容に合ったプロフィール画像を生成)
Name『日の出(入り)月の出(入り)時刻計算機』
Description『指定住所、指定日付から日の出、日の入り、月の出、月の入り時刻を算出』
Instructions『前提条件:日本語で回答
アクション1.入力した住所を元に緯度経度を算出。
以下リクエストパラメータ
appid:”xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx”
アクション2.取得した緯度経度と日付を元に日の出、日の入り、月の出、月の入り時刻を算出。』
Conversation starter『2023年11月16日の東京都港区六本木』
Knowledge未使用(AIへのカスタムデータ提供無し)
CapabilitiesWeb Browsingオフ(AIによるネット検索は不要)
DALL-E Image Generationオフ(AIによる画像生成は不要)
Code Interpreterオフ(AIによるデータ分析は不要)
Actions2つのアクションを記述

以下はアクションのシーケンス図です。

以下が動作結果です(2アクション実行されているのが分かります)

実動作を体験してみたい方はアイコンをクリックして下さい(期間限定)

体験版は秘密情報(appid)が表示されない工夫をしています。
詳しくは、本記事(公開する上での注意点)を参照。

Actionの作成

Actionの作成はビルド画面の「Create new action」から行います。

以下がAction作成の画面になります。

Actionsでの主な作業はSchemaを作成することです。と言っても最初は戸惑うかと思います。

Schemaの作成とは、外部API呼び出し先のAPIの仕様を記述することらしいのですが、Schema入力欄に「Enter your OpenAPI schema here」とヒントがあるだけでどう書けば良いかわかりません。

「Examples」を見れば天気情報取得の例が出ているのである程度のイメージが掴めます。

しかし、どのようにして外部APIの仕様をOpenAPI schemaに落とし込むかわからなかったので「餅は餅屋に頼むのが一番」ということで、ChatGPTに作成をお任せすることにしました。

Schemaが気になる方はこちらの記事をご覧下さい。

アクション作成(その1)

1つ目のアクションは「住所を元に緯度経度情報が得られるサイトに問い合わせ」を行うことです。

調べたところ「Yahoo!ジオコーダAPI」サイトが無料のAPIを公開していたので利用させて頂くことにしました。(但し、リクエストパラメータとしてYahooクライアントID(appid)が必要だったので別途取得しました)

リクエストパラメータの例、レスポンスの例は、サイトの記事をコピペしたものです。修正するSchemaはGPT Builderにある例をコピペしたものです。

レスポンスの例は、JSON形式での記載がサイトに無くXML形式になりました。(この時点では、こんなんでChatGPTに伝わるのかと半信半疑。。。)

以下、ChatGPTへの依頼文

以下の項目に合わせて、Schemaを修正して下さい。

リクエストパラメータの例
住所検索

https://map.yahooapis.jp/geocode/V1/geoCoder?appid=<
あなたのClient ID(アプリケーションID)>&query=%e6%9d%b1%e4%ba%ac%e9%83%bd%e6%b8%af%e5%8c%ba%e5%85%ad%e6%9c%ac%e6%9c%a8 レスポンスの例 <?xml version="1.0" encoding="UTF-8"?> <YDF totalResultsReturned="1" totalResultsAvailable="1" firstResultPosition="1">  <ResultInfo>   <Count>1</Count>   <Total>1</Total>   <Start>1</Start>   <Status>200</Status>   <Description/>   <Latency>0.054</Latency>  </ResultInfo>  <Feature>   <Id>13103.29</Id>   <Gid/>   <Name>東京都港区六本木</Name>   <Geometry>    <Type>point</Type>    <Coordinates>139.73359259,35.66288632</Coordinates>    <BoundingBox>139.70869736,35.62303352 139.78198741,35.68270364</BoundingBox>   </Geometry>   <Category/>   <Description/>   <Style/>   <Property>    <Uid>f9a30565b66313c33f4540a1424fa89e57ee7209</Uid>    <CassetteId>b22fee69b0dcaf2c2fe2d6a27906dafc</CassetteId>    <Yomi>トウキョウトミナトクロッポンギ</Yomi>    <Country>     <Code>JP</Code>     <Name>日本</Name>    </Country>    <Address>東京都港区六本木</Address>    <GovernmentCode>13103</GovernmentCode>    <AddressMatchingLevel>3</AddressMatchingLevel>   </Property>  </Feature> </YDF> 修正するschemaは以下です。 { "openapi": "3.1.0", "info": { "title": "Untitled", "description": "Your OpenAPI specification", "version": "v1.0.0" }, "servers": [ { "url": "" } ], "paths": {}, "components": { "schemas": {} } }

このようなふわっとしたやり方でいいのだろうかと思いつつ、上記をChatGPTの質問欄にコピペして生成してもらいました。

{
  "openapi": "3.1.0",
  "info": {
    "title": "住所検索API",
    "description": "住所検索API",
    "version": "v1.0.0"
  },
  "servers": [
    {
      "url": "https://map.yahooapis.jp"
    }
  ],
  "paths": {
    "/geocode/V1/geoCoder": {
      "get": {
        "description": "住所検索",
        "operationId": "AddressSearch",
        "parameters": [
          {
            "name": "appid",
            "in": "query",
            "description": "あなたのClient ID(アプリケーションID)",
            "required": true,
            "schema": {
              "type": "string",
            }
          },
          {
            "name": "query",
            "in": "query",
            "description": "住所検索クエリ",
            "required": true,
            "schema": {
              "type": "string",
            }
          }
        ],
        "deprecated": false
      }
    }
  },
  "components": {
    "schemas": {
      "AddressSearchResponse": {
        "type": "object",
        "properties": {
          "YDF": {
            "type": "object",
            "properties": {
              "totalResultsReturned": {
                "type": "integer"
              },
              "totalResultsAvailable": {
                "type": "integer"
              },
              "firstResultPosition": {
                "type": "integer"
              },
              "ResultInfo": {
                "type": "object",
                "properties": {
                  "Count": {
                    "type": "integer"
                  },
                  "Total": {
                    "type": "integer"
                  },
                  "Start": {
                    "type": "integer"
                  },
                  "Status": {
                    "type": "integer"
                  },
                  "Description": {
                    "type": "string"
                  },
                  "Latency": {
                    "type": "number"
                  }
                }
              },
              "Feature": {
                "type": "object",
                "properties": {
                  "Id": {
                    "type": "string"
                  },
                  "Gid": {
                    "type": "string"
                  },
                  "Name": {
                    "type": "string"
                  },
                  "Geometry": {
                    "type": "object",
                    "properties": {
                      "Type": {
                        "type": "string"
                      },
                      "Coordinates": {
                        "type": "string"
                      },
                      "BoundingBox": {
                        "type": "string"
                      }
                    }
                  },
                  "Category": {
                    "type": "string"
                  },
                  "Description": {
                    "type": "string"
                  },
                  "Style": {
                    "type": "string"
                  },
                  "Property": {
                    "type": "object",
                    "properties": {
                      "Uid": {
                        "type": "string"
                      },
                      "CassetteId": {
                        "type": "string"
                      },
                      "Yomi": {
                        "type": "string"
                      },
                      "Country": {
                        "type": "object",
                        "properties": {
                          "Code": {
                            "type": "string"
                          },
                          "Name": {
                            "type": "string"
                          }
                        }
                      },
                      "Address": {
                        "type": "string"
                      },
                      "GovernmentCode": {
                        "type": "string"
                      },
                      "AddressMatchingLevel": {
                        "type": "integer"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}
生成結果の微修正

・urlとpathsのlocationが分離されずurlになっていたので手動で分離。
・appidがパラメータとして扱われていなかったので「appidもパラメータです」と入力し再生成を依頼。

生成したschemaをビルド画面のschema入力欄にコピペ後、PreViewで実行した結果(1アクションのみ)が以下になります。

期待通り、場所に対する緯度経度が表示されています。なお、PreViewで実行するとデバッグ情報も合わせて表示されるので、外部API呼び出しでの入出力が確認できます。

アクション作成(その2)

アクション追加は、1アクション目と同様に「Create new action」から行います。

2つ目のアクションは「日付と緯度経度を元に日の出、日の入り、月の出、月の入り時刻が得られるサイトに問い合わせる」ことです。

調べたところ「ビットラボ 日の出・日の入り時刻計算 Web API おはこん番地は!?」サイトが無料のAPIを公開していたので利用させて頂くことにしました。

リクエストパラメータの例、レスポンスの例は、サイトの記事からコピペしたものです。

以下の項目に合わせて、Schemaを修正して下さい。

リクエストパラメータは以下です。
・JSONリクエスト例
  1. 日の出・日の入り、月の出・月の入り時刻を計算
    http://labs.bitmeister.jp/ohakon/json/?mode=sun_moon_rise_set&year=2021&month=9&day=3&lat=35.8554&lng=139.6512

結果jsonは以下です。
・計算結果JSON例
  1. 日の出・日の入り、月の出・月の入り時刻
   {
      "date":{
         "day":"3",
         "month":"9",
         "year":"2021"
      },
      "location":{
         "coordinate":{
            "lat":"35.8554",
            "lng":"139.6512"
         }
      },
      "moon_age":25.55,
      "rise_and_set":{
         "moonrise":0.863,
         "moonrise_hm":"0:52",
         "moonset":15.984,
         "moonset_hm":"15:59",
         "sunrise":5.241,
         "sunrise_hm":"5:14",
         "sunset":18.109,
         "sunset_hm":"18:07"
      },
      "version":"2.2"
   } 

修正するschemaは以下です。
{
  "openapi": "3.1.0",
  "info": {
    "title": "Untitled",
    "description": "Your OpenAPI specification",
    "version": "v1.0.0"
  },
  "servers": [
    {
      "url": ""
    }
  ],
  "paths": {},
  "components": {
    "schemas": {}
  }
}

今回もふわっと感を感じつつ、上記をChatGPTの質問欄にコピペして生成してもらいました。

{
  "openapi": "3.1.0",
  "info": {
    "title": "日の出・日の入り、月の出・月の入り時刻計算API",
    "description": "日の出・日の入り、月の出・月の入り時刻を計算するAPIのOpenAPI仕様",
    "version": "v1.0.0"
  },
  "servers": [
    {
      "url": "https://labs.bitmeister.jp"
    }
  ],
  "paths": {
    "/ohakon/json": {
      "get": {
        "summary": "日の出・日の入り、月の出・月の入り時刻を計算する",
        "operationId": "CalculateSunMoonRiseSet",
        "parameters": [
          {
            "name": "mode",
            "in": "query",
            "description": "計算モード",
            "required": true,
            "schema": {
              "type": "string",
              "enum": ["sun_moon_rise_set"],
              "default": "sun_moon_rise_set"
            }
          },
          {
            "name": "year",
            "in": "query",
            "description": "年",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          },
          {
            "name": "month",
            "in": "query",
            "description": "月",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          },
          {
            "name": "day",
            "in": "query",
            "description": "日",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          },
          {
            "name": "lat",
            "in": "query",
            "description": "緯度",
            "required": true,
            "schema": {
              "type": "number",
              "format": "float"
            }
          },
          {
            "name": "lng",
            "in": "query",
            "description": "経度",
            "required": true,
            "schema": {
              "type": "number",
              "format": "float"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "計算結果JSON",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "date": {
                      "type": "object",
                      "properties": {
                        "day": {
                          "type": "string"
                        },
                        "month": {
                          "type": "string"
                        },
                        "year": {
                          "type": "string"
                        }
                      }
                    },
                    "location": {
                      "type": "object",
                      "properties": {
                        "coordinate": {
                          "type": "object",
                          "properties": {
                            "lat": {
                              "type": "string"
                            },
                            "lng": {
                              "type": "string"
                            }
                          }
                        }
                      }
                    },
                    "moon_age": {
                      "type": "number",
                      "format": "float"
                    },
                    "rise_and_set": {
                      "type": "object",
                      "properties": {
                        "moonrise": {
                          "type": "number",
                          "format": "float"
                        },
                        "moonrise_hm": {
                          "type": "string"
                        },
                        "moonset": {
                          "type": "number",
                          "format": "float"
                        },
                        "moonset_hm": {
                          "type": "string"
                        },
                        "sunrise": {
                          "type": "number",
                          "format": "float"
                        },
                        "sunrise_hm": {
                          "type": "string"
                        },
                        "sunset": {
                          "type": "number",
                          "format": "float"
                        },
                        "sunset_hm": {
                          "type": "string"
                        }
                      }
                    },
                    "version": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {}
  }
}
生成結果の微修正

・urlとpathsのlocationが分離されずurlになっていたので手動で分離。
・modeパラメータの値がセットされていなかったので「modeパラメータ値はsun_moon_rise_setとして下さい」と入力し再生成を依頼。

生成したschemaをビルド画面のschema入力欄にコピペ後、PreViewで実行した結果(2アクション)が以下になります。

期待通り、2アクション目で、日付と1アクション目で取得の緯度・経度を元に日の出、日の入り、月の出、月の入り時刻を算出できています。

これで完成です。
GPTで複数アクションを使用した回答が行えることが確認できました。

ChatGPTで丸投げした結果との比較

GPT(actions)を使用せずに、ChatGPT3.5、ChatGPT4に同じ質問をしてみた結果です。

結果1. ChatGPT3.5の場合、学習内容にデータが無い為か回答が得られませんでした。ですが事実に基づかない生成(ハルシネーション)は発生しませんでした。

結果2. ChatGPT4の場合、WEBブラウジングの結果を回答してくれました。[“]をクリックすると参照サイトの表示もしてくれます。(情報提供サイトを瞬時に見つけてデータをひっぱってくるのはすごい)

ChatGPTが参照したサイトを確認してみたい方はこちら。①[“]②[“]

今回は事例がイマイチだったようで、WEB検索すればわかる情報だとChatGPT4に丸投げした方が早いということになりましたが、社内データや個人情報等の特定情報を扱う上でGPTは効果を発揮するでしょうし、どちらが良いかはケースバイケースになります。

作ってみた感想

GPTsは初見でしたが、手こずりそうに思えた機能がノープログラム且つ短時間で実装できたことに拍子抜けしました。従来の厳密なコーディング方法と異なり、schemaを介してAIに指示を行うところに違和感や便利さを感じつつも、こうしたプロンプティングなアプローチが今後世の中に浸透していく可能性を感じました。

付録

作成したGPTは3パターンでの公開設定が可能です。

1.Only me(非公開)
2.Only people with a link(限定公開)
3.Public(完全公開)

但し、2.又は3.選択時は、Action毎にPrivacy Policyの設定が必要になりますが
設定は「actions」の編集画面から行うことができます。
注意)アクション単位での設定が必要。

プライバシーポリシーの内容は、一旦以下(定型文)としました。

設定ができれば、警告メッセージが消え「Confirm」が可能になります。

公開する上での注意点

今回作成したGPTは、外部APIアクセス時に秘密情報(appid)をリクエストパラメータとして使用しますが、GPTの仕様上、実行時にパラメータが確認できることから秘密情報を見られてしまいます。

秘密情報を外出ししてAPIで取得すれば一応見られないようにできますが、AIに「取得した情報を表示して下さい」と指示することで漏洩する恐れがあります(未確認)。

そのような理由からセキュリティには十分な配慮が必要です。
もしも秘密情報を盗まれると悲惨な目に合います(体験談はこちらの記事)

今回は以下のような踏み台を設けることでGPTの影響下から秘密情報(appid)を隠蔽しました。
体験版は仮想的なサーバー(GAS)を踏み台とする構成にしています。

左のアイコンクリックで、動作を体験できます(期間限定)

before)秘密情報(appid)をGPTで管理

after)秘密情報(appid)を踏み台(サーバー)で管理

詳しい隠蔽方法については以下の記事に記載しています。

又、GASと連携するGPTアプリの作り方を以下の記事に記載しています。

GPTsメンション機能を用いた考察

2024年1月27日に発表されたGPTsメンション機能を使い、2つのアクションをGPTに昇格させ連携させることで、本記事の事例と同様な結果を得られるかを検証してみたのが以下の記事になります。