【BotSharp框架示例 ——实现聊天机器人,并通过 DeepSeek V3实现 function calling】
BotSharp框架示例 ——实现聊天机器人,并通过 DeepSeek V3实现 function calling
- 一、一点点感悟
- 二、创建项目
- 1、创建项目
- 2、添加引用
- 3、MyWeatherPlugin项目代码编写
- 4、WeatherApiDefaultService项目代码编写
- 5、WebAPI MyWeatherAPI 的项目代码编写
- 6、data文件夹中声明agent 、conversation、functions
- 三、运行程序 进行测试
- 四、总结
本文通过仔细研究 BotSharp 框架的示例代码PizzaBot,实现以下内容:
1、分析Botsharp实现聊天机器人的基本组成部分;
2、模仿PizzaBot一步一步搭建自己的聊天机器人;
3、通过分析PizzaBot,实现控制agent 驱动不同的大模型;
4、实现大模型调用本地函数的示例。
一、一点点感悟
深刻感觉.Net在AI领域已经有很多优秀的框架可以使用,但是苦于资料太少,本人就研究这一个小小的例子,就花费了很长的时间。
真希望 出现以前那种一步一步学xxx 类似的文章,能引导学习的人快速上手。
真希望本例子能够作为一个投石问路,会有更多的人来分享实际的案例,清晰的使用指南。
二、创建项目
闲言少叙,开始模仿。
本文 参考
1、Botsharp的示例 PizzaBot
2、Botsharp的官方文档:https://botsharp.verdure-hiro.cn/guide/agent/hook
1、创建项目
目标是搭建一个关于天气咨询的一个聊天机器人,
所以 模仿PizzaBot 创建了三个项目
1、创建类库MyWeatherPlugin(主要部分,承载AgentHook、ConversationHook,以及本地Function的定义)
2、创建类库WeatherApiDefaultService(用于模拟PizzaBot,实现默认的服务注册和方法–此处经过测试,也可以不添加,不是必须要执行的步骤)
3、创建WebAPI项目MyWeatherAPI(通过配置、启动聊天机器人,主要关注配置文件和启动类)
2、添加引用
通过测试,需要添加以下 引用:
1、MyWeatherPlugin中需要添加 BotSharp.Core的引用
2、WeatherApiDefaultService 因为 是非必须的,可以不用添加这个项目了,如果添加了,可以 照着PizzaBot照搬下
3、MyWeatherAPI 需要添加 以下类库引用:
BotSharp.CoreBotSharp.AbstractionBotSharp.LoggerBotSharp.OpenAPIBotSharp.Plugin.ChatHub
由于本例中使用到了 DeepSeek,所以要引用
BotSharp.Plugin.DeepSeekAI
在之前的探索中还使用过 llama本地模型,如果要用llama模型的话,要引用 以下的库
BotSharp.Plugin.LLamaSharp
LLamaSharp
LLamaSharp.Backend.Cpu
LLamaSharp.Backend.Cuda12
3、MyWeatherPlugin项目代码编写
模拟Pizzabot,添加 以下内容
1)添加公共using文件
2)添加MyWeatherPlugin
注意,MyWeatherPlugin文件中需要注册注册一些AgentId,示例中使用的 是 启动后通过postman 创建agent的方法(引用见 Botsharp的官方文档:https://botsharp.verdure-hiro.cn/guide/agent/hook,有启动postman workspace的链接)
using BotSharp.Abstraction.Agents;
using BotSharp.Abstraction.Conversations;
using BotSharp.Abstraction.Plugins;
using MyWeatherPlugin.Hooks;namespace MyWeatherPlugin
{public class MyWeatherPlugin: IBotSharpPlugin{public string Id => "1c8270eb-de63-4ca0-8903-654d83ce5ece";public string Name => "MyWeather AI Assistant";public string Description => "An example of an Weather AI Chatbot.";public string IconUrl => "https://cdn-icons-png.flaticon.com/512/6978/6978255.png";public string[] AgentIds => new[]{"01fcc3e5-0af7-49e6-ad7a-a760bd12dc4d","01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a","6745151e-6d46-4a02-8de4-1c4f21c7da95","dfd9b46d-d00c-40af-8a75-3fbdc2b89869"};public void RegisterDI(IServiceCollection services, IConfiguration config){// Register hooksservices.AddScoped<IAgentHook, MyWeatherAgentHook>();//services.AddScoped<IConversationService, MyWeatherConversationHook>();}}
}
3)新建 Hooks的目录,参考Pizzabot,添加agenthook和 conversationhook
CommonAgentHook
MyWeatherAgentHook
MyWeatherConversationHook
agent是一个智能体,可以决定调用某一个具体的大模型,决定使用什么provider。
conversation是一个会话,也就是一个上下文,是LLM模型都需要的一个参数,用于通过上下文来推理用户的问题。
所以这里的agentHook 是botsharp中用于注册agent和agent行为的 程序,能决定 agent在创建时、加载时等各个事件发生时的一些自定义行为。
本例中没有涉及,但是在实验的时候,通过添加不同的方法,是能够验证,这些事件是能够顺利被触发的。
conversationhook 同理
代码分别是:
using BotSharp.Abstraction.Agents;namespace MyWeatherPlugin.Hooks
{public class CommonAgentHook : AgentHookBase{public override string SelfId => string.Empty;public CommonAgentHook(IServiceProvider services, AgentSettings settings): base(services, settings){}public override bool OnInstructionLoaded(string template, Dictionary<string, object> dict){dict["current_date"] = DateTime.Now.ToString("MM/dd/yyyy");dict["current_time"] = DateTime.Now.ToString("hh:mm tt");dict["current_weekday"] = DateTime.Now.DayOfWeek;return base.OnInstructionLoaded(template, dict);}}}
using BotSharp.Abstraction.Agents.Enums;
using BotSharp.Abstraction.Agents;
using BotSharp.Abstraction.Functions.Models;namespace MyWeatherPlugin.Hooks
{public class MyWeatherAgentHook : AgentHookBase{public override string SelfId => BuiltInAgentId.AIAssistant;public MyWeatherAgentHook(IServiceProvider services, AgentSettings settings): base(services, settings){}public override bool OnInstructionLoaded(string template, Dictionary<string, object> dict){return base.OnInstructionLoaded(template, dict);}}}
using BotSharp.Abstraction.Conversations.Models;
using BotSharp.Abstraction.Conversations;namespace MyWeatherPlugin.Hooks
{public class MyWeatherConversationHook : ConversationHookBase{private readonly IServiceProvider _services;private readonly IConversationStateService _states;public MyWeatherConversationHook(IServiceProvider services,IConversationStateService states){_services = services;_states = states;}public override async Task OnPostbackMessageReceived(RoleDialogModel message, PostbackMessageModel replyMsg){if (replyMsg.FunctionName == "get_my_weather_type"){// message.StopCompletion = true;}return;}public override Task OnTaskCompleted(RoleDialogModel message){return base.OnTaskCompleted(message);}#if USE_BOTSHARPpublic override async Task OnResponseGenerated(RoleDialogModel message){var agentService = _services.GetRequiredService<IAgentService>();var state = _services.GetRequiredService<IConversationStateService>();var agent = await agentService.LoadAgent(message.CurrentAgentId);if (agent.McpTools.Any(item => item.Functions.Any(x => x.Name == message.FunctionName))){var data = JsonDocument.Parse(JsonSerializer.Serialize(message.Data));state.SaveStateByArgs(data);}await base.OnResponseGenerated(message);}
#endif}}
4)添加Functions目录, 参考Pizzabot 添加几个function,
这里是本例子的一个重点,这里主要是 通过 function定义,决定一些在本地可以被调用到的自定义行为。这是非常重要的。本例中添加了三个方法:
GetMyWeatherTypeFn
GetWeatherDesFn(这是本例中最后实现调用的方法)
GetWeatherToolFn
方法中的 Execute 方法,决定了最终方法 最终返回给大模型的 文本内容
using BotSharp.Abstraction.Conversations.Models;
using BotSharp.Abstraction.Conversations;
using BotSharp.Abstraction.Messaging.Models.RichContent.Template;
using BotSharp.Abstraction.Messaging.Models.RichContent;
using BotSharp.Abstraction.Messaging;
using System.Text.Json;namespace MyWeatherPlugin.Functions
{public class GetMyWeatherTypeFn : IFunctionCallback{public string Name => "get_my_weather_type";private readonly IServiceProvider _services;public GetMyWeatherTypeFn(IServiceProvider services){_services = services;}public async Task<bool> Execute(RoleDialogModel message){var states = _services.GetRequiredService<IConversationStateService>();var weatherTypes = new List<string>{"晴天","雨天","雪天"};message.Content = JsonSerializer.Serialize(weatherTypes);message.RichContent = new RichContent<IRichMessage>{Recipient = new Recipient{Id = states.GetConversationId()},FillPostback = true,Message = new ButtonTemplateMessage{Text = "Please select a weather type",Buttons = weatherTypes.Select(x => new ElementButton{Type = "text",Title = x,Payload = x}).ToArray()}};return true;}}}
using BotSharp.Abstraction.Conversations.Models;
using System.Text.Json;namespace MyWeatherPlugin.Functions;public class GetWeatherDesFn : IFunctionCallback
{public string Name => "get_weather_des";public async Task<bool> Execute(RoleDialogModel message){message.Data = new{sunny_desc = "晴空万里,万里无云,艳阳高照,碧海蓝天",rainny_desc = "大雨倾盆,电闪雷鸣,乌云密闭,水位猛涨",snowny_desc = "鹅毛大雪,原驰蜡象,瑞雪丰年,一片雪白"};message.Content =JsonSerializer.Serialize(message.Data);return true;}
}
using BotSharp.Abstraction.Conversations;
using BotSharp.Abstraction.Conversations.Models;namespace MyWeatherPlugin.Functions;public class GetWeatherToolFn : IFunctionCallback
{public string Name => "get_weather_tool";private readonly IServiceProvider _service;public GetWeatherToolFn(IServiceProvider service){_service = service;}public async Task<bool> Execute(RoleDialogModel message){message.Content = "The weather date is 2025-04-25";var state = _service.GetRequiredService<IConversationStateService>();state.SetState("weather_date", "2025-04-25");return true;}
}
最终 MyWeatherPlugin 的结构如图:
4、WeatherApiDefaultService项目代码编写
因为是非必须的,而且完全参考 Pizzabot中的 defaultservice的,所以这部分就略过了
5、WebAPI MyWeatherAPI 的项目代码编写
看下配置文件: appsettings.json
这是 经过实验,需要保留的一些节点,
保留 jwt节点 是因为botsharp框架中涉及到用户认证
LlmProviders 中保留 本例中用到LLM模型的驱动
PluginLoader 是加载的所有库
注意 填上 自己的 appkey
{"Logging": {"LogLevel": {"Default": "Information","Microsoft.AspNetCore": "Warning"}},"AllowedHosts": "*","AllowedOrigins": ["http://localhost:5015","http://0.0.0.0:5015","https://botsharp.scisharpstack.org","https://chat.scisharpstack.org"],"Jwt": {"Issuer": "botsharp","Audience": "botsharp","Key": "31ba6052aa6f4569901facc3a41fcb4adfd9b46dd00c40af8a753fbdc2b89869"},"LlmProviders": [{"Provider": "llama-sharp","Models": [{"Name": "llama-2-7b-guanaco-qlora.Q2_K.gguf","Type": "chat"}]},{"Provider": "deepseek-ai","Models": [{"Name": "deepseek-chat","ApiKey": "xxxxx","Endpoint": "https://api.deepseek.com/v1/","Type": "chat","PromptCost": 0.0015,"CompletionCost": 0.002}]}],"Router": {},"Evaluator": {"AgentId": "dfd9b46d-d00c-40af-8a75-3fbdc2b89869"},"Agent": {"DataDir": "agents","TemplateFormat": "liquid","HostAgentId": "01fcc3e5-0af7-49e6-ad7a-a760bd12dc4d","EnableTranslator": false,"LlmConfig": {//"Provider": "llama-sharp",//"Model": "llama-2-7b-guanaco-qlora.Q2_K.gguf""Provider": "deepseek-ai","Model": "deepseek-chat"}},"MCP": {"Enabled": false,"McpClientOptions": {"ClientInfo": {"Name": "SimpleToolsBotsharp","Version": "1.0.0"}},"McpServerConfigs": [{"Id": "WeatherServer","Name": "WeatherServer","TransportType": "sse","TransportOptions": [],"Location": "http://localhost:58905/sse"}]},"Conversation": {"DataDir": "conversations","ShowVerboseLog": false,"EnableLlmCompletionLog": false,"EnableExecutionLog": true,"EnableContentLog": true,"EnableStateLog": true,"EnableTranslationMemory": false,"CleanSetting": {"Enable": true,"BatchSize": 50,"MessageLimit": 2,"BufferHours": 12,"ExcludeAgentIds": []},"RateLimit": {"MaxConversationPerDay": 100,"MaxInputLengthPerRequest": 256,"MinTimeSecondsBetweenMessages": 2}},"SideCar": {"Conversation": {"Provider": "botsharp"}},"ChatHub": {"EventDispatchBy": "group"},"LlamaSharp": {"Interactive": true,"ModelDir": "F:/models","DefaultModel": "llama-2-7b-guanaco-qlora.Q2_K.gguf","MaxContextLength": 1024,"NumberOfGpuLayer": 20},"AzureOpenAi": {},"RoutingSpeeder": {},"Database": {"Default": "FileRepository","TablePrefix": "BotSharp","BotSharpMongoDb": "","Redis": "botsharp.redis.cache.windows.net:6380,password=,ssl=True,abortConnect=False","FileRepository": "data","Assemblies": [ "BotSharp.Core" ]},"Interpreter": {"Python": {"PythonDLL": "C:/Python313/python313.dll"}},"PluginLoader": {"Assemblies": ["BotSharp.Core","BotSharp.Core.SideCar","BotSharp.Core.Crontab", "BotSharp.Logger", "BotSharp.Plugin.OpenAI","BotSharp.Plugin.AzureOpenAI", "BotSharp.Plugin.DeepSeekAI","BotSharp.Plugin.MetaMessenger","BotSharp.Plugin.ChatHub", "MyWeatherPlugin", "BotSharp.Plugin.LLamaSharp" ],"ExcludedFunctions": ["McpToolAdapter"]}
}
progam.cs 是启动程序,代码如下
using BotSharp.Abstraction.Conversations;
using BotSharp.Abstraction.Messaging.JsonConverters;
using BotSharp.Abstraction.Users;
using BotSharp.Core;
using BotSharp.Core.Agents;
using BotSharp.Core.MCP;
using BotSharp.Logger;
using BotSharp.OpenAPI;
using BotSharp.Plugin.ChatHub;
using MyWeatherPlugin.Hooks;var builder = WebApplication.CreateBuilder(args);// Add services to the container.builder.Services.AddBotSharpCore(builder.Configuration, options =>
{options.JsonSerializerOptions.Converters.Add(new RichContentJsonConverter());options.JsonSerializerOptions.Converters.Add(new TemplateMessageJsonConverter());
}).AddBotSharpOpenAPI(builder.Configuration,
builder.Configuration.GetSection("AllowedOrigins").Get<string[]>() ?? new[]{"http://0.0.0.0:5015","https://botsharp.scisharpstack.org","https://chat.scisharpstack.org"}, builder.Environment, true).AddBotSharpLogger(builder.Configuration);builder.Services.AddControllers();builder.AddServiceDefaults();
builder.Services.AddSignalR();
var app = builder.Build();// Configure the HTTP request pipeline.
app.MapHub<SignalRHub>("/chatHub");
app.UseMiddleware<WebSocketsMiddleware>();app.UseBotSharp().UseBotSharpOpenAPI(app.Environment);//app.MapControllers();app.Run();
6、data文件夹中声明agent 、conversation、functions
这是这个例子中 最重要的部分,
1、程序运行
2、通过 官方文档:https://botsharp.verdure-hiro.cn/guide/quick-start/get-started
中的 postman 链接 如下:
调整成自己的 Host等参数
3)依次 执行
New User Account
Get Access Token
Create Agent
的操作 ,如图:
4)这时候要查找下 创建的 agent目录在哪里,应该在 webAPI的bin目录下
\bin\Debug\net9.0\data\agents
我把这个目录 拷贝到 webAPI的根目录 以及 Myweatherplugin项目的根目录下了
注意每次修改的时候 我都同步拷贝了。(实验是有效的,可能不需要,但是这么做是不出错的)
5)假设创建的agentId是本例的 01fcc3e5-0af7-49e6-ad7a-a760bd12dc4d
注意要修改 appsettings.json 中的 agent节点,修改 MyWeatherPlugin 项目中的 MyWeatherPlugin 类中 加载的 agentid数组
然后拷贝 webAPI目录下的 \MyWeatherAPI(解决方案目录)\MyWeatherAPI(webAPI项目目录)\bin\Debug\net9.0\data\agents\01fcc3e5-0af7-49e6-ad7a-a760bd12dc4d
到
\MyWeatherAPI(解决方案目录)\MyWeatherAPI (webAPI项目目录)\data\agents 目录下
同时拷贝到 \MyWeatherAPI\MyWeatherPlugin\data\agents 目录下
以上步骤 可能不是必须的,但是这么做 不出错。(注意后面改了之后 也同步拷贝下)
6)选择修改 MyWeatherPlugin\data\agents\01fcc3e5-0af7-49e6-ad7a-a760bd12dc4d下的配置信息
结构如图:
改动信息 如下:
functions/get_my_weather_type.json
{"name": "get_my_weather_type","description": "获取所有的天气类型,您可以选择后返回响应的描述","parameters": {"type": "object","properties": {},"required": []}
}
functions/get_weather_des.json
{"name": "get_weather_des","description": "用户选择了天气后,获取关于天气的描述","parameters": {"type": "object","properties": {"weather_type": {"type": "string","description": "The type of the weather."}},"required": [ "weather_type" ]}
}
functions/get_weather_tool.json
{"name": "get_weather_tool","description": "提供一个适合天气出行的外带工具.","parameters": {"type": "object","properties": {"weather_type": {"type": "string","description": "天气类型."},"weather_desc": {"type": "string","description": "天气描述."}},"required": [ "weather_type", "weather_desc" ]}
}
instructions/instructions.liquid
你是一个关于天气资讯的小助手,你可以根据用户的天气问题,回复天气的描述和不同的天气要带什么工具出门。根据一下步骤进行操作:
1: 首先回答下用户今天是 {{current_date}},时间是 {{current_time}}.
2: 如果用户一些需求,你需要询问用户是否需要相关帮助。
3: 如果的问题中包含了关键词 {{weather_type}},你就执行函数 get_my_weather_type 。
response/func.get_weather_des.0.liquid
{% assign weather_type = weather_type | downcase %}
{% if weather_type contains "晴天" -%}关于 {{weather_type}} 的描述是 is ${{ sunny_desc }}.您觉得满意吗?
{%- elsif weather_type contains "雨天" -%}关于 {{weather_type}} 的描述是 is ${{ rainny_desc }}.您觉得满意吗??
{%- elsif weather_type contains "雪天" -%}关于 {{weather_type}} 的描述是 is ${{ snowny_desc }}.您觉得满意吗??
{%- else -%}我们没有 {{weather_type}} 您天气的描述
{%- endif %}
{% if quantity == nil -%}How many slices would you like to order?
{%- endif %}
agent.json ---- 这个最重要,定义了 需要大模型回调的函数声明
{"id": "01fcc3e5-0af7-49e6-ad7a-a760bd12dc4d","name": "Weather About","description": "weather.Talk","createdDateTime": "2024-05-07T10:00:00Z","updatedDateTime": "2024-05-07T10:00:00Z","disabled": false,"isPublic": true,"llmConfig": {"provider": "deepseek-ai","model": "deepseek-chat"},"profiles": [ "weather" ],"functions": [{"name": "get_my_weather_type","description": "获取所有的天气类型,您可以选择后返回响应的描述","parameters": {"type": "object","properties": {},"required": []}},{"name": "get_weather_des","description": "用户选择了天气后,获取关于天气的描述","parameters": {"type": "object","properties": {"weather_type": {"type": "string","description": "The type of the weather."}},"required": [ "weather_type" ]}},{"name": "get_weather_tool","description": "提供一个适合天气出行的外带工具.","parameters": {"type": "object","properties": {"weather_type": {"type": "string","description": "天气类型."},"weather_desc": {"type": "string","description": "天气描述."}},"required": [ "weather_type", "weather_desc" ]}}],"labels": [ "about" ]
}
其他基本不再重要了。
三、运行程序 进行测试
刚刚 postman已经执行了 createagent 操作了。
继续执行 接下来的 New Conversation 和 sendMessage 测试
执行结果如图:
四、总结
1、实现了 通过 agent 控制调用不同的 大模型。
2、通过 conversation 控制 不同的 会话上下文,相当于session,
3、通过输入内容,控制了 让 大模型来调用本地的函数,输出准确的结果。
相关文章:
【BotSharp框架示例 ——实现聊天机器人,并通过 DeepSeek V3实现 function calling】
BotSharp框架示例 ——实现聊天机器人,并通过 DeepSeek V3实现 function calling 一、一点点感悟二、创建项目1、创建项目2、添加引用3、MyWeatherPlugin项目代码编写4、WeatherApiDefaultService项目代码编写5、WebAPI MyWeatherAPI 的项目代码编写6、data文件夹中…...
记录一个单独读取evt.bdf的方法
问题描述 之前只能使用eeglab的工具,读取博瑞康达的data.bdf和evt.bdf时,使用的是eeglab的下面这个读取文件的插件。 evt.bdf使用记事本文件查看是乱码的形式。 实现方法 事实上,我们可以直接对这个文件的16进制进行解析。 对文件的位和…...
是否想要一个桌面哆啦A梦的宠物
是否想拥有一个在指定时间喊你的桌面宠物呢(手动狗头) 如果你有更好的想法,欢迎提出你的想法。 是否考虑过跟开发者一对一,提出你的建议(狗头)。 https://wwxc.lanzouo.com/idKnJ2uvq11c 密码:bbkm...
防爆风扇储能轴流风机风量风压如何保障通风安全?
在化工车间、煤矿巷道等高危环境中,通风安全是保障生产与人员生命安全的关键防线。防爆风扇储能轴流风机凭借独特的风量风压设计与性能优势,成为守护通风安全的可靠屏障。那么,它究竟是如何发挥作用的呢? 从风量设计来看,防爆风…...
Centos 7系统 宝塔部署Tomcat项目(保姆级教程)
再看文章之前默认已经安装好系统,可能是云系统,或者是虚拟机。 宝塔安装 这个比较简单,参考这个老哥的即可: https://blog.csdn.net/weixin_42753193/article/details/125959289 环境配置 进入宝塔面板之后会出现环境安装&…...
Electron读取本地文件
在 Electron 应用中,可以使用 Node.js 的 fs 模块来读取本地文件。以下是如何实现这一功能的详细步骤。 1. 设置项目结构 假设你的项目目录如下: my-electron-app/ ├── main.js ├── index.html └── renderer.js2. 使用 fs 模块读取文件 在主…...
Plesk 下的 IP 地址管理
Plesk是一个方便管理的控制面板,可以简化网站主机和服务器数据中心的自动化管理。它专为提供Windows和Linux服务器的供应商设计。Plesk面板适用于虚拟主机和独立服务器 服务器管理员可以使用Plesk来配置新网站、电子邮件系统和转售商账户,也可以通过Ple…...
基于STM32、HAL库的DS28E15P安全验证及加密芯片驱动程序设计
一、简介: DS28E15P是Maxim Integrated (现为Analog Devices)生产的一款1-Wire EEPROM芯片,具有以下特点: 1-Wire接口通信,仅需单根数据线加地线 1024位(128字节)EEPROM存储器 每个器件具有唯一的64位ROM ID 工作电压范围:2.8V至5.25V 内置CRC16校验功能 可编程写保护功能…...
浅析localhost、127.0.0.1 和 0.0.0.0的区别
文章目录 三者的解释三者的核心区别总结使用场景示例什么是回环地址常见问题开发工具中的地址使用为什么开发工具同时支持localhost和127.0.0.1?实际应用示例VSCode中的Live Server插件VSCode中的VUE项目IDEA中的Spring Boot应用 最佳实践建议 localhost、 127.0.0…...
antd+react实现html图片预览效果
import { Image } from ‘antd’; import { useEffect, useRef, useState } from ‘react’; import styles from ‘./index.module.less’; interface PreviewHtmlWithImagesProps { htmlContent: string; } const PreviewHtmlWithImages: React.FC ({ htmlContent }) >…...
【React】轻松掌握 React 中 useEffect的使用
你有没有想过,为什么你的 React 组件能够轻松应对周围发生的变化,比如每当有新数据到来时自动更新,或者处理可以动态响应实时事件的组件?这就是 useEffect 的用武之地!这个强大的钩子(Hook)就像…...
请简述一下什么是 Kotlin?它有哪些特性?
1 JVM 语言的共性:编译成字节码文件 Kotlin 和 Java 同属于 JVM(Java Virtual Machine)语言,它们的代码最终都会被编译成 JVM 字节码(.class)文件。 编译流程: Kotlin 编译:Kotli…...
Post与Get以及@Requestbody和@Pathvariable标签的应用
Post的使用场景:简单来讲适用于有安全性限制的,因为post请求的内容会被存在某个封装内容中(比如表单、jason格式等),这部分内容是不会被浏览器的cache所捕捉,安全性较强。 Get的使用场景:与pos…...
基于tabula对pdf中的excel进行识别并转换成word(三)
上一节中是基于PaddleOCR对图片中的excel进行识别并转换成word优化,本节改变思路,直接从pdf中读取表格的信息,具体思路如下所述。 PDF中的表格数据如下截图所示: 一、基于tabula从PDF中提取表格 df_list tabula.read_pdf("…...
k8s集群环境部署业务系统
k8s集群环境部署业务系统,通过shell脚本整合部署过程,简化部署流程。操作流程如下: A,B为业务系统服务名。 一.部署前准备。在k8s集群各节点执行该脚本,完成业务系统镜像加载。 #!/bin/bash # 1.删除deployment ech…...
MySQL 8.4.4 安全升级指南:从漏洞修复到版本升级全流程解析
目录 二、升级前关键注意事项 1. 数据安全与备份 2. 版本兼容性与路径规划 三、分步升级操作流程 1. 环境预检与准备 2. 安装包部署 3. 强制升级组件 4. 验证与启动 一、背景与必要性 近期安全扫描发现生产环境的 MySQL 数据库存在多个高危漏洞(CVE 详情参见Oracle 官…...
“假读“操作在I2C接收流程中的原因
在I2C接收流程中,"假读"操作是NXP I2C控制器工作特性要求的必要操作,具体原因如下: // 接收函数关键代码 void i2c_master_read(I2C_Type *base, unsigned char *buf, unsigned int size) {// ...dummy base->I2DR; /* 假读 *…...
TA学习之路——2.3图形的HLSL常用函数详解
1.基本数学运算 函数作用max(a,b)返回a,b值中较大的那个min(a,b)返回a,b值中较小的那个mul(a,b两变量相乘,常用于矩阵abs(a)返回a的绝对值sqrt(x)返回x的平方根rsqrt(x)返回x的平方根的倒数degrees(x)将弧度转成角度radians(x)将角度转成弧度noise(x)噪声函数1.1 创建一个测试…...
Python数据容器:数据容器的分类、数据容器特点总结、各数据容器遍历的特点、数据容器通用操作(统计,转换,排序)
目录 数据容器的分类 数据容器特点总结 数据容器遍历的特点 通用操作 通用统计len()、max()、min() 通用转换list()、tuple()、str()、set() 通用排序sorted 数据容器的分类 分类: 是否支持下标索引 支持:列表、元组、字符串-序列类型不支持&…...
FastAPI的发展历史
参考:https://zhuanlan.zhihu.com/p/710831974 FastAPI 于 2019 年发布,由 Sebastian Ramirez 创建。他是 Pydantic 框架的创建者,也是多个开源项目的贡献者。 FastAPI 的设计初衷是为了解决 Python Web 框架在数据类型验证和文档生成方面的问…...
本地大模型编程实战(28)查询图数据库NEO4J(1)
本文将基于langchain 框架,用LLM(大语言模型)查询图数据库NEO4J。 使用 qwen2.5 做实验,用 llama3.1 查不出内容。 文章目录 安装 NEO4J准备图数据查询图数据总结代码 安装 NEO4J 参见:在windows系统中安装图数据库NEO4J 。 准备图数据 我…...
从厨房到云端:从预制菜到云原生
小美:小猿,你最近在忙什么呢?看你总是加班。 小猿:唉,公司在搞什么“云原生”改造,说是要把我们的应用搬到云上,搞得我头都大了。 小美:云原生?听起来很高大上啊&#…...
单片机-89C51部分:9、串行口通讯
飞书文档https://x509p6c8to.feishu.cn/wiki/WSh3wnADkixHspk7kc8c5esRnad 一、什么是串口?它的作用? 串行口,简称为串口,什么是串口?它的作用是什么? 两个人交流,一般通过在说话在空气中产生…...
C++程序退出时的对象析构陷阱:深度解析与避坑指南
C++程序退出时的对象析构陷阱:深度解析与避坑指南 一、从诡异案例说起:局部对象为何"神秘消失"?二、全局对象 vs 局部对象1. 全局对象生命周期2. 局部对象生命周期三、程序终止的两种姿势:exit() vs return四、atexit():最后的救命稻草1.基础用法2. 核心特性3…...
为什么 Vite 速度比 Webpack 快?
一、webpack会先进行编译,再运行,vite会直接启动,再按需编译文件。 首先看两张图,可以清晰的看到,上面的图是webpack编译过的,而下面的图是vite直接使用工程内文件。 二、区别于Webpack先打包的方式&am…...
指针变量存放在哪?
指针的存储位置取决于其声明方式和作用域,以下是详细分析: 1. 栈(Stack) 局部指针变量:在函数内部声明的指针(非静态),作为局部变量存储在栈中。void func() {int *p; // p本身存储…...
Kafka 消息可靠性深度解析:大流量与小流量场景下的设计哲学
在分布式消息系统的设计中,消息可靠性保障本质上是系统在一致性、可用性、吞吐量三者之间动态博弈的结果。Kafka作为现代流式架构的核心组件,其消息可靠性机制在不同流量场景下呈现出截然不同的设计哲学。本文将从系统设计原理层面,解构大流量…...
python数据分析(六):Pandas 多数据操作全面指南
Pandas 多数据操作全面指南:Merge, Join, Concatenate 与 Compare 1. 引言 在数据分析工作中,我们经常需要处理多个数据集并将它们以各种方式组合起来。Pandas 提供了多种强大的多数据操作方法,包括合并(merge)、连接(join)、连接(concaten…...
在K8S迁移节点kubelet数据存储目录
默认k8s节点kubelet数据目录在 /var/lib/kubelet,如果在部署前没有做好规划,其实默认就存储在系统盘/分区下了,这样会导致一个问题,如果数据量过大会导致kubelet服务异常,其次,系统盘下有一些系统服务引用&…...
k8s 学习记录 (六)_Pod 污点和容忍性详解
一、前言 在 Kubernetes 集群中,我们已经了解了节点亲和性和 Pod 亲和性,它们在 Pod 调度方面提供了很大的灵活性。今天,我们来探讨另外两个重要的概念 ——Pod 污点(Taints)和容忍性(Tolerations…...
hutools工具类中isNotEmpty与isNotBlank区分
基于以下两种情况。在判断的变量是String类型时, 判断是否为空,推荐使用isNotBlank(). 1. isNotEmpty 不会验证str中是否含有空字符串,而 isNotBlank方法会验证 public static boolean isNotEmpty(CharSequence str) {return false isEmpty…...
C#解析USB - HID手柄上摇杆按键数据
1. 了解相关知识 HID设备通信原理:HID设备通过端点报告其状态和数据,设备通过报告描述符来描述数据用途,操作系统通过这个描述符了解设备发送数据的结构。通常一个完整的报告以特定的格式从设备传输至主机。Windows API函数…...
k8s术语pod
Pod概览 理解Pod Pod是kubernetes中你可以创建和部署的最小也是最简的单位,pod代表着集群中运行的进程。 Pod中封装着应用的容器(有的情况下是好几个容器),存储、独立的网络IP,管理容器如何运行的策略选项。Pod代表着部署的一个单位:kubemetes中应用的一个实例,可能由一个…...
使用高德MCP+AI编程工具打造一个旅游小助手
开始之前,我们先来回顾一下,什么是mcp MCP(Model Context Protocol) 模型上下文协议是 2024 年 11 月由 Anthropic 公司推出并开源的一个新标准 核心内容 架构2:MCP 采用客户端 - 服务器架构。AI 应用程序(如聊天工具或 IDE&a…...
AgeTravel | 银发文娱旅游一周新鲜事
银发文娱旅游一周新鲜事 整理 | AgeClub吕娆炜 一周银发文娱旅游产业资讯速览 星期一 4月28日 1 企业动态 携程老友会联合喜悦盛年,首发“旅游兴趣课堂”模式 华寿中心“这YOUNG的老年派”合作聚焦老龄文化需求 通用技术康养Q1营收同比增长129% 2 行业风向…...
【记】Laya2.x数字末尾导致换行异常问题
文章目录 图示 图示...
jspm老年体检信息管理系统(源码+lw+部署文档+讲解),源码可白嫖!
摘要 信息化时代,各行各业都以网络为基础飞速发展,而医疗服务行业的发展却进展缓慢,传统的医疗服务行业已经逐渐不满足民众的需求,有些还在以线下预约的方式接待病人,特别是针对于老年体检的服务,是少之又…...
point-cloud-annotation-tool工具windows复现,史诗巨制(windows 11)
point-cloud-annotation-tool 前期准备1.安装Qt52.安装Cmake3.安装VS 20194.编译构建 VTK 8.15.安装 PCL 1.86.编译Boost 软件环境适配准备1 Boost2 VTK 开始编译开始生成 前期准备 point-cloud-annotation-tool是一款三维标注软件,本次介绍的是再windows11环境下安…...
欧拉计划 Project Euler60(素数对集合)题解
欧拉计划 Project Euler 60 题解 题干思路code 题干 思路 先欧拉筛预处理出素数,然后dfs,注意剪枝,不然太容易炸了 code //13 5197 5701 6733 8389 //26033 #include <bits/stdc.h>using namespace std;using ll long long;bool i…...
DeepSeek破界而来:重构大规模深度检索的算力与边界
深度检索技术革命:DeepSeek万亿参数框架如何突破实时召回率瓶颈 引言:当千亿参数遇见实时检索 在人工智能技术飞速发展的今天,超大规模语言模型已成为推动产业智能化的核心引擎。然而,随着模型参数量突破万亿级门槛,…...
自定义指令input中前面不能输入空格
vue3加el-input 封装自定义指令 utils/trim.ts import type { Directive, DirectiveBinding } from vue;const trim: Directive {mounted(el, binding: DirectiveBinding) {// 获取 el-input 的 DOM 节点const inputEl el.querySelector(input);if (!inputEl) {console.er…...
esp32将partitions.csv文件启用到工程项目中的配置
在ESP32项目里,partitions.csv 文件用于定义闪存的分区表,它规定了不同类型数据(如代码、文件系统、OTA 分区等)在闪存中的存储位置和大小。下面为你详细介绍如何将 partitions.csv 文件集成到工程项目中以及相应的配置步骤。 1.…...
Windows系统下,导入运行 RuoYi-Vue3 项目
基于SpringBootVue3前后端分离的Java快速开发框架 一、后端 1.git下载代码 RuoYi-Vue:RuoYi-Vue: 基于SpringBoot,Spring Security,JWT,Vue & Element 的前后端分离权限管理系统 git clone https://gitee.com/y_project/R…...
Spark 配置 YARN 模式
在大数据处理领域,Spark 是一个强大的分布式计算框架,而 YARN(Yet Another Resource Negotiator)则是 Hadoop 生态系统中出色的资源管理器。将 Spark 配置为 YARN 模式,可以充分利用 YARN 高效的资源管理和调度能力&am…...
【Linux学习笔记】进程替换和自定义shell
【Linux学习笔记】进程替换和自定义shell 🔥个人主页:大白的编程日记 🔥专栏:Linux学习笔记 文章目录 【Linux学习笔记】进程替换和自定义shell前言一.进程程序替换1.1 替换原理1.2 替换函数1.2.1函数解释1.2.2命名理解 二.自主…...
HarmonyOS运动开发:如何监听用户运动步数数据
前言 在开发运动类应用时,准确地监听和记录用户的运动步数是一项关键功能。HarmonyOS 提供了强大的传感器框架,使得开发者能够轻松地获取设备的运动数据。本文将深入探讨如何在 HarmonyOS 应用中实现步数监听功能,同时分享一些开发过程中的经验和技巧,帮助你更好地理解和实…...
Vuex状态管理及其持久化使用指南
1. 安装 Vuex Vue 2 或 Vue 3 项目: npm install vuex4 # Vuex 4 兼容 Vue 2 和 Vue 3 2. 创建 Store 新建 store/index.js: import { createStore } from vuex; import createPersistedState from vuex-persistedstate; // 持久化插件// 定义子模…...
k8s学习笔记
1查看默认命名空间pod kubectl get nodes 查看所有名空间的pod kubectl get pods -A 查看所有节点node kubectl get nodes 2根据配置文件,给集群创建资源 kubectl apply -f xxxx.yaml 3查看集群部署了哪些应用? kubectl get pods -A 4生成、删除、获取所…...
HarmonyOS ArkUI安全控件开发指南:粘贴、保存与位置控件的实现与隐私保护实践
目录 安全控件1. 粘贴控件1.1 约束与限制1.2 开发步骤 2. 保存控件2.1 约束与限制2.2 开发步骤 3. 位置控件3.1 约束与限制3.2 开发步骤 安全控件 安全控件是系统提供的一组系统实现的ArkUI组件,其中保存控件在用户首次使用时,会弹出通知弹窗࿰…...
前端在平常的开发中高度还原ui图的思考规范
问题原因 我们明明按照ui测量 为什么还会出现这么多样式偏差 根据我多次的验收反馈总结如下 使用组件库 不可能所以样式覆盖复制粘贴以前的代码 样式产生偏差有些样式是继承所得 难免疏漏图片等背景问题 或者切图问题padding等结尾问题 解决方案 组件库穿透 主要样式复制粘…...