互联网不景气了那就玩玩嵌入式吧,用纯.NET开发并制作一个智能桌面机器人(四):结合BotSharp智能体框架开发语音交互

news/2025/3/31 1:44:53/文章来源:https://www.cnblogs.com/GreenShade/p/18771608

前言

前段时间太忙了博客一直都没来得及更新,但是不代表我已经停止开发了,刚好最近把语音部分给调整了一下,所以就来分享一下具体的内容了。我想说一下,更新晚还是有好处的,社区已经有很多的小伙伴自己实现了一些语音对话功能的案例,比如小智也有.NET客户端了,还有就是一些树莓派对接实时语音api实现对话的功能,这些都是挺好的案例,很适合有兴趣的小伙伴来学习使用。

我做的还是比较传统的对话,通过Azure的语音服务进行关键字的训练,然后通过文本转语音和语音转文本再结合BotSharp智能体框架可以做到不集成小智服务实现对话的能力,并且拥有会话管理,提示词管理还有一些工具调用的能力。我已经迫不及待的想分享给大家了。
img

问题解答

为啥选择树莓派不是单片机

有朋友觉得树莓派价格贵,还说单片机就能完成开发之类的,我给大家说明下我们目前做的是针对Linux系统下的.NET的一些实践,如果觉得树莓派贵可以买一些国产的板子平替,有能力的可以自己搞库映射。至于单片机开发的事情,如果真的感兴趣单片机开发,那我们可以后期出一些文章讲这个做一些有趣的玩具。

表情播放目前的方案选择是啥

上一篇文章有讲什么是lottie动画,文章有说效果不好,但是我检查代码之后发现是代码写的有bug才导致播放很差,所以就不用转成文本的mp4了,lottie动画文件很小,适合放到板子上。

下图上面是在电脑上使用Lottie动画播放的效果。
img

名词解释

我是真的想让大家亲自上手试试,所以文章会讲一些基础的内容,大家不要嫌啰嗦,能看到这篇文章的小伙伴说明大家至少应该有个板子并且点亮了屏幕。

BotSharp

BotSharp 是一个开源的多智能体应用开发框架,从简单的聊天机器人,再到多智能体协作,以及复杂的任务如【Text To Sql】框架都提供了开箱即用的使用方法,可以快速的将大模型的能力接入到现有的业务系统中,并且内置知识库和会话管理功能等。

在我们的智能桌面机器人项目中,BotSharp提供会话管理和多智能体调用以及工具调用的功能。

  • BotSharp官方GitHub
  • BotSharp文档

Azure语音服务

Azure语音服务是微软提供的云端AI服务,支持语音转文本、文本转语音、语音翻译等功能。在我们的智能桌面机器人项目中,Azure语音服务用于将用户的语音指令转换为文本(用于理解用户意图)以及将机器人的文本回复转换为自然语音输出。此外,它还支持关键词识别功能,可用于唤醒我们的机器人。

  • Azure语音服务官方文档
  • .NET中使用Azure语音服务指南
  • 关键词识别文档

img

ALSA

ALSA(Advanced Linux Sound Architecture)是Linux系统中的音频架构,提供了对声卡硬件的驱动功能和API。在树莓派等Linux设备上,ALSA是处理声音输入输出的基础系统。我们的机器人项目需要ALSA来管理麦克风输入和扬声器输出。

  • ALSA项目主页
  • ALSA Wiki

aplay

aplay是ALSA提供的命令行工具,用于播放音频文件。在Linux系统中,可以使用aplay命令来测试和播放各种格式的音频文件,验证扬声器是否正常工作。在我们的项目中,可以通过程序调用aplay来播放合成的语音文件。

  • aplay命令手册

arecord

arecord是ALSA的录音工具,用于从麦克风或其他音频输入设备捕获音频。在树莓派上,我们可以使用arecord来录制用户的语音,然后将录制的音频文件发送给语音识别服务进行处理。

  • arecord命令手册
  • 使用arecord录音指南

amixer

amixer是ALSA提供的另一个命令行工具,用于控制音频混合器设置,如音量调节、输入输出设备选择等。在我们的机器人项目中,可以使用amixer来调整麦克风和扬声器的音量,确保语音交互体验良好。

  • amixer命令手册

准备工作

首先准备麦克风和喇叭

