WinUI(WASDK)使用BotSharp框架开发多智能体桌面机器人管理助手(生图开关灯不在话下)

news/2024/11/28 0:06:57/文章来源:https://www.cnblogs.com/GreenShade/p/18570348

前言

大语言模型(Large Language Models, LLMs)近年来在各行各业中展现出了巨大的潜力和影响力。从自然语言处理到自动化客服,从内容生成到智能助手,LLMs正在改变我们与技术互动的方式。随着技术的不断进步,LLMs的应用场景也在不断扩展,成为未来发展的重要趋势。这篇文章将介绍如何使用WinUI(WASDK)和BotSharp开发一个多智能体桌面机器人管理助手,展示LLMs在实际应用中的强大功能和广阔前景。

技术介绍

.NET

.NET 是免费的、开源的、跨平台的框架,用于构建新式应用和强大的云服务。

.NET

WinUI(WASDK)

Windows 应用 SDK 是一组新的开发人员组件和工具,它们代表着 Windows 应用开发平台的下一步发展。 Windows 应用 SDK 提供一组统一的 API 和工具,可供从 Windows 11 到 Windows 10 版本 1809 上的任何桌面应用以一致的方式使用。

Windows 应用 SDK 不会用 C++ 替换 Windows SDK 或现有桌面 Windows 应用类型,例如 .NET(包括 Windows 窗体和 WPF)和桌面 Win32。 相反,Windows 应用 SDK 使用一组通用 API 来补充这些现有工具和应用类型,开发人员可以在这些平台上依赖这些 API 来执行操作。 有关更多详细信息,请参阅 Windows 应用 SDK 的优势。

wasdk

BotSharp

BotSharp 是一个开源应用程序框架,可加快将 LLM 集成到您当前的业务系统中的速度。本项目涉及自然语言理解和音频处理技术,旨在推动智能机器人助手在信息系统中的开发和应用。开箱即用的机器学习算法使普通程序员能够更快、更轻松地开发人工智能应用程序。

BotSharp 是一个高度兼容且高度可扩展的平台构建器。它严格按照组件原则,将平台构建器中需要的每个部分解耦。因此,您可以选择不同的 UI/UX,或者选择不同的 NLP 标记器,或者选择更高级的算法来执行 NER 任务。它们都是基于未加密的接口进行调制的。

BotSharp

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

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

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

助手功能介绍

助手名为电子脑壳本身是负责开源硬件ElectronBot桌面机器人和瀚文键盘的操作配置。

新版本重构方向是深度集成多智能体交互的能力,目前新版本重点优化功能如下:

  • 增强对话能力,添加大语言模型对话能力。
  • 增强交互,添加生图和自然语言理解进行硬件控制,例如生图之后直接设置到桌面机器人屏幕上或者键盘屏幕上。
  • 增强语音对话能力。
  • 以前硬编码的逻辑,现在都可以通过大语言模型的函数调用进行语义化理解,更灵活。

electronbot

目前软件还在开发中,但是BotSharp和大语言模型交互的功能已经开发差不多了,所以编写这篇博客记录一下。

博客演示的代码是在电子脑壳源码的dev分支。

目前文字大模型使用的是阿里的通义千问2.5 72b(qwen2.5-72b-instruct)社区开源版本,图片大模型使用的是通义万相(wanx-v1)。

可以通过聊天进行天气查询,开关等,以及学单词,生图片等等其他功能,这些功能可以和上图的一些机器人进行互动。

演示效果如下:
yanshi

代码实现过程

1. 实现BotSharp的LiteDB存储

做这个实现的原因是我想替换掉框架本身默认的文件存储,因为我是开发桌面程序,所以mongodb这类的数据库也不在考虑范围,LiteDB也是文档数据库,使用上也比较简单,就作为数据存储的选项了。而且原本的软件的数据也可以都迁移到LiteDB上,算是统一了一些。

LiteDB

源码我fork到我的名下了修改代码在litedb分支

2. 针对OpenAI插件进行改造

做这个操作的原因是为了兼容国内的大语言模型,有些时候OpenAI访问不了,可以通过国内的一些模型进行替换,例如智普清言,通义千问,以及讯飞的一些模型。

