如何利用OpenAI的函数调用特性

news2024/11/26 5:52:40

如何利用OpenAI的函数调用特性

函数调用能实现哪些功能?

简单来说,函数调用功能可以助你在请求方法时构建结构化的数据。因为生成模型的特性,它产生的数据往往是无结构的,即使在提示(prompt)中指定了输出格式,但实际输出的结果往往偏离预期。

如果你曾经使用过Langchain,那么你一定知道其中有一个叫做OutputParser的模块,这个模块中定义了许多用于快速结构化数据的工具。

然而,面对大规模的模型,这些工具并不总是那么有效。因为模型的输出结果难以预测,这使得在一些复杂场景下,模型输出的结果总是无法被正确解析。即使是使用了Langchain中的OutputFixingParser,也难以让整个流程变得流畅。

因此,我们仍然需要回归到OpenAI官方推出的函数调用特性。在官方提供的一个示例中,它使用function calling让GPT模型提取自然语言中可用于函数调用的信息,然后利用第三方接口获取实时信息,最后生成一个全新的总结。

如何使用函数调用以及其工作原理?

接下来,让我们借助官方的示例来解析一下如何使用function calling这个特性,以及它究竟是如何运作的。

第一步:携带函数调用去请求GPT接口

首先,你需要把一个问题和函数调用一起,作为请求提交到GPT的接口。

messages = [{"role": "user", "content": "What's the weather like in Boston?"}]
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"],
            },
        }
    ]
response = openai.ChatCompletion.create(
        model="gpt-3.5-turbo-0613",
        messages=messages,
        functions=functions,
        function_call="auto",  # auto is default, but we'll be explicit
    )
response_message = response["choices"][0]["message"]

"""
输出:
 <OpenAIObject at 0x7a90d69f1620> JSON: {
  "role": "assistant",
  "content": null,
  "function_call": {
    "name": "get_current_weather",
    "arguments": "{\n  \"location\": \"Boston, MA\"\n}"
  }
}
 """

在这个例子中,先是定义了一个问题: What's the weather like in Boston?, 这个是我们想要回答的问题,其中Boston是问题中的地名,weather是我们想要知道的信息。在下面的function中,我们定义了一个function来指明我们在获取三方的api时需要哪些参数,进而让gpt知道它应该从What's the weather like in Boston? 这句话中提取什么样的信息,以及什么格式的信息。

第二步:拿着解析出来的信息请求接口

import json
# 这是一个虚假的函数,用来模拟三方接口的返回值
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)

if response_message.get("function_call"):
        # Step 3: call the function
        # Note: the JSON response may not always be valid; be sure to handle errors
        available_functions = {
            "get_current_weather": get_current_weather,
        }  # only one function in this example, but you can have multiple
        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(
            location=function_args.get("location"),
            unit=function_args.get("unit"),
        )
"""
输出的function_response:
{"location": "Boston, MA", "temperature": "72", "unit": null, "forecast": ["sunny", "windy"]}
"""

在这一步中,我们通过 function_name = response_message["function_call"]["name"] 这个来获取到了gpt模型解析出的函数名,因为在更复杂的场景中,我们可能一次性让gpt解析更多的方法,因此我们需要获取到所有的函数名。

接着我们又通过response_message["function_call"]["arguments"]这个获取到了解析出来的参数。这边的参数其实就gpt通过我们的问题提取到的能够被用到get_current_weather这个函数中作为参数的内容。比如现在我们需要的两个参数是locationunit, gpt就会根据现有的这个问题来提取是localtionunit意思的信息。

第三步生成一个summary

 messages.append(response_message)  # extend conversation with assistant's reply
        messages.append(
            {
                "role": "function",
                "name": function_name,
                "content": function_response,
            }
        )  # extend conversation with function response
        second_response = openai.ChatCompletion.create(
            model="gpt-3.5-turbo-0613",
            messages=messages,
        )  # get a new response from GPT where it can see the function response

在最后一步只是单纯的根据上下文信息生成一个新的句子,其实没有什么更多的信息。

接下来我们通过另一个稍微复杂一点点的案例再来解释下function calling的用法

举个栗子 让GPT机器翻译

现在我们想让GPT帮我做一个翻译的功能,并且翻译的信息可以结构化的输出,于是我写了这么一个prompt:

prompt_template = """
 Now you have to translate the following text into {lang}:
    ### Text to translate:
    {text}
    ### Target language:
    {lang}
    ### Output format:
    your translation should in the following format:
    {{'res': 'your translation'}}
"""

