大模型开发(十二):Function calling 流程优化并实现多轮对话任务

全文共1w余字,预计阅读时间约25~40分钟 | 满满干货(附代码案例),建议收藏!

本文目标:围绕Chat模型的Function calling功能进行更高层次的函数封装,并实现一个能够调用外部函数的多轮对话任务

写在前面:本文内容的复现过程,如果有条件的,建议使用gpt 4接口,输出稳定,gpt3.5不太稳定,但运行几次也能得到标准结果

如果存在Rate limit 报错,是OpenAI的速率限制,可以绑定信用卡后解除,以保证程序正常运行

image-20230725102104269

代码下载地址

一、Function calling 流程优化思路

在大模型开发(十一):Chat Completions模型的Function calling功能详解中,已经详细解释了Function calling的用法,回顾一下其函数流程是这样的:

22

对于上述这种原始的Function calling实现流程而言,尽管过程清晰,但要完整跑通一个流程所涉及到的代码环节较多,在高频调用该功能的场景中,这个复杂的代码流程会极大程度影响使用和开发效率。因此,需要考虑如何对上述流程进行优化。

总的来说,优化的方向有两个

  1. 优化functions参数的编写效率

比如之前写到的这个外部函数:

def calculate_algorithm(data):"""该函数定义了一种特殊的数据集计算过程:param data: 必要参数,表示带入计算的数据表,用字符串进行表示:return:函数计算后的结果,返回结果为表示为JSON格式的Dataframe类型对象"""data = io.StringIO(data)df_new = pd.read_csv(data, sep='\s+', index_col=0)res = np.sum(df_new, axis=1) - 1return json.dumps(res.to_string())available_functions = {"calculate_algorithm": calculate_algorithm,}calculate_function = {"name": "calculate_algorithm","description": "用于执行计算算法的函数,定义了一种特殊的数据集计算过程","parameters": {"type": "object","properties": {"data": {"type": "string","description": "执行计算算法的数据集"},},"required": ["data"],},}functions = [calculate_function]
functions

image-20230724143509048

在手动实现Function calling功能这个过程中,将外部函数信息输入到functions参数中的过程非常复杂,需要涉及到大量的JSON Schema编写过程,并且,如果函数库中包含大量函数,逐个编写JSON Schema会非常复杂。

因此**找到一种方法,大幅降低Chat模型读取外部函数的门槛,是非常有必要的。**例如当编写完calculate_algorithm(计算函数)后模型就能让大语言模型自动识别这个函数并创建相应的functions参数,而不用手动对其进行编写。

2、优化second response流程

在做交互需求的开发中,其实并不关心向大模型提问时中间有几次调用模型的过程,只希望在一次对话中快速完成需求,即无论是否进行外部函数调用,都希望能够在一次代码交互过程中完成对话任务

因此完成对对话流程进行更高层次的封装,是非常有必要的。也就是说如果对话过程需要调用Function calling,能自动执行second response,并最终在一次对话中返回最终结果。

接下来就针对这两方面依次做一下优化。

二、优化一:自动编写函数

functions参数其实是非常一类高度结构化的参数,而参数中的核心信息其实都来源于函数本身:

image-20230724144255022

因此,functions参数的编写其实本质上就是一个翻译的过程,将函数原始的说明信息翻译成functions参数要求的格式。

而对于大语言模型(LLMs)来说,它对JSON Schema格式的是非常熟悉的:

response = openai.ChatCompletion.create(model="gpt-3.5-turbo-16k-0613",messages=[{"role": "user", "content": "请问什么是JSON Schema?"}]
)response.choices[0].message['content']

看下它的输出:

image-20230724144635875

能够看出,Chat模型是具备JSON Schema相关知识储备的。由此不难判断,只要详细的编写每个函数的函数说明,并且通过合理的提示让模型理解functions参数结构,同时借助模型本身对JSON Schema的理解,是能够让Chat模型自主读取并创建函数的functions参数的。

尝试实现一下:

  • Step 1:提取函数说明

首先通过inspect库中的getdoc方法来将函数说明提取为字符串,代码如下:

import inspectfunction_description = inspect.getdoc(calculate_algorithm)
function_description

看下提取结果:

image-20230724144912530

inspect库中包含了非常多用于辅助验证函数功能的函数,能够非常高效的执行获取函数参数、函数源码、函数说明等功能。

  • Step 2: 测试是否能将函数参数格式转化成JSON Schema类型