通过代码兼容自定义Endpoint,就可以随意切换兼容的模型了。

代码段如下防止图挂了:

    public static OpenAIClient GetClient(string provider, string model, IServiceProvider services){var settingsService = services.GetRequiredService<ILlmProviderService>();var settings = settingsService.GetSetting(provider, model);var options = string.IsNullOrEmpty(settings.Endpoint)? null: new OpenAIClientOptions { Endpoint = new Uri(settings.Endpoint) };return new OpenAIClient(new ApiKeyCredential(settings.ApiKey), options);}

OpenAI

3. 基于核心模块编写UI代码

BotSharp本身的demo是基于web服务编写的,有一套webui和一套封装好的api,但是我是基于桌面程序编写的,所以我就借鉴了社区一些开源的软件的代码,以及一些设计理念,整合了一个简单的聊天UI,针对发送消息,聊天列表,以及生成产物的保存等。

最左边的是机器人功能区域,中间为聊天区域,右侧为灵犀空间,生成的图片,单词以及天气内容都会保存一下,便于后期的查找。

img

4. 功能模块的智能体代码

代码目录结构如下:

agent

以生图函数为例 下面是传给大模型的生图函数定义

{"name": "custom_generate_image","description": "如果用户想生成图片可以调用此方法进行图片生成。","parameters": {"type": "object","properties": {"image_name": {"type": "string","description": "根据用户描述给图片起个名称。"},"image_description": {"type": "string","description": "用户进行的图片描述。"}},"required": [ "image_description" ]}
}

关联的生图函数实现类,可以被大语言模型调用。

public class CustomGenerateImageFn : IFunctionCallback
{public string Name => "custom_generate_image"; //和json配置的函数名字匹配private readonly IServiceProvider _service;private readonly IBotToolService _botToolService;private readonly JsonSerializerOptions _options;private readonly ILingxiSpaceService _lingxiSpaceService;private readonly IConversationService _conversationService;public CustomGenerateImageFn(IServiceProvider service,IBotToolService botToolService,ILingxiSpaceService lingxiSpaceService,IConversationService conversationService){_service = service;_options = new JsonSerializerOptions{PropertyNameCaseInsensitive = true,PropertyNamingPolicy = JsonNamingPolicy.CamelCase,WriteIndented = true,AllowTrailingCommas = true,Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping};_botToolService = botToolService;_lingxiSpaceService = lingxiSpaceService;_conversationService = conversationService;}public async Task<bool> Execute(RoleDialogModel message){// 函数反序列化之后的参数var args = JsonSerializer.Deserialize<CustomGenerateImageFunctionArgs>(message.FunctionArgs ?? "", _options) ?? new CustomGenerateImageFunctionArgs();message.StopCompletion = true;var clientFactory = _service.GetRequiredService<IHttpClientFactory>();using var httpClient = clientFactory.CreateClient();var llmProviderService = _service.GetRequiredService<ILlmProviderService>();var model = llmProviderService.GetSetting("tongyi", "wanx-v1");if (model == null){return false;}var request = new GenerateImageRequest{Model = "wanx-v1",Input = new GenerateImageInput{Prompt = args.ImageDescription},Parameters = new GenerateImageParameters{Style = "<auto>",Size = "1024*1024",N = 1}};var generateImageUrl = $"{model.Endpoint.TrimEnd('/')}/services/aigc/text2image/image-synthesis";// 添加认证头部请求头httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", model.ApiKey);httpClient.DefaultRequestHeaders.Add("X-DashScope-Async", "enable");var result = await httpClient.PostAsJsonAsync(generateImageUrl, request);if (!result.IsSuccessStatusCode){return false;}var taskContent = await result.Content.ReadAsStringAsync();var resultData = JsonSerializer.Deserialize<GenerateImageResponse>(taskContent, _options);var taskUrl = $"{model.Endpoint.TrimEnd('/')}/tasks/{resultData?.Output.TaskId}";var maxRetries = 5;var retryCount = 0;while (retryCount < maxRetries){var taskResult = await httpClient.GetAsync(taskUrl);if (!taskResult.IsSuccessStatusCode){return false;}var taskResultContent = await taskResult.Content.ReadAsStringAsync();var taskResponse = JsonSerializer.Deserialize<ImageTaskResponse>(taskResultContent, _options);if (taskResponse?.Output.TaskStatus == "SUCCEEDED"){var url = taskResponse?.Output.Results.FirstOrDefault()?.Url;if (string.IsNullOrEmpty(url)){return false;}// 下载图片并转换为Base64var imageBytes = await httpClient.GetByteArrayAsync(url);var base64Image = Convert.ToBase64String(imageBytes);var generateImageContent = new GenerateImageContent{Name = args.ImageName,Description = args.ImageDescription,ImageData = $"data:{MediaTypeNames.Image.Png};base64,{base64Image}"};//保存生成的图片var lingxiSpace = await _lingxiSpaceService.AddAsync(new LingxiSpace{Id = Guid.NewGuid().ToString(),ConversationId = _conversationService.ConversationId,Content = JsonSerializer.SerializeToDocument(generateImageContent, _options),Name = args.ImageName,Desc = args.ImageDescription,Type = LingxiSpaceType.Image,CreatedTime = DateTime.UtcNow});WeakReferenceMessenger.Default.Send(lingxiSpace);break;}await Task.Delay(10000); // 等待10秒后再次轮询retryCount++;}return retryCount < maxRetries;}

5. 功能模块的加载

BotSharp采用插件模式开发,需要在配置中配置要加载的模块,然后项目启动就会加载模块注入服务。

目前我启用的模块配置如下:

