M5StackでChatGPT APIを使ってみた!

6月 24, 2023

ChatGPTの利用例としては, PythonやPC向けのアプリケーションが主流で, エッジデバイスにおける利用はライブラリの少なさから制約を受け, 特に作例が見られない状況でした. しかし, このような背景を踏まえ, ESP32をベースとしたM5StackでChatGPTが使えることを確認しました. M5StackではM5Unifiedやm5stack-avatarといったライブラリを使用したC++の開発が活発に行われています. そのため, 今回はChatGPTのAPIをC++で実装しました。

M5StackでChatGPTを利用したいが困っている方々は, この記事を参考にしていただければ幸いです.

ChatGPTのAPIがM5Stack上で使えるようになれば, 曖昧な指示を理解し適切な行動をとるIoTデバイスを作成したり、スタックチャンを通じて会話を行うような応用も可能となるでしょう. 今回は, このM5Stack上でのChatGPTのAPIの使用方法についてご紹介します.

セットアップ

M5Stack

今回ESP32向けにChatGPT APIを使用できるようにしました. ESP32のボードとしてはM5Stackを使って開発しました.

M5GO IoTスターターキット V2.6
スイッチサイエンス

開発環境としてはPlatformIOを使用し, ESP32のボードを指定しました.

インストールするライブラリ

M5StackChatGPTAPI

M5Stack用に作成したChatGPT APIを使用するためのライブラリです.srcフォルダー内のChatGPTAPI.cppとChatGPTAPI.hをコピーして使用してください.

Your Project
├─include
├─lib
└─src
  ├─ChatGPTAPI.cpp
  ├─ChatGPTAPI.h
  ・
  ・
  ・

ArduinoJson

Json形式の情報を取り扱うために, ArduinoJsonをインストールします.

M5Unified

サンプルではM5Stackを使用するのでM5Unifiedをインストールします.

使い方

OPENAI_TOKENには取得したAPIトークンを設定してください.

APIトークンとモデルを指定してインスタンスを生成します. そのメンバ関数に質問を指定すると, responseに返答を返してくれます.

#include "ChatGPTAPI.h"

// setup OpenAI API
#define OPENAI_TOKEN "YOUR_OPENAI_API_TOKEN"
String model = "gpt-4"; // or "gpt-3.5-turbo"
ChatGPTAPI chatGPTAPI(OPENAI_TOKEN, model);

// inputtext
String inputtext = "Where is the capital of Japan?";

// send request for ChatGPT and receive response
response = chatGPTAPI.requestChatCompletion(inputText);

responseの中に返答が入ります.

// response
The capital of Japan is Tokyo.

詳細設定

よりChatGPTの関数の機能を多様に実行するため, 詳細設定できるようにしています.

String prompt = "You are helpful assistant.";
int max_tokens = 100;
float temperature = 1;
float top_p = 1;
String model = "gpt-4";
ChatGPTAPI chatGPTAPI(OPENAI_TOKEN, prompt, max_tokens, temperature, top_p, model);

model

ChatGPTのモデルのバージョンを指定できます.

返答能力, トークン数や価格に応じてモデルを選択します.
gpt-3.5よりgpt-4の方が精度良く回答でき, 汎用的な知識を備えていますが, 高価です.
ChatGPT APIを叩いたときに送ったとき, 受け取ったときの文字数, トークンに応じて料金を請求されます.
また一度に扱えるトークンには最大値があります.

モデル最大トークン数入力価格(dollar)/1Kトークン出力価(dollar)格/1Kトークン
gpt-48,1920.030.06
gpt-4-32k32,7680.060.12
gpt-3.5-turbo4,0960.0015 0.002
gpt-3.5-turbo-16k16,3840.0030.004

temperature

temperatureはランダム性を決めており, この値が低ければ, ChatGPTの出力が決定的に, 高ければランダム性が高くなります. 0-2の値を指定できます. 同じ性質の機能を決めるtop_pとは併用できません.

