OpenAI チャット入力候補を WinUI 3/Windows アプリ SDK デスクトップ アプリに追加する
このハウツーでは、OpenAI の API を WinUI 3/Windows アプリ SDK デスクトップ アプリに統合する方法について説明します。 OpenAI のチャット入力候補 API を使用してメッセージへの応答を生成できる、チャットのようなインターフェイスを構築します。
前提条件
- 開発用コンピューターをセットアップします (「 Get started with WinUI」を参照してください)。
- 「 C# と WinUI 3/Windows アプリ SDKを使用して Hello World アプリを構築する方法」の主要な概念に関する知識 - このハウツーに基づいて構築します。
- OpenAI 開発者ダッシュボードの OpenAI API キー。
- プロジェクトにインストールされている OpenAI SDK。 ライブラリの完全な一覧については、AIドキュメントを開きますを参照してください。 このハウツーでは、betalgo/openaiを使用します。
プロジェクトの作成
- Visual Studio を開き、
File
>New
>Project
で新しいプロジェクトを作成します。 WinUI
を検索して、Blank App, Packaged (WinUI 3 in Desktop)
C# プロジェクト テンプレートを選択します。- プロジェクト名、ソリューション名、ディレクトリを指定します。 この例では、
ChatGPT_WinUI3
プロジェクトはChatGPT_WinUI3
ソリューションに属しており、C:\Projects\
で 作成されます。
次の既定のファイル構造がソリューション エクスプローラーに表示されます。
環境変数を設定する
OpenAI SDK を使用するには、API キーを使用して環境変数を設定する必要があります。 この例では、OPENAI_API_KEY
環境変数を使用します。 OpenAI 開発者ダッシュボードから API キーを取得したら、コマンド ラインから次のように環境変数を設定できます。
setx OPENAI_API_KEY <your-api-key>
この方法は開発には適していますが、運用アプリにはより安全な方法を使用する必要があることに注意してください (たとえば、アプリの代わりにリモート サービスがアクセスできるセキュリティで保護されたキー コンテナーに API キーを格納できます)。 「OpenAI キーの安全性に関するベスト プラクティス」を参照してください。
OpenAI .NET SDK をインストールする
Visual Studio の View
メニューから 、 Terminal
を選択します。 表示のインスタンスが Developer Powershell
表示されます。 SDKをインストールするには、プロジェクトのルート ディレクトリから次のコマンドを実行します。
dotnet add package Betalgo.OpenAI
SDK を初期化する
MainWindow.xaml.cs
で 、API キーを使用して SDK を初期化します。
//...
using OpenAI;
using OpenAI.Managers;
using OpenAI.ObjectModels.RequestModels;
using OpenAI.ObjectModels;
namespace ChatGPT_WinUI3
{
public sealed partial class MainWindow : Window
{
private OpenAIService openAiService;
public MainWindow()
{
this.InitializeComponent();
var openAiKey = Environment.GetEnvironmentVariable("OPENAI_API_KEY");
openAiService = new OpenAIService(new OpenAiOptions(){
ApiKey = openAiKey
});
}
}
}
チャット UI を構築する
StackPanel
を使用してメッセージの一覧を表示し、TextBox
を使用してユーザーが新しいメッセージを入力できるようにします。 次のように MainWindow.xaml
を更新します。
<Window
x:Class="ChatGPT_WinUI3.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:ChatGPT_WinUI3"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid>
<StackPanel Orientation="Vertical" HorizontalAlignment="Stretch">
<ListView x:Name="ConversationList" />
<StackPanel Orientation="Horizontal">
<TextBox x:Name="InputTextBox" HorizontalAlignment="Stretch"/>
<Button x:Name="SendButton" Content="Send" Click="SendButton_Click"/>
</StackPanel>
</StackPanel>
</Grid>
</Window>
メッセージの送信、受信、および表示を実装する
メッセージの 送信、受信、および表示を処理するSendButton_Click
イベント ハンドラーを追加します。
public sealed partial class MainWindow : Window
{
// ...
private async void SendButton_Click(object sender, RoutedEventArgs e)
{
string userInput = InputTextBox.Text;
if (!string.IsNullOrEmpty(userInput))
{
AddMessageToConversation($"User: {userInput}");
InputTextBox.Text = string.Empty;
var completionResult = await openAiService.ChatCompletion.CreateCompletion(new ChatCompletionCreateRequest()
{
Messages = new List<ChatMessage>
{
ChatMessage.FromSystem("You are a helpful assistant."),
ChatMessage.FromUser(userInput)
},
Model = Models.Gpt_4_1106_preview,
MaxTokens = 300
});
if (completionResult != null && completionResult.Successful) {
AddMessageToConversation("GPT: " + completionResult.Choices.First().Message.Content);
} else {
AddMessageToConversation("GPT: Sorry, something bad happened: " + completionResult.Error?.Message);
}
}
}
private void AddMessageToConversation(string message)
{
ConversationList.Items.Add(message);
ConversationList.ScrollIntoView(ConversationList.Items[ConversationList.Items.Last()]);
}
}
アプリを実行する
アプリを実行し、チャットを試してみてください! 次のような結果が表示されます。
チャット インターフェイスを改善する
チャット インターフェイスを次のように改善しましょう。
- スクロールを有効にするには、
ScrollViewer
をStackPanel
に追加します。 - ユーザーの入力とより視覚的に異なる方法で GPT 応答を表示する
TextBlock
を追加します。 - アプリが GPT API からの応答を待機しているタイミングを示す
ProgressBar
を追加します。 - ChatGPT の Web インターフェイスと同様に、ウィンドウ内に
StackPanel
を中央に配置します。 - メッセージがウィンドウの端に達したときに、メッセージが次の行に折り返されるようにします。
TextBox
をより大きくして、Enter
キーへの応答性を高めます。
先頭から次の手順を実行します。
ScrollViewer
を追加します
長い会話で垂直スクロールを有効にするには、ListView
をScrollViewer
に折り返します。
<StackPanel Orientation="Vertical" HorizontalAlignment="Stretch">
<ScrollViewer x:Name="ConversationScrollViewer" VerticalScrollBarVisibility="Auto" MaxHeight="500">
<ListView x:Name="ConversationList" />
</ScrollViewer>
<!-- ... -->
</StackPanel>
TextBlock
を使用します
AddMessageToConversation
メソッドを変更して、ユーザーの入力と GPT 応答のスタイルを異なる方法で設定します。
// ...
private void AddMessageToConversation(string message)
{
var messageBlock = new TextBlock();
messageBlock.Text = message;
messageBlock.Margin = new Thickness(5);
if (message.StartsWith("User:"))
{
messageBlock.Foreground = new SolidColorBrush(Colors.LightBlue);
}
else
{
messageBlock.Foreground = new SolidColorBrush(Colors.LightGreen);
}
ConversationList.Items.Add(messageBlock);
ConversationList.ScrollIntoView(ConversationList.Items.Last());
}
ProgressBar
を追加します
アプリが応答を待機しているタイミングを示すには、ProgressBar
をStackPanel
に追加します 。
<StackPanel Orientation="Vertical" HorizontalAlignment="Stretch">
<ScrollViewer x:Name="ConversationScrollViewer" VerticalScrollBarVisibility="Auto" MaxHeight="500">
<ListView x:Name="ConversationList" />
</ScrollViewer>
<ProgressBar x:Name="ResponseProgressBar" Height="5" IsIndeterminate="True" Visibility="Collapsed"/> <!-- new! -->
</StackPanel>
次に、SendButton_Click
イベント ハンドラーを更新して、ProgressBar
応答の待機中を示します。
private async void SendButton_Click(object sender, RoutedEventArgs e)
{
ResponseProgressBar.Visibility = Visibility.Visible; // new!
string userInput = InputTextBox.Text;
if (!string.IsNullOrEmpty(userInput))
{
AddMessageToConversation("User: " + userInput);
InputTextBox.Text = string.Empty;
var completionResult = await openAiService.Completions.CreateCompletion(new CompletionCreateRequest()
{
Prompt = userInput,
Model = Models.TextDavinciV3
});
if (completionResult != null && completionResult.Successful) {
AddMessageToConversation("GPT: " + completionResult.Choices.First().Text);
} else {
AddMessageToConversation("GPT: Sorry, something bad happened: " + completionResult.Error?.Message);
}
}
ResponseProgressBar.Visibility = Visibility.Collapsed; // new!
}
StackPanel
を中央に揃え
メッセージStackPanel
を中央に配置し、メッセージを下方向にTextBox
引き下げるには、MainWindow.xaml
でGrid
設定を調整します。
<Grid VerticalAlignment="Bottom" HorizontalAlignment="Center">
<!-- ... -->
</Grid>
メッセージをラップする
メッセージがウィンドウの端に達したときに次の行に折り返されるようにするには、ItemsControl
を使用するようにMainWindow.xaml
を更新します 。
編集前:
<ScrollViewer x:Name="ConversationScrollViewer" VerticalScrollBarVisibility="Auto" MaxHeight="500">
<ListView x:Name="ConversationList" />
</ScrollViewer>
編集後:
<ScrollViewer x:Name="ConversationScrollViewer" VerticalScrollBarVisibility="Auto" MaxHeight="500">
<ItemsControl x:Name="ConversationList" Width="300">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Text}" TextWrapping="Wrap" Margin="5" Foreground="{Binding Color}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
その後、バインドと色分けを容易にするMessageItem
クラスを紹介します。
// ...
public class MessageItem
{
public string Text { get; set; }
public SolidColorBrush Color { get; set; }
}
// ...
最後に、新しいMessageItem
クラスを使用するようにAddMessageToConversation
メソッドを更新します。
// ...
private void AddMessageToConversation(string message)
{
var messageItem = new MessageItem();
messageItem.Text = message;
messageItem.Color = message.StartsWith("User:") ? new SolidColorBrush(Colors.LightBlue) : new SolidColorBrush(Colors.LightGreen);
ConversationList.Items.Add(messageItem);
// handle scrolling
ConversationScrollViewer.UpdateLayout();
ConversationScrollViewer.ChangeView(null, ConversationScrollViewer.ScrollableHeight, null);
}
// ...
TextBox
を改善する。
TextBox
をより大きくして、Enter
キーへの応答性を高めるために、次のようにMainWindow.xaml
を更新します。
<!-- ... -->
<StackPanel Orientation="Vertical" Width="300">
<TextBox x:Name="InputTextBox" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" KeyDown="InputTextBox_KeyDown" TextWrapping="Wrap" MinHeight="100" MaxWidth="300"/>
<Button x:Name="SendButton" Content="Send" Click="SendButton_Click" HorizontalAlignment="Right"/>
</StackPanel>
<!-- ... -->
次に、Enter
キーを 処理するにはInputTextBox_KeyDown
イベント ハンドラーを追加します。
//...
private void InputTextBox_KeyDown(object sender, KeyRoutedEventArgs e)
{
if (e.Key == Windows.System.VirtualKey.Enter && !string.IsNullOrWhiteSpace(InputTextBox.Text))
{
SendButton_Click(this, new RoutedEventArgs());
}
}
//...
改善されたアプリを実行する
新しく改善されたチャット インターフェイスは、次のようになります。
まとめ
このチュートリアルで実行した内容は次のとおりです。
- コミュニティ SDK をインストールし、API キーを使用して初期化することで、OpenAI の API 機能を WinUI 3/Windows アプリ SDK デスクトップ アプリに追加しました。
- OpenAI のチャット入力候補 API を使用してメッセージへの応答を生成できる、チャットのようなインターフェイスを構築しました。
- 次の方法でチャット インターフェイスを改善しました。
ScrollViewer
を追加するTextBlock
を使用して GPT 応答を表示する- アプリが GPT API からの応答を待機しているタイミングを示す
ProgressBar
を追加する - ウィンドウ内で
StackPanel
を中央に配置し、 - メッセージがウィンドウの端に到達したときに次の行に折り返されるようにし、
TextBox
を大きくし、サイズを変更でき、Enter
キーに応答できるようにします。
完全なコード例:
<?xml version="1.0" encoding="utf-8"?>
<Window
x:Class="ChatGPT_WinUI3.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:ChatGPT_WinUI3"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid VerticalAlignment="Bottom" HorizontalAlignment="Center">
<StackPanel Orientation="Vertical" HorizontalAlignment="Center">
<ScrollViewer x:Name="ConversationScrollViewer" VerticalScrollBarVisibility="Auto" MaxHeight="500">
<ItemsControl x:Name="ConversationList" Width="300">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Text}" TextWrapping="Wrap" Margin="5" Foreground="{Binding Color}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
<ProgressBar x:Name="ResponseProgressBar" Height="5" IsIndeterminate="True" Visibility="Collapsed"/>
<StackPanel Orientation="Vertical" Width="300">
<TextBox x:Name="InputTextBox" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" KeyDown="InputTextBox_KeyDown" TextWrapping="Wrap" MinHeight="100" MaxWidth="300"/>
<Button x:Name="SendButton" Content="Send" Click="SendButton_Click" HorizontalAlignment="Right"/>
</StackPanel>
</StackPanel>
</Grid>
</Window>
using Microsoft.UI;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Input;
using Microsoft.UI.Xaml.Media;
using System;
using System.Collections.Generic;
using System.Linq;
using OpenAI;
using OpenAI.Managers;
using OpenAI.ObjectModels.RequestModels;
using OpenAI.ObjectModels;
namespace ChatGPT_WinUI3
{
public class MessageItem
{
public string Text { get; set; }
public SolidColorBrush Color { get; set; }
}
public sealed partial class MainWindow : Window
{
private OpenAIService openAiService;
public MainWindow()
{
this.InitializeComponent();
var openAiKey = Environment.GetEnvironmentVariable("OPENAI_API_KEY");
openAiService = new OpenAIService(new OpenAiOptions(){
ApiKey = openAiKey
});
}
private async void SendButton_Click(object sender, RoutedEventArgs e)
{
ResponseProgressBar.Visibility = Visibility.Visible;
string userInput = InputTextBox.Text;
if (!string.IsNullOrEmpty(userInput))
{
AddMessageToConversation("User: " + userInput);
InputTextBox.Text = string.Empty;
var completionResult = await openAiService.ChatCompletion.CreateCompletion(new ChatCompletionCreateRequest()
{
Messages = new List<ChatMessage>
{
ChatMessage.FromSystem("You are a helpful assistant."),
ChatMessage.FromUser(userInput)
},
Model = Models.Gpt_4_1106_preview,
MaxTokens = 300
});
Console.WriteLine(completionResult.ToString());
if (completionResult != null && completionResult.Successful)
{
AddMessageToConversation("GPT: " + completionResult.Choices.First().Message.Content);
}
else
{
AddMessageToConversation("GPT: Sorry, something bad happened: " + completionResult.Error?.Message);
}
}
ResponseProgressBar.Visibility = Visibility.Collapsed;
}
private void AddMessageToConversation(string message)
{
var messageItem = new MessageItem();
messageItem.Text = message;
messageItem.Color = message.StartsWith("User:") ? new SolidColorBrush(Colors.LightBlue) : new SolidColorBrush(Colors.LightGreen);
ConversationList.Items.Add(messageItem);
// handle scrolling
ConversationScrollViewer.UpdateLayout();
ConversationScrollViewer.ChangeView(null, ConversationScrollViewer.ScrollableHeight, null);
}
private void InputTextBox_KeyDown(object sender, KeyRoutedEventArgs e)
{
if (e.Key == Windows.System.VirtualKey.Enter && !string.IsNullOrWhiteSpace(InputTextBox.Text))
{
SendButton_Click(this, new RoutedEventArgs());
}
}
}
}
関連項目
Windows developer