基于千帆(ERNIE-Functions-8K)Function Calling的简单使用

1.Function Calling是什么?

1.1 概念

Function calling是一种将LLM(大语言模型Large language model)连接外部工具的能力,LLM经过微调后,可以检测出何时需要调用函数以及需要调用函数的方法名及参数,并返回给调用方以便调用外部函数,然后把调用外部函数的结果再传给LLM,最后LLM给出用户问题的最终响应。

1.2 流程图

下面是一张Function Calling工作的流程图,重点有3个:

  1. LLM会告诉我们需要调用什么工具函数以及调用工具函数的具体参数
  2. 调用函数发生在我们的系统里(一般使用反射实现),并把函数的执行情况加入到与LLM交互的历史会话中
  3. 会有多次跟LLM的交互过程
    在这里插入图片描述

2. Function Calling的作用?

序号作用内容应用场景
1扩展能力能允许LLM与外部函数或者服务交互,极大地扩展了LLM的能力数据库查询、数学计算、第三方API
2实时性与动态性可以使LLM获取实时的数据,突破LLM的训练数据静态知识的限制股票信息、天气预报
3任务自动化能实现任务的自动编排智能家具系统中灯光、温度、湿度等设备的自动调节
4自然语言到标准化的转化能识别出需要用到的外部函数和参数,完成自然语言到函数和参数的转换我想知道上海的天气? -> {‘name’: ‘get_city_weather’, ‘arguments’: ‘{“city”: “上海市”}’}
5业务逻辑集成能让LLM的智能处理能力与企业的业务逻辑紧密结合一家客服公司基于现有的知识沉淀来做智能客服

基于Function Calling的上述作用,能极大地推进AI在产品形态上的落地,以往我们总希望AI是无所不能的,期望能给它很少的输入,能完成很复杂的事情,但结果往往事与愿违,这样很难得到我们想要的结果。其实应该把复杂的任务拆得足够简单,每次给到AI清晰的prompt,完成单次的任务,最后所有任务完成了再给出最终的响应,这其实就是Function Call的工作机制,而LLM在其中扮演了任务拆分的重要工作。

特别是在已有系统的基础上,利用已有系统的API作为外部函数,让LLM来实现任务拆分和编排,就能很快地让AI为我们业务赋能。

3. Function Calling怎么用?

很多大模型都在微调之后支持了Function Calling,下面是基于百度千帆的ERNIE-Functions-8K的具体实现

注意:ERNIE-Functions-8K的function calling的能力只有python版本的SDK,没有其他语言的。如果python基础好的,可以直接看官方文档,不过官方文档里没有给出调用函数的过程。

3.1 单函数调用

接下来,我们用Python代码看如何实现用户的问题:“请帮我查询一下数据库中用python撰写的代码文件数量”

3.1.1 首先定义函数get_file_num

如下所示,主要就是通过指定语言获取文件数量,这个函数主要是在LLM给予我们返回参数后调用的

def get_file_num(params: dict) -> str:"""获取数据库中指定语言的代码文件数量"""language_low = params.get('language', '').lower()language_map = {"c/c++": 35,"java": 10,"javascript": 25,"python": 35,"go": 32,}return str(language_map.get(language_low, 0))

3.1.2 定义函数Schema

为了让LLM知道什么时候需要调用函数,以及调用函数名及参数,我们需要定义函数的schema传给LLM

# 定义函数schema
func_list = [{"name": "get_file_num",  # 函数名称"description": "获取内部数据库中以某一编程语言编写的文件数量",  # 函数描述"parameters": {"type": "object","properties": {  # 参数schema,如果参数为空,设为空字典即可"language": {  # 参数名称"type": "string",  # 参数类型"description": "代码所运用的编程语言,例如:python、c/c++、go、java"  # 参数描述}},"required": ["language"]  # 必填参数(无默认值)}
}]

3.1.3 第一次调用LLM接口

