Google Chat と ChatGPT を連携させる #1

バックオフィスが生成AIとGASで頑張るシリーズです。
# 目的
ゴールとしては **Google Chat のスペースで「おはようございます。業務開始します！」とか挨拶したら、タイムカードのSaaS（当社の場合はHRMOS勤怠）で出勤の打刻がされる** 的なbotをつくることです。業務開始の宣言含みの挨拶なのか「今日はテレワークです」とかの文意を判断して、業務開始ならば打刻するという流れですが、どれくらいの工数かかりそうなのかもわからないので、少しずつ実現していくことにします。
# ChatGPT API の準備
ChatGPTは月額の有料契約で使用していますが、APIのほうは別枠でデポジット型の料金体系だそうです。でも単価がすごく安いので、個人が普通に使う分には安価だろうと判断して有料利用していきます。
## API Key の取得
[OpenAIのAPIキー取得方法|2024年7月最新版|料金体系や注意事項](https://qiita.com/kurata04/items/a10bdc44cc0d1e62dad3)を参考にさせていただき、API Keyを取得していきます。
[API keysの画面](https://platform.openai.com/api-keys)にアクセスしてサインインし、+ Create new secret key を押下してsecret key を取得します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3840360/22d2f31b-040a-ae83-9a73-e65e23ca86df.png)
おや、Owned by が You か Service account のどちらかを選択しなければいけませんね。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3840360/b93fd356-a2ea-ab5b-87ef-59a7eeb084b3.png)
こんなときは、ChatGPTに聞いてみます。
> 今回のケースでは、「Service Account」を選択するのが最適です。以下に理由と手順を説明します。
>
> **理由** 
> Service Accountの利点:
> + セキュリティ: APIキーの使用を特定のサービスアカウントに制限することで、キーが誤って他人に共有されるリスクを減少させます。
> + 管理しやすさ: サービスアカウントを使用することで、APIキーの管理がしやすくなります。特定のプロジェクトやサービスに対してのみアクセス権を設定できます。
> + スケーラビリティ: 複数のアプリケーションやサービスが同じAPIキーを共有する場合、サービスアカウントを使用することで、一元的に管理できます。

だそうです。ではService account を選択で。
次は課金（デポジット）とのことで、とりあえず$10課金しておきます。若干でも円高傾向に感謝！
これでAPIは使用できる環境が整いました。
## GASの準備
次に、Google Apps Script のほうも準備していきます。

[GASのエディタ](https://script.google.com/)を開き、設定のスクリプトプロパティに```OPENAI_API_KEY```という変数を設定し、値に先ほど取得したAPI Keyを設定しておきます。
## テストコードを記述
今回もChatGPT 4o にお願いしてみました。まずはテストでAPIからリクエストをしてレスポンスがもらえるかを確かめるコードを書いてもらいました。以下のコードをペーストして保存します。
```javascript: testChatGPTAPI
// ChatGPT APIを使用してメッセージを処理
function callChatGPTAPI(prompt) {
  const apiKey = PropertiesService.getScriptProperties().getProperty('OPENAI_API_KEY');
  const url = 'https://api.openai.com/v1/chat/completions';

  const options = {
    'method': 'post',
    'headers': {
      'Authorization': 'Bearer ' + apiKey,
      'Content-Type': 'application/json'
    },
    'payload': JSON.stringify({
      'model': 'gpt-3.5-turbo',
      'messages': [{'role': 'user', 'content': prompt}],
      'max_tokens': 100,
      'temperature': 0.7
    }),
    'muteHttpExceptions': true
  };

  try {
    const response = UrlFetchApp.fetch(url, options);
    const jsonResponse = JSON.parse(response.getContentText());

    if (jsonResponse.choices && jsonResponse.choices.length > 0) {
      return jsonResponse.choices[0].message.content.trim();
    } else {
      return 'Error: Invalid API response';
    }
  } catch (error) {
    return 'Error: ' + error.message;
  }
}

// サンプルのプロンプトを使用してChatGPT APIを呼び出す関数
function testChatGPTAPI() {
  const prompt = "人間が人間である理由を30字以内で説明してください。";
  const response = callChatGPTAPI(prompt);
  
  Logger.log(response);
}
```
保存できたら、実行します（関数はtestChatGPTAPIのほうを選択してください）
```shell
17:25:27	お知らせ	実行開始
17:25:28	情報	他者とのつながりや共感、思いやりを持ち、喜びや悲しみを共有し合うことによって、人間としての存在意義や豊かさを感じるから。
17:25:29	お知らせ	実行完了
```
実行ログにこんな感じで結果が返ってきていたらテスト成功です。
# bot作成
## コードの作成
GASのエディタに新しいスクリプトを作成します。
以下のようなプロンプトでChatGPT 4oに作成依頼してみました。
```:プロンプト
では次に、Google chat での発言を ChatGPTに評価してもらってスペースに発言するbotをつくります。
スペースで何か発言があったら、それをChatGPTに評価させます。ChatGPTに評価させるプロンプトは以下を使用しましょう

以下の発言が、業務開始を意図するものであるかどうかを、0から100の確率での数値で返却してください。
＋ 発言内容

レスポンスの発言者表示は次のフォーマットで。
発言者：(メールアドレス)　確率：(パーセンテージ)

いったんここまで作成して、botをスペースにインストールしてテストしたいと思います
```
で、できたコードをコピペしていきます。
```javascript:bot.gs
// ChatGPT APIを使用してメッセージを評価する関数
function callChatGPTAPI(prompt) {
  const apiKey = PropertiesService.getScriptProperties().getProperty('OPENAI_API_KEY');
  const url = 'https://api.openai.com/v1/chat/completions';

  const options = {
    'method': 'post',
    'headers': {
      'Authorization': 'Bearer ' + apiKey,
      'Content-Type': 'application/json'
    },
    'payload': JSON.stringify({
      'model': 'gpt-3.5-turbo',
      'messages': [{'role': 'user', 'content': prompt}],
      'max_tokens': 100,
      'temperature': 0.7
    }),
    'muteHttpExceptions': true
  };

  try {
    const response = UrlFetchApp.fetch(url, options);
    const jsonResponse = JSON.parse(response.getContentText());

    if (jsonResponse.choices && jsonResponse.choices.length > 0) {
      return jsonResponse.choices[0].message.content.trim();
    } else {
      return 'Error: Invalid API response';
    }
  } catch (error) {
    return 'Error: ' + error.message;
  }
}

// Google Chat Bot
function doPost(e) {
  const contents = JSON.parse(e.postData.contents);

  if (contents.message) {
    const messageText = contents.message.text;
    const prompt = `以下の発言が、業務開始を意図するものであるかどうかを、0から100の確率での数値で返却してください。\n\n発言内容: ${messageText}`;
    const response = callChatGPTAPI(prompt);

    const responseText = `この発言が業務開始を意図するものである確率は ${response} です。`;

    return ContentService.createTextOutput(JSON.stringify({
      "text": responseText
    })).setMimeType(ContentService.MimeType.JSON);
  }

  return ContentService.createTextOutput(JSON.stringify({
    "text": "メッセージを処理しました。"
  })).setMimeType(ContentService.MimeType.JSON);
}

```
## デプロイ
コードを保存したら、デプロイしていきます。このへんのやりかたもChatGPTでは簡単に指示してくれました。
> **デプロイ** 
> 1. スクリプトを保存 します。
> 1. 「デプロイ」 > 「新しいデプロイ」を選択します。
> 1. 「デプロイの設定」を選択し、タイプを「ウェブアプリ」に設定します。
> 1. 「今すぐデプロイ」をクリックし、適切なアクセス権限（「全員に公開」）を設定します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3840360/eca50dab-d0d5-308b-8793-3e828e82c4d5.png)

ウェブアプリはアクセスしているユーザーに実行させます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3840360/be202ec8-ca73-0589-ccec-72960dc771c9.png)

デプロイができたら、ウェブアプリのURLを控えておきます。
## GCP で bot の設定
GCP側の設定についてもChatGPTがフォローしてくれていますが、今回はGoogle公式のガイドにを参考に設定していきます。
[Google Apps Script を使用して Google Chat アプリを作成する](https://developers.google.com/workspace/chat/quickstart/apps-script-app?hl=ja)

途中ですが、いったんここまで。