見出し画像

WinActorからOpenAI APIを叩いてみた

はい、かーでぃです。
この記事は、2023/12/19に開催したRPACommunityのWinActor-Talk支部で話をした内容のまとめ記事です。

イベント情報

19日のイベントなのに、14日にConnpassを公開という…。いつもは2週間ぐらい募集期間があるのに、今回は1週間もない!人が集まるのか心配なところもありますが、14日朝公開で、14日夜には既に30人も登録がありました!

さらになんと!!
WinActor公式アカウントからも、イベントの宣伝ポストがされました!!
これは、ちょいやばい…テンションあがるwww

登壇資料

アーカイブ

まずは概要説明

最終的に制作物は、以下のような表を完成させます。
設定としては、ある観光業企画課のAさんが、上司から「市のリストから、その市のある都道府県名、人口、平均気温、観光名所を埋めて、表を完成させて」と依頼を受けた(投げられた)ところです。
この手の調べもの系の依頼、ちょくちょくありますよね。
そして、往々にしてデータが一か所にまとまっていないため、調べるのにめちゃくちゃ時間がかかるいやーーーーな、パターンのヤツです。

今回は、A列に市の名称が入っているので、そこから残りのB~E列(黄色のセル)を埋めることです。
都道府県名は、これぐらい有名な市(豊橋は愛知県!)ならしらべずともわかりますが、人口や平均気温なんて、どうやって調べれば…

そんな調べものは、知識量は膨大なAI君にやらせてしまおう!というのが今回の趣旨しです。まさに、👇これですねw

そして、そのAI君が、ChatGPTで有名なGPT3.5(無課金のため4が使えなかった…😢)。GPT3.5を使うためにOpenAI APIを利用する、といったわけです。

WinActorからOpenAIを使うために

2つのことが必要です。
あ、前提としては、OpenAI APIキーは取得済としてます。取得の方法は…詳細はググってください(笑)

1 WinActorからAPI連携をする方法
2 OpenAI APIの使い方

この2つがわからないと先に進みません。
WinActorからのAPI連携については、2022年10月のWinActor-Talk支部ですばらしい動画が残ってます!
下記は、その際のイベントページです。動画のリンクも、資料のリンクも掲載されていますので、是非動画と資料を見てください。
自分もこの動画と資料がなかったら、今回のイベントは開催できなかったと思います。

2つめのOpenAI APIは、ググるといろんな方が既に書かれていますので、そちらを参考にして頂ければよいですが、OpenAIが提供しているリファレンスもわかりやすいと思うので、まずはこちらを見るのをオススメします。

https://platform.openai.com/docs/introduction

英語ですが、ブラウザ機能で日本語に翻訳するより、一旦は英語で見て頂くのがいいんじゃないかと思います。
今回はテキスト生成なので、左側メニューの「Text generation」をクリックしてもられば大丈夫です。

少し下にスクロールさせると、modelsと書いてあり、gpt-4,gpt-4 turbo,gpt-3.5-turboのAPI ENDPOINTはこちら、と記載がありますので、URLはココかな、というのがわかります。
もっと下にスクロールさせると、パラメータの与え方等も記載があるので、参考になります。が、ある程度の知識がないとわかりにくいと思うので、その辺りはこの記事で説明していきますね。
ちなみに、サンプルコードとしてPython、node.js、curlが記載されていますが、Pythonとnode.jsはライブラリ利用をしているため、今回はあまり参考になりません。curlが生データを作って組んであるので、そちらで使い方の感覚を掴んでもらえればいいかと思います。

ちなみにAPIとは…
↓のように、サービス提供会社(下記で言えばOpenAI社)が、自社サービスを社外から利用させるためにAPIを提供しており、そのAPIを利用することでOpenAIが提供するAIを間借りすることができています。

