ChatGPT API×LINE botでチャットボットを作成する方法【Google Apps Script】

ChatGPT

今回は、ChatGPT APIをLINEのボットで使う方法について解説します。今回もコピペで動くサンプルコードを載せていますので、ぜひ試してみてください。

2023/3/12 ソースコードをコピーすると全角が混じっていてエラーになるというご指摘をいただいたため、修正しました。コードが長かったので折り畳み表示にした際に、半角が全角になってしまっていました。

コメントくださった「ぬ」さん、ありがとうございました!

2023/5/16 「LINEは既読になるがAPIが動かない」という件について、無料版の試用期間が終了したことが原因であるとご指摘いただきました。コメントくださった「こいる」さん、詳しく教えていただきありがとうございました。

全体像

今回作るBotの全体像は以下になります。

LINEからメッセージを送信したものを、LINE APIと連携したgasが受け取り、さらにそれをChatGPTのAPIに投げ、応答をLINEに返すという流れです。なので、必要となるのは

  • LINEのMessaging API
  • Google Apps Script
  • ChatGPT API

の3つです。いずれも無料で利用できます(ChatGPTは無料分を超えたら有料。詳しくはこちら)。

LINE developersの登録

LINE上でChatGPTを使えるようにするには、LINE developersという開発者用のページにログインする必要があります。LINEのアカウントをお持ちの方は無料で利用できます。

設定方法については以下の記事の「Line Developerへの登録」という章にて解説していますので、そちらをご参照ください。

ChatGPTの登録

GASからChatGPTにアクセスするために、ChatGPT APIのAPIキーを取得します。取得方法については以下の記事の「ChatGPT APIの使い方」という章にて解説していますので、そちらをご参照ください。

Google Apps Scriptの設定

次に、Google Apps Script(GAS)の設定をします。

GASで実装すべきことの確認

GASはLINEとChatGPTのAPIの橋渡し的な存在となります。

したがって、必要な処理は以下の4点です。

  • LINE ⇔ GAS間
    • LINEからのメッセージの受け取り
    • LINEへメッセージの返却
  • GAS ⇔ ChatGPT API間
    • メッセージを質問文としてChatGPTに投げる
    • ChatGPTからの応答文を受け取る

Google Apps Script(GAS)の設定

GASの使い方や実行方法、LINEにスクリプトを設定する方法については以下の記事の「Google Apps Script(GAS)の設定」という章で解説しています。詳細は以下をご確認ください。

GASのサンプルコードはこちら

GASのスクリプトは以下になります。

const GPT_TOKEN = 'XXXXXXXXXXXXXXXXXX'; //ChatGPTのAPIキーを入れてください
const LINE_TOKEN = 'XXXXXXXXXXXXXXXXXX';    // LINEのAPIキーを入れてください
const LINE_ENDPOINT = "https://api.line.me/v2/bot/message/reply";
const GPT_ENDPOINT = 'https://api.openai.com/v1/chat/completions';
const MODEL_NAME = 'gpt-3.5-turbo';
const MODEL_TEMP = 0.5;
const MAX_TOKENS = 256;

// LINEからPOSTリクエストが渡されてきたときに実行される処理
function doPost(e) {

  // LINEからPOSTされるJSON形式のデータをGASで扱える形式(JSオブジェクト)に変換
  const json = JSON.parse(e.postData.contents);
  // LINE側へ応答するためのトークンを作成(LINEからのリクエストに入っているので、それを取得する)
  const reply_token = json.events[0].replyToken;
  if (typeof reply_token === 'undefined') {
    return;
  }

  // LINEから送られてきたメッセージを取得
  const user_message = json.events[0].message.text;
  // 改行で区切って配列にする
  const user_msgarray = user_message.split(/\r\n|\n/);
  // LINEのメッセージをChatGPTに投げるメッセージにセットする
  const messages = [{ "role": "user", "content": user_msgarray[0] }]

  const headers = {
    'Authorization': 'Bearer ' + GPT_TOKEN,
    'Content-type': 'application/json',
  };
  // リクエストオプション
  const options = {
    'method': 'POST',
    'headers': headers,
    'payload': JSON.stringify({
      'model': MODEL_NAME,        // 使用するGPTモデル
      'max_tokens': MAX_TOKENS,   // レスポンストークンの最大値(最大4,096)
      'temperature': MODEL_TEMP,  // 応答の多様性(0-1)※数値が大きいほどランダムな応答になる
      'messages': messages
    })
  };
  // HTTPリクエストでChatGPTのAPIを呼び出す
  const res = JSON.parse(UrlFetchApp.fetch(GPT_ENDPOINT, options).getContentText());

  // ChatGPTから返却されたメッセージを応答メッセージとしてLINEに返す
  lineReply(json, res.choices[0].message.content.trimStart());
}

