はじめに
こんにちは。NewITソリューション部の辻です。
Microsoft Azureには、「Azure OpenAI Service」という生成AIサービスがあります。
今回はこの「Azure OpenAI Service」と、会話型ボットを作成できる「Azure Bot Service」を用いて、
Chat GPTのような生成AIと会話できるアプリを作り、Teamsで使っていきたいと思います。
Azure OpenAI Serviceとは
Azure OpenAI Serviceは、マイクロソフトが提供する「Microsoft Azure」において、人口知能(AI)が利用できるサービスです。
ChatGPTでも用いられているような生成AIモデルをAPI経由で使用することができます。
利用できるAIモデルには、以下のようなものがあります。
- GPT-3.5, GPT-4
- 自然言語を理解・生成できるモデルのシリーズで、ChatGPTにも含まれています。
- GPT-4o
- より大規模なマルチモーダルのモデルであり、テキストや画像の解析から、高精度な内容の文章を出力できます。
- DALL-E
- ユーザが提供する文章から画像を生成します。
- 埋め込み
- 機械学習において、テキストや文章を数値化(ベクトル化)するために使用します。
Azure OpenAI Serviceでは、これらの様々なモデルが提供されるため、開発者が機械学習に関する高度な知識を持たなくとも、容易にアプリケーションとして組み込むことが可能となっています。
また、ユーザーの入力データがモデルの再学習に使われることはないため、個人情報や業務上の機密情報をモデルが学習してしまい、第三者の利用によって該当情報が漏えいしてしまうといったセキュリティ上のリスクを回避することができるという点も、Azure OpenAI Serviceの魅力であると言えます。
アプリ構成
今回はこのような構成で、Azure OpenAI Serviceをバックエンドにしたチャットボットを作成しています。
Azure Bot ServiceのインターフェイスとしてTeamsにChannelを登録し、コードベースで作成したボットをパッケージとしてTeamsに組み込む形となっています。
Azure OpenAI Serviceの利用準備
Azure OpenAI Serviceは現在GA(正式リリース)されているものの、生成モデルの利用には申請が必要となります。
上記のURLから、AzureサブスクリプションID、メールアドレス、利用ケースなどを記入し申請を行います。
申請後は、数日くらいでMicrosoftから承認のメールが届くので、承認されると、AzureポータルからOpenAIのリソースを作成することが出来るようになります。
リソースを作成した後は、「モデル デプロイ」を選択し、Azure OpenAI Studioでモデルを作成します。
この際、モデル毎に「1分あたりのトークン数(Tokens-per-Minute)」に相当するクォータを設定することになりますが、これはリージョン毎に予め一定数設けられており、その中から割り当てられるものになるため、同じリージョンで複数のモデルを扱う場合には超過しないように注意する必要があります。
チャットボットの構築
公式より提供されているMicrosoft Bot Frameworkを使用します。
Bot Framework v4 SDK Templates for Visual Studio
ダウンロード後、展開するとVisual StudioでEcho Botのプロジェクトが新規作成できるようになります。
このテンプレートを用いることで、ロジックの開発、各チャンネル間での中継設定、管理構成の設定などを様々行うことができます。
認証情報の追加
Azure Bot Serviceでは、OAuthにより各ユーザーの資格情報に基づいて生成されたトークンを用いて自動的に認証を行います。
そのため、事前にMicrosoft Entra IDでアプリケーション登録を行う必要があります。
ホームのタブ>「Microsoft Entra ID」>「アプリの登録」>「新規登録」から今回利用するアプリの登録を行います。
登録後は、「証明書とシークレット」>「新しいクライアントシークレット」からパスワードを発行します。
パスワードの値の部分は、画面を切り替えると確認出来なくなるため、作成時点で一旦メモ等に保持しておくと良いかもしれません。
作成したプロジェクトの「appsettings.json」に、登録したアプリケーションの各情報を記載します。
- Microsoft App Id : アプリケーション(クライアント)ID
- Microsoft App Password : クライアント シークレットの値
- Microsoft App Tenant ID : ディレクトリ(テナント)ID
Azure OpenAI Serviceの呼び出し
Bot Framework SDKを使って作成できるボットは、デフォルトだとユーザーの入力をそのまま返すようになっています。
まずは、Azure OpenAI ServiceをAPI経由で呼び出し、ユーザーの入力から結果を出力させるようにします。
リクエスト時のパラメータとして、生成されるトークンの最大数やサンプリング係数などを設定していますが、より多くの様々なパラメータを設定することが可能です。
Azure OpenAI ServiceのREST APIリファレンス
Echobot.cs
private async Task<string> GenerateTextForAzureAI(string inputText, List<gptMessage> messages)
{
string apiKey = "<Azure OpenAIのAPIキー>";
string endpoint = "https://<Azure OpenAIのリソース名>.openai.azure.com/openai/deployments/<デプロイモデル名>/chat/completions?api-version=2023-05-15";
client.DefaultRequestHeaders.Add("api-key", apiKey);
var requestData = new
{
messages,
temperature = (float)0.7,
max_tokens = 800,
frequency_penalty = 0,
presence_penalty = 0
};
var jsonRequestData = JsonConvert.SerializeObject(requestData);
var content = new StringContent(jsonRequestData, Encoding.UTF8, "application/json");
var response = await client.PostAsync(endpoint, content);
if (response.IsSuccessStatusCode)
{
var jsonString = await response.Content.ReadAsStringAsync();
var responseObject = JsonConvert.DeserializeAnonymousType(jsonString, new
{
choices = new[] { new { message = new { role = string.Empty, content = string.Empty } } },
});
var messageObject = responseObject?.choices[0].message;
return messageObject.content;
}
else
{
return response.StatusCode.ToString();
}
}
さらに、「OnMessageActivityAsync」を書き換え、Azure OpenAI Serviceの結果をボットからのメッセージとして出力させるようにします。
Echobot.cs
protected override async Task OnMessageActivityAsync(ITurnContext<IMessageActivity> turnContext, CancellationToken cancellationToken)
{
string inputText = turnContext.Activity.Text;
List<gptMessage> GPTMessages = new List<gptMessage>();
GPTMessages.Add(new gptMessage { role = "user", content = inputText });
```Azure Open APIの呼び出し```
string generatedText = await GenerateTextForAzureAI(inputText, GPTMessages);
GPTMessages.Add(new gptMessage { role = "assistant", content = generatedText });
```Azure OpenAI Serviceからの応答をユーザーに返信```
await turnContext.SendActivityAsync(MessageFactory.Text(generatedText), cancellationToken);
}
ステートメントの維持
Chat GPTのような一連の会話を成立させるためには、過去に行ったやり取りの内容を踏まえていることが前提となります。
つまり、行ったやり取りを含めてOpenAIに送る必要があるため、ボット側で会話の内容や状態を保存しておきます。
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
```既存のコードに追加```
var storage = new MemoryStorage();
var conversationState = new ConversationState(storage);
services.AddSingleton(conversationState);
var userState = new UserState(storage);
services.AddSingleton(userState);
}
今回はメモリ上に保存していますが、アプリの再起動などによって消えてしまうため、ビジネス用途では保存場所としてBlob StorageやCosmos DB Storageなどを利用するのが良いでしょう。
また、ボットが保存するスコープについても以下の種類があります。
- Conversation State Scope
- 「会話」単位で保存します。1:1の会話だけでなく、グループチャット等ユーザーを問わない範囲でも利用できます。
- User State Scope
- ユーザー毎に状態を保存します。
- Private Conversation State Scope
- 特定のユーザーとボットの情報を保存します。
これらについても、利用用途を踏まえた上で適宜保存しておくのが良いでしょう。
さらに、ステート保持のためのクラスを作成して、ボットの会話処理に組み込みます。
State.cs(新規作成)
public class ConversationData
{
public string Timestamp { get; set; }
public string ChannelId { get; set; }
public List<gptMessage> Messages { get; set; }
}
public class gptMessage
{
public string role { get; set; }
public string content { get; set; }
}
public class UserProfile
{
public string Name { get; set; }
}
Echobot.cs
public class EchoBot : ActivityHandler
{
private static HttpClient client = new HttpClient();
private BotState _conversationState;
private BotState _userState;
public EchoBot(ConversationState conversationState, UserState userState)
{
_conversationState = conversationState;
_userState = userState;
}
public override async Task OnTurnAsync(ITurnContext turnContext, CancellationToken cancellationToken = default(CancellationToken))
{
```ターンが終了した時に状態の変更を保存```
await base.OnTurnAsync(turnContext, cancellationToken);
var storage = turnContext.TurnState.Get<IStorage>(nameof(IStorage));
var userState = turnContext.TurnState.Get<UserState>(typeof(UserState).FullName);
await _conversationState.SaveChangesAsync(turnContext, false, cancellationToken);
await _userState.SaveChangesAsync(turnContext, false, cancellationToken);
}
protected override async Task OnMessageActivityAsync(ITurnContext<IMessageActivity> turnContext, CancellationToken cancellationToken)
{
```ConversationStateとUserStateを取得```
var conversationStateAccessors = _conversationState.CreateProperty<ConversationData> (nameof(ConversationData));
var conversationData = await conversationStateAccessors.GetAsync(turnContext, () => new ConversationData());
var userStateAccessors = _userState.CreateProperty<UserProfile>(nameof(UserProfile));
var userProfile = await userStateAccessors.GetAsync(turnContext, () => new UserProfile());
```ユーザーがなければ作成、ユーザー名はTeamsから取得```
if (string.IsNullOrEmpty(userProfile.Name))
{
string userNamefromContext = turnContext.Activity.From.Name;
userProfile.Name = userNamefromContext;
}
string inputText = turnContext.Activity.Text;
List<gptMessage> GPTMessages = new List<gptMessage>();
```これまで会話があれば予め保持しておく```
if (conversationData.Messages?.Count > 0)
{
GPTMessages = conversationData.Messages;
}
GPTMessages.Add(new gptMessage { role = "user", content = inputText });
```Azure Open APIの呼び出し```
string generatedText = await GenerateTextForAzureAI(inputText, GPTMessages);
GPTMessages.Add(new gptMessage { role = "assistant", content = generatedText });
```Azure OpenAI Serviceからの応答をユーザーに返信```
await turnContext.SendActivityAsync(MessageFactory.Text(generatedText), cancellationToken);
```会話のステート保持```
var messageTimeOffSet = (System.DateTimeOffset)turnContext.Activity.Timestamp;
var localMessageTime = messageTimeOffSet.ToLocalTime();
conversationData.Timestamp = localMessageTime.ToString();
conversationData.ChannelId = turnContext.Activity.ChannelId.ToString();
conversationData.Messages = GPTMessages;
}
}
このようにすることで、会話を行う度に都度、内容や状態を維持しておくことが出来るようになります。
また、ユーザー毎に保存しておくこともできるため、将来的にログ収集やデータ分析に役立てることも可能です。
デプロイ
チャットボットの構築が完了したら、プロジェクトを開いたまま「ビルド」を実行し、その後「発行」を行いAzure上にデプロイします。
今回は、Azure App Service (Windows) へデプロイを行っていますが、デプロイ先としてDockerコンテナー等を選択することも可能です。
無事発行されると、自動的にデプロイ先のURLが開くので、メモに控えておきます。
次に、発行したボットをTeamsアプリとして使用するために、Azure Bot Serviceを作成して登録を行います。
Azureポータルから必要な情報を入力して、Azure Botのリソースを作成します。
「Microsoft App ID」の欄については、「既存のアプリの登録を使用する」を選択し、Microsoft Entra IDで登録したアプリのアプリケーションIDを入力します。
リソースを作成したら、Teamsで利用するために必要なボットの設定を行います。
「チャンネル」から「Microsoft Teams」を選択し、チェックに同意して「適用」をクリックします。
「構成」をクリックし、「メッセージングエンドポイント」に以下のURLを入力します。
- https://{発行先のURL}/api/messages
最後に、Teams内でマニフェストを作成し、パッケージをアップロードすることで利用できるようになります。
Teamsの「アプリ」から「開発者ポータル(Developer Portal)」を開き、カスタムアプリを作成します。
アプリ内の設定については、以下のように行います。
- 「基本情報」に必要事項を入力し、「保存」をクリックします。
- 名前、開発者名、説明、URLなど
- 「アプリのURL」とありますが、ここではプライバシーポリシーや使用条件、開発者情報を記載しているURLを入力するため、今回発行した諸々のURLとは特に関係はありません。
- 「アプリの機能」から「ボット」を選択します。
- 「ボットIDの入力」より、先ほど登録したアプリのアプリケーションIDを入力します。
- 「スコープ」を選択し、「保存」をクリックします。
- 今回は、個人チャットで利用するため「Personal」を選択しています。
- 他にもチームタブで利用したい場合は「Team」、グループチャットで利用したい場合は「Group Chat」を選択することができます。
- 「組織に公開する」をクリックし、公開します。
- テナントによっては、管理者による許可が必要なケースがあるため、入念に確認するようにしてください。
- 「アプリパッケージ」からzipファイルのパッケージをダウンロードします。
Teamsの「アプリ」から「アプリを管理」>「アプリをアップロード」>「カスタムアプリをアップロード」で、ダウンロードしてきたパッケージファイルをアップロードします。
成功すると、Teamsでアプリの画面が開き、チャットボットと会話をすることができるようになります。
まとめ
本記事では、生成AIである「Azure OpenAI Service」と「Azure Bot Service」使って、Teamsで使えるChat GPTのようなチャットボットを作成してみました。
実際に、これらのサービスを利用・開発するやり方については様々ありますが、今回のようにBot Framework SDKを用いた開発はテンプレートとしての土台が出来ているため、取っ掛かりが良く、それでいて比較的カスタマイズ性の高いものとなっています。
更なる機能的な拡張案としては、例えば、
- Teamsコマンドによるプロンプトの自動的な切り替え
- Adaptive Cardを利用したテキスト以外の情報収集
- 「いいね」などのスタンプをフィードバックに見立てた会話分析
などが挙げられるでしょう。
また、今回はTeamsでの構築となっていますが、SlackやLINEなど他にも様々なインターフェイスに対応していますので、
これから少しでもOpen AIを活用していきたいという方、ご興味のある方がいらっしゃいましたら是非試してみてはいかがでしょうか。
当社では、「Azure OpenAI Service の導入ソリューション」や「ナレッジマイニングPoCサービス」をはじめとして、お客様の業務に効果的にAIシステムを導入、活用するための支援やサービスを提供しています。
AIを用いた課題解決に興味をお持ちいただけましたら、ぜひ弊社へお声掛けいただけますと幸いです。