見出し画像

非エンジニアがGPTのCodeinterpreterでLINEチャットボットのポケモン図鑑を作ってみた

こんにちは。ニャンタです。

今回、思いつきでLINEチャットボットのポケモン図鑑を作ってみました。

コード生成に用いたツールは
GPTのCode interpreterです。

ちなみに私は元学校教員で非エンジニアです(情報科教員でしたが開発経験はありません!)
今回のBot作成にあたり、あらかじめ何か1つBOTを作ってみると流れが理解できるかと思います。

私は以下のサイトを参考に1つBOTを作ってみました。

送ったメッセージが即スプレッドシートに反映するのは感動しました!!


さて、今回作成したポケモン図鑑チャットボットについての話です。

作成したチャットボットの成果は以下のようになりました

仕様は以下のとおりです

  1. 番号(全国図鑑No)を入力するとポケモンの名前、長さ、体重などが表示

  2. ポケモンの画像も表示

ポケモンの番号以外に名前でも検索できますが、現状、日本語での検索に対応していないです。

備忘録を兼ねて作成した工程を記事にします。
ポケモンだけでなく、他のチャットボット生成にも役に立つと思います。
良かったらご覧ください。

注意!LINECHATBOTにAPIを引っ掛ける部分(Webhook)でエラーが発生していますがそれを無視して進めています!無事に動いたのでよし!という形で終わらせています。ご了承ください。


使ったツール・サービス

まずはこのLINEチャットボット作成にあたり使用したツール、サービスを紹介します。

LINE Developers
LINEチャットボットのアカウント作成、設定に必ず必要です。

GPT Codeinterpreter
コード生成を行ってくれるサービスです。私が作成したときのVersionはgpt-4-1106-previewです。
トークン数によって課金されるため、GPT4のDataAnalysisプラグインをインストールするほうがトークン数による課金がないため、経済的かもしれません。

GoogleAppScript
コードを実装するためのサービスです。通称GASと呼ばれています

コード生成からLINEチャットボットの作成までの流れは以下の図のとおりです

GPT Codeinterpreter

GPT4のCodeinterpreterではコード生成に特化しており、通常のGPT4よりも正確なコード生成が可能です。なお、GPT4の登録(有料)の登録が必須で、Codeinterpreterの使用は入出力されるトークンによって費用がかかります。
※課金の上限設定もできますので予め設定することをおすすめします

1.Assistantsの作成

まずはコード生成をするアシスタントを作成します


私がこれまで作成したアシスタント。今回使用するのはプログラマーBです

アシスタントの設定は以下のようにしました。

Name:プログラマーB
Instructions:あなたは優秀なプログラマです。
要求に対してできる限りコーディングをしてください。
Model:gpt-4-1106-preview

Code interpreterをONにします


2.スクリプト生成

Code interpreterはPlayground上で動作させることができます。下のメッセージBOXにプロンプトを記述し、Add and run で応答が返ってきます。
なお、スレッドの履歴はブラウザを閉じてしまうと消えてしまうので注意してください。

サンプルのGASプログラムを貼り付けて、以下のように命令しました。
※サンプルプログラムはLINEチャットボットで用いられているGASプログラムであれば何でもOKかと思います

User
GASプログラムを参考にポケモンの名前または番号からポケモンの情報を返すLINEボットのスクリプトを作成してほしい

このような結果が返ってきましたが、この後にポケモン図鑑の情報を取得するための具体的な方法について指示します。ポケモン図鑑APIであるpokeapiを使用することにしました。
https://pokeapi.co/

User
pokeapiを用いたスクリプトを作成してほしい

この後、さらに改良を重ねます
なお、プログラムを修正した場合はコードも貼り付けて命令をします。
User
プログラムを改良したいです。
#要件
1.ポケモンの情報は日本語にしてください。
2.ポケモンの情報には長さや体重、説明も含めてください。
3.それぞれの情報は改行して表示してください
4.画像も表示できるようにしてください。

