はじめに
本記事では、OpenAI の ChatGPT API と Whisper API を使って、以下のような LINE のチャットボットを作成します。
- ボットに人格設定可能
- 音声入力にも対応
- スタンプの意味も解釈して返答可能
LINEbot を作ったことがない方はもちろん、プログラミング経験がほとんどない方でも実装できる方法をご紹介します!
なお、本記事は以下の記事の続編です。こちらもよろしければご覧ください。
追記
文脈理解(履歴の記憶)については以下の記事で解説しています。
こちらもあわせてぜひご確認ください(以下の記事は、本記事の続編です)。
1. LINE bot の作成
1-1. LINE Developers に登録する
以下のサイトにアクセスし、Developer登録をしてください。ログインは普段お使いのLINEアカウントで大丈夫です。
1-2. プロバイダーを作成する
以下のプロバイダー作成画面から、新規プロバイダーを作成してください。
プロバイダーというのは会社名のようなものだと思ってください。複数のbotを作成するような場合は、1つのプロバイダーに複数のbotがぶら下がるイメージです。
1-3. チャネルを作成する
作成したプロバイダーを選択し、画面内の新規チャネル作成
を選択してください。
チャネルの種類はMessaging API
を選択してください。
また、ここで設定したチャネルアイコン
とチャネル名
が、そのままbotのアイコンとアカウント名になるので、ご注意ください。
1-4. 作成されたアカウントを確認する
以下のアカウント管理画面に、上記で作成したアカウントがあることを確認してください。
1-5. チャネルアクセストークンを取得する
LINE Developers の画面にもどり、上記で作成したプロバイダー、チャネルを選択します。
その中のMessaging API設定
というタブの最下部にあるチャネルアクセストークン
というところで発行
をクリックします。すると長いトークン(文字列)が発行されるので、これを控えておきます。
2. OpenAI の APIキーを取得する
2-1. OpenAIのアカウントを作成する
以下から作成してください。
2-2. 無料利用枠があるかどうか確認する
上記で作成したアカウントにログインした状態で、以下から無料枠があるかどうか確認してください。
Free trial usage
があればOKです。
2023年3月時点では、アカウント新規作成から3ヶ月間は毎月18ドルの無料枠があるのですが、もしこの提供が終了してしまった場合は、クレカの登録等が必要になってきます。
また、「1つの連絡先に紐づく無料枠は1つまで」という制約があり、たとえば個人アカウントを作った後に会社アカウントを作成し、それらのアカウントが同じ電話番号で紐づいていた場合などは、後から作った会社アカウントの方には無料枠がつかないのでご注意ください(地味にハマりやすいポイントです)。
2-3. APIキーを発行する
以下の画面にログインし、+ Create new secret key
を選択してAPIキーを発行してください。
ここで発行したAPIキーは、すぐに控えないと後から見られないので注意してください。といっても、分からなくなったら新しいものを発行すれば良いので、あまり問題はないです。
3. GoogleAppsScript を準備する
3-1. 新しいスプレッドシートを準備する
Googleスプレッドシートを新規作成し、log
とsettings
という名前のシートを準備します。
log
にはメッセージの履歴、settings
にはbotのキャラクター設定を追加していきます。
3-2. キャラクターの設定を記載する
チャットボットのキャラクターを設定します。
キャラクターの設定を、settings
シートのA1
セルに入力します。
キャラクター設定についてはたくさんの方々が研究されているので、以下のような記事が大変参考になります!色々と試してみてください。
3-3. セルに名前をつける
settings
シートのA1
セルを選択した状態で、メニューの「名前付き範囲」から、このセルにai_settings
という名前をつけてください。以下の画像の通りになればOKです。
3-4. ログ用のシートに見出しをつける
log
シートの1行目に、見出しをつけていきます。
以下の各文字列を、A1
〜I1
セルにそれぞれコピペしてください。
timestamp
userId
messageType
userMessage
botMessage
promptTokens
completionTokens
totalTokens
errorMessage
各項目の解説
項目 | 解説 |
---|---|
timestamp | LINEbotが返答した日時 |
userId | メッセージを返したユーザーのuserId |
messageType | 文字列ならtext 、スタンプならsticker 、音声ならaudio など |
userMessage | ユーザーから送られてきたメッセージ |
botMessage | botが回答したメッセージ |
promptTokens | ChatGPTのAPIに送ったメッセージのトークン数 |
completionTokens | ChatGPTのAPIから受信したメッセージのトークン数 |
totalTokens | 合計のトークン数 |
errorMessage | エラー発生時のメッセージ |
3-5. スクリプトプロパティを設定する
スクリプトプロパティとは、各種パスワード等、コード内にベタ打ちしたくない機密性の高い情報を保存しておく場所です。
まずはスプレッドシートのメニューから拡張機能
> Apps Script
を選択します。すると、Google Apps Script のエディタ画面が開きます。
続いて、左メニューの歯車マークプロジェクトの設定
を選択すると、一番下にスクリプト プロパティを編集
というボタンがあるので、それをクリック。以下の2つを設定します。
プロパティ | 値 |
---|---|
LINE_ACCESS_TOKEN | 1-5.で取得したLINEのチャネルアクセストークン |
OPENAI_API_KEY | 2-3.で取得した OpenAIのAPIキー |
3-6. コードをコピペする
左メニューの<>
のマークエディタ
に戻り、最初から入力されているソースコードを削除した上で、以下のコードをコピペします。
//スプレッドシートからAIのキャラクター設定を取得する
function getAiSettings(){
try{
const ss = SpreadsheetApp.getActiveSpreadsheet();
const aiSettings = ss.getRangeByName("ai_settings").getValue();
return aiSettings;
} catch(e){
return null;
}
}
//promptの内容をキャラクター設定されたAI(ChatGPT API)に投げて回答を取得する
function getReply(prompt){
const OPENAI_API_KEY = PropertiesService.getScriptProperties().getProperty("OPENAI_API_KEY");
let messages = [{
"role": "user",
"content": prompt
}];
const characterSettings = getAiSettings();
if(characterSettings != null){
messages.unshift({
"role": "system",
"content": characterSettings
});
}
const payload = {
"model": "gpt-3.5-turbo",
"temperature" : 0.5, //0〜1で設定。大きいほどランダム性が強い
"max_tokens": 500, //LINEのメッセージ文字数制限が500文字なので、それに合わせて調整
"messages": messages
};
const requestOptions = {
"method": "post",
"headers": {
"Content-Type": "application/json",
"Authorization": "Bearer "+ OPENAI_API_KEY
},
"payload": JSON.stringify(payload)
};
const response = UrlFetchApp.fetch("https://api.openai.com/v1/chat/completions", requestOptions);
const responseText = response.getContentText();
const json = JSON.parse(responseText);
const retObj = {
"payload": payload,
"message": json.choices[0].message.content,
"usage": json.usage
};
return retObj;
}
//file(音声ファイル)の内容をWhisperAPIを利用して文字列として取得する
function speechToText(file){
const OPENAI_API_KEY = PropertiesService.getScriptProperties().getProperty("OPENAI_API_KEY");
const payload = {
"model": "whisper-1",
"temperature" : 0,
"language": "ja", //日本語以外にも対応する場合はこのプロパティは外す
"file": file
};
const requestOptions = {
"method": "post",
"headers": {
"Authorization": "Bearer "+ OPENAI_API_KEY
},
"payload": payload
};
try{
const response = UrlFetchApp.fetch("https://api.openai.com/v1/audio/transcriptions", requestOptions);
const responseText = response.getContentText();
const json = JSON.parse(responseText);
const text = json.text
return text;
} catch(e){
return e.message;
}
}
//LINEでユーザーから送られてきた音声ファイルを取得する
function getContentByUser(messageId){
const LINE_ACCESS_TOKEN = PropertiesService.getScriptProperties().getProperty("LINE_ACCESS_TOKEN");
const url = `https://api-data.line.me/v2/bot/message/${messageId}/content`;
const requestOptions = {
'headers': {
'Authorization': 'Bearer ' + LINE_ACCESS_TOKEN,
},
'method': 'get'
};
const response = UrlFetchApp.fetch(url, requestOptions);
return response.getBlob().setName(`${messageId}.m4a`); //拡張子を指定しないとWhisperAPI側でエラーになるので注意
}
//LINEでユーザーから送られてきたメッセージ(スタンプや音声含む)を文字列に変換する
function convertMessageObjToText(messageObj){
let result = "";
const messageType = messageObj.type;
switch(messageType){
case "text": //文字列
result = messageObj.text;
break;
case "sticker": //スタンプ。キーワードが設定されていればそれを取得する
if(messageObj.keywords === undefined){
result = "???";
} else{
result = messageObj.keywords.join(",");
}
break;
case "image": //画像
result = "この画像が分かりますか?";
break;
case "video": //動画
result = "この動画が分かりますか?";
break;
case "audio": //音声。文字起こしする
if(messageObj.contentProvider.type === "line"){
const audioFile = getContentByUser(messageObj.id);
const transcriptedText = speechToText(audioFile);
result = transcriptedText;
} else{
result = "この音声が聞こえますか?";
}
break;
case "file": //ファイル
result = "このファイルは見られますか?";
break;
case "location": //位置情報
let locationInfo = messageObj.title ? messageObj.title + "\n" : "";
locationInfo += messageObj.address ? messageObj.address + "\n" : "";
locationInfo += `latitude:${messageObj.latitude}\n`;
locationInfo += `longitude:${messageObj.longitude}`;
result = "ここはどんな場所ですか?\n" + locationInfo;
break;
default: //その他
result = "???";
}
return result;
}
//logシートにログを出力する
function appendLog(logArray){
const ss = SpreadsheetApp.getActiveSpreadsheet();
const logSheet = ss.getSheetByName("log");
logSheet.appendRow(logArray);
}
//リクエストが送られるとこの関数が実行される
function doPost(e) {
const LINE_ACCESS_TOKEN = PropertiesService.getScriptProperties().getProperty("LINE_ACCESS_TOKEN");
const events = JSON.parse(e.postData.contents).events;
const url = 'https://api.line.me/v2/bot/message/reply';
const event = events[0];
const replyToken = event.replyToken;
const userMessage = convertMessageObjToText(event.message);
const gptReply = getReply(userMessage);
const botMessage = gptReply.message;
const requestOptions = {
'headers': {
'Content-Type': 'application/json; charset=UTF-8',
'Authorization': 'Bearer ' + LINE_ACCESS_TOKEN,
},
'method': 'post',
'payload': JSON.stringify({
'replyToken': replyToken,
'messages': [{
'type': 'text',
'text': botMessage,
}]
})
};
const response = UrlFetchApp.fetch(url, requestOptions);
const timestamp = Utilities.formatDate(new Date(), "Asia/Tokyo", "yyyy/MM/dd HH:mm:ss");
const userId = event.source.userId;
const messageType = event.message.type;
let errorMessage = "";
if(response !== {}){
errorMessage = response.message;
}
const promptTokens = gptReply.usage.prompt_tokens;
const completionTokens = gptReply.usage.completion_tokens;
const totalTokens = gptReply.usage.total_tokens;
appendLog([
timestamp,
userId,
messageType,
userMessage,
botMessage,
promptTokens,
completionTokens,
totalTokens,
errorMessage
]);
return ContentService.createTextOutput(JSON.stringify({"content": "success"})).setMimeType(ContentService.MimeType.JSON);
}
3-7. 必要な権限を付与する
エディタで一度実行
ボタンを押しましょう。すると、いくつかの権限の許可確認画面が出てくるので、許可(Accept)
を押します。
このプロセスをすっ飛ばすとエラーになるので、要注意です。
実行結果はエラーになると思いますが、気にしなくて大丈夫です。
3-8. ウェブアプリをデプロイする
エディタ右上のデプロイ
ボタンを押し、
新しいデプロイ
> 種類の選択
> ウェブアプリ
を選択します。
また、アクセスできるユーザー
を全員
にした上で、右下のデプロイ
ボタンを押します。
すると、URLが発行されるので、それを控えておきます。
4. LINEbotの設定をする
4-1. WebhookURLを設定する
LINE Developers に戻ります。
作成したプロバイダー
> 作成したチャネル
> Messaging API設定
に進み、Webhook設定
のWebhook URL
の編集
ボタンを押し、3-8.で取得したURLを設定します。
その後、検証
ボタンを押すと、疎通確認ができます。
4-2. Webhookの利用をオンにする
その真下のWebhookの利用
のトグルスイッチをONにします。
4-3. 応答メッセージをオフにする
その少し下のLINE公式アカウント機能
内の応答メッセージ
をOFFにします
5. テストする
5-1. LINEbotを友達追加する
アカウント管理画面を開き、今回作成したbotを選択します。
その後、左メニュー内の友だち追加ガイド
から、友達追加用のURLやQRコードが発行できるので、それを使用して自分のアカウントにbotを友達登録します。
5-2. キャラクター設定を調整する
LINEでbotとやり取りしながら、キャラクター設定を調整します。
スプレッドシートのsettings
シートのA1
セルを編集すればすぐに反映されるので、好みのキャラクターができるまでカスタマイズしてみてください。
(キャラクター設定の変更後、再度デプロイする必要はありません)
あとがき
Whisper APIをGASで使ったという事例が見つからなかったので、音声ファイルの取り扱いなどで地味に苦労しました。
Whisper APIは初めて使ってみましたが、性能がかなり高くて驚いています。ほぼ正確に文字起こししてくれます。
ここまでできたら、次は音声合成ソフトなどを使用して、会話できるbotも作ってみたいですね!
追記
文脈理解(履歴の記憶)については以下の記事で解説しています。
こちらもあわせてぜひご確認ください(以下の記事は、本記事の続編です)。