Radzen.BlazorのRadzenAIChatコンポーネントでAIと会話してみる
はじめに
Blazorを使用した開発に携わる皆様、UIフレームワークは何を使用されてますでしょうか。
私はRadzenを好んで使用します。
いまサラっと「好んで」と綺麗な言葉を使いましたが、正直に言うと、私がBlazorのUIフレームワークを選ぶ際はほとんどRadzenを使用しています。というかまずRadzenで何とかならないか、などと考えます。
そんな自称 Radzen 親善大使な私ですが、いつのまにやらRadzenからRadzenAIChatコンポーネントなるものがリリースされていることを知りました。
Radzen 親善大使としては使用してみる他ありません。
そんなこんなでRadzenへの日頃の感謝も込めて、本記事の作成に至ります。
Radzen Blazor とは
Radzen Blazorは、DataGrid、スケジューラ、チャート、およびマテリアルデザインの堅牢なテーマが満載の90 +無料のネイティブBlazorUIコンポーネントのセットです。
BlazorのUIフレームワークは他にも多々ありますが、Radzenは公式ページにサンプルが豊富で、かつブラウザページ上のEditSoureにてコンポーネントのちょっとした変更をその場で試せる点が素晴らしいです。
RadzenAIChat コンポーネント とは
RadzenAIChatコンポーネントは、BlazorアプリにChatGPT風の会話UIを簡単に組み込めるコンポーネントです。OpenAIやAzure OpenAIなど任意のAIエンドポイントと接続でき、会話履歴の保持、イベントフック、スタイルのカスタマイズなどが可能です。
RadzenAIChatコンポーネント、かなり手軽にチャットUIを作れるようです。イベントや見た目もそれなりに選択肢があり、自分のアプリに合わせてサクッとカスタマイズできそうです。
環境
開発環境
- Visual Studio 2022 IDE
- .NET 9 SDK
- Azure OpenAI
Azure AI Foundryより接続可能なモデルを事前に準備している前提です。
本記事では以下のモデルを使用しています。- モデル名:gpt-4o-mini
- API Version:2024-02-15-preview
NuGet
- Radzen (本記事掲載時はv 7.4.2)
構築するアプリ
クライアントはRadzenを使用したBlazor WASM、サーバーはWebAPI(ASP.NET Core)で構築を行います。
以下は簡単なアーキテクチャ図です。
※Web APIが必要な理由は後述します。
実装
いざいざRadzenAIChatコンポーネントを実装します。
まずは以下2つのプロジェクトをソリューション内に作成します。
- Blazor WASM プロジェクト(BlazorAppRadzenPoC)
- WebAPI プロジェクト(ChatProxyAPI)
BlazorAppRadzenPoCプロジェクトには、AIChat.razorページを追加しており、NavMenu.razorから遷移可能としている前提です。
Radzenを使うためのコード上での設定手順は割愛します。
以下のQiita記事がわかりやすかったので、引用ご紹介とさせていただきます。
- Qiita Blazor向けのUIフレームワークのRadzen.Blazorを使ってみる
クライアント側
BlazorAppRadzenPoCプロジェクトのAIChat.razorにRadzenAIChatコンポーネントを実装します。
以下のようにAIChat.razorページにRadzenAIChatコンポーネントを配置します。
様々なParameterがありますが今回はあまり指定せず、ほとんどデフォルトの設定で配置しています。(面倒でしたので。。)
詳細はRadzenの以下ドキュメントページやRadzenのソースコードを参照ください。
@page "/aichat"
@using Radzen.Blazor
<PageTitle>AI Chat</PageTitle>
<RadzenAIChat SessionId="@sessionId"
Title="AI Chat"
Placeholder="メッセージを入力してください..."
Style="height: 600px;" />
@code {
private string sessionId = Guid.NewGuid().ToString();
}
また、コンポーネント配置の他にRadzenServiceのDI注入が必要です。
以下のようにRadzen.AddAIChatServiceメソッドを使用して、Blazor WASM側のProgram.csにAIChatServiceの設定を追加します。
using BlazorAppRadzenPoC;
using BlazorAppRadzenPoC.Configuration;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
using Radzen;
WebAssemblyHostBuilder builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.RootComponents.Add<App>("#app");
builder.RootComponents.Add<HeadOutlet>("head::after");
// HttpClient を設定
builder.Services.AddScoped(sp => new HttpClient());
// Radzen Services
builder.Services.AddScoped<DialogService>();
builder.Services.AddScoped<NotificationService>();
builder.Services.AddScoped<TooltipService>();
builder.Services.AddScoped<ContextMenuService>();
// RadzenのAIChatServiceの設定を追加
// 各値は仮値です。適宜編集ください。
builder.Services.AddAIChatService(options =>
{
options.Proxy = "https://localhost:7105/api/chat/completions";
options.Model = "gpt-4o-mini";
options.SystemPrompt = "あなたは親切で有用なAIアシスタントです。";
options.Temperature = 0.7;
options.MaxTokens = 2000;
});
await builder.Build().RunAsync();
クライアント側の実装は以上です。簡単ですね。
サーバー側
ChatProxyAPIプロジェクト(WebAPI)に、Azure OpenAIに接続しAIよりレスポンスを受信と返却を行う機能を実装します。
ポイントとして、クライアント側のRadzen.AIChatServiceがAIから応答を得るときに実行されるGetCompletionsAsyncメソッドは、Streamでレスポンスを受信する前提で実装されているようです。
ですので、サーバー側APIの処理もStreamでreturnするように実装する必要があります。
以下はサーバー側WebAPI機能をMinimalAPIで実装した例です。
// ASP.NET Core Web API アプリケーションのビルダーを作成
WebApplicationBuilder builder = WebApplication.CreateBuilder(args);
// CORS(Cross-Origin Resource Sharing)設定
// Blazor WebAssembly アプリケーションからのクロスオリジンリクエストを許可
builder.Services.AddCors(options =>
{
options.AddDefaultPolicy(policy =>
{
// 任意のオリジン(ドメイン)からのアクセスを許可
_ = policy.AllowAnyOrigin()
// 任意のHTTPメソッド(GET, POST, PUT, DELETE等)を許可
.AllowAnyMethod()
// 任意のHTTPヘッダーを許可
.AllowAnyHeader();
});
});
WebApplication app = builder.Build();
app.UseHttpsRedirection();
// CORSミドルウェアを有効化
// 上記で設定したCORSポリシーを適用
app.UseCors();
// RadzenAIChatService用のOpenAI互換プロキシエンドポイントをMinimal APIで実装
// POST /api/chat/completions とエンドポイントを定義
// RadzenのAIChatServiceからのリクエストをAzure OpenAI APIにプロキシ転送
app.MapPost("/api/chat/completions", async (HttpContext context, ILogger<Program> logger) =>
{
try
{
// Azure OpenAI接続情報をセット。適宜設定してください。
// Azure OpenAI リソースのエンドポイントURL
string? endpoint = "https://XXX.openai.azure.com/";
// デプロイメント名(モデル名)
string? deploymentName = "gpt-4o-mini など";
// APIキー
string? apiKey = "XXX";
// API Version
string? apiVersion = "20XX-XX-XX-previewなど"
// Azure OpenAI Chat Completions API のエンドポイントURL構築
string azureOpenAIUrl = $"{endpoint}openai/deployments/{deploymentName}/chat/completions?api-version={apiVersion}";
// プロキシリクエストメッセージを作成
// 元のHTTPリクエストボディをそのままAzure OpenAI APIに転送
HttpRequestMessage request = new(HttpMethod.Post, azureOpenAIUrl)
{
// リクエストボディをストリームとして設定(メモリ効率的)
Content = new StreamContent(context.Request.Body)
};
// Content-Typeヘッダーの設定
// 通常は "application/json" だが、charset情報などを適切に処理
if (context.Request.ContentType != null)
{
// Content-Type から メディアタイプのみを抽出(charset部分を除去)
string mediaType = context.Request.ContentType.Split(';')[0].Trim();
request.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue(mediaType);
}
// Azure OpenAI認証用のAPIキーヘッダーを追加
// Azure OpenAI では "api-key" ヘッダーで認証を行う
request.Headers.Add("api-key", apiKey);
// HttpClientを使用してAzure OpenAI APIにリクエストを送信
using HttpClient httpClient = new();
HttpResponseMessage response = await httpClient.SendAsync(request);
// クライアントに返すレスポンスのステータスコードを設定
// Azure OpenAI APIからのステータスコードをそのまま転送
context.Response.StatusCode = (int)response.StatusCode;
// レスポンスヘッダーをクライアントにコピー
// Azure OpenAI APIからのヘッダーを適切に転送
foreach (KeyValuePair<string, IEnumerable<string>> header in response.Headers.Concat(response.Content.Headers))
{
// "transfer-encoding" ヘッダーは除外
// ASP.NET Core が自動的に処理するため、手動設定すると競合する
if (header.Key != "transfer-encoding")
{
context.Response.Headers[header.Key] = header.Value.ToArray();
}
}
// Azure OpenAI APIからのレスポンスボディをストリーミングで転送
// メモリ効率的にデータを転送し、大きなレスポンスでもメモリ不足を防ぐ
return Results.Stream(
// レスポンスストリーム
await response.Content.ReadAsStreamAsync(),
// Content-Type(デフォルト: application/json)
response.Content.Headers.ContentType?.ToString() ?? "application/json"
);
}
catch (Exception ex)
{
return Results.Problem(ex.Message, statusCode: 500);
}
});
app.Run();
サーバー側の実装としては以上です。
クライアント側からhttpClinetを使用して直接Auzre OpenAI へ接続することも可能と考えますが、APIキーがブラウザ側に渡ってしまうなど、セキュリティの関係上、上記のように仲介するWebAPIを準備するアプローチが推奨されます。
動作確認
いよいよ動作確認を行います。
左側がWebAPI、右側がクライアントです。
gif
無事、AIの回答結果がクライアント側に表示されました。正直、泣いた。
まとめ
ということで、RadzenのAIChatコンポーネントを簡単に実装してみました。
RadzenAIChatコンポーネントを使えば、わずかなコードでChatGPT風の会話UIをBlazorアプリに組み込むことができます。
RadzenAIChatコンポーネントの配置とDI設定だけで基本的な機能が使え、プロキシAPIを通すことでセキュアにAzure OpenAIと連携できます。
Radzenらしく直感的で使いやすく、AIチャット機能を手軽に実装したい場面では非常に有用なコンポーネントだと感じます。
本記事の内容が何かしらのお役に立てば幸いです。
参考
- GitHub Radzen Blazor
- Radzen Blog
Discussion
私は基本はDevExpress です、PDFを扱う場合はSyncfusionになります。
SyncfusionのSpreadSheetは、徳に凄くて、ほぼ、Excelです。
コメントいただきありがとうございます!
DevExpress は少し触ったことがあり堅実な印象でよかったです。Syncfusionは触ったことなかったので、情報提供うれしいです!少し調べましたがSpreadSheetはおっしゃるようにExcelさながらのようですね。需要が高そうです。今度、触ってみます!ありがとうございます。