このようにプログラム生成をGPTとあれこれ行っていきます。
プログラムにはエラーがあったりなかったりなので、この後に紹介するGoogleAppScriptでの動作を確認しながら進めることをおすすめします。

最終的なプログラムはこの記事の末尾に載せておきます

GoogleAppScriptでスクリプト記述

GoogleAppScript(GAS)とはGoogleのスクリプトの記述・実行ができるサービスです。GASを用いるとグーグル上の様々なサービスを活用したり、今回作成するチャットボットのAPIを作成することができます。

プロジェクトの作成

新しいプロジェクトを作成します
プレジェクト名は何でもOKです。

GASの開発画面が表示されます

CodeInterpreterで生成されたコードをそのまま貼り付けます
実行するとエラーが出るかどうかがわかります。


一旦、LINE DevelopersにてLINE BOTのアカウント作成を行い、
最後にGoogleAppScriptにてコード内のチャネルアクセストークン入力、デプロイを行います。


LINE Developersでアカウント作成、設定→公開

1.BOT(チャネル)の作成

LINE Developersにアクセスします。

コンソールにログインします

LINEアカウントを用いてログインします。

自分のアカウントを使って大丈夫かな?という不安があるかも知れませんが、
作成するチャットボットは普段使っているのLINEアカウントとは独立しているので、IDが漏れるといった心配はありません

プロバイダー を作成し、新規チャネルを作成 をクリックします

MessagingAPI を選択します

チャネル名やチャネルの画像など必須項目を入力します。

作成をクリックします。

2.チャネルの詳細設定

Messaging API設定 をクリックします

つづいてWebhookの設定を行います

※WebhookのURLはGASでのデプロイ後に入力します

Webhookの利用をONにします

LINE公式アカウント機能はすべてOFFにします
(指定以外の応答をさせないため)

編集ボタンで無効化できます

3.チャネルアクセストークンのコピー→スクリプトに貼り付け

チャンネルアクセストークンをコピーします

チャネルアクセストークンを発行、コピーします。

GoogleAppScriptに移動し、
GoogleAppScriptのコード内に貼り付けます
※生成したコード内に貼り付ける場所があります。

ちなみにチャネルアクセストークンは流出すると作成したBOTを乗っ取られるリスクがあるので注意してください(その場合、再発行すれば回避できます)


デプロイ

問題がなければ デプロイ新しいデプロイ をクリックします
※デプロイとはネット上に配置することを意味しています。

ここではウェブアプリを選びます


設定は以下の通り
説明:なくてもOK
ウェブアプリ:自分
アクセスできるユーザー:全員

設定が完了したら デプロイ をクリックします。
この作業はしばらく時間がかかります。

ウェブアプリURLをコピーします。

再びLINE Developersに戻ります。

先程のMessaging API設定ページに移動します。

Webhook設定(LINEBOTにあてるAPI)のWebhook URLにコピーしたURLを貼り付けます

通常、検証をクリックして「成功」が表示されればOKです。
今回生成したコードではエラーがでてしまいましたが無事に動作しています(詰めが甘い・・・)

なお、PokeAPIは外部のAPIになりますのでアクセス許可が必要になります。

不具合があれば都度、スクリプトの修正を行います。


Botを友だちに追加→実行!


最後にMessagingAPIの設定ページにQRコードがありますので、普段使っているLINEアカウントにて追加すれば完了です!

ポケモンの全国図鑑番号または名前(英語)で検索ができます

参考にしたサイト

LINEチャットボットを作成するにあたり、役に立ったサイトを紹介します。

ご覧いただきありがとうございました!!

今回、生成したスクリプト↓

