AI Mattermost 開発者ブログ

お手軽! 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外向きwebhookGoogle App Script(GAS) へ送信

3. GAS で履歴を読み込み

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

5. 実行結果を GoogleSpreadsheet に保存

6. GAS から 内向きWebhookMattermost に返却

実装

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

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

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

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

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

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

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

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

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

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

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

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

最新記事

おすすめ記事リンク

-AI, Mattermost, 開発者ブログ
-, , , ,