五、OpenAi之函数调用(Function Calling)(二)

news2024/11/25 23:01:33

在这里插入图片描述


聊天补全模型调用函数


这个笔记包含怎样使用聊天补全API结合外部的函数调用来扩展GPT模型的能力

tools在聊天补全API中是一个可选的参数,可以定义指定的函数调用。目的是能使模型生成遵循指定规范的函数参数。请注意:API实际上不执行任何的函数调用。由开发者使用模型输出时执行函数调用。

在tools参数内部,如果提供了functions参数,那么在默认情况下,模型来决定何时使用其中一个适合的函数。API可通过设置tool_choice参数{“name”: “”},强制调用一个指定的函数。也可以设置tool_choice参数为none,强制不调用任何函数。如果使用了函数,则输出将在响应中包含"finish_reason": “function_call”,以及包含函数名称和生成的函数参数的tool_choice对象。

1. 概述

这个笔记包含下面两部分:

  • 怎样生成函数参数 指定一组函数并使用模型API生成函数参数。
  • 怎样使用模型生成的参数调用函数 通过实际执行带有模型生成参数的函数来关闭循环。

1.1 怎样生成函数参数

安装依赖包

pip install scipy  -i https://pypi.tuna.tsinghua.edu.cn/simple
pip install tenacity
pip install tiktoken
pip install termcolor 
pip install openai
# 导入依赖库
import json
from tenacity import retry, wait_random_exponential, stop_after_attempt
from termcolor import colored 
from openai import OpenAI
from dotenv import load_dotenv, find_dotenv

# 加载 .env 文件中定义的环境变量
_ = load_dotenv(find_dotenv())

GPT_MODEL = "gpt-3.5-turbo-0613"
# 初始化 OpenAI 客户端
client = OpenAI()  # 默认使用环境变量中的 OPENAI_API_KEY 和 OPENAI_BASE_URL

1.2 实用函数

首先定义几个公用的函数,以调用聊天补全API,维护和保持对话记录状态。

@retry(wait=wait_random_exponential(multiplier=1, max=40), stop=stop_after_attempt(3))
def chat_completion_request(messages, tools=None, tool_choice=None, model=GPT_MODEL):
	"""封装OpenAI API调用"""
    try:
        response = client.chat.completions.create(
            model=model,
            messages=messages,
            tools=tools,
            tool_choice=tool_choice,
        )
        return response
    except Exception as e:
        print("Unable to generate ChatCompletion response")
        print(f"Exception: {e}")
        return e
def pretty_print_conversation(messages):
	"""格式化控制台输出"""
    role_to_color = {
        "system": "red",
        "user": "green",
        "assistant": "blue",
        "function": "magenta",
    }
    
    for message in messages:
        if message["role"] == "system":
            print(colored(f"system: {message['content']}\n", role_to_color[message["role"]]))
        elif message["role"] == "user":
            print(colored(f"user: {message['content']}\n", role_to_color[message["role"]]))
        elif message["role"] == "assistant" and message.get("function_call"):
            print(colored(f"assistant: {message['function_call']}\n", role_to_color[message["role"]]))
        elif message["role"] == "assistant" and not message.get("function_call"):
            print(colored(f"assistant: {message['content']}\n", role_to_color[message["role"]]))
        elif message["role"] == "function":
            print(colored(f"function ({message['name']}): {message['content']}\n", role_to_color[message["role"]]))

1.3 基本概念

让我们创建一些特定函数规范与假定的天气API进行交互。我们要传递这些函数规范到聊天补全API,以便生成遵循指定规范函数参数

tools = [
    {
        "type": "function",
        "function": {
            "name": "get_current_weather",
            "description": "返回实时天气",
            "parameters": {
                "type": "object",
                "properties": {
                    "location": {
                        "type": "string",
                        "description": "河北省承德市双桥区",
                    },
                    "format": {
                        "type": "string",
                        "enum": ["celsius", "fahrenheit"],
                        "description": "使用本地区常用的温度单位计量",
                    },
                },
                "required": ["location", "format"],
            },
        }
    },
    {
        "type": "function",
        "function": {
            "name": "get_n_day_weather_forecast",
            "description": "返回近几天的天气预报",
            "parameters": {
                "type": "object",
                "properties": {
                    "location": {
                        "type": "string",
                        "description": "河北省承德市双桥区",
                    },
                    "format": {
                        "type": "string",
                        "enum": ["celsius", "fahrenheit"],
                        "description": "使用本地区常用的温度单位计量",
                    },
                    "num_days": {
                        "type": "integer",
                        "description": "预报的天数",
                    }
                },
                "required": ["location", "format", "num_days"]
            },
        }
    },
]

