LINE×Gemini (Google版ChatGPT) 非エンジニアでも完全に理解できる作り方

この記事は2023年度LINE Developer's Community アドベントカレンダーの22日目の記事です。

https://qiita.com/advent-calendar/2023/lineapi

▼今回の記事はYouTubeでも解説しています。むしろこの記事は、動画の補足としてご活用ください。

<iframe width="560" height="370" src="https://www.youtube.com/embed/CraEt9qG9Ys?si=tTVEhnzNIYTTBGf5&rel=0&controls=0&showinfo=0" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>

※2023年12月22日18時に動画公開

# やりたいこと

Googleが「Gemini Pro」を公開＆Bardに搭載して以来、随時アップデートして進化が止まりませんね。今回はAPIも公開したとのことなので、早速LINEチャットボットを作ってみたいなと思いました。

LINEで動かすところまで頑張って実装してやり方を動画と記事にしたので、ゆっくりしていってね！

▼サンプルで作ったGemini搭載LINEボット

https://lin.ee/nCY9x2fI

▼Geminiについては、下記の動画でも解説しています。

https://youtu.be/OGQT0PL5Kfs

# ChatGPTとの料金の比較

無料ではないですが、Gemini API もお金がかかります。トークンの考え方が一緒じゃないので、単純に比較はできないのですが、表にしてみました。

|モデル |入力トークンの金額 |出力トークンの金額 |
|:-:|:-:|:-:|
|gemini-pro |$0.00025 / 1K char |$0.0005 / 1K char |
|gpt-3.5-turbo-1106 |$0.0010 / 1K char |$0.0020 / 1K tokens |
|gpt-4-1106-preview |$0.01 / 1K tokens |$0.03 / 1K tokens |

* Gemini Pro は、１分間に60クエリまで無料
* Gemini Pro は、１トークン約４文字で、100トークンは約60～80語に相当
* GPT は、英語は１単語１トークンだが、日本語は基本的に１文字２〜３トークン使用

ちなみに、Gemini ProのAPIを使用してみましたが、Bardと同様に爆速でした。怖いくらい安くて速いです。そして、まだGemini Proの上位互換のGemini Ultraが来年控えています。GPT-3.5 Turbo よりも安くて、GPT-4 Turbo よりも精度が良いとなれば、いよいよAI戦争の流れが変わりますね。

それでは、とりあえず今のうちに、LINE × Gemini でLINEボットを作って触っていきましょう。実は作るのはめちゃ簡単です！

# 必要なもの

- LINE Developers アカウント
- Google Cloud Pratform(GCP) アカウント

# はじめに