  "PluginLoader": {"Assemblies": ["BotSharp.Core","BotSharp.Logger","BotSharp.Plugin.OpenAI","BotSharp.Plugin.AzureOpenAI","BotSharp.Plugin.MetaGLM","BotSharp.Plugin.LiteDBStorage","Verdure.Braincase.Copilot.Plugin"]}

服务注入也很简单,主要是AddBotSharpCore的注入,BotSharp本身是有用户的概念的,所以我实现了一个BotUserIdentity做了用户的默认数据初始化,大家可以根据需要操作。

           // add botsharp.AddTransient<AgentViewModel>().AddTransient<AgentPage>().AddTransient<ChatViewModel>().AddTransient<LingxiSpaceViewModel>().AddTransient<ILingxiSpaceService, LiteDBLingxiSpaceService>().AddBotSharpCore(config, options =>{options.JsonSerializerOptions.Converters.Add(new RichContentJsonConverter());options.JsonSerializerOptions.Converters.Add(new TemplateMessageJsonConverter());}).AddSingleton(dbSettings).AddHttpContextAccessor().AddScoped<IUserIdentity, BotUserIdentity>().AddScoped<IBotToolService, BotToolService>().AddScoped<IBotIotService, BotIotService>().AddBotSharpLogger(config)

如果看到这里,大家还是一头雾水的话,可以多看看BotSharp的设计理念,当然如果有需要我可以再写一篇BotSharp的讲解文章。

心得体会

随着大模型能力的提升,大模型的应用场景也会越来越多,以后的大模型应该会作为基础设施供人们使用,基于大模型进行开发的岗位应该会越来越多,感觉大模型真的是生产力工具,我最近在开发这些功能的时候,也会借助Github Copilot进行一些功能的开发,效率高很多。

希望在未来人类是驾驭AI,而不是被AI给取代了。

参考推荐文档项目如下:

  • 电子脑壳源码地址

  • BotSharp 文档

  • 智能体开发框架 BotSharp源码

