🎃

(non-Cloudflare Topic) daab の OpenAI 連携

2024/07/16に公開

前回の記事ではdirect用チャットボットであるdaabをコンテナ環境で起動させました。
https://zenn.dev/kameoncloud/articles/636954f643ff1c

この第二回目ではチャットボットをOpenAIとの対話式に作り変えます。

ping.jsの中身について

その前に一旦前回の中身を見ておきます。作業に使用したAmazon Linux 2023 を停止してしまった場合、以下の手順で起動させます。

sudo systemctl start docker
sudo systemctl status docker
sudo docker ps -a
CONTAINER ID   IMAGE              COMMAND       CREATED       STATUS                     PORTS     NAMES
274793d6f332   amazonlinux:2023   "/bin/bash"   3 hours ago   Exited (137) 2 hours ago             daab

前回のイメージがあることが確認できましたので以下で起動します。

sudo docker start daab

次に以下のコマンドでログインし、作業ディレクトリに移動します。

sudo docker exec -it daab bash
cd daab

チャットボット本体のjsスクリプトを書き換えるためviエディタをインストールします。

yum install vi

インストールが出来たらファイルを開きます。

vi scripts/ping.js
ping.js
/ Description:
//   Utility commands surrounding Hubot uptime.
//
// Commands:
//   ping - Reply with pong
//   echo <text> - Reply back with <text>
//   time - Reply with current time
'use strict';

module.exports = (robot) => {
  robot.respond(/PING$/i, (res) => {
    res.send('PONG');
  });

  robot.respond(/ADAPTER$/i, (res) => {
    res.send(robot.adapterName);
  });

  robot.respond(/ECHO (.*)$/i, (res) => {
    res.send(res.match[1]);
      });

  robot.respond(/TIME$/i, (res) => {
    res.send(`Server time is: ${new Date()}`);
  });
};

以下の部分が前回テストした箇所です。

  robot.respond(/PING$/i, (res) => {
    res.send('PONG');
  });

特定のメッセージであるPINGが来たら単純にPONGを返しています。

ChatGPT連携

1. OpenAIのアカウント作成とAPI Keyの準備

まずOpenAIのモジュールをインストールします。

npm install openai

次にOpenAIのアカウントを以下のページから作成します。

  1. https://openai.com/ja-JP/ へアクセス
  2. 画面下部のAPI Login をクリック
    image.png
  3. APIをクリック
    image.png
  4. 左ペインのAPI Keysをクリック
    image.png
  5. 画面右上Create new secret keysをクリック
    image.png
  6. 適当な名前を付けてcreate secret keyをクリック
    image.png
  7. 出来上がった値をCopy
    image.png

daab スクリプトの改修

先ほど利用したrobot.respondは特定の問い合わせにのみ反応するコマンドです。対話型チャットボットではこれでは少し具合が悪いためrobot.hearを用います。ping.jsを以下に置換します。

ping.js
'use strict';
const OpenAI = require('openai');

module.exports = (robot) => {
  const openai = new OpenAI({
    apiKey: "xxxxxxxxx",
  });

  robot.hear(/(.*)/, async(res) => {
    try {
      const input = res.match[1];
      const match = input.match(/Hubot (.+)/);
      const extracted = match[1];

      const gptResponse = await openai.chat.completions.create({
        model: "gpt-3.5-turbo",
        messages: [{"role": "user", "content": extracted}],
      });
      const botReply = gptResponse.choices[0].message.content.trim();
      res.send(botReply);
    } catch (error) {
      console.error("Error with OpenAI API request:", error);
      res.send("Sorry, I'm having trouble connecting to the AI service.");
    }

  });

  robot.respond(/PING$/i, async (res) => {
   res.send('PONG');
  });

  robot.respond(/ADAPTER$/i, (res) => {
    res.send(robot.adapterName);
  });

  robot.respond(/ECHO (.*)$/i, (res) => {
    res.send(res.match[1]);
  });

  robot.respond(/TIME$/i, (res) => {
    res.send(`Server time is: ${new Date()}`);
  });
};

xxxxx部分は先ほどコピーしたトークンに置き換えておきます。
置換後daab runを実行すれば対話型チャットボットの完成です。
image.png
image.png

コード解説

 robot.hear(/(.*)/, async(res) => {

ですべての入力が一度通る箇所になります。入力された値は

res.match[1]

に入ります。ただし文字列の先頭にHubspotと含まれているため以下の部分でそれを除去しています。

      const input = res.match[1];
      const match = input.match(/Hubot (.+)/);
      const extracted = match[1];

次にこの箇所でChatGPTとの対話を行います。

      const gptResponse = await openai.chat.completions.create({
        model: "gpt-3.5-turbo",
        messages: [{"role": "user", "content": extracted}],
      });

ChatGPTからは戻りがJSON形式となるためこのままではdaabが正しくレスポンスを戻すことが出来ません。このため以下で普通の文字列に整形しています。

const botReply = gptResponse.choices[0].message.content.trim();

またこのコードには先ほどの以下の部分がそのまま残っています。

  robot.respond(/PING$/i, async (res) => {
   res.send('PONG');
  });

このため、pingと入力されるとChatGPTとの対話の後PONGが引き続き戻ります。

Discussion