response = openai.ChatCompletion.create(model="gpt-3.5-turbo-16k-0613",messages=[{"role": "system", "content": "以下是计算函数的函数说明:%s" % function_description},{"role": "user", "content": "请帮我编写一个JSON Schema对象,用于说明计算函数的参数输入规范。输出结果要求是JSON Schema格式的JONS类型对象,不需要任何前后修饰语句。其中description部分请用中文"}]
)
response.choices[0].message['content']

看下输出结果:
image-20230724145330492

Chat函数的输出结果是JSON格式对象,通过json.loads方法将其转化为python对象,代码如下:

json.loads(response.choices[0].message['content'])

看下结果:

image-20230724145557294

对比手动编写的结果,其实模型能够根据函数的参数说明正确识别计算函数的参数格式,并输出对应的JSON Schema对象。

'additionalProperties’关键词表示不存在另一种输入格式,这两个关键词对所描述的对象结构类型并没有任何影响。

  • Step 3:编写自动编写functions参数函数

尝试通过合理的提示,让模型能够自动编写functions参数,以下是Chat模型提示过程,经测试gpt3.5使用Zero-shot输出极不稳定,所以使用Few-shot提示:

# 定义Few-shot提示
Q1_system_prompt = '以下是某函数说明:%s' % function_description
Q1_user_prompt = '根据这个函数的函数说明,请帮我创建一个JSON格式的字典,这个字典有如下5点要求:\1.字典总共有三个键值对;\2.第一个键值对的Key是字符串name,value是该函数的名字:%s,也是字符串;\3.第二个键值对的Key是字符串description,value是该函数的函数的功能说明,也是字符串;\4.第三个键值对的Key是字符串parameters,value是一个JSON Schema对象,用于说明该函数的参数输入规范。\5.输出结果必须是一个JSON格式的字典,且不需要任何前后修饰语句' % calculate_algorithm.__name__A1_system_prompt = "测试算法函数,该函数定义了一种特殊的数据集计算过程\n:param data: 必要参数,要求字符串类型,表示带入计算的数据表\n:return:测试函数计算后的结果,返回结果为json字符串类型对象"
A1_user_prompt = "{'name': 'testg_algorithm', \'description': '测试算法函数,该函数定义了一种特殊的数据集计算过程', \'parameters': {'title': '测试算法函数参数', \'type': 'object', \'properties': {'data': {'description': '字符串类型的数据表', 'type': 'string'}}, \'required': ['data']}}"system_prompt = '以下是某函数说明:%s' % function_description
user_prompt = '根据这个函数的函数说明,请帮我创建一个JSON格式的字典,这个字典有如下5点要求:\1.字典总共有三个键值对;\2.第一个键值对的Key是字符串name,value是该函数的名字:%s,也是字符串;\3.第二个键值对的Key是字符串description,value是该函数的函数的功能说明,也是字符串;\4.第三个键值对的Key是字符串parameters,value是一个JSON Schema对象,用于说明该函数的参数输入规范。\5.输出结果必须是一个JSON格式的字典,且不需要任何前后修饰语句' % calculate_algorithm.__name__

然后带入模型:

response = openai.ChatCompletion.create(model="gpt-3.5-turbo-16k-0613",messages=[{"role": "system", "content": system_prompt},{"role": "user", "content": user_prompt}]
)
response.choices[0].message['content']

来看下最终的结果:

image-20230725090528926

这个过程(按照格式输出文本语义理解的内容)对于大语言模型来说是非常简单的推理过程,在JSON Schema对象编写时,Few-shot效果会明显好于当前的Zero-shot过程。

  • Step 4:高级函数封装

对上述流程进行更高层次的封装,编写一个自动输出functions参数的函数,代码如下:

def auto_functions(functions_list):"""Chat模型的functions参数编写函数:param functions_list: 包含一个或者多个函数对象的列表;:return:满足Chat模型functions参数要求的functions对象"""def functions_generate(functions_list):# 创建空列表,用于保存每个函数的描述字典functions = []# 对每个外部函数进行循环for function in functions_list:# 读取函数对象的函数说明function_description = inspect.getdoc(function)# 读取函数的函数名字符串function_name = function.__name__Q1_system_prompt = '以下是某函数说明:%s' % function_descriptionQ1_user_prompt = '根据这个函数的函数说明,请帮我创建一个JSON格式的字典,这个字典有如下5点要求:\1.字典总共有三个键值对;\2.第一个键值对的Key是字符串name,value是该函数的名字:%s,也是字符串;\3.第二个键值对的Key是字符串description,value是该函数的函数的功能说明,也是字符串;\4.第三个键值对的Key是字符串parameters,value是一个JSON Schema对象,用于说明该函数的参数输入规范。\5.输出结果必须是一个JSON格式的字典,且不需要任何前后修饰语句' % calculate_algorithm.__name__A1_system_prompt = "测试算法函数,该函数定义了一种特殊的数据集计算过程\n:param data: 必要参数,要求字符串类型,表示带入计算的数据表\n:return:测试函数计算后的结果,返回结果为json字符串类型对象"A1_user_prompt = "{'name': 'testg_algorithm', \'description': '测试算法函数,该函数定义了一种特殊的数据集计算过程', \'parameters': {'title': '测试算法函数参数', \'type': 'object', \'properties': {'data': {'description': '字符串类型的数据表', 'type': 'string'}}, \'required': ['data']}}"system_prompt = '以下是某函数说明:%s' % function_descriptionuser_prompt = '根据这个函数的函数说明,请帮我创建一个JSON格式的字典,这个字典有如下5点要求:\1.字典总共有三个键值对;\2.第一个键值对的Key是字符串name,value是该函数的名字:%s,也是字符串;\3.第二个键值对的Key是字符串description,value是该函数的函数的功能说明,也是字符串;\4.第三个键值对的Key是字符串parameters,value是一个JSON Schema对象,类型为object,用于说明该函数的参数输入规范;\5.输出结果必须是一个JSON格式的字典,只输出这个字典即可,前后不需要任何前后修饰或说明的语句' % function_nameresponse = openai.ChatCompletion.create(model="gpt-3.5-turbo-16k-0613",messages=[{"role": "system", "content": "Q:" +  Q1_system_prompt + Q1_user_prompt + "A:" + A1_system_prompt + A1_user_prompt },{"role": "user", "content": 'Q:' + system_prompt + user_prompt}])functions.append(json.loads(response.choices[0].message['content']))return functionsmax_attempts = 3attempts = 0while attempts < max_attempts:try:functions = functions_generate(functions_list)break  # 如果代码成功执行,跳出循环except Exception as e:attempts += 1  # 增加尝试次数print("发生错误:", e)if attempts == max_attempts:print("已达到最大尝试次数,程序终止。")raise  # 重新引发最后一个异常else:print("正在重新运行...")return functions

上述代码把函数功能的主题封装在functions_generate这个内嵌函数中,然后外层函数主要控制报错时的处理流程:即如果函数执行时报错,三次内报错都会反复调用执行functions_generate,三次报错之后则会直接停止运行。这里之所以要设置多次报错仍然反复执行,是因为哪怕user_prompt中明确指出“只输出这个字典即可,前后不需要任何前后修饰或说明的语句”,但模型仍然可能会输出前后说明文字,此时是无法直接使用functions.append(json.loads(response.choices[0].message[‘content’]))来提取functions字典的,但这只是小概率事件,再次进行相同问题的提问,输出的结果大概率不会再包含前后修饰语句,functions_generate即可正常运行。

  • Step 5:单个外部函数带入测试

先提取到JSON Schema格式的描述

functions_list = [calculate_algorithm]functions = auto_functions(functions_list)functions

输出如下:

image-20230725090934690

接下来测试将其带入Chat模型,验证模型是否能顺利执行Function calling功能,代码如下:

# 创建一个DataFrame
df = pd.DataFrame({'x1':[1, 2], 'x2':[3, 4]})# 函数输出的结构都必须是字符串类型才能够被大模型正常的识别
df_str = df.to_string()df_str

image-20230724152911112

messages=[{"role": "system", "content": "数据集data:%s,数据集以字符串形式呈现" % df_str},{"role": "user", "content": "请在数据集data上执行计算算法"}
]response = openai.ChatCompletion.create(model="gpt-3.5-turbo-16k-0613",messages=messages,functions=functions,function_call="auto",  )
response["choices"][0]["message"]

看下输出:

image-20230724163107854

模型返回结果中存在function_call,则说明模型完成了外部模型的挑选,顺利执行了Function calling功能,也进而说明自动functions参数编写函数切实有效。

  • Step 6:多个外部函数带入测试

接下来进一步测试,当添加多个外部函数时,auto_functions函数能否顺利的依次翻译这些函数说明,并组成functions列表,同时在多个函数情况下,模型能否根据实际对话需求智能选择外部函数,代码如下:

