Hinzufügen von OpenAI-Chatabschlüssen zu Ihrer WinUI 3-Desktop-App
In dieser Hilfe & Anleitung erfahren Sie, wie Sie die OpenAI-API in Ihre WinUI 3 / Windows App SDK Desktop-Anwendung integrieren können. Wir erstellen eine Chat-ähnliche Schnittstelle, über die Sie Antworten auf Nachrichten mithilfe der Chat-Abschluss-API von OpenAI generieren können:
Voraussetzungen
- Richten Sie Ihren Entwicklungscomputer ein (siehe Erste Schritte mit WinUI).
- Vertrautheit mit den Kernkonzepten beim Erstellen einer Hallo Welt-App mit C# und WinUI 3 / Windows App SDK – werden wir in diesem Artikel auf dieser Anleitung aufbauen.
- Ein OpenAI-API-Schlüssel aus Ihrem OpenAI-Entwicklerdashboard.
- Ein in Ihrem Projekt installiertes OpenAI SDK. Eine Liste der Community-Bibliotheken finden Sie in der OpenAI-Dokumentation. In dieser Hilfe & Anleitung verwenden wir betalgo/OpenAI.
Erstellen eines Projekts
- Öffnen Sie Visual Studio und erstellen Sie ein neues Projekt über
File
>New
>Project
. - Suchen Sie nach
WinUI
und wählen Sie dieBlank App, Packaged (WinUI 3 in Desktop)
C#-Projektvorlage. - Geben Sie den Projektnamen, den Projektmappennamen und das Verzeichnis an. In diesem Beispiel gehört unser
ChatGPT_WinUI3
Projekt zu einerChatGPT_WinUI3
Projektmappe, die inC:\Projects\
erstellt wird.
Nachdem Sie Ihr Projekt erstellt haben, sollten Sie die folgende Standard-Dateistruktur in Ihrem Projektmappen-Explorer sehen:
Festlegen Ihrer Umgebungsvariablen
Um das OpenAI SDK zu verwenden, müssen Sie eine Umgebungsvariable mit Ihrem API-Schlüssel festlegen. In diesem Beispiel verwenden wir die Umgebungsvariable OPENAI_API_KEY
. Sobald Sie Ihren API-Schlüssel aus dem OpenAI-Entwickler-Dashboard haben, können Sie die Umgebungsvariable wie folgt von der Kommandozeile aus setzen:
setx OPENAI_API_KEY <your-api-key>
Beachten Sie, dass diese Methode gut für die Entwicklung geeignet ist. Für Produktions-Apps sollten Sie jedoch eine sicherere Methode verwenden (z.B. könnten Sie Ihren API-Schlüssel in einem sicheren Schlüsseltresor speichern, auf den ein Remote-Dienst im Namen Ihrer App zugreifen kann). Siehe Bewährte Praktiken für OpenAI-Schlüsselsicherheit.
Installieren des OpenAI SDK
Wählen Sie im Menü von Visual Studio View
die Option Terminal
aus. Es sollte eine Instanz von Developer Powershell
angezeigt werden. Führen Sie den folgenden Befehl im Stammverzeichnis Ihres Projekts aus, um SDK zu installieren:
dotnet add package Betalgo.OpenAI
Initialisieren des SDK
Initialisieren Sie in MainWindow.xaml.cs
das SDK mit Ihrem API-Schlüssel:
//...
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
});
}
}
}
Erstellen der Chat-UI
Wir verwenden eine StackPanel
, um eine Liste von Nachrichten anzuzeigen, und eine TextBox
, damit Benutzer neue Nachrichten eingeben können. Aktualisieren Sie MainWindow.xaml
wie folgt:
<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>
Senden, Empfangen und Anzeigen von Nachrichten implementieren
Fügen Sie einen SendButton_Click
Ereignishandler hinzu, um das Senden, Empfangen und Anzeigen von Nachrichten zu steuern:
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()]);
}
}
Ausführen der App
Führen Sie die App aus, und versuchen Sie, zu chatten! Die Ausgabe sollte in etwa wie folgt aussehen:
Verbessern der Chatschnittstelle
Lassen Sie uns die folgenden Verbesserungen an der Chat-Schnittstelle vornehmen:
- Fügen Sie eine
ScrollViewer
zu derStackPanel
hinzu, um das Scrollen zu aktivieren. - Fügen Sie eine
TextBlock
hinzu, um die GPT-Antwort auf eine Weise anzuzeigen, die sich optisch von der Eingabe des Benutzers unterscheidet. - Fügen Sie eine
ProgressBar
hinzu, um anzuzeigen, dass die App auf eine Antwort von der GPT-API wartet. - Zentrieren Sie
StackPanel
im Fenster, ähnlich wie die Web-Schnittstelle von ChatGPT. - Stellen Sie sicher, dass Nachrichten in die nächste Zeile umgebrochen werden, wenn sie den Rand des Fensters erreichen.
- Machen Sie die
TextBox
größer und ansprechbar auf den SchlüsselEnter
.
Beginnend von oben:
Fügen Sie ScrollViewer
hinzu.
Verpacken Sie die ListView
in einer ScrollViewer
, um das vertikale Scrollen bei langen Unterhaltungen zu ermöglichen:
<StackPanel Orientation="Vertical" HorizontalAlignment="Stretch">
<ScrollViewer x:Name="ConversationScrollViewer" VerticalScrollBarVisibility="Auto" MaxHeight="500">
<ListView x:Name="ConversationList" />
</ScrollViewer>
<!-- ... -->
</StackPanel>
Verwenden Sie TextBlock
Ändern Sie die AddMessageToConversation
Methode so, dass die Eingabe des Benutzers und die GPT-Antwort anders formatiert werden:
// ...
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());
}
Fügen Sie ProgressBar
hinzu.
Fügen Sie eine ProgressBar
zur StackPanel
hinzu, um anzuzeigen, dass die App auf eine Antwort wartet:
<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>
Aktualisieren Sie dann den SendButton_Click
Ereignishandler so, dass er ProgressBar
beim Warten auf eine Antwort anzeigt:
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!
}
Zentrieren Sie die StackPanel
Um die StackPanel
zu zentrieren und die Nachrichten nach unten in Richtung der TextBox
zu ziehen, passen Sie die Einstellungen Grid
in MainWindow.xaml
an:
<Grid VerticalAlignment="Bottom" HorizontalAlignment="Center">
<!-- ... -->
</Grid>
Umbrechen von Nachrichten
Um sicher zu stellen, dass Nachrichten in die nächste Zeile umgebrochen werden, wenn sie den Rand des Fensters erreichen, aktualisieren Sie MainWindow.xaml
, um ein ItemsControl
zu verwenden.
Ersetzen Sie dies:
<ScrollViewer x:Name="ConversationScrollViewer" VerticalScrollBarVisibility="Auto" MaxHeight="500">
<ListView x:Name="ConversationList" />
</ScrollViewer>
Damit:
<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>
Anschließend führen wir eine MessageItem
Klasse ein, um die Bindung und Farbgebung zu vereinfachen:
// ...
public class MessageItem
{
public string Text { get; set; }
public SolidColorBrush Color { get; set; }
}
// ...
Aktualisieren Sie zum Abschluss die AddMessageToConversation
-Methode, um die neue MessageItem
-Klasse zu verwenden:
// ...
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);
}
// ...
Verbessern sie die TextBox
Um die TextBox
größer zu machen und reaktionsschneller auf den Enter
Schlüssel zu gestalten, aktualisieren Sie MainWindow.xaml
wie folgt:
<!-- ... -->
<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>
<!-- ... -->
Fügen Sie dann den InputTextBox_KeyDown
Ereignishandler zum Behandeln des Enter
Schlüssels hinzu:
//...
private void InputTextBox_KeyDown(object sender, KeyRoutedEventArgs e)
{
if (e.Key == Windows.System.VirtualKey.Enter && !string.IsNullOrWhiteSpace(InputTextBox.Text))
{
SendButton_Click(this, new RoutedEventArgs());
}
}
//...
Ausführen der verbesserten App
Ihre neue und verbesserte Chat-Schnittstelle sollte etwa wie folgt aussehen:
Zusammenfassung
Das haben Sie in dieser Hilfe & Anleitung gelernt:
- Sie haben die API-Funktionen von OpenAI zu Ihrer WinUI 3 / Windows App SDK Desktop-Anwendung hinzugefügt, indem Sie ein Community SDK installiert und mit Ihrem API-Schlüssel initialisiert haben.
- Sie haben eine Chat-ähnliche Schnittstelle erstellt, über die Sie Antworten auf Nachrichten mithilfe der Chat-Abschluss-API von OpenAI generieren können.
- Sie haben die Chat-Schnittstelle verbessert, indem Sie:
- eine
ScrollViewer
hinzugefügt haben, - eine
TextBlock
genutzt haben, um die GPT-Antwort anzuzeigen, - eine
ProgressBar
hinzu gefügt haben, um anzuzeigen, dass die App auf eine Antwort von der GPT-API wartet, - die
StackPanel
im Fenster zentriert, - sichergestellt, dass Nachrichten in die nächste Zeile umgebrochen werden, wenn sie den Rand des Fensters erreichen und
- Sodass die
TextBox
Größe größer, verkleinert und auf denEnter
Schlüssel reagiert wird.
- eine
Vollständige Codedateien
<?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());
}
}
}
}
Verwandte Themen
Windows developer