お手軽! Mattermost に ChatGPT Bot を実装してみた!

2023年3月2日 に OpenAI から ChatGPT の API モデル gpt-3.5-turbo が公開されました。

今回は弊社の Mattermost に ChatGPT Bot 専用チャンネル を作成し、適当な会話を楽しんでいこうと思います!

使用するAPIの概要

https://platform.openai.com/docs/api-reference/chat

ChatAPI の gpt-3.5-turbo は、以前までの 4つの自然言語モデルAPI よりも使いやすくなっており、インプットするパラメータが少なく、手軽に使えるものとなっていました。

{
  "model": "gpt-3.5-turbo",
  "messages": [
    {
        "role": "user",
        "content": "Hello!"
    }
  ]
}

モデルを指定し、メッセージのリストを設定することで、過去の文脈を踏まえた文章を生成してくれます。

ChatAPI モデルには gpt-3.5-turbo-0301 と gpt-3.5-turbo がありますが、今回は gpt-3.5-turbo を使って実装しました。

role にはいくつか種類がありますが、投稿者を user に Bot が生成した文章を assistant とすることで一般的な会話が楽しめます。

実装構成

今回実装した Bot の構成図です。

各手順としては以下の通りです。

1. Mattermost のチャンネルからメッセージを投稿

2. Mattermost の 外向きwebhook で Google App Script(GAS) へ送信

3. GAS で履歴を読み込み

4. 履歴と送信メッセージをもので ChatGPT のAPIを実行

5. 実行結果を GoogleSpreadsheet に保存

6. GAS から 内向きWebhook で Mattermost に返却

実装

Mattermost で Bot 用チャンネルを作成し、 内向きWebhook を作成します

BOT用のチャンネルを用意したら 統合機能 から 内向きWebhook を選択し、各種設定を行います

内向きWebhook の設定では以下を求められるので、それぞれ入力します。

  • タイトル: 任意のタイトル
  • 説明: 任意
  • チャンネル: チャンネルは Bot 用に作ったものを選択します
  • このチャンネルに固定する: 任意
  • ユーザー名: Bot が投稿する際の名前の設定
  • プロフィール画像: Bot が投稿する際の Icon の設定

入力を保存すると URL が発行されますが、GAS のコード内でこの URL を使います。

※システムコンソールから統合機能管理を開き「内向きのウェブフックを有効にする」と「外向きのウェブフックを有効にする」を有効にしてください。

※システムコンソールから統合機能管理を開き「統合機能によるユーザー名の上書きを許可する」と「統合機能によるプロフィール画像アイコンの上書きを許可する」を有効にしてください。

GAS から コールバックURL を発行します

GAS に以下のコードを書き込みます。

OpenAI の APIキー 等は以下を参考に取得出来ます。