其实,在大部分情况只下,这个prompt就已经可以输出一份结构化的数据了,但是在复杂场景比如你的文本较长,或者尝试翻译大量文本的时候,模型总会出现一些额外的信息,不仅仅只有你想要的这个json格式,可能还有其他连带出来的输出。

现在我想要做的是,使用function calling 来解析我们的输出,我只想要最后的翻译结果。比较省钱但是费力的方法就是我直接提取最终返回数据中的 {res : xxx} 这个字符串,并获得它的值。 但是在这里我们还是打算使用function calling来帮我们解析这个数据。

现在我们的翻译的执行是这样的:

text = 'Collection of Open Source Projects Related to GPT,GPT相关开源项目合集🚀、精选🔥🔥'
lang = 'en'

prompt = prompt_template.format(text=text, lang=lang)
# completion是我们封装的一个转发openai请求的函数,没有什么特别的实现
response = completion(model_name="gpt-3.5-turbo-0613", prompt=prompt)

response.content
"""
输出:
{'res': 'Collection of Open Source Projects Related to GPT, Compilation of open source projects related to GPT 🚀, selected 🔥🔥'}
"""

现在我们定义一个ResponseSchema来代表这个输出。 因为在复杂的生产场景中,我们往往需要解析的对象是很复杂的,是有多个字段以及不同类型的,我们不可能像官方的例子一样手动去去写每一个parameters。

在这里我使用了python的pydantic包来实现这么一个功能。

from pydantic import BaseModel, Field
import json

class ResponseSchema(BaseModel):
    result: str = Field(..., description="the translated value")

    @classmethod
    def schema(cls, by_alias=True):
        schema = super().schema(by_alias)
        schema.pop('title', None)
        return schema
    @classmethod
    def schema_json(cls, by_alias=True, **dumps_kwargs):

        schema_str = json.dumps(cls.schema(by_alias), **dumps_kwargs)
        return json.loads(schema_str)

在这里我手动的实现了schemaschema_json 函数,因为 pydantic的模型类总会自带一个title的属性,这个属性很大程度上会影响GPT对我们最终想要的输出的理解。

我们执行ResponseSchema.schema_json()就可以获取到和上面官方例子一样的一个关于字段描述的结果:

{'type': 'object',
 'properties': {'result': {'title': 'Result',
   'description': 'the translated value',
   'type': 'string'}},
 'required': ['result']}

于是我们的function只需要写成这样就可以了:

functions=[
            {
                "name": "print_translation",
                "description": "print the translated value from value of res",
                "parameters": ResponseSchema.schema_json(),
            },
        ]

在这个function中,我们的print_translation 函数其实是虚假的函数,我们只是给他起了一个合理的名字以及合理的描述,这样GPT就可以知道我们的参数的上下文是什么样的了。

完整的一个flow如下:

import openai
from pydantic import BaseModel, Field
import json


def completion(model_name, prompt):
  completion = openai.ChatCompletion.create(
            stream=False,
            model=model_name,
            messages=[
                {"role": "user", "content": prompt}
            ]
        )


  return completion.choices[0].message

class ResponseSchema(BaseModel):
    result: str = Field(..., description="the translated value")

    @classmethod
    def schema(cls, by_alias=True):
        schema = super().schema(by_alias)
        schema.pop('title', None)
        return schema
    @classmethod
    def schema_json(cls, by_alias=True, **dumps_kwargs):

        schema_str = json.dumps(cls.schema(by_alias), **dumps_kwargs)
        return json.loads(schema_str)

###  让GPT帮我生成对应语句的翻译,并返回成json
prompt_template = """
 Now you have to translate the following text into {lang}:
    ### Text to translate:
    {text}
    ### Target language:
    {lang}
    ### Output format:
    your translation should in the following format:
    {{'res': 'your translation'}}
"""


text = 'Collection of Open Source Projects Related to GPT,GPT相关开源项目合集🚀、精选🔥🔥'
lang = 'en'

functions=[
            {
                "name": "print_translation",
                "description": "print the translated value from value of res",
                "parameters": ResponseSchema.schema_json(),
            },
       ]

prompt = prompt_template.format(text=text, lang=lang)
response = completion(model_name="gpt-3.5-turbo-0613", prompt=prompt)
final_result = openai.ChatCompletion.create(
		model="gpt-3.5-turbo-0613",
		messages=[{"role": "user", "content": prompt}],
		functions=functions,
        function_call="auto",
    )
message = final_result["choices"][0]["message"]
eval(message['function_call']['arguments'])['result']

"""
最终输出:
Collection of Open Source Projects Related to GPT, GPT Related Open Source Project Collection 🚀, Selected 🔥🔥
"""

总结

