PubNubとChatGPT / OpenAIでチャットボットを構築する
Published: 2024-03-11 14:43:17

ごく最近まで、チャットボットを作ろうと思えば、「自然言語処理」（NLP）を使ってユーザーが何を求めているかを理解・解釈し、バックエンドのロジックを使ってその要求を満たそうとしていました。DialogflowやMicrosoft Bot frameworkのようなソリューションは、機械学習に基づいた会話AIを提供し、チャットボットを作成する開発者に伝統的に人気があります。

OpenAIとOpenAIのChatGPTは、人工知能を搭載したAIチャットボットをこれまで以上に簡単に作成できるようにします。OpenAIの動力源である大規模言語モデル（LLM）は公開データセットに基づいているため、公開知識ベースに存在すると合理的に予想されるあらゆる情報を提供することで、アプリのユーザーエクスペリエンスを簡単に向上させることができます。

PubNubは、以下のような幅広い業界やユースケースのためのリアルタイム体験を提供します。 [カスタマーサポート](https://www.pubnub.com/solutions/communications/)および [アプリ内チャット](https://www.pubnub.com/solutions/chat/)これらのリアルタイム体験はすべて、ユーザーが読めるメッセージやソリューションに関するメタデータなどのデータによって提供されています**。**

_実際の使用例をいくつか挙げてみましょう：_

-   よくある**カスタマーサポートの**質問に対する回答を提供する
    
-   既存のデータを追加コンテンツで**補強**する。例えば、指定された場所の近くで食事ができる場所を提案する。
    
-   メッセージ履歴に基づいて**会話の要約を**提供する。
    

私たちは [記事](https://www.pubnub.com/blog/building-a-chatbot-with-openai-gpt3-and-pubnub/)その古い記事は今でも正しいが、業界の動きは非常に速く、数ヶ月というのは長い。

この記事では、私たちの [PubNubショーケース](http://pubnub-showcase.netlify.app/)アプリケーションの中で動いているものと同じような、OpenAIで動くチャットボットの作り方を説明します。

![The showcase's AI model has been primed to mention PubNub, where appropriate](https://www.pubnub.com/cdn/3prze68gbwl1/6G6aZfuXXtSqh9aBDyA7fw/5c416e9b87a4c0de5070271669bd13dd/showcase_app.png)

**ハイレベルなアーキテクチャ**
-----------------

ChatGPTのようなチャットボットを作成するには、 [OpenAIのチャットAPIを](https://platform.openai.com/docs/api-reference/chat)使用し、アカウントにサインアップして [APIキーを](https://platform.openai.com/account/api-keys)作成する必要があります。 OpenAIのAPIはRESTエンドポイントとしてアクセスできるので、ほとんどのプログラミング言語から呼び出すことができます。

PubNubからデータを抽出し、チャットボットとの会話を開始または継続するための最良の方法は、[Functionsを](https://www.pubnub.com/products/functions/)使用することです。 Functionsは、Node.JSで実行されるサーバレス[JavaScript](https://www.pubnub.com/guides/javascript/)コンテナを提供し、チャットメッセージの受信など、PubNubネットワーク上でイベントが発生するたびにビジネスロジックを実行することができます。 PubNub Functionsは、リアルタイムオンザフライ言語翻訳、冒涜フィルタリング、またはOpenAIのようなサードパーティのサービスを呼び出すために頻繁に使用されます。

この例は次のように動作します：

-   既存のチャットアプリケーションはPubNubを使ってリアルタイムでメッセージを送受信している。
    
-   PubNubファンクションは、特定のチャネルに送信されたメッセージをリッスンします。
    
-   単一のメッセージだけではChatGPTにコンテキストを提供しないので、進行中の対話を可能にするために会話履歴の一部もキャッシュされます。
    
-   PubNub関数はOpenAI APIを呼び出し、レスポンスを取得します。
    
-   レスポンスはチャットボットに関連付けられているPubNubチャンネルに公開されます。
    

下のシーケンス図を見てください：

![Example sequence diagram for implementing a ChatGPT-like chatbot with PubNub and OpenAI](https://www.pubnub.com/cdn/3prze68gbwl1/64ZjHOSpoWo5vfykIpNPVU/44eb69443493a131043d8142133b234c/architecture.png)

1.  50以上のSDKを持つPubNubは、大多数のアプリケーションをサポートすることができ、メッセージはPubNubネットワーク上のチャットボットを表すチャネルに[公開さ](https://www.pubnub.com/docs/general/messages/publish)れます。
    
2.  PubNubの関数は、チャネル上で[パブリッシュイベントが](https://www.pubnub.com/docs/general/serverless/functions/overview#after-publish-or-fire-functions)発生した[後に](https://www.pubnub.com/docs/general/serverless/functions/overview#after-publish-or-fire-functions)呼び出されるように構成されています。 メッセージを受信すると、チャネルの履歴が取得され、会話にいくつかのコンテキストが提供されます。この履歴は、ユーザー入力といくつかの追加のコンテキストと一緒に、[チャット完了APIを介して](https://platform.openai.com/docs/api-reference/chat/create)ChatGPTに提供されます。
    
3.  ChatGPT APIは会話の流れに適したレスポンスを生成します。
    
4.  PubNub関数内で、ChatGPTから応答が受信され、それが解析されてPubNubネットワークに [パブリッシュさ](https://www.pubnub.com/docs/general/messages/publish)れます。
    
5.  PubNubからのメッセージを受信するように[サブスクライブ](https://www.pubnub.com/docs/general/channels/subscribe)したアプリケーションは、チャットボットの応答を表示します。
    

**PubNub関数の設定**
---------------

### 以下のようにPubNub関数を作成します：

前提条件：PubNubを初めて利用する場合は、[ドキュメントに従って](https://www.pubnub.com/docs/general/basics/set-up-your-account)アカウントを設定するか、[ツアーに](https://www.pubnub.com/tour/introduction/)参加してPubNubがどのようなものかを理解してください。

1.  [管理ダッシュボードに](http://admin.pubnub.com/)移動します
    
2.  左側のメニューから`関数を`選択し、関数を作成したい適切なキーセットを選択します。
    
3.  `新規モジュールの`作成を選択します。
    
4.  Module NameとModule Descriptionを入力し、キーセットを選択して`Createを`クリックします。
    
5.  作成したモジュールを選択します。
    
6.  `Create New Functionを`選択します。
    
7.  ファンクションに名前を付け、イベントタイプとして`After PublishまたはFireを`選択します。 このファンクションはメッセージがPubNubに送信された後に呼び出されます。この場合、OpenAIに送信するメッセージのコピーをキャプチャします。関[数の種類についての](https://www.pubnub.com/docs/general/serverless/functions/overview#before-publish-or-fire-functions)詳細はドキュメントを参照してください。
    
8.  関数のチャンネルは、OpenAI に送信したいメッセージを[公開して](https://www.pubnub.com/docs/general/messages/publish)いるチャンネルと一致させる必要があります。 ここではワイルドカードを使うことができるので、例えば`chatgpt.*`は、chatgpt を開始するすべてのチャンネルと一致します。 この関数は同時に複数の人と会話したいので、ワイルドカードを使うことで、アクティブな会話に参加しているユーザを分けることができ、適切なチャンネルで応答できるようになります。複数の人が同時にOpenAIと会話している場合、PubNubはあなたの関数の複数のインスタンスを作成します。 例えば、`chatgpt.daveと` `chatgpt.simonは`2つの異なるチャンネルですが、どちらも`chatgpt.*で`リッスンしているPubNub関数を呼び出します。[PubNubがどのようにチャンネルワイルドカードを処理するか](https://support.pubnub.com/hc/en-us/articles/360051495072-How-do-I-subscribe-to-a-wildcard-channel-)はドキュメントで詳しく説明しています。
    
9.  `マイシークレットを`選択し、OpenAI[APIキー](https://platform.openai.com/account/api-keys)用の新しいシークレットを作成します。以下のコードでは、このシークレットが`OPENAI_API_KEYという`名前であると仮定しています。
    

### **以下はOpenAI / ChatGPTを呼び出すPubNub関数の例です。**

```js
const pubnub = require('pubnub');
const xhr = require('xhr');
const vault = require('vault');

// Process all messages posted to the private ChatGPT channel
// This will be unique for each user, so chat is 1:1 with ChatGPT
export default request => {
  const inboundChannel = request.channels[0];
  if (request.message.content.type == "text" && request.message.sender != "OpenAI")
  {
    //  user’s query
    let naturalCommand = request.message.content.text;
   // Always provide OpenAI with some context of how you want questions answered
    let historicalMsgs = 
      [{"role": "system", "content": "You are a helpful assistant, expert in PubNub"}];
    return getOpenaiApiKey().then(apikey => {
      // Read the last messages from the conversation to provide conversation context
      return pubnub.history({
        channel: inboundChannel,
        count: 10
        }).then((response) => {
          response['messages'].forEach((value, index) => {
            // The message role will vary depending on whether it was sent or received 
            if (value.entry.sender && value.entry.sender == "OpenAI")
              historicalMsgs.push({"role": "assistant", 
                "content": value.entry.content.text})
            else
              historicalMsgs.push({"role": "user", 
                "content": value.entry.content.text})
          });

          // Depending on timing, last published message may or may not yet be stored
          if (historicalMsgs[historicalMsgs.length - 1].content != naturalCommand)
          {
            historicalMsgs.push({"role": "user", 
              "content": naturalCommand});
          }
          return openAI(naturalCommand, historicalMsgs).then(aiResponse => {
            // Respond back by publishing the message to PubNub
            pubnub.publish({
              channel: inboundChannel,	//  In the showcase, this is unique per user
              message: {
                content: {
                  type: "text",
                  text: aiResponse
                },
                sender: "OpenAI",
              },
            });
            return request.ok();
          });
        })
      });
    }
    return request.ok();
  };


  // Get API Key for OpenAI
  // Key is populated via Vault Secret Manager  
  let OPENAI_API_KEY = null;
  function getOpenaiApiKey() {
    // Use cached key
    if (OPENAI_API_KEY) {
      return new Promise(resolve => resolve(OPENAI_API_KEY));
    }
    // Fetch key from vault
    return vault.get("OPENAI_API_KEY").then(apikey => {
      OPENAI_API_KEY = apikey;
      return new Promise(resolve => resolve(OPENAI_API_KEY));
    });  
  }

  // API Call to OpenAI asking the AI to run functions if it thinks it needs to
  function openAI(naturalCommand, msgs) {
    // Generate Function Instructions for the AI
    const url = 'https://api.openai.com/v1/chat/completions';
    const http_options = {
      'method': 'POST',
      'headers': {
        "Content-Type": "application/json",
        "Authorization": `Bearer ${OPENAI_API_KEY}`,
      },
    'body': JSON.stringify({
      "model": "gpt-3.5-turbo",
      "messages": msgs,
      "max_tokens": 300
    }),
    timeout: 9500, // PubNub functions timeout limit can be raised if needed
    retries: 0
  };

  return xhr.fetch(url, http_options).then((resp) => {
    const body = JSON.parse(resp.body);
    return body.choices[0].message.content;
  })
  .catch((err) => {
    console.log(err);
    return "Timed out. Please try again or ask a simpler question";
  });
}
```

**OpenAI APIを呼び出す際の実用的な考慮事項**
-----------------------------

### **チャット補完APIのプロパティ**

この記事で紹介するコードは[OpenAI Chat Completion APIを](https://platform.openai.com/docs/api-reference/chat/create)使用していますが、上記で紹介するよりも多くの設定が可能です。

`streamを`trueに指定すると、OpenAIのレスポンスが[サーバから送信されたイベントの](https://www.pubnub.com/guides/server-sent-events/)データストリームとして返されます。 実用的には、ChatGPTのウェブインタフェースがどのように動作するかに似て、ユーザはより即時のレスポンスを見ることができるので、これは知覚的な待ち時間を減らすことができます。

`model`は、応答を生成するために使用される言語モデルを指定します。 API は、異なる機能と価格帯を持つモデルファミリによって提供され、モデルは頻繁にアップグレードされ、新しいモデルが追加されます。

`max_tokensは`、APIが出力を生成する際に使用する[トークンの](https://platform.openai.com/tokenizer)数を制限します。 一般的に、トークンの数が少なければ少ないほど、出力は複雑でなくなり、コンテキストの追跡も少なくなりますが、レスポンスはより速く提供されます。

メッセージの`役割は`、メッセージの作成者を指定することができます。 この記事の例では、OpenAPIにいくつかの指示を提供するために`systemを`使用し、エンドユーザーによって送信されたメッセージには`userを`使用し、OpenAIによって以前に提供された応答には`assistantを`使用しています。

このモデルは、`temperature`/`top_p`、`frequency_penality`、`presence_penalty` を指定することで、さらに微調整することができます。 これらの詳細については、 [OpenAPI のドキュメントを](https://platform.openai.com/docs/api-reference/chat/create)参照してください。

このAPIでは、モデルの機能を拡張するカスタム関数を定義して指定することもできますが、このブログの範囲外です。 もっと詳しく知りたい場合は、ChatGPTがあなたの代わりにPubNubにパブリッシュする方法を説明した [別の投稿が](https://gist.github.com/stephenlb/de558e9802a6f950951eb0939ab71bcd)あります。"また、ChatGPTが独自のビジネスAPIを呼び出すことを許可して、例えば、製品情報や注文ステータス情報を顧客に直接提供することもできます。

### **チャットにコンテキストを提供する**

[HTTP](https://www.pubnub.com/guides/http/)リクエストはステートレスなので、OpenAIと長く続く会話をするためには、APIにコンテキストを与え、これまでの会話を説明し、その会話の周りのメタデータを提供する方法が必要です。

[チャット完了APIは](https://platform.openai.com/docs/api-reference/chat/create) `メッセージの`配列を受け付け、AIモデルが曖昧な質問から意味を導き出すことを可能にします。 この例を考えてみましょう：

\> ユーザー\] 地球の大きさは？

\> OpenAI\] 地球の直径は約12,742kmです。

\> \[User\]**地球の重さは？**

\> OpenAI\] 地球の質量は約5.97x10^24kgです。

明らかに、"How heavy is it "という質問に答えるために、AIは "it "が何であるかを理解する必要があった。

PubNubを利用したチャットボットを構築する際には、どの程度の会話コンテキストを提供すべきかを決定する必要があります。 PubNubの機能を使用している場合、チャネルのメッセージ履歴全体にアクセスすることができ、この例では会話の前の10件のメッセージを提供しています。 もちろん、ニーズに応じて、必要であればもっと多くのメッセージを提供することもできます：

```js
// Read in the last messages from the conversation to provide some context to Chat GPT
pubnub.history({
  channel: inboundChannel,
  count: 10
}).then((response) => {
  response['messages'].forEach((value, index) => {
    if (value.entry.sender && value.entry.sender == "ChatGPT")
      historicalMsgs.push({"role": "assistant", "content": value.entry.content.text})
    else
      historicalMsgs.push({"role": "user", "content": value.entry.content.text})
});
```

**PubNub内にチャットのコンテキストをすでに持っていれば、OpenAIにそのコンテキストを提供するのは簡単**です。 メッセージがOpenAIに送信されたか、OpenAIから受信されたかによって`役割が`異なることに注意してください。

### **長時間クエリ**

ChatGPTのようなチャットボットを開発し、AIモデルによって使用されるパラメータを設定するとき、クエリに対する予想応答時間を考慮してください。 デフォルトでは、PubNub関数は、呼び出しごとに10秒の最大実行時間を提供し、サポートに連絡することによって増加させることができます。 一般的に、ユーザの質問に応答するのに時間がかかりすぎるチャットボットは、ユーザによって否定的に認識されるので、前述のように、応答性と能力の間のバランスを達成するためにモデルのパラメータをダイヤルすることは良いプラクティスです。

OpenAIのユースケースによっては、秒単位ではなく分単位でのレスポンスを期待することができます。 OpenAIをPubNubソリューションに統合していて、レスポンスの時間がデフォルトのFunctionランタイムを超えることが予想される場合は、弊社に[ご連絡](https://www.pubnub.com/company/contact-us/)ください。

**要約すると**
---------

PubNubとOpenAI / ChatGPTの両方を組み合わせることで、あらゆるオンライン体験にAIとチャットを追加する非常に強力なリアルタイムソリューションを迅速に作成することができます。

始める準備ができたら、私たちの[管理ダッシュボードに](http://admin.pubnub.com/)アクセスして、無料のPubNubキーのセットをサインアップしてください。 私たちは、あらゆる業界をカバーする多くの[チュートリアルや](https://www.pubnub.com/tutorials/) [デモを](https://www.pubnub.com/demos/)用意しています。また、[PubNubショーケース](http://pubnub-showcase.netlify.app/)（[Githubでも](https://github.com/PubNubDevelopers/PubNub-Showcase)利用可能）で、OpenAIを利用したチャットボットの例をご覧ください。

#### 目次

[ハイレベルなアーキテクチャ](#h-0)[PubNub関数を](#h-1)[設定する](#h-0)[PubNub関数を次のように](#h-2)[作成](#h-1)[します:](#h-2)[OpenAI / ChatGPTPを呼び出すPubNub関数の例は](#h-3)次のとおりです[:](#h-2)[OpenAI APIChat](#h-4)[Completion API](#h-5)[プロパティチャットへのコンテキストの](#h-6)[提供](#h-5)[長時間実行する](#h-6)[クエリ](#h-7)[まとめ](#h-8)

PubNubはどのようにお役に立てるでしょうか？
========================

この記事は[PubNub.comに](https://www.pubnub.com/blog/build-a-chatbot-with-pubnub-and-chatgpt-openai/)掲載されたものです。

PubNubのプラットフォームは、開発者がWebアプリ、モバイルアプリ、IoTデバイス向けにリアルタイムのインタラクティブ機能を構築、提供、管理できるように支援します。

私たちのプラットフォームの基盤は、業界最大かつ最もスケーラブルなリアルタイムエッジメッセージングネットワークです。世界15か所以上で8億人の月間アクティブユーザーをサポートし、99.999%の信頼性を誇るため、停電や同時実行数の制限、トラフィックの急増による遅延の問題を心配する必要はありません。

PubNubを体験
---------

[ライブツアーを](https://www.pubnub.com/tour/introduction/)チェックして、5分以内にすべてのPubNub搭載アプリの背後にある本質的な概念を理解する

セットアップ
------

[PubNubアカウントに](https://admin.pubnub.com/signup/)サインアップすると、PubNubキーに無料ですぐにアクセスできます。

始める
---

[PubNubのドキュメントは](https://www.pubnub.com/docs)、ユースケースや[SDKに](https://www.pubnub.com/docs)関係なく、あなたを立ち上げ、実行することができます。