def smallC_algorithm(data):"""smallC算法函数,该函数定义了一种特殊的数据集计算过程:param data: 必要参数,表示带入计算的数据表,用字符串进行表示:return:smallC函数计算后的结果,返回结果为表示为JSON格式的Dataframe类型对象"""df_new = pd.read_json(data)res = np.sum(df_new, axis=1) + 1return res.to_json(orient='records')functions_list = [calculate_algorithm, smallC_algorithm]functions = auto_functions(functions_list)
functions

看下输出结果:

image-20230725091228828

在添加了smallC算法之后,auto-functions函数能够顺利输出正确结果,并且模型也能够根据不同的提示,智能筛选外部函数。

三、优化二:编写自动应答函数

针对第二个优化点:即将Function calling执行时的多轮对话封装在一个函数中。这里涉及到外部函数库字典创建,该字典要求一个键值对代表一个函数,每个键值对的Key表示函数名字符串,对应的Value表示对应的函数。

  • Step 1:创建函数库字典

对于此前的functions_list里面包含的两个函数,可以使用如下方式创建这个函数库字典:

function_dict = {func.__name__: func for func in functions_list}
function_dict

看下输出:

image-20230724163833449

  • Step 2:构造函数
def run_conversation(messages, functions_list=None, model="gpt-3.5-turbo-16k-0613"):"""能够自动执行外部函数调用的Chat对话模型:param messages: 必要参数,字典类型,输入到Chat模型的messages参数对象:param functions_list: 可选参数,默认为None,可以设置为包含全部外部函数的列表对象:param model: Chat模型,可选参数,默认模型为gpt-3.5-turbo-16k-0613:return:Chat模型输出结果"""# 如果没有外部函数库,则执行普通的对话任务if functions_list == None:response = openai.ChatCompletion.create(model=model,messages=messages,)response_message = response["choices"][0]["message"]final_response = response_message["content"]# 若存在外部函数库,则需要灵活选取外部函数并进行回答else:# 创建functions对象functions = auto_functions(functions_list)# 创建外部函数库字典available_functions = {func.__name__: func for func in functions_list}# first responseresponse = openai.ChatCompletion.create(model=model,messages=messages,functions=functions,function_call="auto")response_message = response["choices"][0]["message"]# 判断返回结果是否存在function_call,即判断是否需要调用外部函数来回答问题if response_message.get("function_call"):# 需要调用外部函数# 获取函数名function_name = response_message["function_call"]["name"]# 获取函数对象fuction_to_call = available_functions[function_name]# 获取函数参数function_args = json.loads(response_message["function_call"]["arguments"])# 将函数参数输入到函数中,获取函数计算结果function_response = fuction_to_call(**function_args)# messages中拼接first response消息messages.append(response_message)  # messages中拼接函数输出结果messages.append({"role": "function","name": function_name,"content": function_response,})  # 第二次调用模型second_response = openai.ChatCompletion.create(model=model,messages=messages,)  # 获取最终结果final_response = second_response["choices"][0]["message"]["content"]else:final_response = response_message["content"]return final_response
  • Step 3:函数测试
messages = [{"role": "system", "content": "数据集data:%s,数据集以字符串形式呈现" % df_str},{"role": "user", "content": '请帮我介绍下data数据集'}]
run_conversation(messages = messages, functions_list = functions_list)

模型能够非常顺利的调用外部函数并围绕当前问题进行准确回答。至此,就完成了既定的Function calling功能执行过程的代码优化,通过借助run_conversation函数,只需设置messages和外部函数列表,即可让模型在回答时有选择性的选择外部函数进行回答,全程无需手动进行调整。

四、实现一个多轮对话函数

更进一步,在run_conversation基础之上,再封装一个可以执行多轮对话的函数,代码如下:

def chat_with_model(functions_list=None, prompt="你好呀", model="gpt-4-0613", system_message=[{"role": "system", "content": "你是以为乐于助人的助手。"}]):messages = system_messagemessages.append({"role": "user", "content": prompt})while True:           answer = run_conversation(messages=messages, functions_list=functions_list, model=model)print(f"模型回答: {answer}")# 询问用户是否还有其他问题user_input = input("您还有其他问题吗?(输入退出以结束对话): ")if user_input == "退出":break# 记录用户回答messages.append({"role": "user", "content": user_input})

可以通过传入functions_list测试是否可以调用外部函数。

五、总结