例えば"吾輩は"という有名な小説の序文の一部をChatGPTに投げかけると, temperatureの値によって以下のようにその反応が変わります.

吾輩は

temperature=1

猫である。名前はまだ無い。
夏目漱石の小説『吾輩は猫である』から来ており、主人公の猫が物語の冒頭でこの文章を用いて自己紹介をするというものです。

temperature=2

猫である🐾。ん—a+, もあ=nameⅮみpool「黝始DZ示¾]

top_p

top_pは回答内容を確率の高いものに限定させる機能です. 例えば0.1を指定すると, トップ10%の確率の回答に限定し, 低い確率で選択される回答を選択肢から排除します.

max_tokens

ChatGPTの出力長さを指定できます.

例 : 4回日本語でしりとりしてください

十分に重力長さを確保できている場合(max_tokens=512)

りんご(ご)
ごま(ま)
まくら(ら)
らっぱ(ぱ)

4回分しりとりできました.

全文出力するほど長さを指定しない場合(max_tokens=20)

りんご(ご)
ごま(ま)
まくら(ら)
らっ

文字数の制約(<20(=max_tokens)を受けて, 4回目のらっぱを最後まで出力できていません.

主なプログラム内容

今回はOpenAIのChatGPT APIを呼び出すためのC++クラスを作成しました.

curlコマンドであれば下記のように, modelやmessagesを指定してAPIを使用します.

curl https://api.openai.com/v1/chat/completions \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $OPENAI_API_KEY" \
  -d '{
    "model": "gpt-4",
    "messages": [{"role": "system", "content": "You are a helpful assistant."}, {"role": "user", "content": "Hello!"}]
  }'

上記のようなcurlコマンドの代わりにWiFiClientSecure(client)でHTTPリクエストを行っています.
まず, HTTPS接続を確立し, POSTリクエストを送信します. そのリクエストはJSON形式で, メッセージとともに各種設定を含んでいます.
curlコマンド中では-dで指定していた内容をpayload内で指定しています.

String ChatGPTAPI::requestChatCompletion(String inputText)
{
    String url = "https://api.openai.com/v1/chat/completions";
    String payload = "{\"messages\": [{\"role\": \"system\", \"content\": \"" + prompt + "\"}, {\"role\": \"user\", \"content\": \"" + inputText + "\"}],\"max_tokens\":" + String(maxTokens) + ", \"temperature\":" + float(temperature) + ", \"top_p\":" + float(top_p) + ", \"model\": \"" + model + "\"}";
    if (!client.connect("api.openai.com", 443))
    {
        Serial.println("connection failed");
        return "";
    }

    client.println("POST " + String(url) + " HTTP/1.1");
    client.println("Host: api.openai.com");
    client.println("Content-Type: application/json");
    client.println("Content-Length: " + String(payload.length()));
    client.println("Authorization: Bearer " + String(token));
    client.println("Connection: close");
    client.println();
    client.println(payload);
    ・
    ・
    ・
    return outputText;
}

次に, 応答を読み取り, 結果をJSONとして解析します. 最後に, 応答テキストを抽出して返します.

String ChatGPTAPI::requestChatCompletion(String inputText)
{
    ・
    ・
    ・
    while (client.connected())
    {
        String line = client.readStringUntil('\n');
        if (line == "\r")
        {
            break;
        }
    }

    String response = "";
    while (client.available())
    {
        char c = client.read();
        response += c;
    }

    client.stop();

    // Create DynamicJsonDocument based on the size of the response.
    DynamicJsonDocument jsonDoc(response.length());
    deserializeJson(jsonDoc, response);
    String outputText = jsonDoc["choices"][0]["message"]["content"];

    return outputText;
}

まとめ

この記事では, エッジデバイスの一つであるM5StackでChatGPT APIを使用する方法を解説しました. 記事内ではM5Stackのセットアップ, ChatGPT APIのC++での実装, そして詳細な設定方法について説明しました. みなさんの実装の参考になれば幸いです.