function doPost(e) {
  if (!e || !e.postData) {
    return ContentService.createTextOutput('No data received.');
  }

  let data = JSON.parse(e.postData.contents);
  let events = data.events;

  events.forEach(function(event) {
    if (event.type === 'message' && event.message.type === 'text') {
      let replyToken = event.replyToken;
      let inputText = event.message.text.trim();

      if (/^\d+$/i.test(inputText)) { // 数字のみの場合、ポケモンの番号として扱う
        getPokemonData(replyToken, inputText);
      } else { // 文字列の場合、ポケモンの名前として扱う
        getPokemonData(replyToken, inputText.toLowerCase());
      }
    }
  });

  return ContentService.createTextOutput().setMimeType(ContentService.MimeType.JSON);
}

function getPokemonData(replyToken, identifier) {
  let apiUrl = 'https://pokeapi.co/api/v2/pokemon/' + identifier;
  let response = UrlFetchApp.fetch(apiUrl, {muteHttpExceptions: true});
  if (response.getResponseCode() === 200) {
    let pokemonData = JSON.parse(response.getContentText());
    
    // 種の情報を取得するためspecies APIを呼び出します
    let speciesUrl = pokemonData.species.url;
    let speciesResponse = UrlFetchApp.fetch(speciesUrl, {muteHttpExceptions: true});
    let speciesData = JSON.parse(speciesResponse.getContentText());
    
    // 日本語のエントリーを見つけます
    let japaneseEntry = speciesData.flavor_text_entries.find(function(entry) {
      return entry.language.name === 'ja';
    });
    
    let content = formatPokemonData(pokemonData, japaneseEntry);
    let imageContent = {
      "type": "image",
      "originalContentUrl": pokemonData.sprites.front_default,
      "previewImageUrl": pokemonData.sprites.front_default
    };
    

    reply(replyToken, [content, imageContent]);
  } else {
    replyTextMessage(replyToken, 'ポケモンが見つかりません');
  }
}

function formatPokemonData(pokemonData, japaneseEntry) {
  let name = pokemonData.species.name;
  let id = pokemonData.id;
  let height = pokemonData.height / 10; // decimeters to meters
  let weight = pokemonData.weight / 10; // hectograms to kilograms
  
  let types = pokemonData.types.map(function(t) { return t.type.name; }).join(', ');
  let stats = formatStats(pokemonData.stats);
  let description = japaneseEntry ? japaneseEntry.flavor_text.replace(/\n|\f/g, ' ') : '説明なし';

  return {
    "type": "text",
    "text": `名前: ${name}\n番号: ${id}\nタイプ: ${types}\n長さ: ${height} m\n体重: ${weight} kg\n${stats}\n説明: ${description}`
  };
}

// ポケモンのステータスを整形する新しい関数
function formatStats(stats) {
  let statsOutput = 'ステータス:';
  stats.forEach(function(stat) {
    let statName;
    switch (stat.stat.name) {
      case 'hp':               statName = 'HP'; break;
      case 'attack':           statName = '攻撃'; break;
      case 'defense':          statName = '防御'; break;
      case 'special-attack':   statName = '特別攻撃'; break;
      case 'special-defense':  statName = '特別防御'; break;
      case 'speed':            statName = '速さ'; break;
      default:                 statName = stat.stat.name; break;
    }
    statsOutput += `\n${statName}: ${stat.base_stat}`;
  });
  return statsOutput;
}

function reply(replyToken, messages) {
  let channelAccessToken = 'YOUR_CHANNEL_ACCESS_TOKEN'; // LINEのアクセストークンに置き換える必要があります
  let replyUrl = 'https://api.line.me/v2/bot/message/reply';
  let headers = {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer ' + channelAccessToken
  };
  
  let postData = {
    'replyToken': replyToken,
    'messages': messages
  };

  let options = {
    'method': 'post',
    'headers': headers,
    'payload': JSON.stringify(postData),
    'muteHttpExceptions': true
  };

  UrlFetchApp.fetch(replyUrl, options);
}

function replyTextMessage(replyToken, content) {
  reply(replyToken, [{ "type": "text", "text": content}]);
}

この記事が参加している募集

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