可以看到,我们把用户的问题(msgs)和函数schema(funclist)传给了LLM

    chat_comp = qianfan.Function()msgs = qianfan.QfMessages()msgs.append(query, role='user')resp = chat_comp.do(messages=msgs,functions=func_list)

上述是千帆的SDK,实际调用LLM的Prompt如下:

        {"role": "user","content": "接下来的所有对话中,你可以使用外部的工具来回答问题。\n你必须按照规定的格式来使用工具,当你使用工具时,我会在下一轮对话给你工具调用结果,然后你应该根据实际结果判断是否需要进一步使用工具,或给出你的回答。\n工具可能有多个,每个工具由名称、描述、参数组成,参数符合标准的json schema。\n\n下面是工具列表:\n名称:get_file_num\n描述:获取内部数据库中以某一编程语言编写的文件数量\n参数:{\"type\": \"object\", \"properties\": {\"language\": {\"type\": \"string\", \"description\": \"代码所运用的编程语言,例如:python、c/c++、go、java\"}}, \"required\": [\"language\"]}\n\n\n如果你需要使用外部工具,那么你的输出必须按照如下格式,只包含2行,不需要输出任何解释或其他无关内容:\nAction: 使用的工具名称\nAction Input: 使用工具的参数,json格式\n\n如果你不需要使用外部工具,不需要输出Action和Action Input,请输出你的回答。\n你的问题:请帮我查询一下数据库中用python撰写的代码文件数量"}

可以看出,整体是由:千帆system_prompt + 函数schema + 用户问题

3.1.4 解析第一次调用结果

第一次调用LLM,LLM发现要解决用户的问题,需要调用函数,则会返回如下的格式:

 {"id": "as-09dj7y7hzz","object": "chat.completion","created": 1736684991,"result": "","is_truncated": false,"need_clear_history": false,"finish_reason": "normal","usage": {"prompt_tokens": 226,"completion_tokens": 17,"total_tokens": 243},"function_call": {"name": "get_file_num","arguments": "{\"language\": \"python\"}"}
}

千帆会把需要调用的函数和参数放到function_call中返回,所以我们用如下代码获得函数及调用的参数:

func_call_result = resp['body'].get('function_call')

3.1.5 调用函数获得结果

如下所示,我们获取需要调用的函数和参数,然后使用globals()反射的方式使用"{“language”: “python”}"作为参数调用函数get_file_num函数得到结果func_resp

    if func_call_result:# 获取函数名称、入参func_name = func_call_result["name"]func_param = json.loads(func_call_result["arguments"])func_resp = None# 使用 globals() 动态调用函数if func_name in globals() and callable(globals()[func_name]):func_resp = globals()[func_name](func_param)print("函数" + func_name + "执行结果:"+ func_resp)else:print(f"Function {func_name} does not exist or is not callable.")

3.1.6 把执行结果和调用返回添加到历史会话

我们执行函数得到结果后,需要把执行结果和调用函数名以及参数添加到历史返回中,方便下次调用LLM接口

      # 将函数返回值转换成json字符串func_content = json.dumps({"return": func_resp})# 添加到历史对话记录msgs.append(resp, role="assistant")msgs.append(func_content, role="function")

上述代码实际上是把下面的内容加入到了历史会话:

        {"role": "assistant","content": "Action: get_file_num\\nAction Input: {\"language\": \"python\"}"},{"role": "user","content": "{\"return\": \"35\"}"}

3.1.7 第二次调用LLM接口

我们得到函数的执行结果后,再次调用LLM接口

        # 再次调用chat_completionsecond_resp = chat_comp.do(messages=msgs,functions=func_list)

这里要注意,还需要把函数schema和历史会话、用户问题一起发送给LLM,实际的prompt如下:

{"role": "user","content": "接下来的所有对话中,你可以使用外部的工具来回答问题。\n你必须按照规定的格式来使用工具,当你使用工具时,我会在下一轮对话给你工具调用结果,然后你应该根据实际结果判断是否需要进一步使用工具,或给出你的回答。\n工具可能有多个,每个工具由名称、描述、参数组成,参数符合标准的json schema。\n\n下面是工具列表:\n名称:get_file_num\n描述:获取内部数据库中以某一编程语言编写的文件数量\n参数:{\"type\": \"object\", \"properties\": {\"language\": {\"type\": \"string\", \"description\": \"代码所运用的编程语言,例如:python、c/c++、go、java\"}}, \"required\": [\"language\"]}\n\n\n如果你需要使用外部工具,那么你的输出必须按照如下格式,只包含2行,不需要输出任何解释或其他无关内容:\nAction: 使用的工具名称\nAction Input: 使用工具的参数,json格式\n\n如果你不需要使用外部工具,不需要输出Action和Action Input,请输出你的回答。\n你的问题:请帮我查询一下数据库中用python撰写的代码文件数量"},{"role": "assistant","content": "Action: get_file_num\\nAction Input: {\"language\": \"python\"}"},{"role": "user","content": "{\"return\": \"35\"}"}

3.1.8 得到最后的结果

这时LLM发现不需要再调用函数而能直接得出结果,并且把结果放到了result中,具体格式如下:

 {"id": "as-10dw9zdfic","object": "chat.completion","created": 1736684992,"result": "数据库中用Python撰写的代码文件数量为35。","is_truncated": false,"need_clear_history": false,"finish_reason": "normal","usage": {"prompt_tokens": 252,"completion_tokens": 10,"total_tokens": 262}
}

可以看到,这时候没有function_call的字段了,我们只要直接解析result返回就可以:

        # 得到答案return second_resp['body']['result']

3.1.9 完整的Python代码

import os
import qianfan
import jsonos.environ["QIANFAN_AK"] = os.getenv("QIANFAN_API_KEY")
os.environ["QIANFAN_SK"] = os.getenv("QIANFAN_SECRET_KEY")# 定义可被调用函数
def get_file_num(params: dict) -> str:"""获取数据库中指定语言的代码文件数量"""language_low = params.get('language', '').lower()language_map = {"c/c++": 35,"java": 10,"javascript": 25,"python": 35,"go": 32,}return str(language_map.get(language_low, 0))# 定义函数schema
func_list = [{"name": "get_file_num",  # 函数名称"description": "获取内部数据库中以某一编程语言编写的文件数量",  # 函数描述"parameters": {"type": "object","properties": {  # 参数schema,如果参数为空,设为空字典即可"language": {  # 参数名称"type": "string",  # 参数类型"description": "代码所运用的编程语言,例如:python、c/c++、go、java"  # 参数描述}},"required": ["language"]  # 必填参数(无默认值)}
}]def ai_querying_data(query : str) -> str:"""AI查询数据"""#QianFan Function is an agent for calling QianFan ChatCompletion with function call API.chat_comp = qianfan.Function()msgs = qianfan.QfMessages()msgs.append(query, role='user')# 第一次调用,目的是获取需要调用的方法和参数print("第一次调用msgs:", msgs._msg_list)resp = chat_comp.do(messages=msgs,functions=func_list)print("第一次调用真实的请求:\n", json.dumps(resp.request.json_body, indent=4, ensure_ascii=False))print("第一次调用返回的 resp.body:\n", json.dumps(resp['body'], indent=4))func_call_result = resp['body'].get('function_call')print("function_call:", func_call_result)# 如果存在function_call,则直接实际方法的调用print("******"*100)if func_call_result:# 获取函数名称、入参func_name = func_call_result["name"]func_param = json.loads(func_call_result["arguments"])func_resp = None# 使用 globals() 动态调用函数if func_name in globals() and callable(globals()[func_name]):func_resp = globals()[func_name](func_param)print("函数" + func_name + "执行结果:"+ func_resp)else:print(f"Function {func_name} does not exist or is not callable.")# 将函数返回值转换成json字符串func_content = json.dumps({"return": func_resp})# 添加到历史对话记录msgs.append(resp, role="assistant")msgs.append(func_content, role="function")# 第二次调用,目的是获取最后的结果print("第二次调用msgs:", msgs._msg_list)# 再次调用chat_completionsecond_resp = chat_comp.do(messages=msgs,functions=func_list)print("第二次调用真实的请求:\n", json.dumps(second_resp.request.json_body, indent=4, ensure_ascii=False))print("第二次调用返回的 resp.body:\n", json.dumps(second_resp['body'], indent=4, ensure_ascii=False))# 得到答案return second_resp['body']['result']if __name__ == '__main__':#query = "请帮我查询一下数据库中用java撰写的代码文件数量"query = "请帮我查询一下数据库中用python撰写的代码文件数量"print("最终结果:", ai_querying_data(query))