本文首先概述了Function calling流程的优化思路,接着分别详细介绍了两种主要的优化方法:自动编写函数和编写自动应答函数。这两种优化方法可以显著提高Function calling的效率和实用性。最后,演示了如何实现一个多轮对话函数。

最后,感谢您阅读这篇文章!如果您觉得有所收获,别忘了点赞、收藏并关注我,这是我持续创作的动力。您有任何问题或建议,都可以在评论区留言,我会尽力回答并接受您的反馈。如果您希望了解某个特定主题,也欢迎告诉我,我会乐于创作与之相关的文章。谢谢您的支持,期待与您共同成长!

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

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

相关文章

华为数通HCIA-数通网络基础

基础概念 通信&#xff1a;两个实体之间进行信息交流 数据通信&#xff1a;网络设备之间进行的通信 计算机网络&#xff1a;实现网络设备之间进行数据通信的媒介 园区网络&#xff08;企业网络&#xff09;/私网/内网&#xff1a;用于实现园区内部互通&#xff0c;并且需要部…

WAF/Web应用安全(拦截恶意非法请求)

Web 应用防火墙&#xff08;Web Application Firewall&#xff0c; WAF&#xff09;通过对 HTTP(S) 请求进行检测&#xff0c;识别并阻断 SQL 注入、跨站脚本攻击、跨站请求伪造等攻击&#xff0c;保护 Web 服务安全稳定。 Web 安全是所有互联网应用必须具备的功能&#xff0c…

Python实现抽象工厂模式

抽象工厂模式是一种创建型设计模式&#xff0c;用于创建一系列相关或依赖对象的家族&#xff0c;而无需指定具体类。在Python中&#xff0c;可以通过类和接口的组合来实现抽象工厂模式。 下面是一个简单的Python实现抽象工厂模式的示例&#xff1a; # 抽象产品接口 class Abs…

【前端知识】React 基础巩固(三十二)——Redux的三大原则、使用流程及实践

React 基础巩固(三十二)——Redux的三大原则 一、Redux的三大原则 单一数据源 整个应用程序的state被存储在一颗object tree 中&#xff0c;并且这个object tree 只存储在一个store中&#xff1b;Redux并没有强制让我们不能创建多个Store&#xff0c;但是那样做不利于数据维护…

OpenHarmony与HarmonyOS联系与区别

目录 1. 背景 2.OpenHarmony 3.HarmonyOS 4.鸿蒙生态 5.OpenHarmony与HarmonyOS的技术上实现区别 1.语言支持 2.SDK 的不同 3.运行调测方式不同 4.对APK的兼容性不同 5.包含关系 6.调试命令 6.何时选择OpenHarmony或是HarmonyOS&#xff1f; 1. 背景 开篇就说“关于…

RabbitMQ集群搭建

说明&#xff1a;集群&#xff0c;不管是Redis集群&#xff0c;还是MQ集群&#xff0c;都是为了提高系统的可用性&#xff0c;使系统不至于因为Redis、MQ宕机而崩溃。本文介绍RabbitMQ集群搭建&#xff0c;RabbitMQ集群分为以下三类&#xff1a; 普通集群 镜像集群 仲裁队列 …

C语言中指针(简略复习)

一、指针 1. 指针运算 设p为指针&#xff0c;T为类型&#xff0c;n为整型数字&#xff0c;则pn表示&#xff0c;指针从当前位置向后移动n个数据单位&#xff0c;而不是n个字节。 数据类型不同&#xff0c;移动的数据单位字节数也不同&#xff0c;实际上移动的字节数为n*sizeo…

3DVR全景旅游,最新数字化智慧文旅

导语&#xff1a; 随着科技的飞速发展&#xff0c;3DVR全景旅游正以其独特的特点和无限的优势&#xff0c;成为当今智慧文旅的领航者。穿戴上VR设备&#xff0c;只需一个轻轻的点击&#xff0c;你将被带入一个全新的数字世界&#xff0c;领略美景、探索奇迹。让我们一起深入了…

探索容器镜像安全管理之道

邓宇星&#xff0c;Rancher 中国软件架构师&#xff0c;7 年云原生领域经验&#xff0c;参与 Rancher 1.x 到 Rancher 2.x 版本迭代变化&#xff0c;目前负责 Rancher for openEuler(RFO)项目开发。 最近 Rancher v2.7.4 发布了&#xff0c;作为一个安全更新版本&#xff0c;也…

【Spring MVC】Spring MVC的功能使用和相关注解介绍