また、APIを使う上で切っても切り離せないのがJSONです。JSONのJSは、JavaScriptの略ですが、適用範囲はJavaScriptにとらわれず、広く使われています。
JSONの良いところは、構造化データとして、キーと値のつながりが強く、そしてテキストで扱えるため、分かりやすく汎用性が高いところですかね。
ただ、自分はニガテです(笑)
CSVも構造化データではあるのですが、ヘッダ行が無いと構造化データとは言えず、繋がりが弱いためファイル単位でのデータ受け渡しではよく使われますが、API等ではあまり使われないですね。

もう少し詳しく見ていきます

実際にシナリオに入るまえに、もう少しだけ雑学です。
今回、GPTから「都道府県」「人口」「平均気温」「観光名所」と4つの項目を取得したいと思います。
ChatGPTで叩くと、こんな感じで返ってきます。

あってる/あってないは、ひとまず置いておいて…
どうですか?この返事をもらって、先ほどのエクセルにうめれますでしょうか?
「都道府県名:」をキーワードに文字列検索すれば、「愛知県」は取れそうです。

では、人口は?平均気温は?観光名所は?

文字列操作でこれらの値を取得するのは困難です。できたとしてもシナリオが複雑化しすぎますし、そもそも毎回ChatGPTがこのような形で返してくれるとも限りません。

ではどうするか?

JSON形式で返してもらうように依頼します。
JSON形式で返してもらえれば、余分な情報は無いし、値を取り出すのも簡単です。
では、どうすればChatGPTは、JSON形式で返してくれるのか?

そこで出てくるのが、プロンプトエンジニアリングです。

プロンプトエンジニアリング

AIは膨大な知識量をもっています。その知識の中から欲しい情報をプロンプト(問合せ文字列)を用いて問合せをします。
その際に、欲しい情報にピンポイントに寄せたり、出力形式を整えてもらうようにプロンプトを調整するのが、プロンプトエンジニアリングです(深津式とか有名ですよね!)。

今回は、下記のようなプロンプトを準備しました。$CITY$は、文字列置換でエクセルから取得した市と置き換えます。

あなたは日本一の不動産屋の社長です。
「$CITY$」の都道県名、人口(単位は万人)、平均気温、観光名所を100文字以内で、JSON形式で出力してください。

制約事項
・JSON形式で回答すること。
・回答に単位は含めない。
・回答の数値項目は、数値のみとする。

JSONの形式
・Prefecture: 都道府県名
・Population: 人口
・AverageTemperature: 年間平均気温
・TouristAttractions:観光名所

ついでに言うと、プロンプト自体はテキストファイルで持たせています。プロンプトを変更したいときは、シナリオをいじらず、このテキストファイルを変更するだけで済むので、楽ですよね。

とりあえず座学がここまで。
この後、実際のシナリオを見ていきます!

全体フロー

ざっくり書くと、下記の①~⑥の繰り返しになります。
①プロンプト準備
②OpenAI APIを叩く
③返ってきたJSONからCONTENTキーの値を取得
④CONTENTの値をから各値を取り出す
⑤エクセルに転記

それでは、もう少し詳しく見ていきましょう!

①プロンプト準備とAPI実行

最初に、テキストファイルを読み込んで変数strPromptに代入しています。
その後、エクセルファイルの読み取りですね。
読み取った結果、A列が空欄ならシナリオを終了するようにしています。

IF文内のTRUE側が、このブロックのメインどころになります。OpenAI APIにデータを送るには………わからないので、OpenAI APIのリファレンスを見てみましょう!

curl https://api.openai.com/v1/chat/completions \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $OPENAI_API_KEY" \
  -d '{
    "model": "gpt-3.5-turbo",
    "messages": [
      {
        "role": "system",
        "content": "You are a helpful assistant."
      },
      {
        "role": "user",
        "content": "Who won the world series in 2020?"
      },
      {
        "role": "assistant",
        "content": "The Los Angeles Dodgers won the World Series in 2020."
      },
      {
        "role": "user",
        "content": "Where was it played?"
      }
    ]
  }'