AutoWorker〜Google Apps Script(G...
【2023年版】OpenAIのAPIキー発行・取得手順!ChatGPTやGPT-4、DALL-E3をAPI経由で利用可能 | AutoWorker〜... 2022年11月にOpenAIが発表した文章生成AI「ChatGPT」は大きな反響を集めました。 現在ChatGPTはサービスページからのみ利用できませんが、ChatGPTのような...

OpenAPI の APIキー と 内向きWebhook の URL を各変数に割り当てます。

// openAIApi.gs
class OpenAIAPI {

  constructor (api_key, organization){
    this.api_key = api_key;
    this.organization = organization;
  }

  getModels(){
    const url = "https://api.openai.com/v1/models";
    const options = {
      "headers": {
        'Authorization': 'Bearer ' + this.api_key,
        'OpenAI-Organization': this.organization
      }
    }
    return UrlFetchApp.fetch(url,options);
  }

  textChatCompletions(messages){
    const url="https://api.openai.com/v1/chat/completions"

    const payload = {
      "model": "gpt-3.5-turbo",
      "messages": messages,
    }
    const options = {
      "headers": {
        'Authorization': 'Bearer ' + this.api_key,
        'Content-Type': 'application/json',
      },
      "payload": JSON.stringify(payload)
    }
    return UrlFetchApp.fetch(url, options);
  }
}
// main.gs
// OpenAiのApi情報その他
const API_KEY = 'OpenAIのAPIkey';
const ORGANIZATION = 'OpenAIのORGANIZATION';

function chatHistoryWriter(role, content){
  const id = "スプレッドシートのID";
  const spreadSheet = SpreadsheetApp.openById(id);
  const sheetName = "履歴シートの名前";

  spreadSheet.getSheetByName(sheetName).appendRow(
    [new Date(), role, content]
  );
}

function chatHistoryReader(){
  const id = "スプレッドシートのID";
  const spreadSheet = SpreadsheetApp.openById(id);
  const sheetName = "履歴シートの名前";
  const sheetChatHistory = spreadSheet.getSheetByName(sheetName)

  try{
    const range = sheetChatHistory.getRange(1, 2, sheetChatHistory.getLastRow(), sheetChatHistory.getLastColumn());
    return range.getValues()
  }catch{
    return []
  }
}


function mattermostSendMessage(text, url){
  const payload = {
    "text": text,
  }
  const options = {
    "headers": {
      'Content-Type': 'application/json',
    },
    "payload": JSON.stringify(payload)
  }
  UrlFetchApp.fetch(url, options);
}

function jpChat(text){
  // 内向きhookURL日本語
  const jpURL = "内向きWebhookのurl"

  // 履歴読み込み
  const chatHistory = chatHistoryReader()
  const inputText = {"role": "user", "content": text}

  const messages = []
  chatHistory.forEach(v=>{
    messages.push({"role": v[0], "content": v[1]})
  })

  messages.push(inputText)

  // openai初期化
  const openAIAPI = new OpenAIAPI(API_KEY, ORGANIZATION);

  // chatApi実行
  const response = openAIAPI.textChatCompletions(messages)
  const responseJson = JSON.parse(response.getContentText())
  const responseMessage = responseJson["choices"][0]["message"]

  // 履歴書き込み
  chatHistoryWriter(inputText["role"], inputText["content"])
  chatHistoryWriter(responseMessage["role"], responseMessage["content"])

  // mattermostへの送信
  mattermostSendMessage(responseMessage["content"], jpURL)
}

// コールバックで呼び出される関数
function doPost(e) {

  const params = JSON.stringify(e);
  const json = JSON.parse(params)

  const contents = json["parameters"]

  const text = contents["text"][0]
  const channel_name = contents["channel_name"][0]

  // 会話処理実行
  jpChat(text)
}

GAS にて上記のコードを入力した後、デプロイボタンを押すことでURLが発行されます

※デプロイ時、「アクセスできるユーザー」を「全員」としなければ Mattermost 側からのコールバックを受け取れないので注意してください

Mattermost で 外向きWebhook の設定をします。

外向きWebhook の設定では以下を求められるので、それぞれ入力します

  • タイトル: 任意のタイトル
  • 説明: 任意
  • チャンネル: チャンネルは Bot 用に作ったものを選択します
  • コンテントタイプ: application/json
  • トリガーワード(1つにつき1行): なし
  • トリガーとなる条件: 最初の単語がトリガーワードと正確に一致する
  • このチャンネルに固定する: 任意
  • コールバックURL(1つにつき1行): GAS のコードデプロイ時の URL を記入
  • ユーザー名: なし
  • プロフィール画像: なし

上記入力後保存します、以上で実装は完了です。

Mattermost で Bot と会話を楽しむ

「卵を使った料理」について聞いてみました。

2回目の「魚と組み合わせた料理」のオススメを聞いたのは、「卵と組み合わせて」という意味だったのですが、そこは察してくれませんでした。

3回目は「魚と卵を組み合わせた料理」と指定して聞きました。

魚と卵を組み合わせた料理ってあまり聞かないのですが、しっかり答えてくれました。

フィズバズのコードを書いてもらいました。

噂通りコードも書けるんですね、ChatGPT すごい!

まとめ

ChatGPT Bot を Mattermost に実装してみました。

GAS と ChatGPT だけで優秀な Bot が簡単に実装できました、コードは GAS のみで後は設定を行うだけ!

Mattermost への ChatGPT Bot 実装はこの記事だけでも完結するので、興味のある方は是非試して見てください!

Mattermost
Mattermost導入サービス

Recruit

ディーメイクでは各ポジションで一緒に働く仲間を募集中! エンジニア、デザイナー、ディレクターなど、多彩な職種があります。
一緒に成長していきましょう!

  • URLをコピーしました!