RocketChatとOpenAIのChatGPTを連携させてみました

2023.03.08 23:34 ブログ

RocketChatとOpenAIのChatGPTを連携させてみましたの画像

 
ことの発端はChatGPTすごいという噂をよく聞いていたので使ってみたらすごかった・・
というわけで、社内のチャットツールであるRocketChat(オープンソース)内で同じようなことができれば楽になるなと。

RocketChatについて

RocketChatは、オープンソースのWebチャットアプリケーションで、企業やコミュニティによる内部チャット、ビデオ会議、音声通話、ドキュメント共有など、多くのコラボレーション機能を提供します。

※RocketChat最高です!

RocketChatには WEBHOOKという拡張機能があるので
それを利用して実装してみたいと思います。

ちなみに、現在導入するAIは OpenAIのAPIモデルgpt-3.5-turboになります。
※APIの使用には料金が発生します。gpt-3.5-turboから安くなったみたいです。


※RocketChatのバージョン6系になってから、弊社環境では下記方法でエラーとなってしまい、chatgptとrocketchatのサーバーを中継させるサーバーを用意してRocketChatとChatGptの連携を可能にしております。もっとスマートな方法が分かり次第また記事にしたいと思います。

まずは、OpenAIのAPI登録
https://openai.com/product

「GetStarted」から登録できます。




登録が完了したら、APIキーの取得を行います
ダッシュボードの右上メニューから「View Api keys」に進んで
「Create new secret key」 からAPIキーを作成してください。(APIキーをどこかへ保存しておいてください)




その次に、RocketChat側での設定です。

チャット画面の設定メニューより「Workspace」をクリック



統合メニューをクリック




新規ボタンから新規作成


発信中タブをクリック



各項目を以下の通り設定
 
項目
イベントトリガー メッセージが送信されました
有効 オン
名前 なんでもOK
チャンネル all_public_channels ,all_private_groups, all_direct_messages
トリガーになる言葉 @ai
URLs URLならなんでもOK。Scriptの方で上書きします
投稿ユーザー ai ※事前にai ユーザーを作成してください。
エイリアス (オプション) OpenAI
スクリプトを有効にする オン


その後ScriptをTypeScript似て記述
※ざっと作ったので、コードは汚いです。ご参考までにご利用ください。
※RocketChatのRestAPIも使えるように設定している前提です。
/* exported Script */
/* globals console, _, s */

/** Global Helpers
 *
 * console - A normal console instance
 * _       - An underscore instance
 * s       - An underscore string instance
 */

class Script {
    /**
     * @params {object} request
     */
    messageId;
    userName;
    authToken;
    aiUserName;
    apiKey;
    rocketChatUrl;

    constructor(){
        this.messageId="";
        this.userName = "";
        this.authToken = "";
        this.aiUserName = "ai";
        this.apiKey = "ここに先程取得したAPIキーをセット";
        this.rocketChatUrl = "現在利用しているRocketchatのURLアドレスを入力 https://〇〇〇〇";
    }

    prepare_outgoing_request({ request }) {
        let d = new Date();
        console.log("---- openAI START ----- "+d.toString());
        let match;
        if(request.data.tmid){
            this.messageId = request.data.tmid;
        }else{
            this.messageId = request.data.message_id;
        }

        this.userName = request.data.user_name;
        this.authToken = request.data.token;
        let pat = new RegExp("^\@"+this.aiUserName);
        match = request.data.text.match(pat);
        // リクエストURLの設定
        request.url = "https://api.openai.com/v1/chat/completions";
        request.headers = {
            'Authorization': 'Bearer '+this.apiKey,
            'Content-Type': 'application/json'
        };

        // データの成形
        let userInput = request.data.text;
        let pat2 = new RegExp("\@"+this.aiUserName,"g");
        let mymessage = userInput.replace(pat2,'').replace(/ /g,'');
        let messages = [];
        //スレッドのデータが有ればすべて取得
        if(request.data.tmid){
            let resultMessages;
            let loginResult;

            try {
                //rocketChatAPIにログイン
                loginResult = HTTP("POST", this.rocketChatUrl+"/api/v1/login?",{
                    data:{
                        "user":"****",
                        "password":"****"
                    }
                });
                let authParam = JSON.parse(loginResult.result.content);
                if(authParam.data.authToken) {
                    let authToken = authParam.data.authToken;
                    //rocketChatAPIからスレッドのメッセージをすべて取得
                    let rootMessages = HTTP("GET", this.rocketChatUrl + "/api/v1/chat.getMessage?msgId=" + request.data.tmid, {
                        headers: {
                            "X-Auth-Token": authToken,
                            "X-User-Id": authParam.data.userId,
                            "Content-type": "application/json"
                        },
                    });
                    if(rootMessages) {
                        let rootMessageData = JSON.parse(rootMessages.result.content);
                        messages.push({
                            "role": "user",
                            "content": rootMessageData.message.msg
                        });
                    }
                    resultMessages = HTTP("GET", this.rocketChatUrl + "/api/v1/chat.getThreadMessages?tmid=" + request.data.tmid + "&tlm=2019-04-08T14:40:27.724Z", {
                        headers: {
                            "X-Auth-Token": authToken,
                            "X-User-Id": authParam.data.userId,
                            "Content-type": "application/json"
                        },
                    });
                    if (resultMessages.result.content) {
                        let messagesData = JSON.parse(resultMessages.result.content);

                        for (var prop in messagesData.messages) {
                            let role = "user";
                            if (messagesData.messages[prop].u.username == "ai") {
                                role = "assistant";
                            }
                            //いらない文字を削除
                            let mes = messagesData.messages[prop].msg;
                            mes = mes.replace(/\@(.*?)\s/, " ");
                            messages.push({
                                "role": role,
                                "content": mes
                            });
                        }
                    }
                }
            }catch(e){
                console.log(e);
            }

        }else{
            //スレッドじゃない場合(はじめの投稿の場合は自分のメッセージをセット)
            messages.push({
                "role":"user",
                "content":mymessage
            });
        }

        let data = {
            "model": "gpt-3.5-turbo",
            "messages":messages
        };
        if (match) {
            console.log(data);
            return {
                url: request.url,
                headers: request.headers,
                method: 'POST',
                data: data
            };
        }

    }

    /**
     * @params {object} request, response
     */
    process_outgoing_response({ request, response }) {
        // パースが必要ない場合
        let jsonData = JSON.parse(response.content);
        if(jsonData && jsonData.choices){
            return {
                content: {
                    tmid:this.messageId,
                    text: "@"+this.userName+" "+jsonData.choices[0].message.content,
                    parseUrls: false
                }
            };
        }
    }
}
設定できましたら、保存してください。

RocketChatAPi Endpoint一覧はコチラ

その後、@ai ユーザーをどこかのROOMに招待し、そのROOM内で @ai でメンションをつけて質問してみてください。
以下のようにスレッドで返信が来れば成功です。




※ROOMへ@ai を招待してから @ai 宛に送信しないととメッセージが処理されません。



ご利用の環境や設定などによって使えない事もあるかもしれませんので、あくまでも私の環境での一例になります。