curlのサンプルコードは、こんな風に書かれていますね。

ヘッダ情報は「-H」のに該当し、ボディ情報は「-d」が該当します。
 -H ヘッダ情報
 -d data情報
$OPENAI_API_KEY$は、個人(会社?)で取得したAPIキーを設定してください(取得の方法はググってみてくださいw)。
それを踏まえて、99_外部サービス連携▷02_HTTP関連▷内のHTTPライブラリを見てみましょう。

HTTPライブラリの基本設定になります。
メソッドは、今回はPOSTに設定します。URLは、先ほど示したOpenAIのリファレンスに書かれていたURLになります。

こちらが要求タブのヘッダ情報になります。
先ほどcurlの構文で見たヘッダ情報と同じものが、キーと値に分かれて書かれています。
こちらのライブラリでは、このように設定した値がJSONとしてヘッダ情報になるようです。

こちらは要求タブのボディ部になります。
curlの構文のボディと同じような内容ですが、2点異なっています。
1つめは、messagesですが、値は変数jsonMessagesが設定されています。

では、少しフローを戻ってjsonMessageの設定を確認してみましょう。
まず、変数設定で以下のように設定します。設定値は、curl構文のボディ部のmessagesの中身と似た感じですね。

続いて、ファイルから取得したプロンプト内に埋め込まれた$CITY$と、エクセルから取得した「市」を置換します。

最後に、jsonMessagesの中にある$prompt$を、👆で市に置き換えたプロンプトで置き換えます。

置換後のjsonMessagesの値は、こんな感じになり、この値をHTTPライブラリの要求タブのボディにmessagesキーの値としてセットしています。

[{"role": "user", "content": "あなたは日本一の不動産屋の社長です。「豊橋市」の都道府県名、人口………"}]

②実行結果

実行結果は、応答タブのボディ内で受け取ります。キーと値を正しく設定すれば、変数として受け取ることができるのですが、今回はファイルに落としています。
変数で試したところ、いくつかあるJSONライブラリを試してみたのですがうまくいかず、ファイルに落としたところ一発でうまくいったので、こちらの方が安定してるんじゃないかな、と思っています。
ちなみにヘッダは特に設定する必要はありません。

こちらは、実際に取得したchat応答.jsonの中身です。
必要な情報は、contentキーの中に入っています。

{
  "id": "chatcmpl-8UuzxlIyvd4WPqfcwxmAIn88TmEsd",
  "object": "chat.completion",
  "created": 1702379633,
  "model": "gpt-3.5-turbo-0613",
  "choices": [
    {
      "index": 0,
      "message": {
        "role": "assistant",
        "content": "{\n  \"Prefecture\": \"神奈川県\",\n  \"Population\": 72,\n  \"AverageTemperature\": 15,\n  \"TouristAttractions\": \"瑞穂町の瑞穂の園、相原公園、深沢公園、多摩動物公園\"\n}"
      },
      "finish_reason": "stop"
    }
  ],
  "usage": {
    "prompt_tokens": 185,
    "completion_tokens": 79,
    "total_tokens": 264
  },
  "system_fingerprint": null
}

それでは、このcontentを取り出すところを見ていきましょう。

③contentの取り出し

取り出すには、98_構造データ関連▷01_JSON▷JSONファイル 配列読み取りライブラリで、保存したchat応答.jsonを読み込みます。
👆にあるように、目的のcontentは、choicesキーの中に居ますので、👇ではキーにchoicesを指定しています。

これでchoices配下の値を変数jsonChoicesMessageに格納されました。
jsonChoicesMessageの中身は、下記になります。

{
      "index": 0,
      "message": {
        "role": "assistant",
        "content": "{\n  \"Prefecture\": \"神奈川県\",\n  \"Population\": 72,\n  \"AverageTemperature\": 15,\n  \"TouristAttractions\": \"瑞穂町の瑞穂の園、相原公園、深沢公園、多摩動物公園\"\n}"
      },
      "finish_reason": "stop"
    }