在本文中,我们深入探索了OpenAI的函数调用特性,并解析了其在实现结构化数据生成中的巨大价值。虽然我们通常认为生成模型产生的数据是无结构的,但在文章中证明,借助函数调用特性,我们完全有可能创建出结构化的数据。

通过编程示例,我们详细解释了函数调用的工作原理,包括如何向GPT接口提交带有问题和函数调用的请求,如何利用模型提取出的信息去请求第三方接口获取实时数据,以及如何生成一个全新的总结。

接着,我们展示了如何借助函数调用特性实现机器翻译功能,并结构化输出翻译的信息。本文向大家展示了,通过为虚拟函数定义合理的名字和描述,GPT模型能够理解参数的上下文,并按照预设的格式生成结构化的翻译结果。

总的来说,本文为大家提供了一个实用的、结构化的数据生成方法,希望这对于您在大规模模型的数据解析以及多样化的信息提取上能够带来实质性的帮助。

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

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

相关文章

3-Linux实操

Linux实践操作 开关机、重启、用户登陆注销关机&重启用户登陆和注销 用户管理添加用户修改用户密码删除用户查询用户信息切换用户查看当前用户用户组的添加和删除用户和组相关文件 实用指令指定运行级别init 命令帮助指令文件目录类时间日期类搜索查找类&#x1f50d;压缩和…

新能源汽车直流充电桩和交流充电桩的区别

直流充电桩和交流充电桩的区别 你是否曾经想过&#xff0c;为什么有的电动汽车可以在半小时内充满电&#xff0c;而有的却需要几个小时?其实&#xff0c;这都取决于它们所使用的充电桩的不同。那么&#xff0c;直流充电桩和交流充电桩到底有哪些区别呢? 首先&#xff0c;工作…

网络变压器配套使用的网口连接器的选型注意事项及选购关注要点

Hqst盈盛&#xff08;华强盛&#xff09;电子导读&#xff1a;采购人员在网口连接器选型中如何选用到合适的产品&#xff0c;选用时要注意到哪些事项&#xff0c;这节将结合网口连接器实物和大家一起探讨&#xff0c;希望对大家有些帮助。 我们可以通过对下面五个方面的详细了解…

网工内推 | 售前、售后工程师,IE认证优先

01 广州佳杰科技有限公司 招聘岗位&#xff1a;IT售前工程师 职责描述&#xff1a; 1、负责所在区域 IT 产品的售前技术支持工作,包括客户交流、方案编写、配置报价、投标应标、测试、赋能等; 2、与厂商相关人员建立和保持良好的关系,相互配合,提高项目成功率和厂商满意度; 3、…

每日一题——链表中倒数最后k个结点

题目 输入一个长度为 n 的链表&#xff0c;设链表中的元素的值为 ai &#xff0c;返回该链表中倒数第k个节点。 如果该链表长度小于k&#xff0c;请返回一个长度为 0 的链表。 数据范围&#xff1a;0≤n≤$105&#xff0c;0≤ai≤109&#xff0c;0≤k≤109 要求&#xff1a;…

MongoDB索引结构,到底是B-Tree还是B+Tree,请看这里!!

起因 网上关于MongoDB的索引结构到底是b树&#xff0c;还是b树的争论有很多&#xff0c;无法统一结论。 由来 MongoDB从3.2版本开始默认采用了WiredTiger存储引擎&#xff0c;网上很多说法是此引擎是BTree的索引结构&#xff0c;甚至有图有真相。但是认为MongoDB一直是B-Tre…

Docker compose(容器编排)

Docker compose&#xff08;容器编排&#xff09; 一、安装Docker compose 1.安装Docker compose Docker Compose 环境安装 Docker Compose 是 Docker 的独立产品&#xff0c;因此需要安装 Docker 之后在单独安装 Docker Compose#下载 curl -L https://github.com/docker/co…

htmlCSS-----定位

目录 前言 定位 分类和取值 定位的取值 1.相对定位 2.绝对位置 元素居中操作 3.固定定位 前言 今天我们来学习html&CSS中的元素的定位&#xff0c;通过元素的定位我们可以去更好的将盒子放到我们想要的位置&#xff0c;下面就一起来看看吧&#xff01; 定位 定位posi…

HR SaaS厂商,进入决赛圈

在愈发需要降本增效的节点&#xff0c;数字化的价值也更在被越来越多的企业重新审视&#xff0c;这种重视不再是之前或有或无的可选项&#xff0c;而是基于真正人效比维度的必选项 作者|斗斗 编辑|皮爷 出品|产业家 SaaS行业&#xff0c;正在发生一些微妙的变化。 以HR …

