👋

OpenAI APIで遊ぶのにWix Chatと Velo by Wix が意外に便利[本編]

2023/07/04に公開

前提

WixやWixの管理画面に詳しくない方向けに以下の記事
OpenAI APIで遊ぶのにWix Chatと Velo by Wixが意外に便利 予備知識編 があります。

実際に記事を読みながら作業したい方で、

  • Wixのアカウントを持っていない
  • サイトを持っていない、チャット機能をつけていない
  • 管理画面の使い方がわからない
  • Velo by Wixについて知らない
    といった方は先に上記の記事をご参照ください。

はじめに

OpenAI のAPIを使って、趣味や学習用に何かを作って共有したい。
しかし、本体とは別にChat UIの作成やインフラ構築が面倒、あるいは本体の実装に専念したい。
そういう時の選択肢の1つとして Wix(Wix Chat)を使った簡単なChat bot作成例を紹介します。

Wix Chatのメッセージに入力された内容を使い、OpenAI APIを使ってLLMに質問をし、受け取った回答をWix Chatで質問者に返信するお問い合わせbotを作ります。

WixChat送信メッセージの取得

Wix Chatに入力されたメッセージは Velo のバックエンドコードを用いて取得することができます。
ただし、ウェブモジュール(jsw)ではなく、events.js という予約された名称のファイルに実装します。ここで実装された処理は対応するイベントが発生すると自動的に呼び出されます。

少しだけWixの使い方の復習ですが、Veloでの開発をするためにはサイトエディッタにて開発モードを有効にする必要があります。画面上部の「開発モード」ボタンから開発モードを有効してください。

では、events.jsを作成します。
一番左側のメニューから「公開・バックエンド」(波括弧のマーク)を選びます。
バックエンド欄にフォーカスすると表示されるプラスマークをクリックし、
「バックエンドイベントの処理」を選択します。
「events.js」というファイル名でファイルを直接作成しても構いません。

作成されたファイルにあるコメントは一読をお勧めします、不要であれば削除しましょう。

このイベントに、利用したいイベントハンドラを実装します。
今回利用するのは wixChat_onMessageです。

export async function wixChat_onMessage(event) {
  console.log('wixChat_message')
  console.log(event, 'event')
  console.log(event.direction, 'direction')
  if(event.direction == "VisitorToBusiness") {
	//お客様からサイトへのメッセージ
  }
  console.log(event.payload.text, 'message')
}

「公開」を押したら、公開サイトから早速チャットメッセージを送信してみましょう。
通常は単体でのテスト(行数の左横の三角印で実行)を行うのですが、eventオブジェクトを自前で定義するのが煩わしいので省いてしまいます。

ログの閲覧方法ですがサイトエディッタの一番左のメニュー、下から2番目のレンチマークの「デベロッパーツール」を選択し、サイトイベントをクリックし別ウインドウを開きます。

以下のようにログが表示されるのですが、注意点としては先にこの画面を開いて、ログ取得を有効化する必要があります。つまりこの画面を開く前、ログ停止状態時のログは取得できません。

eventオブジェクトの内容ですが、後述のつなぎこみの部分でもう少し触れますが、
リファレンスEvents: onMessage / APIドキュメント
を読んだり、ログをとって中身をのぞいて、必要に応じて調べてください。使える値を見つけるとアイデアが浮かぶこともあるかと思います。
注意点としては、ドキュメントに記載されている値のパターン(組み合わせ?)やその説明は結構ざっくりに感じました。自分でテストしながらログをとって確認することを勧めます。

WixChatメッセージ返信

次はメッセージの返信をしてみましょう。

その前に、events.jsを肥大化させるのは行儀が悪いので、別ファイルに実装することにします。
ウェブモジュールを使うべきか、通常のjsファイルを使うか迷ったんですが、フロントエンドから使わないという点を根拠に chat.jsという名前のファイルを作成します。
手順は先ほどとほぼ同様で「新規.jsファイルを作成」を選択します。