如果我们询问模型关于当前的天气,它会给出消息让你指定具体的区域

messages = []
messages.append({"role": "system", "content": "不要假设应该把什么值代入函数中。如果用户的请求模棱两可,要求对方澄清。"})
messages.append({"role": "user", "content": "今天的天气怎样?"})
chat_response = chat_completion_request(
    messages, tools=tools, tool_choice="none"
)
assistant_message = chat_response.choices[0].message
messages.append(assistant_message)
assistant_message

在这里插入图片描述

一旦你提供了缺失的消息,模型将为我们生成适合的参数。

messages.append({"role": "user", "content": "我在河北省承德市双桥区"})
chat_response = chat_completion_request(
    messages, tools=tools
)
assistant_message = chat_response.choices[0].message
messages.append(assistant_message)
assistant_message

由于提示词的不同,可以获得我们已经告诉模型的目标函数:
在这里插入图片描述

继续:

messages = []
messages.append({"role": "system", "content": "不要假设应该把什么值代入函数中。如果用户的请求模棱两可,要求对方澄清。"})
messages.append({"role": "user", "content": "接下来北京、上海、承德的天气将会怎样?"})
chat_response = chat_completion_request(
    messages, tools=tools, tool_choice="none"
)
assistant_message = chat_response.choices[0].message
messages.append(assistant_message)
assistant_message

在这里插入图片描述
模型又一次要求我们要明确的指出几天,因为我们没有给出足够的信息。这个例子已经知道要询问地区的天气,但需要指出几天的

messages.append({"role": "user", "content": "5天"})
chat_response = chat_completion_request(
    messages, tools=tools
)
chat_response.choices[0]

在这里插入图片描述

强制使用规范函数或不使用

我们可以强制模型使用指定规范的函数,例如:get_n_day_weather_forecast被用作function_call参数。这样做会强制模型使用指定的函数

messages = []
messages.append({"role": "system", "content": "不要假设应该把什么值代入函数中。如果用户的请求模棱两可,要求对方澄清。"})
messages.append({"role": "user", "content": "给我报告河北省承德市双桥区的天气"})
chat_response = chat_completion_request(
    messages, tools=tools, tool_choice={"type": "function", "function": {"name": "get_n_day_weather_forecast"}}
)
chat_response.choices[0].message

在这里插入图片描述
在这里插入图片描述

1.4 并行函数调用

最新的模型像:GPT-4-1106或GPT-3.5-turbo-1106一次可以调用多个函数:

messages = []
messages.append({"role": "system", "content": "不要假设应该把什么值代入函数中。如果用户的请求模棱两可,要求对方澄清。"})
messages.append({"role": "user", "content": "北京、上海接下3的天气是怎样的?"})
chat_response = chat_completion_request(
    messages, tools=tools, model='gpt-3.5-turbo-1106'
)

assistant_message = chat_response.choices[0].message.tool_calls
assistant_message

在这里插入图片描述

2. 怎样使用模型生成的参数调用函数

在接下来的案例中,我们将演示怎样执行函数调用,且输入是由模型产生,实现一个代理,能回答关于数据库的相关问题。为简单起见,使用chinook音乐案例数据库。SQLITE

注意:生成SQL在生产环境会造成高风险,由于模型在生成完全可靠的正确SQL上还不完善。

2.1 指定执行SQL查询的函数

首先,定义一些公用的函数,从SQLite数据库获得数据
在这里插入图片描述

def get_table_names(conn):
    """返回所有表名."""
    table_names = []
    tables = conn.execute("SELECT name FROM sqlite_master WHERE type='table';")
    for table in tables.fetchall():
        table_names.append(table[0])
    return table_names


def get_column_names(conn, table_name):
    """返回所有列名."""
    column_names = []
    columns = conn.execute(f"PRAGMA table_info('{table_name}');").fetchall()
    for col in columns:
        column_names.append(col[1])
    return column_names


def get_database_info(conn):
    """
        返回一个包含数据库中每个表的名字和列的字典列表。
    """
    table_dicts = []
    for table_name in get_table_names(conn):
        columns_names = get_column_names(conn, table_name)
        table_dicts.append({"table_name": table_name, "column_names": columns_names})
    return table_dicts

现在可以使用这些公用的函数获取数据库的模式定义

