Azure AI Bot ServiceとAzure OpenAIを使って「生成AIチャットボット」を作ってみる – 第2回:生成AIを使って質問に回答できるようにする

「Azure AI Bot Service」と「Azure OpenAI」を使えば、生成AIチャットボットも簡単に作れます。
2023.11.20

みなさん、こんにちは!
福岡オフィスの青柳です。

「Microsoft Azureで生成AIチャットボットを作ってみる」シリーズの第2回です。

  • 第1回: シンプルな「オウム返し」ボットを作る
    • (Azure AI Bot Serviceを触ってみる)
  • 第2回: 生成AIを使って質問に回答できるようにする ← 当記事です
    • (Azure OpenAIを組み合わせてみる)
  • 第3回: 社内情報に基づいた回答ができるようにする (公開予定)
    • (Cognitive Searchを使ったRAGを組み込む)

前回は、生成AIを使わないシンプルなチャットボットを作成することで、Azure AI Bot Serviceの使い方を理解しました。

今回は、前回作成したチャットボットに「Azure OpenAI」サービスを組み込んで、「生成AIチャットボット」を作成したいと思います。

作業の流れ

今回の作業は、前回の記事で構築した環境の続きとして行います。

  • (1) Azure OpenAIをセットアップする
  • (2) アプリケーションを生成AIに対応させる
  • (3) ローカル環境でアプリケーションをテストする
  • (4) Azure環境にアプリケーションをデプロイする
  • (5) Azure環境にデプロイしたチャットボットをテストする

(1) Azure OpenAIをセットアップする

Azureポータル画面で「すべてのサービス」を開き、カテゴリ「AI + Machine Learning」にある「Azure OpenAI」をクリックします。

「Azure OpenAI」の画面が開きますので、上部メニューにある「作成」をクリックします。

ウィザード画面に沿って必要な情報を入力して行きます。

項目名 設定値
プロジェクトの詳細 サブスクリプション リソース作成先のサブスクリプションを選択
リソースグループ リソース作成先のリソースグループを選択
インスタンスの詳細 リージョン Japan East
名前 任意の名前を指定 (例:kumamon-openai)
価格レベル Standard S0

続いて、ネットワークに関する設定項目を選択します。

項目名 設定値
種類 インターネットを含むすべてのネットワークがこのリソースにアクセスできます。

ここでは、デフォルトの選択のまま次へ進みます。

続いて、リソースの「タグ」を設定する画面になります。

必要に応じてタグの設定を行います。

ウィザードの最後は、設定内容のレビュー画面になります。

内容を確認して、「作成」をクリックします。

リソースの作成中 (デプロイ中) の画面でしばらく待つと、「デプロイに成功しました」と表示されます。

画面下部の「リソースへ移動」をクリックすると、作成されたAzure OpenAIリソースの画面に遷移します。

続けて、Azure OpenAIで使用する「モデル」のデプロイを行います。

上部メニューにある「Azure OpenAI Studioに移動する」をクリックします。

「Azure OpenAI Studio」画面に遷移します。

左側のメニューから「デプロイ」を選択します。

「デプロイ」画面で、上部メニューにある「新しいデプロイの作成」をクリックします。

モデルのデプロイに関する設定を入力します。

項目名 設定値
モデルを選択してください gpt-4
モデルバージョン 自動更新を既定に
デプロイ名 gpt-4
コンテンツフィルター Default
1分あたりのトークンレート制限 (数千) 任意の値を指定
(あまり高いとアカウント全体に影響を与えますし、あまり低いと使い辛くなります)
動的クォータを有効にする 有効

「作成」をクリックします。

デプロイされたモデルが表示されていることを確認します。

これで「Azure OpenAI」サービスの準備は終わりです。

(2) アプリケーションを生成AIに対応させる

Node.jsアプリケーションからAzure OpenAIを利用するために、Microsoftが提供する「Azure OpenAIクライアントライブラリ」をインストールします。

JavaScript 用 Azure OpenAI クライアント ライブラリ | Microsoft Learn
https://learn.microsoft.com/ja-jp/javascript/api/overview/azure/openai-readme

my-chat-botディレクトリへ移動します。

cd my-chat-bot

npm installコマンドを実行して、アプリケーションのディレクトリに「@azure/openai」パッケージをインストールします。 (-gオプションを付けないように気をつけてください)

npm install @azure/openai

