结合SK和ChatGLM3B+whisper+Avalonia实现语音切换城市

结合SK和ChatGLM3B+whisper+Avalonia实现语音切换城市

先创建一个Avalonia的MVVM项目模板,项目名称GisApp

项目创建完成以后添加以下nuget依赖

<PackageReference Include="Mapsui.Avalonia" Version="4.1.1" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Http" Version="8.0.0" />
<PackageReference Include="Microsoft.SemanticKernel" Version="1.0.0-beta8" />
<PackageReference Include="NAudio" Version="2.2.1" />
<PackageReference Include="Whisper.net" Version="1.5.0" />
<PackageReference Include="Whisper.net.Runtime" Version="1.5.0" />
  • Mapsui.Avalonia是Avalonia的一个Gis地图组件
  • Microsoft.Extensions.DependencyInjection用于构建一个DI容器
  • Microsoft.Extensions.Http用于注册一个HttpClient工厂
  • Microsoft.SemanticKernel则是SK用于构建AI插件
  • NAudio是一个用于录制语音的工具包
  • Whisper.net是一个.NET的Whisper封装Whisper用的是OpenAI开源的语音识别模型
  • Whisper.net.Runtime属于Whisper

修改App.cs

打开App.cs,修改成以下代码

public partial class App : Application
{public override void Initialize(){AvaloniaXamlLoader.Load(this);}public override void OnFrameworkInitializationCompleted(){if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop){var services = new ServiceCollection();services.AddSingleton<MainWindow>((services) => new MainWindow(services.GetRequiredService<IKernel>(), services.GetRequiredService<WhisperProcessor>()){DataContext = new MainWindowViewModel(),});services.AddHttpClient();var openAIHttpClientHandler = new OpenAIHttpClientHandler();var httpClient = new HttpClient(openAIHttpClientHandler);services.AddTransient<IKernel>((serviceProvider) =>{return new KernelBuilder().WithOpenAIChatCompletionService("gpt-3.5-turbo-16k", "fastgpt-zE0ub2ZxvPMwtd6XYgDX8jyn5ubiC",httpClient: httpClient).Build();});services.AddSingleton(() =>{var ggmlType = GgmlType.Base;// 定义使用模型var modelFileName = "ggml-base.bin";return WhisperFactory.FromPath(modelFileName).CreateBuilder().WithLanguage("auto") // auto则是自动识别语言.Build();});var serviceProvider = services.BuildServiceProvider();desktop.MainWindow = serviceProvider.GetRequiredService<MainWindow>();}base.OnFrameworkInitializationCompleted();}
}

OpenAIHttpClientHandler.cs,这个文件是用于修改SK的访问地址,默认的SK只支持OpenAI官方的地址并且不能进行修改!

public class OpenAIHttpClientHandler : HttpClientHandler
{protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken){if (request.RequestUri.LocalPath == "/v1/chat/completions"){var uriBuilder = new UriBuilder("http://您的ChatGLM3B地址/api/v1/chat/completions");request.RequestUri = uriBuilder.Uri;}return base.SendAsync(request, cancellationToken);}
}

修改ViewModels/MainWindowViewModel.cs

public class MainWindowViewModel : ViewModelBase
{private string subtitle = string.Empty;public string Subtitle{get => subtitle;set => this.RaiseAndSetIfChanged(ref subtitle, value);}private Bitmap butBackground;public Bitmap ButBackground{get => butBackground;set => this.RaiseAndSetIfChanged(ref butBackground, value);}
}
  • ButBackground是显示麦克风图标的写到模型是为了切换图标
  • Subtitle用于显示识别的文字

添加SK插件

创建文件/plugins/MapPlugin/AcquireLatitudeLongitude/config.json:这个是插件的相关配置信息

{"schema": 1,"type": "completion","description": "获取坐标","completion": {"max_tokens": 1000,"temperature": 0.3,"top_p": 0.0,"presence_penalty": 0.0,"frequency_penalty": 0.0},"input": {"parameters": [{"name": "input","description": "获取坐标","defaultValue": ""}]}
}

创建文件/plugins/MapPlugin/AcquireLatitudeLongitude/skprompt.txt:下面是插件的prompt,通过以下内容可以提取用户城市然后得到城市的经纬度

请返回{{$input}}的经纬度然后返回以下格式,不要回复只需要下面这个格式:
{"latitude":"","longitude":""
}

修改Views/MainWindow.axaml代码,将[素材](# 素材)添加到Assets中,

<Window xmlns="https://github.com/avaloniaui"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:vm="using:GisApp.ViewModels"xmlns:d="http://schemas.microsoft.com/expression/blend/2008"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"x:Class="GisApp.Views.MainWindow"x:DataType="vm:MainWindowViewModel"Icon="/Assets/avalonia-logo.ico"Width="800"Height="800"Title="GisApp"><Design.DataContext><vm:MainWindowViewModel /></Design.DataContext><Grid><Grid Name="MapStackPanel"></Grid><StackPanel HorizontalAlignment="Right" VerticalAlignment="Bottom" Background="Transparent" Margin="25"><TextBlock Foreground="Black" Text="{Binding Subtitle}" Width="80" TextWrapping="WrapWithOverflow" Padding="8"></TextBlock><Button Width="60" Click="Button_OnClick" Background="Transparent" VerticalAlignment="Center" HorizontalAlignment="Center"><Image Name="ButBackground" Source="{Binding ButBackground}" Height="40" Width="40"></Image></Button></StackPanel></Grid>
</Window>

修改Views/MainWindow.axaml.cs代码

public partial class MainWindow : Window
{private bool openVoice = false;private WaveInEvent waveIn;private readonly IKernel _kernel;private readonly WhisperProcessor _processor;private readonly Channel<string> _channel = Channel.CreateUnbounded<string>();private MapControl mapControl;public MainWindow(IKernel kernel, WhisperProcessor processor){_kernel = kernel;_processor = processor;InitializeComponent();mapControl = new MapControl();// 默认定位到深圳mapControl.Map = new Map(){CRS = "EPSG:3857",Home = n =>{var centerOfLondonOntario = new MPoint(114.06667, 22.61667);var sphericalMercatorCoordinate = SphericalMercator.FromLonLat(centerOfLondonOntario.X, centerOfLondonOntario.Y).ToMPoint();n.ZoomToLevel(15);n.CenterOnAndZoomTo(sphericalMercatorCoordinate, n.Resolutions[15]);}};mapControl.Map?.Layers.Add(Mapsui.Tiling.OpenStreetMap.CreateTileLayer());MapStackPanel.Children.Add(mapControl);DataContextChanged += (sender, args) =>{using var voice = AssetLoader.Open(new Uri("avares://GisApp/Assets/voice.png"));ViewModel.ButBackground = new Avalonia.Media.Imaging.Bitmap(voice);};Task.Factory.StartNew(ReadMessage);}private MainWindowViewModel ViewModel => (MainWindowViewModel)DataContext;private void Button_OnClick(object? sender, RoutedEventArgs e){if (openVoice){using var voice = AssetLoader.Open(new Uri("avares://GisApp/Assets/voice.png"));ViewModel.ButBackground = new Avalonia.Media.Imaging.Bitmap(voice);waveIn.StopRecording();}else{using var voice = AssetLoader.Open(new Uri("avares://GisApp/Assets/open-voice.png"));ViewModel.ButBackground = new Avalonia.Media.Imaging.Bitmap(voice);// 获取当前麦克风设备waveIn = new WaveInEvent();waveIn.DeviceNumber = 0; // 选择麦克风设备,0通常是默认设备WaveFileWriter writer = new WaveFileWriter("recorded.wav", waveIn.WaveFormat);// 设置数据接收事件waveIn.DataAvailable += (sender, a) =>{Console.WriteLine($"接收到音频数据: {a.BytesRecorded} 字节");writer.Write(a.Buffer, 0, a.BytesRecorded);if (writer.Position > waveIn.WaveFormat.AverageBytesPerSecond * 30){waveIn.StopRecording();}};// 录音结束事件waveIn.RecordingStopped += async (sender, e) =>{writer?.Dispose();writer = null;waveIn.Dispose();await using var fileStream = File.OpenRead("recorded.wav");using var wavStream = new MemoryStream();await using var reader = new WaveFileReader(fileStream);var resampler = new WdlResamplingSampleProvider(reader.ToSampleProvider(), 16000);WaveFileWriter.WriteWavFileToStream(wavStream, resampler.ToWaveProvider16());wavStream.Seek(0, SeekOrigin.Begin);await Dispatcher.UIThread.InvokeAsync(() => { ViewModel.Subtitle = string.Empty; });string text = string.Empty;await foreach (var result in _processor.ProcessAsync(wavStream)){await Dispatcher.UIThread.InvokeAsync(() => { ViewModel.Subtitle += text += result.Text; });}_channel.Writer.TryWrite(text);};Console.WriteLine("开始录音...");waveIn.StartRecording();}openVoice = !openVoice;}private async Task ReadMessage(){try{var pluginsDirectory = Path.Combine(Directory.GetCurrentDirectory(), "plugins");var chatPlugin = _kernel.ImportSemanticFunctionsFromDirectory(pluginsDirectory, "MapPlugin");// 循环读取管道中的数据while (await _channel.Reader.WaitToReadAsync()){// 读取管道中的数据while (_channel.Reader.TryRead(out var message)){// 使用AcquireLatitudeLongitude插件,解析用户输入的地点,然后得到地点的经纬度var value = await _kernel.RunAsync(new ContextVariables{["input"] = message}, chatPlugin["AcquireLatitudeLongitude"]);// 解析字符串成模型var acquireLatitudeLongitude =JsonSerializer.Deserialize<AcquireLatitudeLongitude>(value.ToString());// 使用MapPlugin插件,定位到用户输入的地点var centerOfLondonOntario = new MPoint(acquireLatitudeLongitude.longitude, acquireLatitudeLongitude.latitude);var sphericalMercatorCoordinate = SphericalMercator.FromLonLat(centerOfLondonOntario.X, centerOfLondonOntario.Y).ToMPoint();// 默认使用15级缩放mapControl.Map.Navigator.ZoomToLevel(15);mapControl.Map.Navigator.CenterOnAndZoomTo(sphericalMercatorCoordinate, mapControl.Map.Navigator.Resolutions[15]);}}}catch (Exception e){Console.WriteLine(e);}}public class AcquireLatitudeLongitude{public double latitude { get; set; }public double longitude { get; set; }}
}

流程讲解:

  1. 用户点击了录制按钮触发了Button_OnClick事件,然后在Button_OnClick事件中会打开用户的麦克风,打开麦克风进行录制,在录制结束事件中使用录制完成产生的wav文件,然后拿到Whisper进行识别,识别完成以后会将识别结果写入到_channel
  2. ReadMessage则是一直监听_channel的数据,当有数据写入,这里则会读取到,然后就将数据使用下面的sk执行AcquireLatitudeLongitude函数。
 var value = await _kernel.RunAsync(new ContextVariables{["input"] = message}, chatPlugin["AcquireLatitudeLongitude"]);
  1. 在解析value得到用户的城市经纬度
  2. 通过mapControl.Map.Navigator修改到指定经纬度。

完整的操作流程就完成了,当然实际业务会比这个更复杂。

素材

分享总结

讨论总结:
在本次会议中,讨论了如何结合SK、ChatGLM3B、Whisper和Avalonia来实现语音切换城市的功能。具体讨论了创建Avalonia的MVVM项目模板,添加了相关的NuGet依赖,修改了App.cs、ViewModels/MainWindowViewModel.cs以及添加了SK插件的相关配置和文件。

行动项目:

  1. 创建Avalonia的MVVM项目模板,项目名称为GisApp
  2. 添加所需的NuGet依赖,包括Mapsui.Avalonia, Microsoft.Extensions.DependencyInjection, Microsoft.Extensions.Http, Microsoft.SemanticKernel, NAudio, Whisper.netWhisper.net.Runtime
  3. 修改App.csOpenAIHttpClientHandler.csViewModels/MainWindowViewModel.cs以及相关的视图文件。
  4. 添加SK插件,包括创建相关的配置信息和prompt文件。
  5. 实现录制语音、语音识别和切换城市的功能流程。

技术交流群:737776595

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/196308.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

asp.net_sql2008公司人事管理系统

登录模块 流程图&#xff1a; ◆ 职工输入用户名、密码并选择登录身份&#xff0c;根据选择的身份做不同的操作 ◎ 若选择的是“职工”&#xff0c;系统将查询数据库中的用户表Users的记录&#xff0c;用户名密码核对正确后加载职工主界面&#xff1b; ◎ 若选择的是“管理员…

手把手教你搭建个人地图服务器(高德离线部署解决方案):获取地图瓦片数据、高德JS API、私有化部署和调用。。。

一、概述 众所周知&#xff0c;目前常见的地图&#xff08;高德、百度、腾讯等&#xff09;只提供在线API服务&#xff0c;对于一些内网应用而言&#xff0c;如果需要使用地图展示&#xff0c;则由于不能访问互联网而无法使用类似的第三方地图服务。 本文&#xff0c;通过将高…

【刷题日志】牛客 HJ73 计算日期到天数转换

计算日期到天数转换 阅读题目解题方案 及 解题思路方法一 . 手撕日期类方法二 . 分别直接算出平年和闰年每个月的时间 并对应下标存入数组中&#xff0c;判断该年份为平年还是闰年&#xff0c;再 for循环依次求和优化&#xff1a;也不用分别算出平年和闰年的每个月的时间&#…

win10、11系统安装班智达藏文输入法并正常使用(完美解决)

1. 结果图 2. 先闲聊两句 班智达输入法对于藏语初学者可谓是太好用了&#xff08;哈哈&#xff09;特别是联想提示的功能。不禁为开发团队点个赞。 表扬完了该批评批评了。班智达输入法的安装难度真是一言难尽。也许是开发者没有继续维护的缘故吧。想当年&#xff0c;哪个藏语…

DCDC电源的选择

https://blog.csdn.net/xiahailong90/article/details/79086490 先说结论&#xff1a; 高开关频率的交换式电源转换器有利也有弊&#xff0c;本文提到的好处包括体积更小、瞬时响应更快以及电压overshoot 和undershoot 值都更小&#xff0c;主要缺点则是效率降低和热量增加。 …

朋友圈7大黄金发圈时间

众所周知&#xff0c;朋友圈运营是私域运营必不可少的重要环节。 因为做好朋友圈运营&#xff0c;能够打造形成高质量、高价值的私域流量&#xff0c;加快实现用户成交。 那么如何形成一个吸粉又吸金的人设&#xff0c;做出高质量的朋友圈发圈内容呢&#xff1f; 那么如何确保能…

光纤和光模块的那点事儿

你们好&#xff0c;我的网工朋友。 应该不少朋友在工作中会遇到光纤传输布线的活吧&#xff0c;不得不说&#xff0c;会遇上的问题还挺多&#xff0c;比如说…… 光纤收发器怎么接上不亮&#xff1f; 光纤收发器和交换机插光模块能不能搭配使用&#xff1f; 带光口的球机可…

第1章 理解知识图谱:知识图谱现状、知识图谱应用场景(二)

&#x1f497;&#x1f497;&#x1f497;欢迎来到我的博客&#xff0c;你将找到有关如何使用技术解决问题的文章&#xff0c;也会找到某个技术的学习路线。无论你是何种职业&#xff0c;我都希望我的博客对你有所帮助。最后不要忘记订阅我的博客以获取最新文章&#xff0c;也欢…

执行栈和执行上下文

前端面试大全JavaScript执行栈和执行上下文 &#x1f31f;经典真题 &#x1f31f;执行上下文 &#x1f31f;栈数据结构 &#x1f31f;执行上下文生命周期 &#x1f31f;真题解答 &#x1f31f;总结 &#x1f31f;经典真题 谈谈你对 JavaScript 执行上下文栈理解 &#…

如何将unity项目托管到github(快速便捷)

如何将unity项目托管到github&#xff08;快速便捷&#xff09; 文章目录 如何将unity项目托管到github&#xff08;快速便捷&#xff09;前置准备Gitgithubgit-lfs 具体操作1.配置.gitignore文件2.配置.gitattributes3.使用git 前置准备 Git github git-lfs 这些内容省略&…

你的AI生成物侵权了吗?

你的AI生成物侵权了吗&#xff1f; 本文目录&#xff1a; 一、前置背景 1.1、什么是版权 1.2、什么是作品 1.3、什么是创作 1.4、什么是肖像权 1.5、什么是名誉 二、AI生成的作品是否具备版权&#xff1f;如果具备&#xff0c;版权应该属于谁&#xff1f; 三、AI 学习时…

ruby安装(vscode、rubymine)

https://rubyinstaller.org/downloads/ 下载exe安装即可 会弹出 输入3 安装成功 vscode插件市场安装ruby插件 新建一个目录&#xff0c;打开terminal bundle init //进行初始化&#xff08;如果执行不了&#xff0c;应该是环境变量没生效&#xff0c;重启vscode&#…

菜鸟学习日记(python)——数据类型转换

在python中&#xff0c;数据类型的转换有两种方式&#xff1a;隐式类型转换和显示类型转换。 隐式类型转换一般在进行计算时&#xff0c;自动完成转换&#xff0c;显示类型转换一般要用到类型函数来完成转换&#xff0c;它的格式为&#xff1a;数据类型&#xff08;要转换的数…

使用Docker和Selenium构建自动化测试环境

随着软件开发的日益复杂和迭代速度的加快&#xff0c;自动化测试被越来越广泛地应用于软件开发流程中。它能够提高测试效率、减少测试成本&#xff0c;并保证软件质量的稳定性。在构建自动化测试环境方面&#xff0c;Docker 和 Selenium 是两个非常有用的工具。下面将介绍如何使…

springboot流浪动物救助管理系统源码丨文档+调试+答疑

&#x1f345; 简介&#xff1a;500精品计算机源码学习 &#x1f345; 欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; 文末获取源码 目录 一、以下学习内容欢迎领取&#xff1a; 二、文档资料截图&#xff1a; 三想了解更多&#xff0c;请收藏、评论、留言&#xff1a;…

项目实战-编写ssm整合配置文件

1、父工程pom.xml <properties><maven.compiler.source>17</maven.compiler.source><maven.compiler.target>17</maven.compiler.target><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><spring.version>…

【每日一题】1038. 从二叉搜索树到更大和树-2023.12.4

题目&#xff1a; 1038. 从二叉搜索树到更大和树 给定一个二叉搜索树 root (BST)&#xff0c;请将它的每个节点的值替换成树中大于或者等于该节点值的所有节点值之和。 提醒一下&#xff0c; 二叉搜索树 满足下列约束条件&#xff1a; 节点的左子树仅包含键 小于 节点键的节点。…

[SaaS] 天猫商品海报生成 灵感艺术家

AIGC在天猫商品海报生成上的探索没有灵感GPT&#xff0c;画不出来SD。https://mp.weixin.qq.com/s/_CkkqoWmHDZ0YqAhmAhL1A天猫在海报图生成上的探索。 技术路线&#xff1a; 初看不觉得什么&#xff0c;细看还真有点不一样&#xff0c;通常我们用canny controlnet是为了控制商…

人工智能发展史

人工智能&#xff08;AI&#xff09;的发展史是一段跨越数十年的旅程&#xff0c;涵盖了从早期理论探索到现代技术革新的广泛内容。人工智能的发展历程展示了从最初的概念探索到现代技术突破的演变。尽管经历了多次起伏&#xff0c;但AI领域持续进步&#xff0c;不断拓展其应用…

【性能测试】资深老鸟总结,常见并发问题汇总(二)

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 1、极限值并发的问…