database_schema_dict = get_database_info(conn)
database_schema_string = "\n".join(
    [
        f"Table: {table['table_name']}\nColumns: {', '.join(table['column_names'])}"
        for table in database_schema_dict # 构造表名和列名字符数组
    ]
)

和之前一样,我们为函数调用定义一个函数规范,让模型API生成参数。请注意:我们要插入数据库的模型到函数规范中。让模型知道这一点很重要:

tools = [
    {
        "type": "function",
        "function": {
            "name": "ask_database",
            "description": "使用这个函数回答用户关于音乐的问题. 输入为格式化好的SQL语句.",
            "parameters": {
                "type": "object",
                "properties": {
                    "query": {
                        "type": "string",
                        "description": f"""
                                通过SQL查询提取用户问题的答案。
                                SQL语句应该使用这个数据库模式:
                                {database_schema_string}
                                查询结果应该以纯文本形式返回,而不是JSON格式。
                                """,
                    }
                },
                "required": ["query"],
            },
        }
    }
]

2.2 执行SQL查询

现在让我们实现函数真正的执行数据库查询

messages = []
messages.append({"role": "system", "content": "通过chinook音乐数据库生成SQL查询来回答用户问题"})
messages.append({"role": "user", "content": "谁是按曲目数量排名前五的艺术家?"})
chat_response = chat_completion_request(messages, tools)
assistant_message = chat_response.choices[0].message
assistant_message.content = str(assistant_message.tool_calls[0].function)
messages.append({"role": assistant_message.role, "content": assistant_message.content})
if assistant_message.tool_calls:
    results = execute_function_call(assistant_message)
    messages.append({"role": "function", "tool_call_id": assistant_message.tool_calls[0].id, "name": assistant_message.tool_calls[0].function.name, "content": results})
pretty_print_conversation(messages)

在这里插入图片描述

messages.append({"role": "user", "content": "歌曲最多的专辑叫什么名字?"})
chat_response = chat_completion_request(messages, tools)
assistant_message = chat_response.choices[0].message
assistant_message.content = str(assistant_message.tool_calls[0].function)
messages.append({"role": assistant_message.role, "content": assistant_message.content})
if assistant_message.tool_calls:
    results = execute_function_call(assistant_message)
    messages.append({"role": "function", "tool_call_id": assistant_message.tool_calls[0].id, "name": assistant_message.tool_calls[0].function.name, "content": results})
pretty_print_conversation(messages)

在这里插入图片描述

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

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

相关文章

高校疫情防控系统的全栈开发实战

✍✍计算机编程指导师 ⭐⭐个人介绍:自己非常喜欢研究技术问题!专业做Java、Python、微信小程序、安卓、大数据、爬虫、Golang、大屏等实战项目。 ⛽⛽实战项目:有源码或者技术上的问题欢迎在评论区一起讨论交流! ⚡⚡ Java实战 |…

大模型入门

大模型 一般指1亿以上参数的模型,目前万亿级参数以上的模型也有了。 参数大小 175B、60B、540B等,这些一般指参数的个数,B是Billion/十亿的意思,175B是1750亿参数,这是ChatGPT大约的参数规模。 显存占用 6B的大模…

多模态基础--- word Embedding

1 word Embedding 原始的单词编码方式: one-hot,维度太大,不同单词之间相互独立,没有远近关系区分。 wordclass,将同一类单词编码在一起,此时丢失了类别和类别间的相关信息,比如class1和class3…

[java基础揉碎]二维数组

目录 什么是二维数组: 二维数组在内存中的布局: 动态初始化: 静态初始化: 杨辉三角: 使用细节和注意事项: 什么是二维数组: 1.从定义形式上看 int[][] 2.可以这样理解,原来的一维数组的每个元素是一维数组,就构成二维数…

第5个-模糊加载

Day 5 - Blurry Loading 1. 演示效果 2. 分析思路 变化过程 数字从 0 不断增长到 100;中间的百分比数字逐渐消失,即透明度 opacity 从 1 到 0;背景图片从模糊变为清晰,滤镜 filter.blur()的参数设置为从 30px 到 0px。 小 tips…

Java 基于 SpringBoot+Vue 的校园交友网站,附源码

博主介绍:✌程序员徐师兄、7年大厂程序员经历。全网粉丝12w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ 🍅文末获取源码联系🍅 👇🏻 精彩专栏推荐订阅👇…

同学,请实现一个扫码登录

