GPT function calling v2
原文:GPT function calling v2 - 知乎
OpenAI在2023年11月10号举行了第一次开发者大会(OpenAI DevDays),其中介绍了很多新奇有趣的新功能和新应用,而且更新了一波GPT的API,在1.0版本后的API调用与之前的0.X版本有了比较大的更新,尤其是GPT的function calling这个重要功能,所以这篇文章就来具体介绍如何使用新发布的API来实现function calling。虽然OpenAI在最新的API文档中将function calling改称为tools calling,但其实二者的差异不大,所以本文也还是继续使用function calling这个词来做相关的说明。
关于1.0版本之前的API使用,可以参考本人之前写的一篇文章,里面包含function calling的基本原理,流程和简单应用。
间断的连续:GPT function calling6 赞同 · 0 评论文章编辑
1. Openai API basic
以openai最常使用的chat API而言,以下的代码片段能够直观地体现如何利用新的API来实现简单的交流:
import os
import json
import loguru
from openai import OpenAI# Load from json configuration file
CONFIG_FILE = "configs/config.json"
API_KEY_TERM = "opeanai_api_key"
MODEL_TERM = "openai_chat_model"try:with open(CONFIG_FILE) as f:configs = json.load(f)API_KEY = configs[API_KEY_TERM]MODEL = configs[MODEL_TERM]
except FileNotFoundError:loguru.logger.error(f"Configuration file {CONFIG_FILE} not found")# Load from env variables
# API_KEY = os.environ.get("OPENAI_API_KEY")
# MODEL = "gpt-3.5-turbo-1106"# Create new OpenAI client object
client = OpenAI(api_key=API_KEY)# Get the response from OpenAI with system and user prompt
response = client.chat.completions.create(model = MODEL, messages = [{"role": "system", "content": "You are a helpful assistant."},{"role": "user", "content": "Introduce yourself."}]
)# Extract response messages
print(response.choices[0].message.content)
API_KEY和chat模型可以从一个JSON配置文件中读取,或者从环境变量中获取(这个完全取决于个人的开发习惯)。之后创建OpenAI客户端对象。使用这个client对象来使用chat.completions.create来向OpenAI发送消息。在得到回复之后,使用response.choices[0].message.content来抽取信息。在终端运行得到的结果如下:
2. Function calling
2.1. function calling recap
简单来说,function calling是让GPT或者是大语言模型能够使用外部工具的能力。在API调用中,用户可以向gpt-3.5或者gpt-4.0描述需要调用的函数声明(function declaration)包括函数的名称和函数所需的参数,然后让模型智能选择输出一个包含调用函数的JSON对象。模型之后会生成一个JSON文件,用户可以在代码中用来调用该函数。
换句话说就是GPT虽然不能直接访问和使用外部数据源或者工具,但GPT能够根据语境知道何时需要访问外部资源,而且能够生成符合满足API调用的格式文件(一般为JSON文件),让用户可以在自己的代码中利用生成的格式文件和声明好的函数根据语境自动实现某种功能,如写邮件,网络搜索或者是实时天气查询。
2.2. Single function calling
针对简单的任务,可以使用单一的function calling来实现,如实现某个文件夹中的文件查询,实现如下。
首先得先有一个用于查询特定文件的函数实现。这里有一个实现细节,那就是return的files要转类型至string,因为GPT目前只能识别和处理信息中的文本和字符串,不能处理诸如列表,数组和字典等数据结构。
def list_files_in_directory(directory: str):try:files = os.listdir(directory)return str(files) if files else "The directory is empty."except FileNotFoundError:print(f"Directory '{directory}' not found")return []
之后需要将该函数的签名,所需变量和返回值格式化为一个JSON格式:
tools = [{"type": "function","function": {"name": "list_files_in_directory","description": "List all files in a directory","parameters": {"type": "object","properties": {"directory": {"type": "string","description": "The name of directory to list files in"},},"required": ["directory"],},},}
]
需要注意的是如果函数中不包含任何参数,也没有任何返回值,则可以写成以下格式:
tools = [{"type": "function","function": {"name": "YOUR FUNCTION NAME”,"description": "YOUR FUNCTION DESCRIPTION","parameters" : {"type": "object", "properties": {}}}},
]
对于不包含任何参数和返回值的function calling其实可以作为一种柔性的条件判断来使用,也就是说它可以用于检测是否触发了某种意图如结束谈话或者是打招呼等。
通过函数实现和定义好的JSON格式,GPT就可以在函数调用时正确地生成相对应的参数,之后就可以调用GPT的API来实现工具调用了。
messages = [{"role": "system", "content": "You are a friendly chatbot that can use external tools to offer reliable assistance to human beings."},{"role": "user", "content": "List all the files in a directory in '../tools'."},
]response = client.chat.completions.create(model=OPENAI_CHAT_MODEL,messages=messages,tools=tools,
)messages.append(response.choices[0].message)
此时,GPT返回的message中文本的内容是None,而是tool_calls则会包含需要调用的函数名和参数(与之前写好的函数实现是一致)。
ChatCompletion(id='chatcmpl-8cGeaG5CErdDUYDU2b5Pl4zZnKhXf', choices=[Choice(finish_reason='tool_calls', index=0, message=ChatCompletionMessage(content=None, role='assistant', function_call=None, tool_calls=[ChatCompletionMessageToolCall(id='call_k3GrVfDAt78erknnB8q1JNI3', function=Function(arguments='{"directory":"../tools"}', name='list_files_in_directory'), type='function')]), logprobs=None)], created=1704131172, model='gpt-3.5-turbo-1106', object='chat.completion', system_fingerprint='fp_772e8125bb', usage=CompletionUsage(completion_tokens=17, prompt_tokens=88, total_tokens=105))
至此,GPT需要得到函数执行后的结果来得到最后的response。也就是说需要得到函数执行后的实际结果并再次发送给GPT来最终生成回复。在这个过程中,需要不断地将得到的中间结果加入到message中以此来构筑上下文,否则GPT会返回Bad Request error。
available_tools = {"list_files_in_directory": list_files_in_directory,
}tool_calls = response.choices[0].message.tool_callsfor tool_call in tool_calls:function_name = tool_call.function.namefunction_to_call = available_tools[function_name]function_args = json.loads(tool_call.function.arguments)function_response = function_to_call(**function_args)messages.append({"tool_call_id": tool_call.id,"role": "tool","name": function_name,"content": function_response,})
之后发送新的message得到返回结果
second_response = client.chat.completions.create(model=OPENAI_CHAT_MODEL,messages=messages,tools=tools,
)print(second_response.choices[0].message.content)
针对以下对应的文件目录得到的结果如下:
工程目录结构
GPT能够识别到给定目录下的文件
2.3. Sequential function calling
对于一些任务,仅使用一次function calling可能并不能实现最终的效果,如在上述的文件查询之后要求GPT能够去执行文件夹中的特定文件,从而得到某种结果。如果该文件不存在,就需要GPT自己去编写并且保存在该文件目录中,再去调用来得到结果。这就需要GPT能够多次调用不同的functions来实现最终的功能。这里可以用过while循环来实现,只有当finishi_resaon为“stop”的时候才停止生成。这里给出一种实现方式:
def list_files_in_directory(directory: str):try:files = os.listdir(directory)return str(files) if files else "The directory is empty."except FileNotFoundError:print(f"Directory '{directory}' not found")return []def is_file_in_directory(directory, filename):file_path = os.path.join(directory, filename)return os.path.exists(file_path)def save_to_file(filename, text):with open(filename, 'w') as f:f.write(text)return f"Content saved to {filename}"def execute_python_file(filename):try:result = subprocess.run(['python', filename], capture_output=True, text=True, check=True)return result.stdoutexcept subprocess.CalledProcessError as e:return f"Error: {e}"tools = [{"type": "function","function": {"name": "list_files_in_directory","description": "List all files in a directory","parameters": {"type": "object","properties": {"directory": {"type": "string","description": "The name of directory to list files in"},},"required": ["directory"],},},},{"type": "function","function": {"name": "is_file_in_directory","description": "Check if a file exists in a directory","parameters": {"type": "object","properties": {"directory": {"type": "string","description": "The name of directory to check file in"},"filename": {"type": "string","description": "The name of file to check"}, },"required": ["directory", "filename"],},},},{"type": "function","function": {"name": "save_to_file","description": "Save content to a file","parameters": {"type": "object","properties": {"filename": {"type": "string","description": "The name of file to save content to"},"text": {"type": "string","description": "The content to save to file"},},"required": ["filename", "text"],},},},{"type": "function","function": {"name": "execute_python_file","description": "Execute python file and get the output","parameters": {"type": "object","properties": {"filename": {"type": "string","description": "The name of file to execute"},},"required": ["filename"],},},}
]available_tools = {"list_files_in_directory": list_files_in_directory,"is_file_in_directory": is_file_in_directory,"save_to_file": save_to_file,"execute_python_file": execute_python_file,
}system_prompt = f"""
You are a friendly chatbot that can use external tools to offer reliable assistance to human beings.
"""
user_prompt = f"""
Write a Python file called 'platform_info.py' to check hardware information on a device if 'platform_info.py' does not exist in the directory called '../tools'.
Otherwise, get the result by executing the 'platform_info.py' and warp the result in natural language.
"""messages = [{"role": "system", "content": system_prompt},{"role": "user", "content": user_prompt},
]while True:response = client.chat.completions.create(model=OPENAI_CHAT_MODEL,messages=messages,tools=tools,tool_choice="auto")response_msg = response.choices[0].messagemessages.append(response_msg)print(response_msg.content) if response_msg.content else print(response_msg.tool_calls)finish_reason = response.choices[0].finish_reasonif finish_reason == "stop":breaktool_calls = response_msg.tool_callsif tool_calls:for tool_call in tool_calls:function_name = tool_call.function.namefunction_to_call = available_tools[function_name]function_args = json.loads(tool_call.function.arguments)function_response = function_to_call(**function_args)messages.append({"tool_call_id": tool_call.id,"role": "tool","name": function_name,"content": str(function_response),})
基于以上文件目录,上述的程序运行完之后得到的结果如下:
2.4. Parallel function calling
这个其实是这次API更新的一大亮点,那就是如何能够让GPT可以实现一个API的并行调用。例如当用户希望同时查询北京,上海,广州和深圳四个城市的天气时,如果使用上述的sequential function calling也是可以实现的,但其实在每一次的调用中,其调用的函数都是一样的,只是不同的参数,这其实完全能够利用并行算法来实现更快地response生成。这个例子其实OpenAI在其官网已经给出了详细的代码,这里给出链接,就不再赘述。
https://platform.openai.com/docs/guides/function-callingplatform.openai.com/docs/guides/function-calling
2.5. JSON mode
在本次的API更新中还有一个比较有意思的点就是GPT的JSON mode,这其实也在给GPT的格式化输出提供了一个一个新方法,使用方法也很简单,代码如下:
response = client.chat.completions.create(model = MODEL, messages = [{"role": "system", "content": "You are a helpful assistant and give answer in json format."},{"role": "user", "content": "Introduce yourself."}],response_format= { "type": "json_object" },max_tokens=1024,timeout=10)
得到的结果如下:
需要注意的是JSON mode目前只有gpt-4-1106-preview
orgpt-3.5-turbo-1106
两个聊天模型可以使用。
3. 结语
关于更多细节的更新可以参考官方的网页:
https://platform.openai.com/docs/guides/text-generation/completions-apiplatform.openai.com/docs/guides/text-generation/completions-api
其中还包括有生成可重复性,tokens管理和参数调节等较为细节的更新,而本文则着重于function calling或者说tools calling这一功能的介绍。
OpenAI GPT的function calling很强大,但其闭源的特性或许是很多开发者或者是企业不太喜欢的,大家都喜欢自己可以掌握和完全可控的工具。所以基于开源模型的开源function calling其实已经逐步发展,我认为一个很好的例子就是chatGLM3,其API的调用就包含了function calling。而且也有很多研究者和开发者在不断开发新的开源function calling工具比如LLMCompiler。
https://github.com/SqueezeAILab/LLMCompilergithub.com/SqueezeAILab/LLMCompiler编辑
function calling结合其他功能其实可以实现一些很有意思的想法,例如模仿计算机内存层级架构来拓展语言模型上下文窗口长度的MemGPT,这个项目当中就需要使用function calling。
MemGPT: Towards LLMs as Operating Systemsarxiv.org/abs/2310.08560编辑
这其实也从侧面暗示了LLM结合function calling和memory等功能,很有可能演化为一种新型的操作系统的核心。
相关文章:

GPT function calling v2
原文:GPT function calling v2 - 知乎 OpenAI在2023年11月10号举行了第一次开发者大会(OpenAI DevDays),其中介绍了很多新奇有趣的新功能和新应用,而且更新了一波GPT的API,在1.0版本后的API调用与之前的0.…...

【Golang】IEEE754标准二进制字符串转为浮点类型
IEEE754介绍 IEEE 754是一种标准,用于表示和执行浮点数运算的方法。在这个标准中,单精度浮点数使用32位二进制表示,分为三个部分:符号位、指数位和尾数位。 符号位(s)用一个位来表示数的正负,0表示正数,1表…...

【开源项目】轻量元数据管理解决方案——Marquez
大家好,我是独孤风。 又到了本周的开源项目推荐。最近推荐的元数据管理项目很多,但是很多元数据管理平台的功能复杂难用。 那么有没有轻量一点的元数据管理项目呢? 今天为大家推荐的开源项目,就是一个轻量级的元数据管理工具。虽然…...
dirty file page
转自:https://www.cnblogs.com/zhiminyu/p/17330763.html 0.前言 Linux 内核Page Cache 和Buffer Cache 关系及演化历史 一文中讲过Linux 2.4之后将Page Cache和Buffer Cache 进行了融合,在buffer_head 中添加了b_page,很容易就能找到缓存的…...

HTAP(Hybrid Transactional/Analytical Processing)系统之统一存储的实时之道
文章目录 HTAP与时俱进LASER中的存储关键知识LSM(Log-Structured Merge Tree)SkipList(跳表)CDC(Changed Data Capture)SST(Sorted Sequence Table) 特性列组(Column Gro…...

【linux】tcpdump 使用
tcpdump 是一个强大的网络分析工具,可以在 UNIX 和类 UNIX 系统上使用,用于捕获和分析网络流量。它允许用户截取和显示发送或接收过网络的 TCP/IP 和其他数据包。 一、安装 tcpdump 通常是默认安装在大多数 Linux 发行版中的。如果未安装,可…...

数字图像处理常用算法的原理和代码实现详解
本专栏详细地分析了常用图像处理算法的数学原理、实现步骤。配有matlab或C实现代码,并对代码进行了详细的注释。最后,对算法的效果进行了测试。相信通过这个专栏,你可以对这些算法的原理及实现有深入的理解! 如有疑问…...
Pandas实战100例 | 案例 26: 检测异常值
案例 26: 检测异常值 知识点讲解 在数据分析中,检测和处理异常值(或离群值)是一个重要的步骤。异常值可能会影响数据的整体分析。一种常用的方法是使用四分位数和四分位数间距(IQR)来识别异常值。 四分位数和 IQR: …...

C语言学习NO.11-字符函数strlen,strlen函数的使用,与三种strlen函数的模拟实现
(一)strlen函数的使用 strlen函数的演示 #include <stdio.h> #include <string.h>int main() {char arr1[] "abcdef";char arr2[] "good";printf("arr1 %d,arr2 %d",strlen(arr1),strlen(arr2));return …...
Vue3+ts获取props的值并且定义props值的类型的方法。
1.引入withDefaults模块,给defineProps绑定默认值。 import { withDefaults } from vue2.定义Props传输值的类型。 interface Props {// 类型type: string;name: string;id: number; }3.给props的值设置默认值。 const props withDefaults(defineProps<Prop…...
EasyExcel 不使用科学计数发并以千分位展示
EasyExcel 不使用科学计数发并以千分位展示 不使用科学计数法 不使用科学计数法 BigDecimalStringConverter 将 BigDecimal 类型的数值转换为字符串类型,并将其导出到 Excel 文件中。在 convertToExcelData 方法中,我们将 BigDecimal 转换为字符串&…...

【Python机器学习】SVM——调参
下面是支持向量机一个二维二分类数据集的训练结果: import mglearn import matplotlib.pyplot as plt from sklearn.svm import SVCplt.rcParams[font.sans-serif] [SimHei] plt.rcParams[axes.unicode_minus] False X,ymglearn.tools.make_handcrafted_dataset()…...

网络传输(TCP)
前言 我们tcpdump抓包时会看到除报文数据外,前面还有一段其他的数据,这段数据分为两部分,ip包头(一般20字节)和tcp包头(一般20字节),一般这两个头长度和为40,我们直接跳…...
MFC模拟消息发送,自定义以及系统消息
在MFC框架下,有很多系统已经定义好的消息,例如ON_WM_LBUTTONDOWN()、ON_WM_MBUTTONDOWN()等等。我们在使用的时候只需要声明并调用就可以了,最简单的用法。 提升了一点难度的用法就是自己设置自定义消息,再提升一点难度的就是如何…...
并发,并行,线程与UI操作
并行和并发是计算机领域中两个相关但不同的概念。 并行(Parallel)指的是同时执行多个任务或操作,它依赖于具有多个处理单元的系统。在并行计算中,任务被分成多个子任务,并且这些子任务可以同时在不同的处理单元上执行…...
react 6种方式编写样式
在React中,编写样式主要有以下几种方式: 1. 内联样式: 直接在React组件中使用style属性来定义样式。这种方式比较适合定义动态的样式,因为它允许你将JavaScript表达式作为样式的值。 2. 外部样式表 :通过创建外部的…...

计算机找不到msvcr100.dll的多种解决方法分享,轻松解决dll问题
msvcr100.dll作为系统运行过程中不可或缺的一部分,它的主要功能在于提供必要的运行时支持,确保相关应用程序能够顺利完成编译和执行。因此,当操作系统或应用程序在运行阶段搜索不到该文件时,自然会导致各类依赖于它的代码无法正常…...

系分笔记数据库反规范化、SQL语句和大数据
文章目录 1、概要2、反规范化3、大数据4、SQL语句5、总结 1、概要 数据库设计是考试重点,常考和必考内容,本篇主要记录了知识点:反规范化、SQL语句及大数据。 2、反规范化 数据库遵循范式的设计,使得多表查询和连接表查询较多的时…...

php实现支付宝商户转账
目录 一:背景介绍 一:准备工作 三:代码实现 一:背景介绍 最近工作中,要用到支付宝的商家转账功能,用php代码实现,网上找的内容,有些是老版本的实现,有些是调用sdk&am…...
并发编程(十一)
性能测试的常用命令 1、Netstat是在内核中访问网络连接状态及其相关信息的程序,它能够显示协议统计和当前TCP/IP的网络连接。 Netstat命令的常用格式如下: netstat -a:显示所有网络连接和侦听端口。 netstat -b:显示在创建网络…...

通过Wrangler CLI在worker中创建数据库和表
官方使用文档:Getting started Cloudflare D1 docs 创建数据库 在命令行中执行完成之后,会在本地和远程创建数据库: npx wranglerlatest d1 create prod-d1-tutorial 在cf中就可以看到数据库: 现在,您的Cloudfla…...

MODBUS TCP转CANopen 技术赋能高效协同作业
在现代工业自动化领域,MODBUS TCP和CANopen两种通讯协议因其稳定性和高效性被广泛应用于各种设备和系统中。而随着科技的不断进步,这两种通讯协议也正在被逐步融合,形成了一种新型的通讯方式——开疆智能MODBUS TCP转CANopen网关KJ-TCPC-CANP…...
WEB3全栈开发——面试专业技能点P2智能合约开发(Solidity)
一、Solidity合约开发 下面是 Solidity 合约开发 的概念、代码示例及讲解,适合用作学习或写简历项目背景说明。 🧠 一、概念简介:Solidity 合约开发 Solidity 是一种专门为 以太坊(Ethereum)平台编写智能合约的高级编…...

《基于Apache Flink的流处理》笔记
思维导图 1-3 章 4-7章 8-11 章 参考资料 源码: https://github.com/streaming-with-flink 博客 https://flink.apache.org/bloghttps://www.ververica.com/blog 聚会及会议 https://flink-forward.orghttps://www.meetup.com/topics/apache-flink https://n…...
Android第十三次面试总结(四大 组件基础)
Activity生命周期和四大启动模式详解 一、Activity 生命周期 Activity 的生命周期由一系列回调方法组成,用于管理其创建、可见性、焦点和销毁过程。以下是核心方法及其调用时机: onCreate() 调用时机:Activity 首次创建时调用。…...

面向无人机海岸带生态系统监测的语义分割基准数据集
描述:海岸带生态系统的监测是维护生态平衡和可持续发展的重要任务。语义分割技术在遥感影像中的应用为海岸带生态系统的精准监测提供了有效手段。然而,目前该领域仍面临一个挑战,即缺乏公开的专门面向海岸带生态系统的语义分割基准数据集。受…...

莫兰迪高级灰总结计划简约商务通用PPT模版
莫兰迪高级灰总结计划简约商务通用PPT模版,莫兰迪调色板清新简约工作汇报PPT模版,莫兰迪时尚风极简设计PPT模版,大学生毕业论文答辩PPT模版,莫兰迪配色总结计划简约商务通用PPT模版,莫兰迪商务汇报PPT模版,…...

STM32HAL库USART源代码解析及应用
STM32HAL库USART源代码解析 前言STM32CubeIDE配置串口USART和UART的选择使用模式参数设置GPIO配置DMA配置中断配置硬件流控制使能生成代码解析和使用方法串口初始化__UART_HandleTypeDef结构体浅析HAL库代码实际使用方法使用轮询方式发送使用轮询方式接收使用中断方式发送使用中…...

mac:大模型系列测试
0 MAC 前几天经过学生优惠以及国补17K入手了mac studio,然后这两天亲自测试其模型行运用能力如何,是否支持微调、推理速度等能力。下面进入正文。 1 mac 与 unsloth 按照下面的进行安装以及测试,是可以跑通文章里面的代码。训练速度也是很快的。 注意…...

spring Security对RBAC及其ABAC的支持使用
RBAC (基于角色的访问控制) RBAC (Role-Based Access Control) 是 Spring Security 中最常用的权限模型,它将权限分配给角色,再将角色分配给用户。 RBAC 核心实现 1. 数据库设计 users roles permissions ------- ------…...