如何提高小程序UV?实用策略助你增加用户规模和活跃度

摘要&#xff1a;小程序的UV&#xff08;Unique Visitors&#xff09;是衡量用户规模和活跃度的重要指标&#xff0c;对于开发者和运营者来说具有重要意义。本文将分享一些实用策略&#xff0c;帮助你提高小程序的UV&#xff0c;增加用户规模和活跃度。从优化推广渠道、提升用户…

css实现鼠标滑动左下角弹框带动画效果

代码 <div classNamekuang></div> css代码 .kuang {height: 500px;width: 400px;// background-color: #fff;position: absolute;z-index: 10;bottom: 0;transform: translateX(-390px)}.kuang:hover {animation: myanimation 3s linear 1;animation-fill-mode:f…

LT9211C 是一款MIPI/RGB/2PORT LVDS互转的芯片

LT9211C 1.描述&#xff1a; Lontium LT9211C是一个高性能转换器&#xff0c;可以在MIPI DSI/CSI-2/双端口LVDS和TTL之间相互转换&#xff0c;除了24位TTL到24位TTL&#xff0c;并且不推荐同步和DE的2端口10位LVDS和24位TTL之间的转换。LT9211C反序列化输入的MIPI/LVDS/TTL视…

认识雪花id

首先,个人理解,雪花id不是全球的,它只能保证一个分布式服务的范围内的ID是不重复的. 一.SnowFlake 雪花算法 SnowFlake 中文意思为雪花&#xff0c;故称为雪花算法。最早是 Twitter 公司在其内部用于分布式环境下生成唯一 ID。在2014年开源 scala 语言版本。 雪花算法的原理…

项目经理好,还是产品经理好?

我首先介绍一下产品经理和项目经理的区别&#xff0c;然后再说一下产品经理和项目经理的薪资差距&#xff0c;然后你自己决定做产品经理还是项目经理。 1、产品经理和项目经理的区别&#xff1a; 产品经理和项目经理的不同之处在于&#xff0c;产品经理注重思考&#xff0c;关…

操作系统18:磁盘I/O速度、磁盘可靠性、数据一致性

目录 1、提高磁盘I/O速度的途径 &#xff08;1&#xff09;磁盘高速缓存(Disk Cache) 1.1 - 数据交付(Data Delivery)方式 1.2 - 置换算法 1.3 - 周期性地写回磁盘 &#xff08;2&#xff09;提高磁盘I/O速度的其它方法 2.1 - 提前读 2.2 - 延迟写 2.3 - 优化物理块的…

存储简单了解

存储目前常用的有磁盘&#xff08;磁性存储器&#xff09;和固态硬盘&#xff08;半导体存储器&#xff09; 磁盘由盘片&#xff0c;磁头和移动磁头的机械装置组成。磁盘从空间结构上分为扇区和磁道&#xff0c;每个扇区存储大小一致。 固态硬盘由多个闪存芯片组成&#xff0c;…

性能测试怎么做?一文从5个方面带你做性能测试

大家好&#xff0c;今天小濠从5个方面来介绍性能测试 一、什么是性能测试 二、性能测试的目的 三、如何做性能测试 四、性能测试关注的指标 五、性能结果分析 一、什么是性能测试 是不断的通过不同场景的系统表现去探究系统设计与资源消耗之间的平衡。 我们可以认为性能测试是…

ARM day8 key1/2/3led

key_led.h #ifndef _KEY_H_ #define _KEY_H_#include "stm32mp1xx_rcc.h" #include "stm32mp1xx_gpio.h" #include "stm32mp1xx_exti.h" #include "stm32mp1xx_gic.h"//EXTI编号 typedef enum {EXTI0,EXTI1,EXTI2,EXTI3,EXTI4,EXTI5,…

D. Maximum Subarray

Problem - 1796D - Codeforces 思路&#xff1a;想了个假dp做法推了半天&#xff0c;果然是dp。考虑用dp[i][j]表示以i结尾的&#xff0c;并且选择j个&#xff0b;x的最长连续子序列&#xff0c;那么如果我不选择第i位&#xff0c;那么会有f[i][j]max(w[i]-x,f[i-1][j]w[i]-x)&…

理解基本的Android编程(2/2)

6、Android Activity 活动代表了一个具有用户界面的单一屏幕&#xff0c;如 Java 的窗口或者帧。 如果你曾经用 C,C 或者 Java 语言编程&#xff0c;你应该知道这些程序从 main() 函数开始。很类似的&#xff0c;Android 系统初始化它的程序是通过活动中的 onCreate() 回调的…