Avalonia是什么?
Avalonia是一个跨平台的UI框架,专为.NET开发打造,提供灵活的样式系统,支持Windows、macOS、Linux、iOS、Android及WebAssembly等多种平台。它已成熟并适合生产环境,被Schneider Electric、Unity、JetBrains和GitHub等公司采用。
许多人认为Avalonia是WPF的继任者,它为XAML开发人员提供了一种熟悉且现代的跨平台应用开发体验。尽管与WPF相似,但Avalonia并非完全复制,而包含了许多改进。
SemanticKernel是什么?
Semantic Kernel是一个SDK,它可以将大型语言模型(如OpenAI、Azure OpenAI和Hugging Face)与常规编程语言(如C#、Python和Java)整合。特殊之处在于,Semantic Kernel通过允许定义和链式调用插件,能够自动调度并组合这些AI模型。其功能是,用户可以向LLM提出个性化目标,由Semantic Kernel的规划器生成实现目标的计划,然后由系统自动执行这份计划。
硅基流动介绍
硅基流动致力于打造大模型时代的AI基础设施,通过算法、系统和硬件的协同创新,跨数量级降低大模型应用成本和开发门槛,加速AGI普惠人类。
SiliconCloud是集合主流开源大模型的一站式云服务平台,为开发者提供更快、更便宜、更全面、体验更丝滑的模型API。
目前,SiliconCloud已上架包括DeepSeek-Coder-V2、Stable Diffusion 3 Medium、Qwen2、GLM-4-9B-Chat、DeepSeek V2、SDXL、InstantID在内的多种开源大语言模型、图片生成模型,支持用户自由切换符合不同应用场景的模型。同时,SiliconCloud提供开箱即用的大模型推理加速服务,为生成式AI应用带来更高效的用户体验。
我们知道在国内使用OpenAI不太方便同时成本也比较高。现在已经有很多开源的大模型了,但是对于个人开发者而言,部署它们的一大难点是硬件资源。没有显卡,也能部署一些参数少一些的开源大模型,但是推理速度肯定是很慢的,这里选择硅基流动的原因是第一,之前注册送了42元的额度,该额度不会过期,可以一直使用,第二,试了一下推理速度真的很快,第三(也是最重要的一点)(白嫖),硅基流动宣布:SiliconCloud平台的Qwen2(7B)、GLM4(9B)、Yi1.5(9B)等顶尖开源大模型免费使用。
构建什么样的工具
最近在学习Avalonia,动手做一个小工具实现自己的需求是一个很好的开始。同时对SemanticKernel也比较感兴趣,所以选择从最基本的制作一个基于大模型的聊天应用开始。个人对大模型的一大需求就是翻译,在查看英文网站时,遇到不太理解的地方,总喜欢问大模型,将某某某翻译为中文。因此选择构建解决自己这个需求的Avalonia练手小工具。该工具的效果如下所示:
聊天
英译中
中译英
开始实践
在SemanticKernel中使用SiliconCloud提供的API服务
要解决的第一个问题就是如何在SemanticKernel中使用SiliconCloud提供的服务。
SemanticKernel中并没有告诉我们如何连接其他的大模型,但由于SiliconCloud提供的接口是与OpenAI兼容的,因此可以通过在发送请求时,改变发送请求的地址来实现。
添加OpenAIHttpClientHandler类:
public class OpenAIHttpClientHandler : HttpClientHandler { protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { UriBuilder uriBuilder; switch (request.RequestUri?.LocalPath) { case "/v1/chat/completions": uriBuilder = new UriBuilder(request.RequestUri) { // 这里是你要修改的 URL Scheme = "https", Host = "api.siliconflow.cn", Path = "v1/chat/completions", }; request.RequestUri = uriBuilder.Uri; break; } HttpResponseMessage response = await base.SendAsync(request, cancellationToken); return response; } }
kernel通过这种方式构建:
var handler = new OpenAIHttpClientHandler(); var builder = Kernel.CreateBuilder() .AddOpenAIChatCompletion( modelId: "Qwen/Qwen1.5-7B-Chat", apiKey: "你的apikey", httpClient: new HttpClient(handler)); _kernel = builder.Build();
_kernel为全局私有变量:
private Kernel _kernel;
构建页面
axaml如下所示:
<Window xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:vm="using:AvaloniaChat.ViewModels" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:views="clr-namespace:AvaloniaChat.Views" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" x:Class="AvaloniaChat.Views.MainWindow" Icon="/Assets/avalonia-logo.ico" Title="AvaloniaChat"> <Design.DataContext> <!-- This only sets the DataContext for the previewer in an IDE, to set the actual DataContext for runtime, set the DataContext property in code (look at App.axaml.cs) --> <vm:MainViewModel /> </Design.DataContext> <StackPanel> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="*" /> <ColumnDefinition Width="*" /> </Grid.ColumnDefinitions> <Grid Grid.Column="0"> <StackPanel> <StackPanel Orientation="Horizontal"> <Button Content="问AI" Margin="10" Command="{Binding AskCommand}"></Button> <!--<Button Content="翻译为:"></Button>--> <Label Content="翻译为:" HorizontalAlignment="Center" VerticalAlignment="Center"></Label> <ComboBox ItemsSource="{Binding Languages}" SelectedItem="{Binding SelectedLanguage}" HorizontalAlignment="Center" VerticalAlignment="Center"></ComboBox> <Button Content="翻译" Margin="10" Command="{Binding TranslateCommand}"></Button> </StackPanel> <TextBox Height="300" Margin="10" Text="{Binding AskText}" TextWrapping="Wrap" AcceptsReturn="True"></TextBox> </StackPanel> </Grid> <Grid Grid.Column="1"> <StackPanel> <Button Content="AI回答" Margin="10"></Button> <TextBox Height="300" Margin="10" Text="{Binding ResponseText}" TextWrapping="Wrap"></TextBox> </StackPanel> </Grid> </Grid> </StackPanel> </Window>
界面效果如下所示:
构建ViewModel
ViewModel如下所示:
public partial class MainViewModel : ViewModelBase { private Kernel _kernel; [ObservableProperty] private string askText; [ObservableProperty] private string responseText; [ObservableProperty] private string selectedLanguage; public string[] Languages { get; set; } public MainViewModel() { var handler = new OpenAIHttpClientHandler(); var builder = Kernel.CreateBuilder() .AddOpenAIChatCompletion( modelId: "Qwen/Qwen1.5-7B-Chat", apiKey: "你的apikey", httpClient: new HttpClient(handler)); _kernel = builder.Build(); AskText = " "; ResponseText = " "; SelectedLanguage = " "; Languages = new string[] { "中文","英文"}; } [RelayCommand] private async Task Ask() { if(ResponseText != "") { ResponseText = ""; } await foreach (var update in _kernel.InvokePromptStreamingAsync(AskText)) { ResponseText += update.ToString(); } } [RelayCommand] private async Task Translate() { string skPrompt = """ {{$input}} 将上面的输入翻译成{{$language}},无需任何其他内容 """; if (ResponseText != "") { ResponseText = ""; } await foreach (var update in _kernel.InvokePromptStreamingAsync(skPrompt, new() { ["input"] = AskText,["language"] = SelectedLanguage })) { ResponseText += update.ToString(); } } }
使用流式返回
[RelayCommand] private async Task Ask() { if(ResponseText != "") { ResponseText = ""; } await foreach (var update in _kernel.InvokePromptStreamingAsync(AskText)) { ResponseText += update.ToString(); } }
实现效果如下:
写提示
当我们需要翻译功能的时候,只需要翻译文本,其他的内容都不要,简易的模板如下:
string skPrompt = """ {{$input}} 将上面的输入翻译成{{$language}},无需任何其他内容 """;
{{$input}}
与{{$language}}
是模板里的参数,使用时会被替换,如下所示:
await foreach (var update in _kernel.InvokePromptStreamingAsync(skPrompt, new() { ["input"] = AskText,["language"] = SelectedLanguage })) { ResponseText += update.ToString(); }
通过以上这几个步骤,我们就使用Avalonia制作完成一个简易的小工具了。