可以通过购买现成的声卡,接到树莓派上的USB口或者OTG口,并且根据上面的aplay和arecord工具测试麦克风和喇叭是否正常。

 cat /proc/asound/cards #查看声卡设备cat /proc/asound/devices #查看设备arecord -l # 列出设备aplay -l # 列出设备arecord -D "plughw:0,0" -f S16_LE -r 16000 -d 5 -t wav test.wav #录制一段测试声音aplay -D "plughw:0,0" test.wav # 播放测试声音alsamixer #调节系统的音量

部分指令测试图如下:

img

获取Azure语音服务的API KEY

需要在微软的Azure服务中创建语音资源,获取Api Key。

获取微软的Azure OpenAI或者其他的大模型的API KEY

目前微软新出的Azure AI Foundry服务,可以在里面创建并使用多种模型,适合有订阅的用户使用,价格还算合适大家可以试试,流行的一些模型都可以在上面使用。
img
如果没有大家也可以申请国内的一些Key,例如阿里的百炼平台和DeepSeek的一些API KEY。

BotSharp简单上手

BotSharp

BotSharp 是一个开源的多智能体应用开发框架,从简单的聊天机器人,再到多智能体协作,以及复杂的任务如【Text To Sql】框架都提供了开箱即用的使用方法,可以快速的将大模型的能力接入到现有的业务系统中,并且内置知识库和会话管理功能等。
img

大语言模型的函数调用(这个是理解BotSharp框架的核心知识点)

函数调用允许您将模型连接到外部工具和系统。这对于许多事情都很有用,例如为 AI 助手提供功能,或在应用程序和模型之间构建深度集成。

openai官方文档函数调用介绍文档

运行BotSharp源码

首先准备一台安装了Visual Studio并安装了Aspire组件的电脑,电脑再安装nodejs环境用来运行前端UI项目。
克隆前后端代码到同一目录。

BotSharp C#源码克隆指令如下:

git clone https://github.com/SciSharp/BotSharp.git

BotSharp前端UI代码克隆指令如下:

git clone https://github.com/SciSharp/BotSharp-UI.git

命令行进入到BotSharpUI的代码目录执行npm install 安装依赖

确保前端连接的后台服务地址为本地服务

img

然后Visual Studio打开BotSharp解决方案,配置大模型的api key的然后启动图上的项目

img

详细的使用文档请查看社区的文档链接:BotSharp社区文档

ElectronBot.Standalone 项目整体代码讲解

主流程

关键词的唤醒和BotSharp嵌入的流程图如下:

图上有些功能并没有实现,但是辅助大家理解是足够的了。
img

图上流程图对应的代码在下图所示的HostedService类中
img

