ETJava Beta | Java    注册   登录
  • 搜索:
  • 动手学Avalonia:基于SemanticKernel与硅基流动构建AI聊天与翻译工具

    发表于      阅读(1)     博客类别:Crawler     转自:https://www.cnblogs.com/mingupupu/p/18281546
    如有侵权 请联系我们删除  (页面底部联系我们)  

    Avalonia是什么?

    Avalonia是一个跨平台的UI框架,专为.NET开发打造,提供灵活的样式系统,支持Windows、macOS、Linux、iOS、Android及WebAssembly等多种平台。它已成熟并适合生产环境,被Schneider Electric、Unity、JetBrains和GitHub等公司采用。

    许多人认为Avalonia是WPF的继任者,它为XAML开发人员提供了一种熟悉且现代的跨平台应用开发体验。尽管与WPF相似,但Avalonia并非完全复制,而包含了许多改进。

    image-20240703120741584

    SemanticKernel是什么?

    Semantic Kernel是一个SDK,它可以将大型语言模型(如OpenAI、Azure OpenAI和Hugging Face)与常规编程语言(如C#、Python和Java)整合。特殊之处在于,Semantic Kernel通过允许定义和链式调用插件,能够自动调度并组合这些AI模型。其功能是,用户可以向LLM提出个性化目标,由Semantic Kernel的规划器生成实现目标的计划,然后由系统自动执行这份计划。

    image-20240703121053734

    硅基流动介绍

    硅基流动致力于打造大模型时代的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>
    

    界面效果如下所示:

    image-20240703134726518

    构建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制作完成一个简易的小工具了。