Spring MVC主要有三个功能&#xff1a; 连接获取参数输出数据 对于 Spring MVC 来说&#xff0c;掌握了以上 3 个功能就相当于掌握了Spring MVC。 1.连接 连接的功能&#xff1a;将⽤户&#xff08;浏览器&#xff09;和 Java 程序连接起来&#xff0c;也就是访问⼀个地址能…

基于YOLOv5的WiderFace人脸检测检测系统(PyTorch+Pyside6+YOLOv5模型)

摘要&#xff1a;基于YOLOv5的WiderFace人脸检测系统可用于日常生活中检测与定位人脸目标&#xff0c;利用深度学习算法可实现图片、视频、摄像头等方式的人脸目标检测识别&#xff0c;另外支持结果可视化与图片或视频检测结果的导出。本系统采用YOLOv5目标检测模型训练数据集&…

css终极方案PostCSS

一见如故 原理 所有的css框架都在一样的事&#xff0c;那就是由一个css生成一个新的css&#xff0c;那么postcss就来做了一个抽离&#xff1a; 1、将原有的css解析成抽象语法树 2、中间经过若干个插件 3、重新文本化&#xff0c;形成新的css postcss.config.js module.expor…

【C++】容器对象作为函数参数传递时,如何保证外部容器对象不被修改(以vector为例)

几种传参方式简单对比 传值 1.1 参数形式&#xff1a;void fun(vector<int> v); 1.2 函数调用&#xff1a;fun(v); 1.3 函数内使用&#xff1a;cout << v[1]; 1.4 是否可以改变函数外对象的值&#xff1a;否 1.5 是否会调用拷贝构造函数&#xff1a;是传指针 2.1 …

快速了解新一轮Moonbeam Grants申请提案

随着Moonbeam Grant第二期计划的发布&#xff0c;超过12个项目同时提交了生态Grant申请的提案。任何大于25万枚GLMR Grant的申请都将会要求项目在Moonbeam社区治理论坛上发布Grant提案&#xff0c;内容包含项目概览、申请金额、Grant使用方案以及背后的原因等等。 Grant的发放…

【软件架构】企业架构4A定义

文章目录 前言战略、BA、DA、AA、TA五者的关系1、业务架构&#xff08;BA&#xff09;2、数据架构&#xff08;DA&#xff09;3、应用架构&#xff08;AA&#xff09;4、技术架构&#xff08;TA&#xff09;总结 前言 业务架构是跨系统的业务架构蓝图&#xff0c;应用架构、数…

Redis

介绍 redis是内存数据结构存储&#xff0c;可用作数据库、缓存、消息代理和流引擎。 提供的数据结构有&#xff1a;String、hash、lists、sets、sorted sets、bitmaps、hyperloglogs、streams 字符串、哈希、列表、集、带有范围查询的排序集、位图、超日志日志、地理空间索引…

【每日运维】RockyLinux8非容器化安装Mysql、Redis、RabitMQ单机环境

系统版本&#xff1a;RockyLinux 8.6 安装方式&#xff1a;非容器化单机部署 安装版本&#xff1a;mysql 8.0.32 redis 6.2.11 rabbitmq 3.11.11 elasticsearch 6.7.1 前置条件&#xff1a;时间同步、关闭selinux、主机名、主机解析host 环境说明&#xff1a;PC电脑VMware Work…

互联网医院系统|互联网医院软件开发|互联网医院搭建方案

随着互联网技术的发展&#xff0c;互联网医院系统逐渐成为医疗服务的新模式&#xff0c;为患者和医生提供了更加方便和高效的医疗体验。下面将介绍互联网医院系统的功能优势。   在线挂号和预约&#xff1a;互联网医院系统可以提供在线挂号和预约功能&#xff0c;患者可以通过…

C++_01_初步认识C++语言

本人博客园亦可见 一、认识 “C语言” 一、首先聊聊什么是语言&#xff1f; 语言是一套具有“语法”、“词法”规律的系统&#xff0c;是思维的工具。   计算程序设计语言是计算机可以识别的语言&#xff0c;用于描述解决问题的方法&#xff0c;供计算机阅读和执行。 语言由…

vue中实现列表自由拖拽排序

元素的 dragable 属性设置 为 true &#xff08;文本 图片 链接 的draggable 属性默认为 true&#xff09;则元素可拖放 <template><transition-group class"list"><uldragstart"dragstart(index)"dragenter"dragenter($event, index)…