(私が作業した環境では、パッケージのバージョンは「1.0.0-beta.7」でした)

ここからは、ボットのメイン処理が記述されているbot.jsファイルの内容を更新していきます。

まず、モジュールインポートの宣言に「@azure/openai」を追加します。

bot.js

// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

const { ActivityHandler, MessageFactory } = require('botbuilder');
const { OpenAIClient, AzureKeyCredential } = require("@azure/openai");

Azure OpenAIのAPIを呼び出すための「エンドポイント」「APIキー」「使用するモデル (デプロイ名)」を定数宣言します。

bot.js

const endpoint = 'https://XXXXXXXX.openai.azure.com/';
const apiKey = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX';
const deploymentName = 'gpt-4';

「エンドポイント」「APIキー」は、Azureポータルで「Azure OpenAI」リソースの「キーとエンドポイント」画面で確認できます。

「使用するモデル (デプロイ名)」は、Azure OpenAI Studioの「デプロイ」画面で確認できます。

生成AI (Azure OpenAI) を呼び出して質問から回答を生成する処理を、関数「getReply()」として用意します。

bot.js

const getReply = async (req) => {
    const messages = [
        { role: "user", content: req },
    ];

    var res = '';

    try {
        const client = new OpenAIClient(endpoint, new AzureKeyCredential(apiKey));
        const events = client.listChatCompletions(deploymentName, messages, { maxTokens: 128 });
        for await (const event of events) {
            for (const choice of event.choices) {
                const delta = choice.delta?.content;
                if (delta !== undefined) {
                    res += delta;
                }
            }
        }
    } catch (err) {
        console.log(err);
        throw err;
    }

    return res;
}

チャット形式で回答を生成する場合、「getCompletions」APIを使う方法と「listChatCompletions」APIを使う方法があります。

ただし、GTPモデルの最新バージョンでは「getCompletions」APIは使用できなくなっているので、注意してください。 (今回は「listChatCompletions」APIを使用しています)

チャットボットと会話のやり取りを行う処理を、以下のように更新します。