// LINEへの応答
function lineReply(json, replyText) {

  // 応答用のメッセージを作成
  const message = {
    "replyToken": json.events[0].replyToken,
    "messages": [{
      "type": "text",         // メッセージのタイプ(画像、テキストなど)
      "text": replyText
    }] // メッセージの内容
  };
  // LINE側へデータを返す際に必要となる情報
  options = {
    "method": "post",
    "headers": {
      "Content-Type": "application/json; charset=UTF-8",  // JSON形式を指定、LINEの文字コードはUTF-8
      "Authorization": "Bearer " + LINE_TOKEN           // 認証タイプはBearer(トークン利用)、アクセストークン
    },
    "payload": JSON.stringify(message)                    // 応答文のメッセージをJSON形式に変換する
  };
  // LINEへ応答メッセージを返す
  UrlFetchApp.fetch(LINE_ENDPOINT, options);
}

サンプルコード概要

function doPost(e)というのは、HTTPリクエストを受け取る際の書き方になります。「e」のなかにLINEから渡された情報が入っており、その情報をGASで扱える形式に変換して変数「json」に格納します。

LINEから渡された情報のなかからテキストメッセージを取り出します。取り出したメッセージをChatGPTへの質問文としてセットします。

ChatGPT APIへのリクエストの設定で、「max_tokens」「temperature」とありますが、こちらはご自身の好みや目的に合わせて設定してください。この2つは任意設定なのでなくても動作しますが、LINEの最大文字数は10,000文字までですので何も設定しないと大量のトークンを消費する可能性があります。

ChatGPTにAPIリクエストをし、GAS(JavaScript)で使えるJSONの形式に変換して変数resに格納します。最後にlineReply関数を呼び出してLINEに応答メッセージを返せば完了です。

LINEへの応答は以下のように行います。

参考:LINE Developers/ Messaging APIリファレンス

応用:Botのキャラ設定をする

前回記事でも書いた通り、ChatGPT APIではBOTの振る舞いを設定することができます。今回は、マダム風アシスタントとしてマウントをとってもらいます。

最初のやりとりは何も設定していない状態、二番目のやりとりはマダム風アシスタントとしてふるまわせた結果です。

マウントというより罵られた感

他にも、「関西弁で」「執事風に」「へっぽこアシスタント」「有名レストランのシェフとして」など、設定を追加することで振る舞いに大きな差が生まれます。

気になった点

ChatGPT APIをLINEから使ってみて、気になった点を以下に記載します。

ChatGPT APIは過去の会話履歴を保持していない

ChatGPT APIは前回記事でも触れたとおり、過去の会話履歴を保持していません。履歴を持たせるにはAPIリクエストに過去の会話を持たせる必要がありますが、GASで会話履歴を保持させるには「スクリプトプロパティ」を使用するようです。詳しくは以下の記事が参考になりましたので載せておきます。

翻訳の精度がイマイチ

もうひとつ気になったのが、翻訳の精度です。ChatGPTは英語学習に使えると聞いたので、試しに英文を翻訳させてみたのですが、基本的に直訳なのでかなり機械的な感じがしました。特に、格言などの意訳が必要な内容にはそれが顕著に出ています。

冬の裸足は寒い。でも靴下は嫌い

例えば、「It always seems impossible until it’s done.」の翻訳結果が「それは常に不可能に思える。それが達成されるまで。」となっています。間違ってはいないけど、めちゃくちゃストレートな翻訳となっています。同じ文章をDeepLに翻訳させた結果は以下です。

ChatGPT APIは、文法について質問したり、例文を作ってもらう文には問題ありませんでしたが、翻訳ツールとして使うには向いてなさそうです。

LINE×DeepL APIで翻訳Botを作る方法については以下をご参照ください。

BOTが動かないとき

BOTが動かない原因としては、OpenAI APIの試用期間が終わったことが考えられます。デフォルトでは試用版となっており、18ドル分の無料クレジットが3か月間付与されています。その期間を過ぎると、試用版のままでは一切APIの利用ができなくなります。有料版への移行方法・料金体系については以下の記事にて解説しています。