次はcontentキーのすぐ上にあるmessageキーを指定します。
ライブラリは、01_JSON▷JSON変数 読み取りを使います。下記のようにすることで、message配下の値が、strMessageに格納されます。

"role": "assistant",
"content": "{\n  \"Prefecture\": \"神奈川県\",\n  \"Population\": 72,\n  \"AverageTemperature\": 15,\n  \"TouristAttractions\": \"瑞穂町の瑞穂の園、相原公園、深沢公園、多摩動物公園\"\n}"

ここまでくれば、あとはcontentキーの値を取得するだけですね。
👆と同様に、JSON変数 読み取りを使って、変数strContentに格納します。

"{\n  \"Prefecture\": \"神奈川県\",\n  \"Population\": 72,\n  \"AverageTemperature\": 15,\n  \"TouristAttractions\": \"瑞穂町の瑞穂の園、相原公園、深沢公園、多摩動物公園\"\n}"

これで、strContentには、GPTから得た欲しい回答が取得できました…?

はい、そうですね。欲しい回答は「JSON形式で」とお願いしましたので、ちゃんとJSON形式で返ってきています。
なので、ここからさらにJSONで値を切り出していきます。

今回はキーを毎回指定しながら取得しましたが、下記のように一発で取得することも可能です。
こちらは、JSONが応答変数に入っており、それをキーを「.」で複数繋げて、キー「content」の値を変数回答に格納しています。

④contentの分解・分析

分解する前に、少しプロンプトをおさらいしましょう。
プロンプトでは、結果をJSONで出力してください、とお願いしたのと同時に、JSONの形式も指定しています。

・Prefecture: 都道府県名
・Population: 人口
・AverageTemperature: 年間平均気温
・TouristAttractions:観光名所

GPTから得られた回答も同じ形式になっております。
以前は、ここまでちゃんと返してくれなかったような気がするのですが、最近のGPTはその辺りの調整が進んでるのかもしれませんね。

さて、得たい値は、こちらが指定したキーで設定されていますので、あとは1つづつ取得するだけですね。

ということで、今回使ったJSON系ライブラリは、赤枠付けたこの2つだけなんですよ。
JSON系ライブラリ、合計で20以上あるんですが…2つしか使ってないので、たぶんもっと使いやすいライブラリとかあると思うのですが…この辺りは、また調べてnote記事にアップしていきますね。

⑤エクセルへの転記

最後は、Excel操作(値の設定2)ライブラリを使って、指定の場所に取得した各項目を設定していきます。

以上で、OpenAI APIを叩いて得た回答をエクセルに転記するシナリオができました。

おまけ

目ざとい人は気づいてるかも!?
エクセルファイルの名前がガンダムリストになってることを…。
そして、シート名がSheet2になってることを…。

そうです、実はSheet1にはガンダムネタも仕込んでます!(笑)
ガンダムネタは、IT x ガンダムLT大会でお披露目予定w

こちらも、こうご期待❣❣

12/20追記 忘備録(天の声)

HTTPライブラリ
配列等含むJSONが返ってくる場合には変数受け取りができない
⇒ HTTP(詳細)ライブラリの活用

JSONライブラリの01系と02系
02系の方が新しく機能が豊富だけど…バグ見つけちゃった by 天の声
⇒ 次回更新時に修正予定とのこと。

エスケープコードが消えた問題
JSONを取得する際に、「転記」を選択すると「"」を消す等の処置をしてくれる。その際にエスケープコードも消えるらしい。
一方「値の参照」の場合は、生データが取得されるので、「"」も付与されたままだし、エスケープコードも残る

12/20追記 チャットで頂いた市の調査結果


この記事が気に入ったらサポートをしてみませんか?