using BotSharp.Abstraction.Agents.Enums;
using BotSharp.Abstraction.Conversations;
using BotSharp.Abstraction.Conversations.Models;
using BotSharp.Abstraction.Routing;
using ElectronBot.Standalone.Core.Contracts;
using ElectronBot.Standalone.Core.Enums;
using ElectronBot.Standalone.Core.Models;
using ElectronBot.Standalone.Core.Repositories;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using NetCoreAudio;namespace ElectronBot.Standalone.Core.Services;/// <summary>
/// A hosted service providing the primary conversation loop for Semantic Kernel with OpenAI ChatGPT.
/// </summary>
public class HostedService : IHostedService, IDisposable
{private readonly ILogger<HostedService> _logger;private readonly IWakeWordListener _wakeWordListener;private readonly IServiceProvider _serviceProvider;private BotSpeechSetting _options;private Task? _executeTask;private readonly CancellationTokenSource _cancelToken = new();// Notification sound supportprivate readonly string _notificationSoundFilePath;private readonly Player _player;private readonly IServiceScopeFactory _serviceScopeFactory;private readonly IBotPlayer _botPlayer;/// <summary>/// Constructor/// </summary>public HostedService(IWakeWordListener wakeWordListener,ILogger<HostedService> logger,IServiceProvider serviceProvider,BotSpeechSetting options,IServiceScopeFactory serviceScopeFactory,IBotPlayer botPlayer){_logger = logger;_wakeWordListener = wakeWordListener;_notificationSoundFilePath = Path.Combine(AppContext.BaseDirectory, "Asserts", "bing.mp3");_player = new Player();_serviceProvider = serviceProvider;_options = options;_serviceScopeFactory = serviceScopeFactory;_botPlayer = botPlayer;}/// <summary>/// Start the service./// </summary>public Task StartAsync(CancellationToken cancellationToken){ _botPlayer.ShowDateToSubScreenAsync();_executeTask = ExecuteAsync(_cancelToken.Token);return Task.CompletedTask;}/// <summary>/// Primary service logic loop./// </summary>public async Task ExecuteAsync(CancellationToken cancellationToken){while (!cancellationToken.IsCancellationRequested){try{// Play a notification to let the user know we have started listening for the wake phrase.await _player.Play(_notificationSoundFilePath);var botSpeech = _serviceProvider.GetRequiredService<IBotSpeecher>();// Wait for wake word or phraseif (!await _wakeWordListener.WaitForWakeWordAsync(cancellationToken)){continue;}await _player.Play(_notificationSoundFilePath);var helloString = _options.AnswerText;// Say hello on startupawait botSpeech.SpeakAsync(helloString ?? "Hello!", cancellationToken);// Start listeningwhile (!cancellationToken.IsCancellationRequested){// Listen to the uservar userSpoke = await botSpeech.ListenAsync(cancellationToken);await _botPlayer.StopLottiePlaybackAsync();_logger.LogInformation($"User spoke: {userSpoke}");// Get a reply from the AI and add it to the chat history.var reply = string.Empty;using (var scope = _serviceScopeFactory.CreateScope()){var serviceProvider = scope.ServiceProvider;var repo = serviceProvider.GetRequiredService<IBraincaseRepository>();var setting = await repo.GetSettingAsync();var inputMsg = new RoleDialogModel(AgentRole.User, userSpoke){MessageId = Guid.NewGuid().ToString(),CreatedAt = DateTime.UtcNow};var conversationService = serviceProvider.GetRequiredService<IConversationService>();var routing = serviceProvider.GetRequiredService<IRoutingService>();routing.Context.SetMessageId(setting.CurrentConversationId, inputMsg.MessageId);conversationService.SetConversationId(setting.CurrentConversationId, new());try{// 启动动画但不阻塞当前执行流程var animationTask = _botPlayer.PlayLottieByNameIdAsync("think", -1);// 可以选择添加异常处理animationTask?.ContinueWith(t =>{if (t.IsFaulted){_logger.LogError($"Animation playback failed: {t.Exception}");}}, TaskContinuationOptions.OnlyOnFaulted);}catch (Exception ex){_logger.LogError($"Failed to start animation: {ex.Message}");await _botPlayer.StopLottiePlaybackAsync();// 根据需要处理异常}await Task.Run(async () =>{await conversationService.SendMessage(VerdureAgentId.VerdureChatId, inputMsg,replyMessage: null,msg =>{reply = msg.Content;return Task.CompletedTask;});});await _botPlayer.StopLottiePlaybackAsync();// Speak the AI's replyawait botSpeech.SpeakAsync(reply, cancellationToken);// If the user said "Goodbye" - stop listening and wait for the wake work again.if (userSpoke.StartsWith("再见") || userSpoke.StartsWith("goodbye", StringComparison.InvariantCultureIgnoreCase)){break;}}}}catch (Exception aiex){await _botPlayer.StopLottiePlaybackAsync();_logger.LogError($"OpenAI returned an error.{aiex.Message}");}}}/// <summary>/// Stop a running service./// </summary>public Task StopAsync(CancellationToken cancellationToken){_cancelToken.Cancel();return Task.CompletedTask;}/// <inheritdoc/>public virtual void Dispose(){_cancelToken.Dispose();_wakeWordListener.Dispose();}
}

语音服务

微软的Azure认知服务提供了开箱即用的C#接入SDK,我们直接集成代码并且配置我们的麦克风设备和语音服务的apikey就行了。主要是要配置麦克风设备,因为在树莓派上的录音设备如果不是默认的,需要我们指定具体的设备,不然听不到声音,我配置的设备信息如下可以作为参考。

img

ElectronBot.Standalone 项目如何集成BotSharp

对话机器人项目地址如下:
https://github.com/maker-community/ElectronBot.Standalone

项目是使用Nuget包的形式集成BotSharp,宿主是WebAPI项目,当然也可以是桌面项目或者控制台项目。

img

项目的启动代码如下:

using BotSharp.Abstraction.Messaging.JsonConverters;
using BotSharp.Abstraction.Repositories;
using BotSharp.Abstraction.Users;
using BotSharp.Core;
using BotSharp.Logger;
using ElectronBot.Standalone.Core.Contracts;
using ElectronBot.Standalone.Core.Handlers;
using ElectronBot.Standalone.Core.Models;
using ElectronBot.Standalone.Core.Repositories;
using ElectronBot.Standalone.Core.Services;
using ElectronBot.Standalone.DataStorage;
using ElectronBot.Standalone.DataStorage.Repository;
using Verdure.Braincase.Copilot.Plugin.Services.BotSharp;var builder = WebApplication.CreateBuilder(args);// Add services to the container.builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var dbSettings = new BotSharpDatabaseSettings();
builder.Configuration.Bind("Database", dbSettings);
builder.Services.AddSingleton(dbSettings);var brainSettings = new BraincaseDatabaseSettings();
builder.Configuration.Bind("Database", brainSettings);brainSettings.BraincaseLiteDB = "braincase.db";builder.Services.AddSingleton(brainSettings);var botSpeechSettings = new BotSpeechSetting();
builder.Configuration.Bind("BotSpeechSetting", botSpeechSettings);
builder.Services.AddSingleton(botSpeechSettings);builder.Services.AddScoped<BraincaseLiteDBContext>();
builder.Services.AddSingleton<IBotPlayer, DefaultBotPlayer>();
builder.Services.AddSingleton<IBotSpeecher, AzBotSpeecher>();
builder.Services.AddSingleton<IWakeWordListener, AzCognitiveServicesWakeWordListener>();
builder.Services.AddScoped<IBotCopilot, DefaultBotCopilot>();
builder.Services.AddScoped<IBraincaseRepository, BraincaseRepository>();
builder.Services.AddHostedService<HostedService>();
builder.Services.AddBotSharpCore(builder.Configuration, options =>{options.JsonSerializerOptions.Converters.Add(new RichContentJsonConverter());options.JsonSerializerOptions.Converters.Add(new TemplateMessageJsonConverter());}).AddBotSharpLogger(builder.Configuration);builder.Services.AddHttpContextAccessor();
builder.Services.AddScoped<IUserIdentity, BotUserIdentity>();
var app = builder.Build();// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{app.UseSwagger();app.UseSwaggerUI();
}app.UseAuthorization();app.MapControllers();
// Use BotSharp
//app.UseBotSharp();// Add startup code
app.Lifetime.ApplicationStarted.Register(async () =>
{// Your startup code hereConsole.WriteLine("Application has started.");// Retrieve IBotCopilot from DI containerusing (var scope = app.Services.CreateScope()){var botCopilot = scope.ServiceProvider.GetRequiredService<IBotCopilot>();await botCopilot.InitCopilotAsync();}
});app.Run();

ElectronBot.Standalone 代码测试

我们配置完Azure语音服务的Key和大模型的key并设置到对应的智能体上,默认使用的是Azure AI Foundry里的gpt-4o-mini的模型,如果需要修改,请根据下图的配置进行修改。

我训练的唤醒关键词为 小娜,可以用你好小娜进行关键词唤醒。

img

启动项目便会将配置更新到LiteDB存储里,大家也可以直接修改数据库里的数据。

代码做了兼容,意味着在windows系统下也可以进行测试。

测试效果如下:
img

代码到这里就算是讲解完了,实际的效果大家可以亲自测试下。

总结感悟

写这篇文章距离上一篇隔了有一两个月了,不禁感慨AI行业发展的是真的快,之前的MCP协议还不是很火,随着智能体框架的兴起以及一些大厂的跟进,MCP也火了起来,.NET社区针对MCP的支持也迅速完善了。

说起DeepSeek,他们最近也更新了V3版本的模型,虽然是小版本号升级,但是能力提升却很大了,之前不能胜任的一些操作现在也可以很好的支持了。我使用了BotSharp框架里比较复杂的功能测试DeepSeek V3的新模型发现效果出奇的好,国产大模型真是越来越好了。

OpenAI 也发布了新的生图功能,针对按指令生图修图完成的都很好了,说句实话,感觉这样下去,文章都不用写了,大家直接跟着AI学习就好了。

总而言之希望大家都能进步,这篇文章也能给大家带来一些启发。

参考推荐文档项目如下:

  • 创客社区地址
  • BotSharp文档
  • 桌面桌面机器人仓库地址
  • .NET IoT库源码地址
  • .NET IoT官方地址
  • .NET IoT社区人员张高兴推荐
  • 什么是lottie动画?
  • 参考的语音对话项目conversational-speaker
  • 小智C#客户端
  • C# MCP SDK
  • 树莓派学习之旅(2)— 使用USB声卡进行录音
  • BotSharp官方GitHub
  • EdgeVoice AI 聊天机器人

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

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