気になった人は以下の記事も読んでみてください。
Creating a Web Module

顧客から送られてきたメッセージへの返信は以下のような実装になります。
実装箇所は、メッセージを受信し、OpenAIに質問を送信、結果を取得した後になります。
一旦別ファイルに処理だけ実装してしまいます。

import wixChatBackend from 'wix-chat-backend';

export async function sendChatMessage(channelId, message) {
    try {
        await wixChatBackend.sendMessage({
            "messageText": message,
            "channelId": channelId,
            "metadata": {},
            "sendAsVisitor": false
        })
    } catch (e) {
        console.log(e, 'message sent error');
        return false
    }
    return true
}

importでモジュールを読み込み、wixChatBackend.sendMessageを実行します。

パラメターについては以下を参考にしてください。
wix-chat-backend onMessage / velo Reference

channelIDについて少し補足しますが、ドキュメントにはChannelのIDみたいに記載されています。じゃあChannelって何?となると、、、なかなか定義が見つからず以下の一文が手掛かりに考えます。

You cannot use a channel programmatically until it is created. A channel is created the first time the business or a visitor sends a message via the chatbox widget to a specific recipient.

あくまで推測ですが、会話が始まる際に、特定の相手毎に作成されるルームのような概念ですかね。
もし仕事で使うなら、じゃあ会話中にログアウトしたら、逆に未ログイン状態で会話を開始した後ログインしたら?複数ブラウザ、複数ID、複数端末、、、みたいな疑問が出てきて、1つ1つ検証しないといけないわけですが、今回は仕事ではないので流します。
もし業務で今回の記事を参照されている方はご注意くださいね。

ここで作った実装はパラメータがシンプルなので単体テストだけやって先に進みます。

コード左側、行番号横の三角マークを押します。
タブが切り替わるので、パラメータを指定して実行を押します。
channelIdは先ほどメッセージの受信テストをしたときのログから探してください。

OpenAI API のnpmパッケージのインストール

次はOpenAIに質問を投げて結果を取得する部分に進みますが、OpenAI APIを使うための、Node.jsパッケージが必要です。
Velo の面白いところは、結構な数のnpmパッケージをインストールして利用することができることです。
以下の手順でインストールしましょう。
左メニュー、上から4番目のパッケージを選択します。

npmからパッケージをインストールをクリックします。

パッケージの検索画面が表示されるので、openaiで検索します。
Versionはこの記事を書いている2023年7月4日で3.2.1でした。
手元の環境だと3.3.0。3.3.0が20日程度前に出ているので最新のものがすぐ使えるわけではないようです。利用するライブラリによっては大きなデメリット、リスクになるので業務で活用される方は慎重に検討してください。
(最悪自前のサーバーを立てて、Veloから自作APIを叩いて回避できますけどね)

脱線しますが、sendgrid、stripeなどで検索すると利用可能になっています。色々作ってみたくなりませんか?
もし対応していない場合でも「リクエスト」ボタンがあれば検討してもらえるようです。もちろんNGケースもあるようで、LangChainは未サポートと表示され、リクエストもできません(笑)

OpenAI APIのサンプルコード

OpenAI API に質問を投げて回答を得る部分の実装をします。

この部分ではOpenAIから取得したAPIキーを利用します。
バックエンド側に実装しているとはいえ、APIキーはより安全な管理が求められます。
シークレットマネージャーを利用して管理しましょう。

サイドメニュー下から2番目の「デベロッパーツール」から「シークレットマネージャー」のページに飛んでください。

遷移先のページで「シークレットを保存」ボタンを押してキーを登録します。

ここで登録した値の使い方の例は以下を参考にしてください。
(このコードは使わないので保存不要です)

import { getSecret } from 'wix-secrets-backend';
export async function test() {
    const mySecret = await getSecret("OPENAI_API_KEY");
}

詳細は以下の資料も参考にしてください
Velo: About the Secrets Manager / Wix Help Center

