LangChain的函数,工具和代理(一):OpenAI的函数调用

一、什么是函数调用功能

几个月前OpenAI官方发布了其API的函数调用功能(Function calling), 在 API 调用中,您可以描述函数,并让模型智能地选择输出包含调用一个或多个函数的参数的 JSON 对象。API函数“ChatCompletion” 虽然不会实际调用该函数;但是模型会生成这些基于函数参数的JSON对象,您可以使用它来调用代码中的实际函数。

也就是说当用户和ChatGPT对话的过程中需要调用某些外部的函数或者API时,我们可以让ChatGPT生成调用外部函数所需的参数,然后我们再使用这些参数再去实际的调用外部函数,目前OpenAl 对 gpt-3.5-turbo-0613 和 gpt-4-0613 模型进行了微调,使它们具备了以下函数调用功能:

1. 接受额外的参数,用户可以通过这些参数传入函数的描述。
2. 如果相关,则返回要使用的函数的名称,以及带有适当输入参数的 JSON 对象。

二,如何实现OpenAI的函数调用功能

在实现OpenAI的函数调用功能之前,我们先定义一个外部函数,当用户和ChatGPT对话时,ChatGPT会自动判断是否需要调用外部函数,当需要调用外部函数时ChatGPT会返回调用函数的json对象给用户:

import json# 查询天气的模拟函数示例
# 在生产中,这可能是您的后端 API 或外部 API
def get_current_weather(location, unit="fahrenheit"):"""Get the current weather in a given location"""weather_info = {"location": location, #城市"temperature": "72", # 温度"unit": unit, #温度单位"forecast": ["sunny", "windy"], #天气情况}return json.dumps(weather_info)

这里我们定义一个外部函数get_current_weather,他用来查询特定城市的天气情况,并返回一个jons对象作为查询结果。接下来我们需要定义一个该函数的描述对象,该描述对象后面会作为参数传递给ChatGPT:

#函数描述对象
functions = [{"name": "get_current_weather","description": "Get the current weather in a given location","parameters": {"type": "object","properties": {"location": {"type": "string","description": "The city and state, e.g. San Francisco, CA",},"unit": {"type": "string", "enum": ["celsius", "fahrenheit"]},},"required": ["location"],},}
]

下面我们来说明一下函数描述对象的主要成员:

  • name: 外部函数名称如get_current_weather
  • description:外部函数功能的描述
  • parameters:外部函数的参数集
  • parameters-type:外部函数的参数集的类型
  • properties:外部函数的具体参数集
  • location:具体的外部函数的参数
  • location-type:外部函数的参数的类型
  • location-description:外部函数的参数的描述
  • unit:具体的外部函数的参数
  • unit-type:外部函数的参数的类型
  • enum:外部函数的参数的枚举值
  • required:必填的参数

这里我们生成了一个外部函数的描述对象,该描述对象会告诉ChatGPT该外部函数的作用,以及我们需要在恰当的时候来调用该函数,至于什么时候才是“恰当的时候”这需要由ChatGPT根据用户对话的上下文来判断。接下来我们向ChatGPT询问一个关于天气的问题:

import openai
openai.api_key = "XXXXXXXXX"messages = [{"role": "user","content": "上海的天气怎么样?"}
]response = openai.ChatCompletion.create(model="gpt-3.5-turbo-1106",messages=messages,functions=functions
)print(response)

这里我们向ChatGPT提出了关于天气的问题:“上海的天气怎么样?”, 从ChatGPT的返回结果中我们看到"function_call",这告诉我们接下来我们该调用外部函数了,同时ChatGPT还返回了调用外部函数的参数location和unit,以及所需调用的外部函数名:get_current_weather,有意思的是这里返回的unit为“celsius”即摄氏度而非美国使用的"fahrenheit(华氏度)", 这似乎说明ChatGPT知道中国使用摄氏度作为温度的单位,下面我们询问一下美国城市的天气:

messages = [{"role": "user","content": "What's the weather like in Boston?" #波士顿 的天气怎么样?}
]response = openai.ChatCompletion.create(model="gpt-3.5-turbo-1106",messages=messages,functions=functions
)print(response)

这里我们用英语询问了美国城市波士顿的天气情况,从ChatGPT的返回结果中我们看到arguments中只包含了location,而没有包含unit, 而在我们的外部函数get_current_weather中unit为非必填参数,它有一个默认值为:unit="fahrenheit",因此在实际调用外部函数时我们只需将chatgpt返回结果中的arguments中取出对应的参数然后传递给外部函数即可,接下来我们从ChatGPT的返回结果中获取参数来实际调用外部函数get_current_weather:

args = json.loads(response_message["function_call"]["arguments"])
result=get_current_weather(args)
print(result)

 接下来我们来测试一下ChatGPT能否准确识别何时该调用外部函数,下面我们会对ChatGPT发送一个简单的问候语:hi,  当Chatgpt收到该问候语时不应该触发函数调用功能:

messages = [{"role": "user","content": "hi!",}
]response = openai.ChatCompletion.create(model="gpt-3.5-turbo-1106",messages=messages,functions=functions,
)print(response)

从上面的chatgpt的返回结果中我们看到不存在先前的“function_call"内容即没有生成外部函数的调用参数,这说明此时我们不需要调用外部函数。

三、设置OPAI API的默认参数

openai的API函数ChatCompletion.create中存在一个function_call的参数,该参数的默认值为“auto”即让模型自己来选择是否需要调用外部函数:

messages = [{"role": "user","content": "hi!",}
]
response = openai.ChatCompletion.create(model="gpt-3.5-turbo-1106",messages=messages,functions=functions,function_call="auto",
)
print(response)

上面我们在openai.ChatCompletion.create的方法中加入了function_call="auto",意思是让模型根据上下文来确定是否调用外部函数,我们看到当我们向ChatGPT打招呼时,如输入“hi”时 ,chatgpt的返回结果中没有“function_call”的内容。这说明ChatGPT知道此时不应该调用外部函数。

messages = [{"role": "user","content": "hi!",}
]
response = openai.ChatCompletion.create(model="gpt-3.5-turbo-1106",messages=messages,functions=functions,function_call="none",#禁止调用外部函数
)
print(response)

上面当我们将function_cal设置为"none"时(即禁止chatGPT调用外部函数),chatGPT的返回结果中也不会出现“function_call”的内容。

messages = [{"role": "user","content": "What's the weather in Boston?",}
]
response = openai.ChatCompletion.create(model="gpt-3.5-turbo-1106",messages=messages,functions=functions,function_call="none", #禁止调用外部函数
)
print(response)

在上面的代码中我们向ChatGPT询问了波士顿的天气,但是我们设置了function_call="none",也就是说虽然我们询问了波士顿的天气情况,但我们却禁止chatgpt调用外部函数,从chatgpt的返回结果中我们看到仍然没有“function_cal”的相关内容。

下面我们设置chatgpt强制调用外部函数,看看会发生上面情况:

messages = [{"role": "user","content": "hi!",}
]
response = openai.ChatCompletion.create(model="gpt-3.5-turbo-1106",messages=messages,functions=functions,function_call={"name": "get_current_weather"},#强制调用外部函数
)
print(response)

在上面的代码中我们在 ChatCompletion.create中设置了function_call={"name": "get_current_weather"}意思是让chatgpt强制生成调用get_current_weather函数的参数,但是我们向chatgpt发送的用户消息却是:hi!, 这时会让chatgpt产生困惑,因为用户消息中没有有关询问天气的内容,但是却要强制chatgpt去生成外部函数的调用参数,所以在chatgpt的返回结果中function_call中的arguments中给出了一个随机的location:San Francisco,CA。

下面我们向chatgpt询问波士顿的天气,并且让chatgpt强制调用get_current_weather,看看会发生什么情况:

messages = [{"role": "user","content": "What's the weather like in Boston!",}
]
response = openai.ChatCompletion.create(model="gpt-3.5-turbo-1106",messages=messages,functions=functions,function_call={"name": "get_current_weather"}, #强制调用外部函数
)
print(response)

从上面的chatgpt的返回结果中我们看到了“function_call”中的内容。这说明只要我们设置了chatgpt强制指定了外部调用函数时,它总会生成相应的函数参数。

四、外部函数的调用结果的应用

上面我们让chatgpt来判断是否应该调用外部函数,并且让chatgpt返回了调用外部函数的参数,接下来我们要做的是用chatgpt提供的参数去实际调用外部函数,并将外部函数的返回结果再喂给chatgpt,这样做的目的是让chatgpt来汇总所有的信息并产生最终对用户友好的返回信息。

#整合chatgpt的返回结果
messages.append(response["choices"][0]["message"])
#从chatgpt的返回结果中获取外部函数的调用参数
args = json.loads(response["choices"][0]["message"]['function_call']['arguments'])
#调用外部函数
observation = get_current_weather(args)messages.append({"role": "function","name": "get_current_weather","content": observation, #外部函数的返回结果}
)response = openai.ChatCompletion.create(model="gpt-3.5-turbo-0613",messages=messages,
)
print(response)

 这里我们看到ChatGPT最终返回了一个非常友好的回复,该回复是在外部函数调用结果的基础上经过整理后得到的。

五、关于token统计

我们知道chatgpt的API是通过token来收费的,这里我们在使用chatgpt的函数调用功能时我们创建了一个函数描述对象functions,因此functions也会作为上下文的一部分被统计token数,下面我们去掉ChatCompletion.create中的functions和function_call这两个参数看看最后chatgpt返回的总的token数是多少:

messages = [{"role": "user","content": "What's the weather like in Boston!",}
]
response = openai.ChatCompletion.create(model="gpt-3.5-turbo-1106",messages=messages
)
print(response)

从上面的返回结果中我们看到当我们去掉了ChatCompletion.create中的functions和function_call这两个参数时,总token数为48,而先前的总token数为99,这说明外部函数描述对象functions被统计了token数。

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

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

相关文章

批量创建表空间数据文件(DM8:达梦数据库)

DM8:达梦数据库 - - 批量创建表空间数据文件 环境介绍1 批量创建表空间SQL2 达梦数据库学习使用列表 环境介绍 在某些场景(分区表子表)需要批量创建表空间,给不同的表使用,以下代码是批量创建表空间的SQL语句; 1 批量创建表空间SQL --创建 24个数据表空间,每个表空间有3个数…

经典的回溯算法题leetcode组合问题整理及思路代码详解

目录 组合问题 leetcode77题.组合 leetcode216题.组合总和III leetcode40题.组合总和II leetcode39题.组合总和 倘若各位不太清楚回溯算法可以去看我上一篇文章。 回溯算法详解-CSDN博客 组合问题 一般组合和排列类的问题我们都会转化成一个树形问题,更便于…

使用Typecho搭建个人博客网站,并内网穿透实现公网访问

使用Typecho搭建个人博客网站,并内网穿透实现公网访问 文章目录 使用Typecho搭建个人博客网站,并内网穿透实现公网访问前言1. 安装环境2. 下载Typecho3. 创建站点4. 访问Typecho5. 安装cpolar6. 远程访问Typecho7. 固定远程访问地址8. 配置typecho 前言 …

通过CLSID修改Windows右键菜单顺序

效果 排序顺序: 上下文菜单分为3个区: 2 - 默认菜单部分 (在顶部). 1 - 发送到、复制到、移动到 菜单部分(在中间). 0 - 重命名菜单部分 (在底部). 调整菜单步骤: 打开regeditHKEY_CLASSES_ROOT > CLSID 键.在 CLSID, 寻找你想修改位置…

机器学习【01】相关环境的安装

学习实例 参考资料:联邦学习实战{杨强}https://book.douban.com/subject/35436587/ 项目地址:https://github.com/FederatedAI/Practicing-Federated-Learning/tree/main/chapter03_Python_image_classification 一、环境准备 GPU安装CUDA、cuDNN pytho…

JavaScript基础—引入方式、注释和结束符、输入和输出、变量、常量、数据类型、检测数据类型、类型转换、综合案例—用户订单信息

版本说明 当前版本号[20231123]。 版本修改说明20231123初版 目录 文章目录 版本说明目录JavaScript 基础 - 第1天介绍引入方式内部方式外部形式 注释和结束符单行注释多行注释 结束符输入和输出输出输入 变量声明赋值变量初始化更新变量 关键字变量名命名规则 常量数据类型…

深度之眼Paper带读笔记GNN.08.GCN(下)

文章目录 前言细节四:卷积核介绍图卷积核初代目图卷积核二代目契比雪夫多项式例子小结 GCN公式推导 实验设置和结果分析数据集节点分类任务消息传递方式比较运行效率 总结关键点创新点启发点 代码复现train.pyutil.pymodel.pylayer.py 作业 前言 本课程来自深度之眼…

【C++】POCO学习总结(五):功能介绍

【C】郭老二博文之:C目录 1、POCO 简介 github:https://github.com/pocoproject/poco 官网:https://pocoproject.org/index.html POCO第一个版本于 2005 年 2 月发布 POCO完全免费:POCO C 库根据 Boost 软件许可证获得许可。非…

Vue框架学习笔记——Vue实例中el和data的两种写法

文章目录 前文提要Vue实例的el第一种写法第二种写法小结 Vue实例中data第一种写法&#xff0c;对象式效果图片第二种写法&#xff0c;函数式效果图片小结 前文提要 本文仅做自己的学习记录&#xff0c;如有错误&#xff0c;请多谅解 Vue实例的el 第一种写法 <body><…

spring 是如何开启事务的, 核心原理是什么

文章目录 spring 是如何开启事务的核心原理1 基于注解开启事务2 基于代码来开启事务 spring 是如何开启事务的 核心原理 Spring事务管理的实现有许多细节&#xff0c;如果对整个接口框架有个大体了解会非常有利于我们理解事务&#xff0c;下面通过讲解Spring的事务接口来了解…

【CCF-PTA】第03届Scratch第04题 -- 数字加密

数字加密 【题目描述】 "狼群战术"是第二次世界大战中德军对大西洋上盟军商船所使用的潜艇战术&#xff0c;一度遏制住英国的海上贸易。直到艾伦图灵成功破译了德国的英格尔码密码&#xff0c;成为二战的一个重要转折点。时至今日&#xff0c;图灵仍然是计算机的一…

【数据结构/C++】线性表_顺序表的基本操作

#include <iostream> using namespace std; #define MaxSize 10 // 1. 顺序表 // 静态分配 typedef struct {int data[MaxSize];int length; // 当前长度 } SqList; // 静态分配初始化顺序表 void InitList(SqList &L) {for (int i 0; i < MaxSize; i){L.data[i]…