马上要到春节了,小伙伴们的公司是不是已经可以申请请假调休呢?虽然今年刚入职没有年假(好像国家不是这么规定的,但也不好跟公司硬杠),大小周的我已经攒了 7 天调休,也可以提前回家过年啦! 即使是年底&…

optee imx8mm

总仓库 git clone https://github.com/Xsyin/imx8mqevk.git -b container_region 替换imx8mqevk中的optee-client git clone https://github.com/nxp-imx/imx-optee-client.git -b lf-5.15.32_2.0.0 用 5.15.32 kernel 会有如下报错,需要将optee os升级到分支 lf-…

Istio复习总结:xDS协议、Istio Pilot源码、Istio落地问题总结

1、xDS协议 1)、xDS是什么 xDS是一类发现服务的总称,包含LDS、RDS、CDS、EDS以及SDS。Envoy通过xDS API可以动态获取Listener(监听器)、Route(路由)、Cluster(集群)、Endpoint&…

不同AI分析错误代码的差异:谁更胜一筹?谁才是最强者?结果出乎意料!

先祝大家新春快乐,我已经提前三天上班了~~为了年后新框架能上线运行,这几天没人打扰,能安静地冲一下代码,嘎嘎嘎。 准备 错误代码: ... foreach($arr_config[path] as $value_path) {if(file_exists($value_path)){r…

制作怎么自己搭建一个网站

制作怎么自己搭建一个网站 一.领取一个免费域名和SSL证书,和CDN 1.打开网站链接:https://www.rainyun.com/ycpcp_ 首先创建一个CDN,这里以我加速域名“cdntest.biliwind.com 1”为例 这里就要填写 cdntest.biliwind.com 1 ,而…

Java 和 JavaScript 的奇妙协同:语法结构的对比与探索(上)

🤍 前端开发工程师、技术日更博主、已过CET6 🍨 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 🕠 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 🍚 蓝桥云课签约作者、上架课程《Vue.js 和 E…

FuckIt.py库让你的代码从此远离bug

今天给你推荐的这个库叫 “FuckIt.py”,名字一看就是很黄很暴力的那种,作者是这样介绍它的: FuckIt.py uses state-of-the-art technology to make sure your Python code runs whether it has any right to or not. Some code has an error…

无心剑中译莎士比亚《劝君缔结连理枝》

莎士比亚十四行诗第8首 Sonnet 8 - 劝君缔结连理枝 Music to hear, why hear’st thou music sadly? Sweets with sweets war not, joy delights in joy. Why lovest thou that which thou receivest not gladly, Or else receivest with pleasure thine annoy? If the tru…

BUGKU-WEB bp

题目描述 题目截图如下: 进入场景看看: 解题思路 提示说:弱密码top1000?z???(爆破?)先看看源码有没有提示 相关工具 Burp Suit 爆破top1000字典,点击下载 解题步骤 随便测试账号密码admin、admin 得到提…

scIMC:scRNA-seq插补方法基准

在scRNA-seq中一个主要的挑战即为“dropout”事件,它扭曲了基因表达,显著影响了单细胞转录组的下游分析。为了解决这个问题,已经做了很多努力,并开发了几种基于模型和基于深度学习的scRNA-seq插补方法。但是,目前还缺乏…

彻底理解无刷电机

前言 现在很多设备都是搭载的无刷电机而不是有刷电机了,为啥?性能好啊! 引入 同性相斥异性相吸 可以看出,只要改变磁铁的极性,电机就能转起来 那 怎么改变磁铁极性呢? 右手螺旋定则可以根据电流的流向…

ch3-homework-基于InternLM和LangChain搭建自己的知识库

ch3-homework-基于InternLM和LangChain搭建自己的知识库 复现课程知识库助手搭建过程先看结果环境配置语料开源词向量模型Sentence Transformer知识库搭建InternLM 接入 LangChain构建检索问答链,并基于Gradio框架部署 基础作业: 复现课程知识库助手搭建…

【Day42】代码随想录之动态规划0-1背包_416. 分割等和子集

文章目录 动态规划理论基础动规五部曲:出现结果不正确: 416. 分割等和子集 动态规划理论基础 动规五部曲: 确定dp数组 下标及dp[i] 的含义。递推公式:比如斐波那契数列 dp[i] dp[i-1] dp[i-2]。初始化dp数组。确定遍历顺序&am…

Android---Jetpack Compose学习006

1. 点击 clickable 修饰符允许应用检测对已应用该修饰符的元素的点击。 示例:点击控件,使得内容发生改变 class MainActivity : ComponentActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setCo…