上記にあてはまらないけど動かないという場合は、こちらのトラブルシューティング記事を確認いただければと思います。

また、不明点等はお気軽にコメントにてご質問ください。

まとめ

今回は、ChatGPT APIを用いてLINE上でChatGPTを使う方法について解説しました。LINE上で使えるようになると、わざわざPCを開く手間もなく、新たにアプリを入れる必要もないのでお手軽ですね。

なお、スマホからはBingアプリ・EdgeアプリからChatGPTのチャット機能を利用することができます。音声入力にも対応しており、現時点では無料なので利用制限など気にせず使いたい方はそちらもご検討ください。

chaso

文系出身、数字が苦手な平凡主婦。塾講師、大手企業SE、不動産事務、Webライター、結婚後はパートタイムでエンジニアをしています。機械音痴だけど効率化や自動化をこよなく愛しています!お仕事の依頼・ご相談は問い合わせよりお願いいたします♪

chasoをフォローする

コメント

  1. より:

    とても分かりやすい内容でした。
    サンプルコードをコピペした際に’などの記号が全角?になっておりGASからエラーが吐かれます。対処法をおしえていただけますでしょうか?

    • chaso chaso より:

      コメントありがとうございます。
      今試してみたところ、私の環境でもエラーになっていました。
      ソースコードが長かったので折り畳み表示にした際に、意図せず文字形式が変換されてしまっているところがあったようです。
      ご指摘いただけて大変助かりました。ブログのほうにも追記して更新させていただきました。
      もし、設定していて分からない点や動かないなどありましたら、また気軽にコメントいただければと思います。

      • より:

        引き続き質問失礼します。
        GAS側でのエラー表示は消えたのですが、LINEBOTが何を送信しても無反応で、既読だけつく状態です。
        APIキーの部分にのみ変更を加え、LINEBOT側の設定も応答メッセージ等もオフにしている状態です。
        何かわかることがあればぜひ教えていただきたいです。

        • chaso chaso より:

          GASで既読はつくのに無反応ということは、以下が考えられます。

          ・「Webhook設定」がオフになっている
          LINE Developers>Messaging API設定の「Webhook設定」がオフになっている
          LINE Official Account Manager>応答設定の「Webhook」がオフになっている
          両方オンになっていないと、応答が得られません。

          ・ChatGPTのAPIキーが設定されていない
          OpenAIの公式サイトから、ChatGPTのAPIキーを発行して、それをGASのスクリプトに設定する必要があります。

          上記を試してみて、また分からないことがありましたらご質問いただければと思います。

  2. より:

    わざわざ返答をくださりありがとうございます。
    webhook設定は両方オンにしておりました。
    また、APIキーの設定も済んでおります。
    また、API keysのLAST USEDの表示がneverから日付に変わっているためchatGPT側に送信する段階までは到達しているかと思います。
    まだわかることがあれば教えていただければ幸いです。
    よろしくお願いいたします。

    • chaso chaso より:

      APIの利用履歴が表示されているということは、LINEのメッセージをもとに
      ChatGPTのAPIを呼び出すところまでは上手くいってそうですね。

      そうなると、
      ・ChatGPTからGASに値がうまくわたっていないか、
      ・ChatGPTから渡された値をLINEに返すときに失敗しているか
      のいずれかが原因かな?と思います。

      lineReply(json, res.choices[0].message.content.trimStart());
      を、
      lineReply(json, “test”);
      に変えて実行してみてください。
      これでLINEに応答がなかったらLINEへの応答部分に原因がありそうです。

      LINE_TOKENの値に、「チャネルアクセストークン(長期)」の値を
      正しく設定できているか確認してみてください。

      LINEに応答があった場合は、
      lineReply(json, res.choices[0].message.content.trimStart());
      を、
      lineReply(json, res.choices[0].message.content);

      にして試してみてください。
      またいつでもご質問いただければと思います。

      • より:

        LINEのアクセストークンの値の設定確認しました。
        また、lineReply(json, res.choices[0].message.content.trimStart());
        を、
        lineReply(json, “test”);
        に変更した際も応答がありませんでした。
        別サイトに記載のおうむ返しをするGASのソースコードでデプロイした際には正常に動作したので、LINE DevelopersやLINE Official Account Managerの設定などには問題はないと思います。
        度重なる質問でご迷惑おかけしますが、よろしくお願いします。

        • chaso chaso より:

          GASからLINEに応答する際に失敗しているようですが、
          同じソースコード、同じ設定をしていて、何が失敗の原因になっているかこちらでは分かりかねますので、
          参考にされた別サイトのソースコードをベースに、ChatGPT APIの通信用に書き換えて動かしてみていただければと思います。

          オウム返しをする場合、LINEに値を返却するときに以下のようになっているかと思いますので、
          ‘messages’: [{
          ‘type’: ‘text’,
          ‘text’: LINEから受け取った値,
          }]

          ‘text’の値に、ChatGPTのAPIから返却された値を入れてみてください。
          ソースコードの編集が難しそうであれば、こちらで中身を確認させていただきますのでまた連絡いただければと思います!

          • より:

            ソースコードの編集を試してみましたが難しそうでしたのでご連絡させていただきました。
            以下成功したおうむ返しのソースコードになります。

            /** メッセージが送付された際に、実行される関数 */
            function doPost(e) {

            // アクセストークン
            const ACCESS_TOKEN = ‘トークン入れる’
            const replyToken = JSON.parse(e.postData.contents).events[0].replyToken;
            const userMessage = JSON.parse(e.postData.contents).events[0].message.text;
            const url = ‘https://api.line.me/v2/bot/message/reply’;
            const headers = {
            ‘Content-Type’: ‘application/json; charset=UTF-8’,
            ‘Authorization’: ‘Bearer ‘ + ACCESS_TOKEN
            };

            //メッセージ送信内容
            const payload = JSON.stringify({
            ‘replyToken’: replyToken,
            ‘messages’: [{
            ‘type’: ‘text’,
            ‘text’: userMessage
            }]
            })

            const options = {
            ‘headers’: headers,
            ‘method’: ‘post’,
            ‘payload’: payload
            };

            // レスポンス
            const response = UrlFetchApp.fetch(url, options);

            const data = JSON.parse(response.getContentText());
            console.log(data);
            }

          • chaso chaso より:

            コメントありがとうございます。
            上記のソースコードをベースに、ChatGPTとの通信処理を追加しました。

            /* ▼追加 */
            const GPT_TOKEN = ‘GPTのアクセストークン’;
            const LINE_ENDPOINT = “https://api.line.me/v2/bot/message/reply”;
            const GPT_ENDPOINT = ‘https://api.openai.com/v1/chat/completions’;
            const MODEL_NAME = ‘gpt-3.5-turbo’;
            const MODEL_TEMP = 0.5;
            const MAX_TOKENS = 256;
            /* ▲追加 */

            /** メッセージが送付された際に、実行される関数 */
            function doPost(e) {

            // アクセストークン
            const ACCESS_TOKEN = ‘トークン入れる’;
            const replyToken = JSON.parse(e.postData.contents).events[0].replyToken;
            const userMessage = JSON.parse(e.postData.contents).events[0].message.text;

            /* ▼追加 */
            // LINEのメッセージをChatGPTに投げるメッセージにセットする
            const messages = [{ “role”: “user”, “content”: userMessage }]
            const headers_gpt = {
            ‘Authorization’: ‘Bearer ‘ + GPT_TOKEN,
            ‘Content-type’: ‘application/json’,
            };
            // リクエストオプション
            const options_gpt = {
            ‘method’: ‘POST’,
            ‘headers’: headers_gpt,
            ‘payload’: JSON.stringify({
            ‘model’: MODEL_NAME, // 使用するGPTモデル
            ‘max_tokens’: MAX_TOKENS, // レスポンストークンの最大値(最大4,096)
            ‘temperature’: MODEL_TEMP, // 応答の多様性(0-1)※数値が大きいほどランダムな応答になる
            ‘messages’: messages
            })
            };
            // HTTPリクエストでChatGPTのAPIを呼び出す
            const res = JSON.parse(UrlFetchApp.fetch(GPT_ENDPOINT, options_gpt).getContentText());
            /* ▲追加 */

            const url = “https://api.line.me/v2/bot/message/reply”;
            const headers = {
            ‘Authorization’: ‘Bearer ‘ + ACCESS_TOKEN,
            ‘Content-type’: ‘application/json’
            };

            //メッセージ送信内容
            const payload = JSON.stringify({
            “replyToken”: replyToken,
            “messages”: [{
            “type”: “text”,
            “text”: res.choices[0].message.content // “messages”から変更
            }]
            })

            const options = {
            “headers”: headers,
            “method”: “post”,
            “payload”: payload
            };

            // レスポンス
            const response = UrlFetchApp.fetch(url, options);

            const data = JSON.parse(response.getContentText());
            console.log(data);
            }

            追加・変更した部分についてはコメントしています。
            ご確認よろしくお願いします。

  3. あむ より:

    エラー
    TypeError: Cannot read properties of undefined (reading ‘postData’)
    doPost @ コード.gs:13
    となってしまいます
    解決策ご教授くださいmm

    • chaso chaso より:

      コメントありがとうございます!

      エラー内容を見てみると、LINEから渡されたデータを取得するところでエラーになっているようです。
      このエラーは、GASのスクリプトエディタから「実行」を押して動かした時に発生するエラーです。

      本記事のサンプルソースは、LINEからなにかしらのメッセージが送信した際に動くものですので、
      GASから実行するとエラーになります。
      LINEからメッセージを投稿して動作することを確認してみてください。
      またわからなければお気軽にコメントください!

  4. いえ より:

    記事を書いてくださりありがとうございます。
    手順通りに行っているはずなのですが,LINEで何を送っても既読が付くのみとなってしまいます。以下,現状です。

    ・「Webhook設定」はオン
    LINE Developers>Messaging API設定の「Webhook設定」オン
    LINE Official Account Manager>応答設定の「Webhook」オン

    ・APIキー設定済
    LINEもchatGPTもAPIキー取得し,設定

    ・API keysのLAST USEDの表示がneverのまま

    botのキャラ設定などは行っておらず,サンプルコードをそのままとりあえずいれている状態です。
    ご教授いただければ幸いです。

    • chaso chaso より:

      コメントいただきありがとうございます。

      ChatGPTのAPI使用履歴がneverのままということは、ChatGPTの通信で失敗していると思われます。
      以下の手順で、エラーの原因を確認していただければと思います。

      1. LINEの応答ができるか確認
      1-1. ChatGPTリクエストの部分をコメントアウト
      const res = JSON.parse(UrlFetchApp.fetch(GPT_ENDPOINT, options).getContentText()); を、
      // const res = JSON.parse(UrlFetchApp.fetch(GPT_ENDPOINT, options).getContentText()); に変更

      2-2. LINEへの応答部分を変更
      lineReply(json, res.choices[0].message.content.trimStart());
      を、
      lineReply(json, ‘test’);
      に変更

      上記手順で、LINEに「test」と返ってきたら、続いて以下を試してみてください。

      2. ChatGPTの応答を確認
      2-1. LINE応答部分をコメントアウト
      lineReply(json, res.choices[0].message.content.trimStart()); を、
      // lineReply(json, res.choices[0].message.content.trimStart()); に変更

      上記を実行して、ChatGPTのAPI利用履歴が更新されるか確認してみてください。
      もし、更新されないようでしたら、再度APIキーを取り直して実行してみてください。

      それでも解決しない場合は、再度ご連絡いただければと思います。よろしくお願いいたします!

  5. こいる より:

    私も他の方と同じ現象が起きていましたが、原因を特定しましたのでここに記します。

    もう解決されていると思いますが、後続のために。

    原因は「OpenAIの試用期間が終わったため」です。APIの料金は18ドルほど無料で付与されますが、3ヵ月経つと自動的に未使用分も使用不可になります。

    そうすると、コードや接続自体は正しくても挙動がOpenAIのAPIに投げられた所で終了するため、ラインには既読だけがつく状態となります。(他の原因でも同じ挙動になるため分かりづらい)
    おとなしくお金を払うか他アカウントを使わない限り解決しません。(従量課金制なのでリミットの設定をお忘れなく)

    他のサイトでも同じ現象で悩まれている方は多い気がしているので、この文章を見た方はまずはOpenAIのUsageで自分のトークンの使用期限が過ぎていないか確認してみてください。

    他のサイトも見て回りましたが、回答者は大体課金済みのため、構造的に気づきにくい問題だと思います。

    私は3時間ほどかけてあらゆるサイトを参考にしてコードを書き換えていましたが、これが根本の原因で課金したら通常通りの挙動になりました。一人でも無駄な時間を過ごす方が減る事を祈っています。

    • chaso chaso より:

      コメントを残していただき、ありがとうございます。

      こいるさんが書かれている通り、私が実行した際のアカウントは無料期間中の状態でした。
      確かに記事を書いている方は課金済みの人が多く、気づきにくい問題ですね。
      せっかく記事を読みにきていただいたのに、お力になれずすみませんでした。

      詳しく説明していただき、大変参考になりました。
      ブログ記事にもこの旨追記しておきます。

タイトルとURLをコピーしました