  • 聊天界面参考的项目 Rodel Agent

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

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

相关文章

【C++编程】五个分区: 堆、栈、静态(全区)区、 常量区 代码区

在C++中,程序的内存管理被分为几个区域,这些区域每个都有其特定的用途。下面是你提到的五个分区的详细描述: 一、栈区(Stack) 用途:用于存储局部变量和函数调用时的上下文(如返回地址与参数等)。 特点: 采用先进后出(LIFO)原则进行管理。 内存由编译器自动分配和释放…

突破自我

1.寻找红利 红利大抵有以下几种:城市红利、行业红利、时代红利。 城市红利,一个高速发展、人口长期净流入的城市就有城市红利,城市红利主要体现在两点:房产、认知信息、人脉差。有红利的城市在过去20年房价一路向上,安家了基本就等于资产不断升值。有红利的城市带来的认知…

【C++编程】五个分区 堆、栈、静态(全区)区、 常量区 代码区

在C++中,程序的内存管理被分为几个区域,这些区域每个都有其特定的用途。下面是你提到的五个分区的详细描述: 一、栈区(Stack) 用途:用于存储局部变量和函数调用时的上下文(如返回地址与参数等)。 特点: 采用先进后出(LIFO)原则进行管理。 内存由编译器自动分配和释放…

【原创】linux实时操作系统xenomai看门狗(watchdog)机制及作用介绍

本文介绍linux实时操作系统xenomai中的watchdog机制及用途。版权声明:本文为本文为博主原创文章,转载请注明出处 https://www.cnblogs.com/wsg1100。如有错误,欢迎指正。 目录一、前言PREEMPT-RT(RT Throttling)一、xenomai watchdog介绍二、xenomai watchdog工作原理三、…

Logisim-020-☆4位快速加法器

电路文件所在 电路/3-data.circ 中的 ☆4位快速加法器仓库地址 https://gitee.com/gitliang/logisim-to-cpu

基于Java+SpringBoot+Mysql在线简单拍卖竞价拍卖竞拍系统功能设计与实现九

运行环境:windows/Linux均可、jdk1.8、mysql5.7、redis3.0、idea/eclipse均可。 技术点:SpringBoot+SpringDataJPA+Mysql+Freemaker+Bootstrap+JS+CSS+HTML 部分功能:前台项目信息控制器Controller(商品列表、商品管理、商品审核、商品上下架)一、前言介绍: 免费学习:猿…

RGB/INT8 输入注意事项

01 技术背景 在大多数情况下,我们都推荐用户在编译处理图像任务的模型时,将 input_type_rt 参数配置 nv12,这是考虑到视频通路传来的数据通常都是 nv12 类型,这样配置可以最大化地节约耗时,提高全流程的处理效率。 但在前期的算法验证阶段,往往用户更希望模型能直接读取 …

海波龙系统HFM的数据表和各维度取值方法

数据表包括DCE表、DCN表、DCT表 DCE表格存储和值以及相关的日记账调整。 DCN表格存储价值维度剩余成员的数据。 DCT表格存储日记账调整。当日记账被过账时,数据值流向DCE和/或DCN表格。 表的命名格式:APPNAME_[表][场景][年份] 例如:APPNAME_DCE_1_2024, APPNAE应用,DCE表…

.NET9 - Swagger平替Scalar详解(四)

本文分享Swagger中常用功能在Scalar中的使用,包括版本说明、接口分类、接口及参数描述、枚举类型、文件上传和JWT认证等,并提供相关代码示例和效果展示,以及可能遇到的问题和解决方案。书接上回,上一章介绍了Swagger代替品Scalar,在使用中遇到不少问题,今天单独分享一下之…

秒懂Java为什么只有值传递

在Java语言中,数据类型分为基本数据类型和引用数据类型。 基本数据类型(如int、double、char等)的值直接保存在栈上。这些类型的变量在栈内存中有固定的大小,并且值是直接存储在这些变量中的,数据的传递为值传递,这个好理解。以下以引用数据类型来讲解。 引用和实例化对象…

几何校准 和 ros环境下标定Balser相机

几何校准 基本概念 内参数内参数是相机内部的参数,与相机的位置无关,由镜头和感光元器件的特性决定。 包括:主距,主点,畸变参数畸变参数 径向畸变:正和负径向畸变[@] 正径向畸变 (桶形畸变):在这种畸变中,图像中心附近的点会向外移动,图像看起来像是从中心向外膨胀,尤…

datax的安装与使用

1、datax简介概述 DataX 是阿里巴巴集团内被广泛使用的离线数据同步工具/平台,实现包括 MySQL、Oracle、SqlServer、Postgre、HDFS、Hive、ADS、HBase、TableStore(OTS)、MaxCompute(ODPS)、DRDS 等各种异构数据源之间高效的数据同步功能。支持的数据源2、架构 为了解决异构数…