class EchoBot extends ActivityHandler {
    constructor() {
        super();
        // See https://aka.ms/about-bot-activity-message to learn more about the message and other activity types.
        this.onMessage(async (context, next) => {
            const replyText = await getReply(context.activity.text);
            await context.sendActivity(MessageFactory.text(replyText, replyText));
            // By calling next() you ensure that the next BotHandler is run.
            await next();
        });

42行目で、ボットの返答内容replyTextにセットしている内容を、以下のように変更します。

  • 変更前: const replyText = `Echo: ${ context.activity.text }`;
  • 変更後: const replyText = await getReply(context.activity.text);

変更前は「オウム返し」処理でしたが、変更後は「getReply()」関数を呼び出す処理に変更しています。

最後に、これは無くても構わないのですが、ボットが最初に話すメッセージを「こんにちは、ようこそ!」から「質問をどうぞ!」に変更します。

bot.js

        this.onMembersAdded(async (context, next) => {
            const membersAdded = context.activity.membersAdded;
            const welcomeText = '質問をどうぞ!';

アプリケーションコードの変更は以上で全てです。

参考に、更新後の「bot.js」のコード全体を下記に掲載します。

bot.js (クリックすると展開します)
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

const { ActivityHandler, MessageFactory } = require('botbuilder');
const { OpenAIClient, AzureKeyCredential } = require("@azure/openai");

const endpoint = 'https://XXXXXXXX.openai.azure.com/';
const apiKey = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX';
const deploymentName = 'gpt-4';

const getReply = async (req) => {
    const messages = [
        { role: "user", content: req },
    ];

    var res = '';

    try {
        const client = new OpenAIClient(endpoint, new AzureKeyCredential(apiKey));
        const events = client.listChatCompletions(deploymentName, messages, { maxTokens: 128 });
        for await (const event of events) {
            for (const choice of event.choices) {
                const delta = choice.delta?.content;
                if (delta !== undefined) {
                    res += delta;
                }
            }
        }
    } catch (err) {
        console.log(err);
        throw err;
    }

    return res;
}

class EchoBot extends ActivityHandler {
    constructor() {
        super();
        // See https://aka.ms/about-bot-activity-message to learn more about the message and other activity types.
        this.onMessage(async (context, next) => {
            const replyText = await getReply(context.activity.text);
            await context.sendActivity(MessageFactory.text(replyText, replyText));
            // By calling next() you ensure that the next BotHandler is run.
            await next();
        });

        this.onMembersAdded(async (context, next) => {
            const membersAdded = context.activity.membersAdded;
            const welcomeText = '質問をどうぞ!';
            for (let cnt = 0; cnt < membersAdded.length; ++cnt) {
                if (membersAdded[cnt].id !== context.activity.recipient.id) {
                    await context.sendActivity(MessageFactory.text(welcomeText, welcomeText));
                }
            }
            // By calling next() you ensure that the next BotHandler is run.
            await next();
        });
    }
}

module.exports.EchoBot = EchoBot;

(3) ローカル環境でアプリケーションをテストする

更新したアプリケーションをローカル環境で実行して、「Bot Framework Emulator」を使ってテストします。

現在のディレクトリがmy-chat-botであることを確認して、以下のコマンドを実行します。

npm start

アプリケーションが待ち受け状態になりましたら、「Bot Framework Emulator」を起動します。 Bot URLhttp://localhost:3978/api/messagesを指定して接続します。

何か質問してみましょう。 ちゃんと生成AIが回答してくれましたか?

もし回答してくれない場合は、ここまでの手順や、アプリケーションコードの変更内容を、もう一度見直してみてください。 また、アプリケーションを起動しているターミナル画面に何かエラーが表示されていないか確認してみるのも良いでしょう。

テストが終わりましたら、アプリケーションを「Ctrl+C」で停止しておきます。

(4) Azure環境にアプリケーションをデプロイする

それでは、作成したアプリケーションをAzure環境にデプロイして実行しましょう。

前回 (第1回) で行ったように、App Serviceへのアプリケーションのデプロイは、ローカル環境でAzure CLIを使って行います。

まず、アプリケーションのディレクトリ (エントリポイントであるindex.jsが存在するディレクトリ) へ移動します。

cd my-chat-bot

前回デプロイを行った際に作成したzipファイルが残っている場合は、念のために削除しておきます。

rm ../deploy.zip

アプリケーション資産の一式をzipで圧縮します。

zip -r ../deploy.zip .

圧縮が終わったら、1つ親のディレクトリ (作成されたzipファイルが存在するディレクトリ) へ移動します。

cd ..

App Service (Webアプリ) のアプリケーションをデプロイするAzure CLIコマンドを実行します。

「リソースグループ名」「App Service (Webアプリ) の名前」は、皆さんの環境に合わせて入力してください。

az webapp deployment source config-zip --resource-group "<リソースグループ名>" --name "<App Service (Webアプリ) の名前>" --src ./deploy.zip

コマンドを実行しましたら、デプロイが正常に開始されることを確認します。

$ az webapp deployment source config-zip --resource-group "resourcegroup" --name "kumamon-chatbot-app" --src ./deploy.zip
Getting scm site credentials for zip deployment
Starting zip deployment. This operation can take a while to complete ...
Deployment endpoint responded with status code 202

ステータスコード「202」(HTTP Accept) が表示されていれば、デプロイ開始しています。

ここからしばらく時間がかかりますので待ちます。

デプロイが完了すると、結果が表示されます。

{
  "active": true,
  "author": "N/A",
  "author_email": "N/A",
  "build_summary": {
    "errors": [],
    "warnings": []
  },
  "complete": true,
  ・・・
  (中略)
  ・・・
}

エラーが表示されていないことを確認します。

また、Azureポータルの「App Service」リソース画面でデプロイの履歴を確認することもできます。

(5) Azure環境にデプロイしたチャットボットをテストする

アプリケーションがデプロイできましたら、動作をテストしてみましょう。

Azureポータルで「Bot Service」の「Webチャットでテスト」を使ってテストします。

Microsoft Teamsからテストします。

無事に動作しましたでしょうか?

これで、Azureで作成した「生成AIチャットボット」の完成です!

おわりに

今回の記事では、前回作成した「シンプルなチャットボット」に「Azure OpenAI」を組み込みました。 ベースとなる「シンプルなチャットボット」が作成できていれば、それほど手間をかけずに「生成AIチャットボット」に進化させることができます。

今回作成したアプリケーションにはいくつか課題 (「アプリケーションコードにAPIキーがハードコードされている」等) がありますが、機会があれば改善してみたいと思います。

次のステップでは、今回作成した生成AIチャットボットに「Cognitive Search」サービスを使った「RAG」を組み込んで、より本格的な生成AIチャットボットにしたいと思います。