相关文章

【AI News | 20250327】每日AI进展

AI Repos 1、playwright-mcp 使用Playwright提供浏览器自动化功能的MCP服务,核心是让LLM通过结构化的可访问性快照与网页交互,不需要依赖截图或视觉模型。可以用来自动填写网页表单、自动收集网页信息、自动进行网页测试等。支持两种模式:快照模式(默认):使用可访问性快照…

markdown常用命令行格式

Markdown 主要命令(语法)如下:标题 使用 # 号表示标题,# 的个数决定标题的级别:一级标题 二级标题 三级标题 四级标题 五级标题 六级标题段落 & 换行 直接输入文字形成段落,使用两个以上空格或 进行换行:这是一个段落。 这是同一段的下一行。 使用 <br> 也可…

微调可以获得什么

1.改变模型的行为: 使模型的响应更稳定; 使模型聚焦于某一领域; 发展期潜力,在某一方面更加出色,比如对话 2.获取新的知识: 学习预训练阶段没学过的知识; 纠正过时的错误和信息;

2022CCPC Online Contest G - Count Permutation

利用大写字母较少的性质,记录两个大写字母中间的串是否相等来进行转移。 设 \(f[i][j]\) 表示考虑用到第 \(i\) 个大写字母的时候,\(s\) 匹配到第 \(j\) 个字符时最长匹配数。当前大写字母 \(t[pos[i]] = s[j]\) 时,显然有 \(f[i][j] = f[i][j - 1] + 1\)如果 \(s[pos[i - 1…

第2章 C#2

第2章 C#2 2.1 泛型 2.1.1 示例:泛型诞生前的集合 在泛型诞生之前(.NET1),开发者常用如下方式创建集合:数组普通对象集合 如 ArrayList​、Hashtable​专用类型集合 如 StringCollection​// 数组 static string[] GenerateNames() {string[] names = new string[4];names…

ESP32S3串口UART0,UART1UART2,软件模拟串口,USB虚拟串口的使用 - 基于ArduinoIDE

硬件串口的使用 硬件资源我使用的具体的模组型号为 ESP32-S3-WROOM-1(U), 根据官方手册其有3个串口。UART0:通常用于下载和输出调试信息串口,信号管脚默认与 GPIO43(TX) ~ GPIO44(RX) 复用,可以通过 GPIO 交换矩阵连接到任意 GPIO. UART1:信号管脚默认与 GPIO17(TX) ~ GPIO…

从0搭建nacos单点、集群

主机IP 主机名10.0.0.91 elk9110.0.0.92 elk9210.0.0.93 elk93nacos单机部署使用内置数据库 1.下载解压nacos [root@elk91 ~]# wget https://github.com/alibaba/nacos/releases/download/2.5.1/nacos-server-2.5.1.tar.gz[root@elk91 ~]# tar xf nacos-server-2.5.1.tar.gz -C…

ESP32在ArduinoIDE中的配置

🟡 注意 使用 Arduino IDE 开发 ESP32 时除了要看 Arduino 官方的资料一定还要看乐鑫的支持包的资料。详见↗️ 安装ArduinoIDE 到 Arduino 官网 下载最新版的 Arduino IDE 并安装。 🟡 压缩包格式的下载选项意义不大,Arduino IDE 2 无法制作为便携版,参考:绿色(Portabl…

文献阅读《Convolutional Neural Networks on Graphs with Fast Localized Spectral Filtering》

参考博客: 论文解读二代GCN《Convolutional Neural Networks on Graphs with Fast Localized Spectral Filtering》 - 别关注我了,私信我吧 - 博客园 (cnblogs.com) 摘要 为将CNN推广到高维图结构数据中,基于spectral graph theory(谱图理论),设计了一种通用的fast local…

1013 Div3 F题目加注释

https://codeforces.com/contest/2091/problem/F这题主题思路就是递推,从下往上递推,然后用差分和前缀合得到下一行可能性,详细看代码注释点击查看代码 #include <bits/stdc++.h> using namespace std; using ll = long long; using pii = pair<int, int>; const…

20244319 实验二《Python程序设计》实验报告

20244319 2024-2025-2 《Python程序设计》实验二报告 课程:《Python程序设计》 班级: 2443 姓名: 梁悦 学号:20244319 实验教师:王志强 实验日期:2025年3月26日 必修/选修: 公选课 一、实验内容 1.设计并完成一个完整的计算机应用程序,完成加、减、乘、除、log等运算,…