C# WPF 自制ChatGPT对话界面
By
jerryxjr1220
at 2023-09-09 • 0人收藏 • 1206人看过
与ChatGPT玩《海龟汤》游戏
后面会放出详细制作过程 以及编译后程序
14 个回复 | 最后更新于 2023-09-21
2023-09-10
#2
在C# WPF中按照MVVM框架结构进行拆分:
架构 | 名称 | 用途 |
Model | Message | 用来存放对话内容 |
ViewModel | ChatGPTAPI | 实现对话的模型 |
View | MainWindow | 主界面 |
Model:
namespace WpfAppOpenAI.Models; public class Message { public string? role { get; set; } public string? content { get; set; } public string? showname { get; set; } }
ViewModel:
using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using System.Net.Http; using System.Text; using System.Threading.Tasks; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using WpfAppOpenAI.Models; namespace WpfAppOpenAI.ViewModels; public partial class ChatGPTAPI : ObservableObject { [ObservableProperty] private HttpClient client; [ObservableProperty] private Dictionary<string, object> data; [ObservableProperty] private ObservableCollection<Message> messages; [ObservableProperty] private string model; [ObservableProperty] private string prompt; [ObservableProperty] private double temperature; [ObservableProperty] private Uri url; public ChatGPTAPI(string? key, string? model, string? prop, double temperature = 0.7) { Client = new HttpClient(); Url = new Uri("https://api.chatanywhere.com.cn/v1/chat/completions"); Client.DefaultRequestHeaders.Add("Authorization", "Bearer " + key); Client.DefaultRequestHeaders.Add("ContentType", "application/json"); Model = model ?? "gpt-3.5-turbo"; Prompt = prop ?? "You are a helpful assistant. Try your best to answer the question."; Temperature = temperature; Messages = new ObservableCollection<Message>(); Messages.Add(new Message { role = "user", content = Prompt, showname = "You: " }); Data = new Dictionary<string, object>(); Data["model"] = Model; Data["messages"] = Messages; Data["temperature"] = Temperature; } [RelayCommand] public async Task<string> ask(string question) { Messages.Add(new Message { role = "user", content = question, showname = "You: " }); Data["messages"] = Messages; var js = JsonConvert.SerializeObject(Data); HttpContent content = new StringContent(js, Encoding.UTF8, "application/json"); var resp = await Client.PostAsync(Url, content); var jobj = JObject.Parse(resp.Content.ReadAsStringAsync().Result); var answer = jobj["choices"].First()["message"]["content"].ToString(); Messages.Add(new Message { role = "assistant", content = answer, showname = "GPT: " }); Data["messages"] = Messages; return answer; } }
View:
Xaml
<Window x:Class="WpfAppOpenAI.Views.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:WpfAppOpenAI" mc:Ignorable="d" d:DataContext="{d:DesignInstance Type=vm:ChatGPTAPI}" xmlns:fa="http://schemas.fontawesome.io/icons/" xmlns:hc="https://handyorg.github.io/handycontrol" xmlns:vm="clr-namespace:WpfAppOpenAI.ViewModels" WindowStyle="None" ResizeMode="NoResize" Title="MainWindow" Height="500" Width="800" FontFamily="JetBrains Mono" FontSize="14"> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="200" /> <ColumnDefinition Width="*" /> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition Height="35" /> <RowDefinition Height="*" /> </Grid.RowDefinitions> <WrapPanel Grid.ColumnSpan="2" HorizontalAlignment="Left" VerticalAlignment="Center" Margin="10,5,0,0" MouseDown="MainWindow_OnMouseDown"> <fa:ImageAwesome Icon="Wechat" Width="20" Foreground="{StaticResource SuccessBrush}" /> <TextBlock Text="OpenAI - ChatGPT" FontSize="20" Margin="10,0,0,0" Foreground="{StaticResource PrimaryBrush}" /> </WrapPanel> <hc:ButtonGroup Grid.Row="0" Grid.Column="1" HorizontalAlignment="Right" FontSize="20"> <Button Content="-" Click="ButtonMinimize_OnClick" Foreground="{StaticResource PrimaryBrush}" /> <!--<Button Content="^" Click="ButtonMaximize_OnClick"/>--> <Button Content="X" Background="{StaticResource DangerBrush}" Foreground="{StaticResource LightInfoBrush}" Click="ButtonClose_OnClick" /> </hc:ButtonGroup> <StackPanel Grid.Row="1" Grid.Column="0" HorizontalAlignment="Center" VerticalAlignment="Top" Margin="20,10,0,0"> <TextBlock Style="{StaticResource TextBlockTitle}" Text="Settings" FontSize="14" Foreground="{StaticResource DangerBrush}" Margin="0,0,0,10" /> <Expander Header="sk Key" FontSize="24" Foreground="{StaticResource PrimaryBrush}" Margin="0,0,0,10"> <hc:TextBox x:Name="tbKey" FontSize="10" Text="sk-你申请的key" /> </Expander> <Expander Header="Model" FontSize="24" Foreground="{StaticResource PrimaryBrush}" Margin="0,0,0,10"> <hc:ButtonGroup> <RadioButton x:Name="rbGPT3" Content="GPT 3" FontSize="10" /> <RadioButton x:Name="rbGPT35" Content="GPT 3.5" IsChecked="True" FontSize="10" Padding="-2,0,-2,0" /> <RadioButton x:Name="rbGPT4" Content="GPT 4" FontSize="10" /> </hc:ButtonGroup> </Expander> <WrapPanel Orientation="Vertical" Margin="0,0,0,10"> <TextBlock Text="{Binding ElementName=sdTemperature, Path=Value, StringFormat=Temperature: {0:0.0}}" FontSize="10" Foreground="{StaticResource PrimaryBrush}" HorizontalAlignment="Left" VerticalAlignment="Center" /> <Slider x:Name="sdTemperature" Minimum="0.1" Maximum="0.9" Value="0.7" Width="100" IsHitTestVisible="True" /> </WrapPanel> <WrapPanel Orientation="Vertical" Margin="0,0,0,10"> <TextBlock Text="Prompt" FontSize="10" Foreground="{StaticResource PrimaryBrush}" HorizontalAlignment="Left" VerticalAlignment="Center" /> <TextBox x:Name="tbPrompt" Width="150" Height="220" FontSize="10" VerticalContentAlignment="Top" MaxLines="12" TextWrapping="Wrap" AcceptsReturn="True" Foreground="{StaticResource SecondaryTextBrush}" Text="You are a helpful assistant. Try your best to answer the questions." /> </WrapPanel> <Button Content="Change and Save" Style="{StaticResource ButtonPrimary}" Click="ButtonSave_OnClick" /> </StackPanel> <StackPanel Grid.Row="1" Grid.Column="1" HorizontalAlignment="Left" VerticalAlignment="Top"> <!--<TextBox x:Name="tbOutput" Width="550" Height="400" ScrollViewer.VerticalScrollBarVisibility="Visible" IsReadOnly="True" VerticalContentAlignment="Top" TextWrapping="Wrap" Foreground="{StaticResource PrimaryBrush}" FontSize="14" />--> <ItemsControl x:Name="icOutput" ItemsSource="{Binding Messages}" Padding="0,0,10,0" Height="400"> <ItemsControl.Template> <ControlTemplate TargetType="ItemsControl"> <ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled"> <ItemsPresenter /> </ScrollViewer> </ControlTemplate> </ItemsControl.Template> <ItemsControl.ItemTemplate> <DataTemplate> <hc:TextBox Text="{Binding content}" IsReadOnly="True" hc:InfoElement.Title="{Binding showname}" hc:InfoElement.TitlePlacement="Left" TextWrapping="Wrap" Style="{StaticResource TextBoxExtend}" Foreground="{StaticResource PrimaryBrush}"/> </DataTemplate> </ItemsControl.ItemTemplate> </ItemsControl> <WrapPanel Orientation="Horizontal" Margin="10,10,0,0"> <TextBox x:Name="tbQuestion" Width="530" Height="45" FontSize="13" VerticalAlignment="Top" MaxLines="5" TextWrapping="Wrap" AcceptsReturn="True" Foreground="{StaticResource SecondaryTextBrush}" /> <Button Style="{StaticResource ButtonPrimary}" Click="ButtonSend_OnClick"> <fa:FontAwesome Icon="Send" /> </Button> </WrapPanel> </StackPanel> </Grid> </Window>
CS
using System; using System.Windows; using System.Windows.Input; using System.Windows.Media; using HandyControl.Controls; using WpfAppOpenAI.ViewModels; using MessageBox = HandyControl.Controls.MessageBox; using Window = System.Windows.Window; namespace WpfAppOpenAI.Views; /// <summary> /// Interaction logic for MainWindow.xaml /// </summary> public partial class MainWindow : Window { private ChatGPTAPI api; public MainWindow() { InitializeComponent(); var key = tbKey.Text; var model = string.Empty; if (rbGPT3.IsChecked == true) model = "gpt-3.5-turbo-0301"; if (rbGPT35.IsChecked == true) model = "gpt-3.5-turbo"; if (rbGPT4.IsChecked == true) model = "gpt-3.5-turbo-0613"; var prompt = tbPrompt.Text; var temperature = Math.Round(sdTemperature.Value * 10) / 10; api = new ChatGPTAPI(key, model, prompt, temperature); DataContext = api; } private void ButtonMinimize_OnClick(object sender, RoutedEventArgs e) { WindowState = WindowState.Minimized; } // private void ButtonMaximize_OnClick(object sender, RoutedEventArgs e) // { // if (this.WindowState == WindowState.Maximized) // this.WindowState = WindowState.Normal; // else // this.WindowState = WindowState.Maximized; // } private void ButtonClose_OnClick(object sender, RoutedEventArgs e) { Application.Current.Shutdown(); } private void MainWindow_OnMouseDown(object sender, MouseButtonEventArgs e) { DragMove(); } private void ScrollToBottom() { // Scroll to the bottom of the ScrollViewer var scrollViewer = FindVisualChild<ScrollViewer>(icOutput); scrollViewer?.ScrollToEnd(); } private static T FindVisualChild<T>(DependencyObject parent) where T : DependencyObject { for (var i = 0; i < VisualTreeHelper.GetChildrenCount(parent); i++) { var child = VisualTreeHelper.GetChild(parent, i); if (child is T result) return result; var descendant = FindVisualChild<T>(child); if (descendant != null) return descendant; } return null; } private async void ButtonSend_OnClick(object sender, RoutedEventArgs e) { try { var question = tbQuestion.Text; var ans = await api.ask(question); //tbOutput.Text += "You: " + question + "\r\n"; //tbOutput.Text += "GPT: " + ans + "\r\n\r\n"; ScrollToBottom(); } catch { //tbOutput.Text += "Get Error! Try other models again!\r\n\r\n"; MessageBox.Show("Got Error! Try again!"); } } private void ButtonSave_OnClick(object sender, RoutedEventArgs e) { var key = tbKey.Text; var model = string.Empty; if (rbGPT3.IsChecked == true) model = "gpt-3.5-turbo-0301"; if (rbGPT35.IsChecked == true) model = "gpt-3.5-turbo"; if (rbGPT4.IsChecked == true) model = "gpt-3.5-turbo-0613"; var prompt = tbPrompt.Text; var temperature = Math.Round(sdTemperature.Value * 10) / 10; api = new ChatGPTAPI(key, model, prompt, temperature); DataContext = api; } }
PS:由于我使用的是免费api,所有只能用GPT 3.5。 GPT 4需要申请收费key。
2023-09-14
#4
根据B站上一位up主的提示词工具,把ChatGPT升级成AutoGPT,会根据用户的目标分解任务并给出解决方案。
只需要把Prompt修改为:
你作为精通问题解决方案的X教授,你的职责是通过了解用户的目标和偏好,然后选择最合适该任务的专家代理来帮助用户达到他们的目标。 初始化: 我是一位${role}的X教授,我知道${context}。我会逐步推理,确定达到${goal}的最佳行动方案。我可以使用${tools}来协助这个过程。 我将通过以下步骤来帮你实现你的目标:${reasoned steps}。 当${completion}时,我的任务结束。 ${first step, question} 解决问题的步骤: 首先,X教授需要收集相关信息,并通过提问明确用户目标。 一旦用户确认,X教授会挑选专家代理,支持用户直到达到目标。 命令: /start - X教授自我介绍并开始第一步 /save - X教授需要重申SMART目标,总结目前进展,并建议下一步 /reason - X教授和专家代理一起逐步推理,然后为用户提供继续前进的建议 /settings - X教授更新目标或代理 /new - 忘记以前的输入 规则: - 每个输出都以一个问题或推荐的下一步结束 - 在第一次输出中列出你的命令,或者当用户询问时提供 - 作为X教授,在挑选新的专家代理前先询问 - 最后,记得用中文和我交流
尤其是对于一个庞大的问题,它会自动拆分成若干小任务目标,然后依次给出解决方案。
建议用该Prompt时,把Temperature尽量调小,这样对话基本就会严格按照我们制定的规则进行。
2023-09-17
#6
继续优化,新增了语音播放功能,并且增加了对话保存功能
视频教程:
2023-09-21
#9
继续优化,针对不同使用场景增加不同Prompt提示词模板。
目前设了3个Prompt:
1、通用场景 General 2、专业问题解决场景 Professional 3、翻译场景 Translate
2023-09-21
#10
//MainWindow.xaml.cs using System; using System.Globalization; using System.IO; using System.Text; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Input; using System.Windows.Media; using Newtonsoft.Json.Linq; using WpfAppOpenAI.Model; using WpfAppOpenAI.ViewModel; using MessageBox = HandyControl.Controls.MessageBox; using ScrollViewer = HandyControl.Controls.ScrollViewer; using Window = System.Windows.Window; namespace WpfAppOpenAI.View; /// <summary> /// Interaction logic for MainWindow.xaml /// </summary> public partial class MainWindow : Window { private ChatGPTAPI api; public MainWindow() { InitializeComponent(); tbPrompt.Text = """ You are a helpful assistant. Try your best to answer the question or provide a possible solution. """; var key = tbKey.Text; var model = string.Empty; if (rbGPT3.IsChecked == true) model = "gpt-3.5-turbo-0301"; if (rbGPT35.IsChecked == true) model = "gpt-3.5-turbo"; if (rbGPT4.IsChecked == true) model = "gpt-3.5-turbo-0613"; var prompt = tbPrompt.Text; var temperature = Math.Round(sdTemperature.Value * 10) / 10; api = new ChatGPTAPI(key, model, prompt, temperature); DataContext = api; } private void ButtonMinimize_OnClick(object sender, RoutedEventArgs e) { WindowState = WindowState.Minimized; } // private void ButtonMaximize_OnClick(object sender, RoutedEventArgs e) // { // if (this.WindowState == WindowState.Maximized) // this.WindowState = WindowState.Normal; // else // this.WindowState = WindowState.Maximized; // } private void ButtonClose_OnClick(object sender, RoutedEventArgs e) { Application.Current.Shutdown(); } private void MainWindow_OnMouseDown(object sender, MouseButtonEventArgs e) { DragMove(); } private async void ButtonSend_OnClick(object sender, RoutedEventArgs e) { try { var question = tbQuestion.Text; var ans = await api.ask(question); //tbOutput.Text += "You: " + question + "\r\n"; //tbOutput.Text += "GPT: " + ans + "\r\n\r\n"; } catch { //tbOutput.Text += "Get Error! Try other models again!\r\n\r\n"; MessageBox.Show("Got Error! Try again!", "Error", MessageBoxButton.OK, MessageBoxImage.Error); } } private void ButtonSave_OnClick(object sender, RoutedEventArgs e) { var key = tbKey.Text; var model = string.Empty; if (rbGPT3.IsChecked == true) model = "gpt-3.5-turbo-0301"; if (rbGPT35.IsChecked == true) model = "gpt-3.5-turbo"; if (rbGPT4.IsChecked == true) model = "gpt-3.5-turbo-0613"; var prompt = tbPrompt.Text; var temperature = Math.Round(sdTemperature.Value * 10) / 10; api = new ChatGPTAPI(key, model, prompt, temperature); DataContext = api; } private void LbHistory_OnSelectionChanged(object sender, SelectionChangedEventArgs e) { var selectedHistory = api.HistoryList[lbHistory.SelectedIndex]; var historyFile = api.historyDictionary[selectedHistory]; var jsonString = File.ReadAllText(historyFile, Encoding.UTF8); var jobj = JArray.Parse(jsonString); api.Messages.Clear(); for (var i = 0; i < jobj.Count; i++) { if (jobj[i].SelectToken("role")!.ToString() == "user") api.Messages.Add(new YouMessage { role = "user", content = jobj[i].SelectToken("content")!.ToString() }); else api.Messages.Add(new GptMessage { role = "assistant", content = jobj[i].SelectToken("content")!.ToString() }); } } private void Rabtn0_OnClick(object sender, RoutedEventArgs e) { if (rabtn0.IsChecked==true) { tbPrompt.Text = """ You are a helpful assistant. Try your best to answer the question or provide a possible solution. """; var key = tbKey.Text; var model = string.Empty; if (rbGPT3.IsChecked == true) model = "gpt-3.5-turbo-0301"; if (rbGPT35.IsChecked == true) model = "gpt-3.5-turbo"; if (rbGPT4.IsChecked == true) model = "gpt-3.5-turbo-0613"; var prompt = tbPrompt.Text; var temperature = Math.Round(sdTemperature.Value * 10) / 10; api = new ChatGPTAPI(key, model, prompt, temperature); DataContext = api; } } private void Rabtn1_OnClick(object sender, RoutedEventArgs e) { if (rabtn1.IsChecked == true) { tbPrompt.Text = """ 你作为问题解决方案的X教授,你的职责是通过了解用户的目标和偏好,然后选择最合适该任务的专家代理来帮助用户达到他们的目标。 初始化: 我是一位${role}的X教授,我知道${context}。我会逐步推理,确定达到${goal}的最佳行动方案。我可以使用${tools}来协助这个过程。 我将通过以下步骤来帮你实现你的目标:${reasoned steps}。 当${completion}时,我的任务结束。 ${first step, question} 解决问题的步骤: 首先,X教授需要收集相关信息,并通过提问明确用户目标。 一旦用户确认,X教授会挑选专家代理,支持用户直到达到目标。 命令: /start - X教授自我介绍并开始第一步 /save - X教授需要重申SMART目标,总结目前进展,并建议下一步 /reason - X教授和专家代理一起逐步推理,然后为用户提供继续前进的建议 /settings - X教授更新目标或代理 /new - 忘记以前的输入 规则: - 每个输出都以一个问题或推荐的下一步结束 - 在第一次输出中列出你的命令,或者当用户询问时提供 - 作为X教授,在挑选新的专家代理前先询问 - 最后,记得用中文和我交流 """; var key = tbKey.Text; var model = string.Empty; if (rbGPT3.IsChecked == true) model = "gpt-3.5-turbo-0301"; if (rbGPT35.IsChecked == true) model = "gpt-3.5-turbo"; if (rbGPT4.IsChecked == true) model = "gpt-3.5-turbo-0613"; var prompt = tbPrompt.Text; var temperature = Math.Round(sdTemperature.Value * 10) / 10; api = new ChatGPTAPI(key, model, prompt, temperature); DataContext = api; } } private void Rabtn2_OnClick(object sender, RoutedEventArgs e) { if (rabtn2.IsChecked==true) { tbPrompt.Text = """ You are a professional translator. Try your best to translate from Chinese to target language or from source language to Chinese. Rules: - I will put the content need to translate into "[" and "]" symbols; - You should put the translated content into "[" and "]" symbols also; """; var key = tbKey.Text; var model = string.Empty; if (rbGPT3.IsChecked == true) model = "gpt-3.5-turbo-0301"; if (rbGPT35.IsChecked == true) model = "gpt-3.5-turbo"; if (rbGPT4.IsChecked == true) model = "gpt-3.5-turbo-0613"; var prompt = tbPrompt.Text; var temperature = Math.Round(sdTemperature.Value * 10) / 10; api = new ChatGPTAPI(key, model, prompt, temperature); DataContext = api; } } } public class ShortValueConvert : IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { return Math.Round(((double)value) * 10) / 10; } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { throw new NotImplementedException(); } }
// MainViewWindow.cs using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.IO; using System.Linq; using System.Net.Http; using System.Text; using System.Threading.Tasks; using System.Windows; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using WpfAppOpenAI.Model; using MessageBox = HandyControl.Controls.MessageBox; namespace WpfAppOpenAI.ViewModel; public partial class ChatGPTAPI : ObservableObject { [ObservableProperty] private HttpClient client; [ObservableProperty] private Dictionary<string, object> data; [ObservableProperty] private ObservableCollection<Message> messages; [ObservableProperty] private string model; [ObservableProperty] private string prompt; [ObservableProperty] private double temperature; [ObservableProperty] private Uri url; [ObservableProperty] private BindingList<string> historyList; public Dictionary<string, string> historyDictionary; public ChatGPTAPI(string? key, string? model, string? prop, double temperature = 0.7) { Client = new HttpClient(); Url = new Uri("https://api.chatanywhere.com.cn/v1/chat/completions"); Client.DefaultRequestHeaders.Add("Authorization", "Bearer " + key); Client.DefaultRequestHeaders.Add("ContentType", "application/json"); Model = model ?? "gpt-3.5-turbo"; Prompt = prop ?? "You are a helpful assistant. Try your best to answer the question."; Temperature = temperature; Messages = new ObservableCollection<Message>(); Messages.Add(new YouMessage { role = "user", content = Prompt }); Data = new Dictionary<string, object>(); Data["model"] = Model; Data["messages"] = Messages; Data["temperature"] = Temperature; historyDictionary = new Dictionary<string, string>(); HistoryList = new BindingList<string>(); string curdir = Directory.GetCurrentDirectory(); var files = Directory.GetFiles(curdir + @"\log", "*.log", SearchOption.TopDirectoryOnly); foreach (var file in files) { var filename = Path.GetFileName(file); historyDictionary[filename] = file; HistoryList.Add(filename); } } [RelayCommand] public async Task<string> ask(string question) { Messages.Add(new YouMessage { role = "user", content = question }); Data["messages"] = Messages; var js = JsonConvert.SerializeObject(Data); HttpContent content = new StringContent(js, Encoding.UTF8, "application/json"); var resp = await Client.PostAsync(Url, content); var jobj = JObject.Parse(resp.Content.ReadAsStringAsync().Result); var answer = jobj["choices"].First()["message"]["content"].ToString(); Messages.Add(new GptMessage { role = "assistant", content = answer }); Data["messages"] = Messages; return answer; } [RelayCommand] public void SaveHistory() { string curdir = Directory.GetCurrentDirectory(); string filename = DateTime.Now.ToString("yyMMddHHmmss") + ".log"; string logpath = curdir + @"\log\" + filename; var jsonString = JsonConvert.SerializeObject(Messages); File.WriteAllText(logpath, jsonString, Encoding.UTF8); historyDictionary[filename] = logpath; HistoryList.Add(filename); MessageBox.Show("Dialogue Saved!", "Info", MessageBoxButton.OK, MessageBoxImage.Information); } }
登录后方可回帖
前几天在Github上找到了一个免费的ChatGPT的API接口,此API服务器是在国内的,所有访问速度比之前翻墙访问要快不少,最主要是它可以免费使用GPT3.5(GPT4是收费的)。
https://github.com/chatanywhere/gpt_api_free
免费申请到一个sk-key以后就可以访问了
根据API文档,可以知道几个必要参数:
api网址:
http的header
json序列化的数据
其中model是选择的模型,temperature是话题新鲜度,messages中存放对话
有了这些信息就可以先在python中进行测试了
正确收到返回的响应后就可以在C#中尝试封装了。