上記を踏まえ、OpenAIに質問を投げて、答えを受け取る処理を実装します。
OpenAIのAPIを使ってみた系の記事として、質問をそのまま送って、そのまま結果を返すのでは、周回遅れ感が否めないので、Function callingを無理やり詰め込んでみました。
他、特筆すべきことはないので詳細は省きます。

import wixChatBackend from 'wix-chat-backend';
import { Configuration, OpenAIApi } from "openai";
import { getSecret } from 'wix-secrets-backend';

export function sendChatMessage(channelId, message) {
  //中略
}

export async function execChat(mesg) {
    if(!mesg) {
        return false
    }
    try {
        const configuration = new Configuration({
            apiKey: await getSecret('OPENAI_API_KEY'),
        });
        const openai = new OpenAIApi(configuration);
        const completion = await openai.createChatCompletion({
            model: "gpt-3.5-turbo-0613",
            messages: [{
                    role: "system",
                    content: "あなたはWixで運営されているサイトの管理人です。利用者からのメッセージに答えてあげてください。意味不明 なメッセージはスパム報告してください",
                },
                {
                    role: "user",
                    content: mesg,
                },
            ],
            function_call: "auto",
            functions: [{
                name: "spamreport",
                description: "意味不明なメッセージはスパム報告する",
                parameters: {
                    type: "object",
                    properties: {
                        mesg: {
                            type: "string",
                            description: "提案文",
                        },
                    },
                    required: ["mesg"],
                },
            }, ],
        });
        if(completion.data.choices[0].message.function_call) {
            //本来なら指定されたfunctionを実行するが省略
            return 'スパムメッセージとして報告させていただきました。'
        }
        return completion.data.choices[0].message.content
    } catch (e) {
        console.log(e, 'openai error');
    }
}

普通の会話文を入れると通常通り回答してくれますが、"sdfsdfsdf"のような出鱈目な文字列を送るとfunction_callが返ってきます。

繋ぎこみ

それでは今まで作ったものを1つに繋ぎ込んでみましょう。

import { sendChatMessage, execChat } from 'backend/chat'

export async function wixChat_onMessage(event) {
  console.log('wixChat_message')
  const chatMessage = event.payload.text;
  const channelId = event.channelId
  if(event.direction == "VisitorToBusiness") {
      const ans = await execChat(chatMessage)
      await sendChatMessage(channelId, ans)
  }
}

event.directionの判定は忘れないようにしましょう。
無限ループが発生します。
(rate limitがあるのでどこかで停止すると思いますが、、、)

それでは保存して、公開サイトからメッセージを送って試してみましょう。
不特定多数に公開しているので、くれぐれも APIキーに利用制限をかけるのをお忘れなく。
使わなくなったらAPIキーやサイトは削除しましょう。

まとめ

少し細かめに説明したので、難しそうな印象を与えてしまったかもしれませんが、Wix、Velo by WIxを使うと、気軽にOpenAI APIを使ったアプリケーションが作れます。

好き嫌いが分かれるところかもしれませんが、ブラウザで実装が完結するので、環境のセットアップもいりません。サッと作って、すぐに誰かに使ってもらえます。

実は、数ヶ月前に Wixのストア機能とブログ機能を使った架空のECサイトを作り、サイトコンテンツをVectorDBに格納し、それをLLMに参照させるお問合せボットの検証をしていました。
その後長らく検証をしていなかったのですが、Function callingが実装されたのをきっかけに手を加えたくなり、思い出すのを兼ねて記事にしてみました。

思わず記事作成に時間がかかってしまいましたが、ストア・ブログコンテンツのVectorDB化なども、そのうち記事にしてみたいと思っています。

また、今回軽く触れた、SendGridやStripeとの連携も記事にする機会があるかもしれないので、その際はご一読ください。

参考文献

Velo Backend Events / Wix Help Center

wix-chat-backend / Velo Reference

追記

  • 2023.07.14 サンプルコードを修正しました。
    (event.direction == "VisitorToBusiness")の時だけexecChatを呼び出す

Discussion