3.1.10 流程图

在这里插入图片描述

3.2 多函数调用

当LLM发现要解决用户的问题,还需要继续用到工具,则会再次返回用户function_call,也就是下面绿色部分可能会执行多次。
在这里插入图片描述
我们举例说明下,比如我们的问题变成了:请帮我查询一下数据库中用java撰写的代码文件数量,并且查询哈尔滨的天气

3.2.1 查询天气函数

这时候发现只依赖之前的get_file_num方法不能完全解决这个问题,所以我们先增加一个查询天气的函数:

def get_city_weather(params: dict) -> str:"""获取指定城市的天气"""city_weather_map = {"上海": 15,"北京": 10,"广州": 25,"深圳": 28,"哈尔滨": 5}return str(city_weather_map.get(params.get('city', ''), 0))

3.2.2 函数schema

这时候把上述get_city_weather也在函数schema中声明:


# 定义函数schema
func_list = [{"name": "get_file_num",  # 函数名称"description": "获取内部数据库中以某一编程语言编写的文件数量",  # 函数描述"parameters": {"type": "object","properties": {  # 参数schema,如果参数为空,设为空字典即可"language": {  # 参数名称"type": "string",  # 参数类型"description": "代码所运用的编程语言,例如:python、c/c++、go、java"  # 参数描述}},"required": ["language"]  # 必填参数(无默认值)}},{"name": "get_city_weather",  # 函数名称"description": "获取指定城市的天气",  # 函数描述"parameters": {"type": "object","properties": {  # 参数schema,如果参数为空,设为空字典即可"city": {  # 参数名称"type": "string",  # 参数类型"description": "需要查询的城市,例如:上海、北京、深圳、广州,最后不要带市"  # 参数描述}},"required": ["city"]  # 必填参数(无默认值)}}
]

3.2.3 循环调用

因为这时候LLM会不止一次返回给我们需要调用外部函数,所以我们要利用如果返回的内容包括function_call则就一直循环调用返回的函数,并把执行结果加入到历史的会话以便下次调用LLM接口,直到返回的结果内容不包含funcion_call,直接获取result作为最终的相应。


def ai_querying_data(query: str) -> str:"""AI查询数据"""chat_comp = qianfan.Function()msgs = qianfan.QfMessages()msgs.append(query, role='user')# 开始循环调用 chat_comp.dowhile True:resp = chat_comp.do(messages=msgs, functions=func_list)func_call_result = resp['body'].get('function_call')# 检查是否有 function_call,如果没有则跳出循环if not func_call_result:break# 调用指定的函数func_name = func_call_result["name"]func_param = json.loads(func_call_result["arguments"])func_resp = Noneif func_name in globals() and callable(globals()[func_name]):func_resp = globals()[func_name](func_param)else:# 如果函数不存在,可以选择退出循环或处理错误break# 将函数返回值转换为 JSON 字符串,并更新对话记录func_content = json.dumps({"return": func_resp})msgs.append(resp, role="assistant")msgs.append(func_content, role="function")i = i + 1# 循环结束后,获取并打印最终结果result = resp['body'].get('result')if result:return resultelse:print("未获取到最终结果。")return None

3.2.4 完整代码

import os
import qianfan
import jsonos.environ["QIANFAN_AK"] = os.getenv("QIANFAN_API_KEY")
os.environ["QIANFAN_SK"] = os.getenv("QIANFAN_SECRET_KEY")# 定义可被调用函数
def get_file_num(params: dict) -> str:"""获取数据库中指定语言的代码文件数量"""language_low = params.get('language', '').lower()language_map = {"c/c++": 35,"java": 10,"javascript": 25,"python": 35,"go": 32,}return str(language_map.get(language_low, 0))# 定义可被调用函数
def get_city_weather(params: dict) -> str:"""获取指定城市的天气"""city_weather_map = {"上海": 15,"北京": 10,"广州": 25,"深圳": 28,"哈尔滨": 5}return str(city_weather_map.get(params.get('city', ''), 0))# 定义函数schema
func_list = [{"name": "get_file_num",  # 函数名称"description": "获取内部数据库中以某一编程语言编写的文件数量",  # 函数描述"parameters": {"type": "object","properties": {  # 参数schema,如果参数为空,设为空字典即可"language": {  # 参数名称"type": "string",  # 参数类型"description": "代码所运用的编程语言,例如:python、c/c++、go、java"  # 参数描述}},"required": ["language"]  # 必填参数(无默认值)}},{"name": "get_city_weather",  # 函数名称"description": "获取指定城市的天气",  # 函数描述"parameters": {"type": "object","properties": {  # 参数schema,如果参数为空,设为空字典即可"city": {  # 参数名称"type": "string",  # 参数类型"description": "需要查询的城市,例如:上海、北京、深圳、广州,最后不要带市"  # 参数描述}},"required": ["city"]  # 必填参数(无默认值)}}
]def ai_querying_data(query: str) -> str:"""AI查询数据"""chat_comp = qianfan.Function()msgs = qianfan.QfMessages()msgs.append(query, role='user')# 开始循环调用 chat_comp.doi = 1;while True:print("第" + str(i) + "次调用msgs:", msgs._msg_list)resp = chat_comp.do(messages=msgs, functions=func_list)func_call_result = resp['body'].get('function_call')print("第" + str(i) + "次调用真实的请求:\n", json.dumps(resp.request.json_body, indent=4, ensure_ascii=False))print("第" + str(i) + "次调用返回的 resp.body:\n", json.dumps(resp['body'], indent=4, ensure_ascii=False))print("第" + str(i) + "次function_call:", func_call_result)# 检查是否有 function_call,如果没有则跳出循环if not func_call_result:break# 调用指定的函数func_name = func_call_result["name"]func_param = json.loads(func_call_result["arguments"])func_resp = Noneif func_name in globals() and callable(globals()[func_name]):func_resp = globals()[func_name](func_param)else:print(f"Function {func_name} does not exist or is not callable.")# 如果函数不存在,可以选择退出循环或处理错误break# 将函数返回值转换为 JSON 字符串,并更新对话记录func_content = json.dumps({"return": func_resp})msgs.append(resp, role="assistant")msgs.append(func_content, role="function")i = i + 1# 循环结束后,获取并打印最终结果result = resp['body'].get('result')if result:return resultelse:print("未获取到最终结果。")return Noneif __name__ == '__main__':query = "请帮我查询一下数据库中用java撰写的代码文件数量,并且查询哈尔滨的天气"print("最终结果:", ai_querying_data(query))

4. 需要注意的点

序号问题内容
1性能复杂任务会存在多次外部工具调用,只能串行调用
2程序健壮性调度交给了LLM,可能会死循环,记得加中止条件

5. 总结

Function Calling极大地扩展了LLM的能力,特别是在LLM在实际业务场景落地时能发挥巨大作用,比如把系统不同模块的API(权限、客服、主业务、数据库的知识沉淀、第三方API等)定义到函数schema中,以智能机器人的方式向外提供服务,用户只需要输入自然语言,系统就能自动匹配出用户需要使用的模块API,并完成任务编排,最后直接给到用户最终的答案。

欢迎留言说出AI在你们业务场景中的应用,一起讨论!

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

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

相关文章

Deep Attentional Guided Image Filtering

This work achieved the first place in the real depth map SR challenge held in ACM ICMR 2021. 目的:从一个target image(如低分辨的深度图)和guidance image(如高分辨的RGB图),得到输出(如高分辨的深度图)。 主要思想是考虑了target和guidance之间的…

SpringBoot+Lombok项目实体属性名xXxx格式,前端接收不到

问题解析 今天发现后端传给前端的实体类中,有属性为xXxxx格式的,前端也使用相同名称接收,结果却不显示值!研究了一会发现接口请求回来后,原xXxxx的属性名,会被转为全小写。具体原因为:使用Lombo…

高级运维:shell练习2

1、需求:判断192.168.1.0/24网络中,当前在线的ip有哪些,并编写脚本打印出来。 vim check.sh #!/bin/bash# 定义网络前缀 network_prefix"192.168.1"# 循环遍历1-254的IP for i in {1..254}; do# 构造完整的IP地址ip"$network_…

为深度学习创建PyTorch张量 - 最佳选项

为深度学习创建PyTorch张量 - 最佳选项 正如我们所看到的,PyTorch张量是torch.Tensor​ PyTorch类的实例。张量的抽象概念与PyTorch张量之间的区别在于,PyTorch张量为我们提供了一个可以在代码中操作的具体实现。 在上一篇文章中,我们看到了…

28.找出字符串中第一个匹配项的下标【力扣】KMP前缀表 ≈ find() 函数、暴力解法

class Solution { public: //得到前缀表void getNext(int *next,string needle){int j0;for(int i1;i<needle.size();i){while(j>0 && needle[j]!needle[i]) jnext[j-1];//**j>0**>j0是出口if(needle[i]needle[j]) j;next[i]j;//若写入if中&#xff0c;则该…

mysql 创建临时表报错

1. 问题描述 5.7.31 版本 mysql 数据库创建临时表报错 -- 报错语句 CREATE TEMPORARY TABLE temp_table_name SELECT * FROM table_name LIMIT 0;报错截图 [HY000][1005] Can’t create table ‘new_tbl’ (errno: 13) 2. 解决方案 步骤一&#xff1a; 查询 linux m…

使用 WPF 和 C# 将纹理应用于三角形

此示例展示了如何将纹理应用于三角形,以使场景比覆盖纯色的场景更逼真。以下是为三角形添加纹理的基本步骤。 创建一个MeshGeometry3D对象。像往常一样定义三角形的点和法线。通过向网格的TextureCoordinates集合添加值来设置三角形的纹理坐标。创建一个使用想要显示的纹理的 …

mac homebrew配置使用

本文介绍mac上homebrew工具的安装、配置过程。homebrew功能类似于centos的yum&#xff0c;用于软件包的管理&#xff0c;使用上有命令的差异。 本次配置过程使用mac&#xff0c;看官方文档&#xff0c;在linux上也可以用&#xff0c;但我没试过&#xff0c;有兴趣的同学可以试试…

OpenCV基础:矩阵的创建、检索与赋值

本文主要是介绍如何使用numpy进行矩阵的创建&#xff0c;以及从矩阵中读取数据&#xff0c;修改矩阵数据。 创建矩阵 import numpy as npa np.array([1,2,3]) b np.array([[1,2,3],[4,5,6]]) #print(a) #print(b)# 创建全0数组 eros矩阵 c np.zeros((8,8), np.uint8) #prin…

Unreal Engine 5 (UE5) Metahuman 的头部材质

在图中&#xff0c;你展示了 Unreal Engine 5 (UE5) Metahuman 的头部材质部分&#xff0c;列出了头部材质的多个元素。以下是对每个部分的解释&#xff1a; 材质解释 Element 0 - MI_HeadSynthesized_Baked 作用&#xff1a; 这是 Metahuman 的主要头部材质&#xff0c;控制整…

鸿蒙中自定义slider实现字体大小变化

ui&#xff1a; import { display, mediaquery, router } from kit.ArkUI import CommonConstants from ./CommonConstants; import PreferencesUtil from ./PreferencesUtil; import StyleConstants from ./StyleConstants;// 字体大小 Entry Component struct FontSize {Sta…

LabVIEW智能水肥一体灌溉控制系统

本文详细介绍了一种基于LabVIEW的智能水肥一体灌溉控制系统的设计与实现。该系统采用模糊控制策略&#xff0c;能够自动调节土壤湿度和肥液浓度&#xff0c;满足不同作物在不同生长阶段的需求&#xff0c;有效提高水肥利用效率&#xff0c;对现代精准农业具有重要的实践和推广价…

【高可用自动化体系】自动化体系

架构设计的愿景就是高可用、高性能、高扩展、高效率。为了实现架构设计四高愿景&#xff0c;需要实现自动化系统目标&#xff1a; 标准化。 流程自助化。 可视化&#xff1a;可观测系统各项指标、包括全链路跟踪。 自动化&#xff1a;ci/cd 自动化部署。 精细化&#xff1a…

Docker与虚拟机的区别及常用指令详解

在现代软件开发中&#xff0c;容器化和虚拟化技术已经成为不可或缺的工具。Docker和虚拟机&#xff08;VM&#xff09;是两种常见的技术&#xff0c; 它们都可以帮助开发者在不同的环境中运行应用程序。然而&#xff0c;它们的工作原理和使用场景有很大的不同。本文将详细探讨D…

【JVM-2.3】深入解析JVisualVM:Java性能监控与调优利器

在Java应用的开发和运维过程中&#xff0c;性能监控与调优是不可或缺的环节。无论是排查内存泄漏、分析CPU瓶颈&#xff0c;还是优化线程使用&#xff0c;开发者都需要借助一些强大的工具来辅助诊断。JVisualVM 正是这样一款由Oracle提供的免费工具&#xff0c;它集成了多种性能…

简聊MySQL并发事务中幻读、虚读问题的解决方案

在MySQL数据库中&#xff0c;事务的幻读和虚读问题是并发控制中的关键挑战。以下是针对这两个问题的解决方案及原理说明&#xff0c;并附上相关示例。 一、幻读问题及其解决方案 幻读问题的定义 幻读是指一个事务在前后两次查询同一个范围的时候&#xff0c;后一次查询看到了…

WINFORM - DevExpress -> gridcontrol ---->控件(ColumnEdit控件)

ImageComboBoxEdit--带图片的下拉菜单 DevExpress&#xff1a;带图片的下拉菜单ImageComboBoxEdit_weixin_34313182的博客-CSDN博客 ImageEdit--图片按钮 DevExpress控件中的gridcontrol表格控件&#xff0c;如何在属性中设置某一列显示为图片&#xff08;图片按钮&#xff…

IntelliJ IDEA Type Hierarchy Scope Pattern 学习指南

IntelliJ IDEA Type Hierarchy Scope Pattern 学习指南 什么是 Type Hierarchy&#xff1f; Type Hierarchy 是 IntelliJ IDEA 提供的一个工具&#xff0c;允许开发者查看某个类的继承关系及其实现的接口结构。它是理解类关系的重要工具&#xff0c;尤其在处理复杂的继承体系…

分布式ID的实现方案

1. 什么是分布式ID ​ 对于低访问量的系统来说&#xff0c;无需对数据库进行分库分表&#xff0c;单库单表完全可以应对&#xff0c;但是随着系统访问量的上升&#xff0c;单表单库的访问压力逐渐增大&#xff0c;这时候就需要采用分库分表的方案&#xff0c;来缓解压力。 ​…

Python爬虫-汽车之家各车系周销量榜数据

前言 本文是该专栏的第43篇,后面会持续分享python爬虫干货知识,记得关注。 在本专栏之前,笔者在文章《Python爬虫-汽车之家各车系月销量榜数据》中,有详细介绍,如何爬取“各车系车型的月销量榜单数据”的方法以及完整代码教学教程。 而本文,笔者同样以汽车之家平台为例,…