今回は、すぐ実装できるように[プログラム付きのスプレッドシート](https://docs.google.com/spreadsheets/d/1AJhg5nvskyNV5jcvNDcfQxSDnWVk-dwBnEa4Hg0jMOc/copy)を既に用意しているので、ご活用ください。

:::note
スプレッドシートをデータベースに動かすところまで既に実装してあります。
:::

LINE Developers の細かい設定やWebフックの発行方法は、下記の記事や動画を参考にしてください。手順に沿って行えば、中学生でもできると思いますのでご安心ください。

https://qiita.com/njn0te/items/2c08c936661a9febb3b0

# Google Cloud Platform (GCP) の設定

下記のページを開き、コンソールへ移動してください。

https://cloud.google.com/

今回は、２つのAPIを有効にする必要があります。

### ①Vertex AI API

![スクリーンショット 2023-12-21 8.24.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/216701/4a3ebb4d-27d0-3488-681e-c8f8b07909c4.png)

左上のハンバーガーマークから、「APIとサービス」の中の「有効な API とサービス」を選択し、まずは、「Vertex AI API」と検索して有効にします。

下記のように、課金が聞かれたら、クレジットカードを登録する必要があります。

![スクリーンショット 2023-12-21 8.25.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/216701/2d87484c-cc96-97e9-0966-c3cb1ade70c3.png)

クレジットカードが登録できたら、請求先アカウントが設定できていると思いますので、「アカウントを設定」をクリック。

![スクリーンショット 2023-12-21 8.27.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/216701/c2ba4886-386c-cc54-febd-56b0ba254185.png)

画面遷移して、有効になっていればOKです。

### ②Google Sheets API

![スクリーンショット 2023-12-21 10.38.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/216701/435c8d28-24ac-6a08-fb5a-9bf82d39d58b.png)

## Apps Script の設定

スプレッドシートの「拡張機能」から「Apps Script」を選択すると、別タブでプログラムを書くことができる画面に遷移します。その画面の左側にある歯車マーク「プロジェクトの設定」を開き、「「appsscript.json」を有効にする」にチェックを入れます。

![スクリーンショット 2023-12-22 8.34.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/216701/db396e6f-bd98-3851-37eb-20c1e17654f7.png)

「エディタ」に戻ると、新しいファイル「appsscript.json」があるので、その中に有効にしたAPIを使用するために `oauthScopes` を追加します。

```appsscript.json
{
  "timeZone": "Asia/Tokyo",
  "dependencies": {},
  "webapp": {
    "executeAs": "USER_DEPLOYING",
    "access": "ANYONE_ANONYMOUS"
  },
  "exceptionLogging": "STACKDRIVER",
  "oauthScopes": [
    "https://www.googleapis.com/auth/script.external_request",
    "https://www.googleapis.com/auth/cloud-platform",
    "https://www.googleapis.com/auth/spreadsheets"
  ],
  "runtimeVersion": "V8"
}
```

# プログラム

3つのファイルを用意しています。

## settings.gs

まずは設定のファイル。APIとかスプレッドシートの連携のための定義とか、書いています。

```js:settings.gs
// GEMINI API 設定部分
const PROJECT_ID = `**********`;
const REGION = `asia-northeast1`; // 東京、日本
const GEMINI_VER   = "gemini-pro";
const GEMINI_URL = `https://${REGION}-aiplatform.googleapis.com/v1beta1/projects/${PROJECT_ID}/locations/${REGION}/publishers/google/models/${GEMINI_VER}:streamGenerateContent`

// LINE API設定部分
const LINE_ACCESS_TOKEN = `********************`;
const LINE_REPLY_URL = 'https://api.line.me/v2/bot/message/reply';

// スプレッドシートの情報
const SHEET_TEST        = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("検証用");
```

`PROJECT_ID` は、GCPでAPIを発行したプロジェクトのIDです。
`LINE_ACCESS_TOKEN` は、LINE Developers のメッセージングAPIのチャネル設定で発行できるチャネルアクセストークンです。

## gemini.gs

次に、Geminiを使用するためのプログラムです。

```js:gemini.gs
function getGeminiReply(prompt) {
  let replyText;
  // prompt = `バナナブレッドのレシピをください`;
  const data = {
    "contents": {
      "role": "user",
      "parts": {
          "text": prompt
      },
    },
    "safety_settings": {
      "category": "HARM_CATEGORY_SEXUALLY_EXPLICIT",
      "threshold": "BLOCK_LOW_AND_ABOVE"
    },
    "generation_config": {
      "temperature": 0.2,
      "topP": 0.8,
      "topK": 40,
      "maxOutputTokens": 200,
      "stopSequences": [".", "?", "!"]
    }
  };

  const options = {
    'payload': JSON.stringify(data),
    'method' : 'POST',
    'muteHttpExceptions': true,
    'headers': {"Authorization": "Bearer " + ScriptApp.getOAuthToken()},
    'contentType':'application/json'
  };

  try {
    const response = UrlFetchApp.fetch(GEMINI_URL, options);
    const content = response.getContentText();
    const json = JSON.parse(content);
    replyText = "現在テスト運用中です\n\n"
    for (i in json){
      replyText += `${json[i].candidates[0].content.parts[0].text}`;
    }
    Logger.log(json);
  } catch (error) {
    replyText = `予期せぬエラーが発生しました`;
    Logger.log('Error: ' + error);
  }
  // console.log(replyText);
  return (replyText);
}
```

まだ頻繁にエラーが出るので、何か処理でエラーが出た時は「予期せぬエラーが発生しました」と出力するようにしています。

プログラムは、Gemini APIのリファレンスのサンプルを参考にしました。

https://cloud.google.com/vertex-ai/docs/generative-ai/model-reference/gemini?hl=ja

:::note warn
リファレンスにあったサンプルのほぼコピペなので、全然もっと良く書けると思います。変更リクエストお待ちしています！
:::

## line.gs

最後に、LINEから受け取ったメッセージを、先ほど書いた `getGeminiReply(prompt)` に渡してLINEにリプライメッセージとして返信するプログラムです。

```js:line.gs
function doPost(e) {
  let replyText;
  const data = JSON.parse(e.postData.contents).events[0]; // 情報取得
  if (!data)
    return SHEET_TEST.appendRow(["Webhook 検証OK"]);
  const replyToken = data.replyToken; // リプレイトークン
  const dataType   = data.type; // データのタイプ
  if (dataType == "follow" || dataType == "unfollow")
    return ;
  const postText = data.message.text; // 送信されたメッセージ
  if (postText === undefined)
    replyText = `まだ文章しか認識できません。`;
  else
    replyText = getGeminiReply(postText);
  return sendMessage(replyToken, replyText);
}

// LINEに返答
function sendMessage(replyToken, replyText) {
  const postData = {
    "replyToken" : replyToken,
    "messages" : [
      {
        "type" : "text",
        "text" : replyText
      }
    ]
  };  
  postMessage(postData);
  return replyText;
}

function postMessage(postData) {
  const headers = {
    "Content-Type" : "application/json; charset=UTF-8",
    "Authorization" : `Bearer ${LINE_ACCESS_TOKEN}`
  };
  const options = {
    "method" : "POST",
    "headers" : headers,
    "payload" : JSON.stringify(postData)
  };
  return UrlFetchApp.fetch(LINE_REPLY_URL, options);
}
```

さあ、ここまでできたら、LINEで実際に動かしてみましょう！

# さいごに

Udemyで、「LINE×ChatGPT」の講座を出しています。もしよければご受講いただけると嬉しいです！

https://www.udemy.com/course/chatgpt-line-function-calling-ai/?couponCode=2023LASTSALE

※「2023LASTSALE」という割引クーポンを発行しておきました。

YouTubeも、よければチャンネル登録お願いします。
来年はチャンネル登録数10,000人を目指しています。（現在3,050人）

https://youtube.com/@njn0te?sub_confirmation=1

最後までお読みただきまして、誠にありがとうございました。