OpenAI的多函数调用(Multiple Function Calling)简介

文章讲述了OpenAI在GPT模型中更新了函数调用功能,允许在单次对话中并发调用多个函数,降低了开发复杂度。作者通过实例展示了如何利用这一新特性,如实现百度、谷歌、必应搜索结果的汇总。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  我在六月份写了一篇关于GPT 函数调用(Function calling) 的博客https://blog.csdn.net/xindoo/article/details/131262670,其中介绍了函数调用的方法,但之前的函数调用,在一轮对话中只能调用一个函数。就在上周,OpenAI在开发者大会上,升级了函数调用的功能,在新的gpt-3.5和gpt-4模型中,可以在单次对话中调用多个函数了,而且在python SDK中也提供了并发函数调用相关的接口,无疑这将大幅减少大语言模型和现实世界之间交互的开发复杂度,接下来就让我用一个具体的示例,带你了解下OpenAI的新特性。
在这里插入图片描述
  这里假设我需要利用gpt实现一个百度、谷歌、必应三个搜索引擎搜索结果汇总的功能。我现在有以下的几个搜索函数(我们假装已经实现了从分别从百度、谷歌、必应获取搜索结果的逻辑)。

def search_baidu(keyword):
    """从百度搜索引擎中搜索关键词"""
    return f"{keyword}是一个技术博主"

def search_google(keyword):
    """从谷歌搜索引擎中搜索关键词"""
    return f"{keyword}是一个后端工程师"

def search_bing(keyword):
    """从必应搜索引擎中搜索关键词"""
    return f"{keyword}是一个Python爱好者"

  接下来我们需要将这三个搜索函数按照openai给定的格式用json字符串描述出来,具体可以参考官方文档,我这里直接给出上面三个函数的json描述。

tools = [
    {
        "type": "function",
        "function": {
            "name": "search_baidu",
            "description": "从百度搜索引擎中搜索关键词",
            "parameters": {
                "type": "object",
                "properties": {
                    "keyword": {
                        "type": "string",
                        "description": "搜索关键词",
                    }
                },
                "required": ["keyword"],
            },
        }
    },    
    {
        "type": "function",
        "function": {
            "name": "search_google",
            "description": "从google搜索引擎中搜索关键词",
            "parameters": {
                "type": "object",
                "properties": {
                    "keyword": {
                        "type": "string",
                        "description": "搜索关键词",
                    }
                },
                "required": ["keyword"],
            },
        }
    },        
    {
        "type": "function",
        "function": {
            "name": "search_bing",
            "description": "从bing搜索引擎中搜索关键词",
            "parameters": {
                "type": "object",
                "properties": {
                    "keyword": {
                        "type": "string",
                        "description": "搜索关键词",
                    }
                },
                "required": ["keyword"],
            },
        }
    }
]
available_functions = { "search_baidu": search_baidu, "search_google": search_google, "search_bing": search_bing } 

  上面这个的目的是将所有函数的作用和使用方法(入参)描述给gpt,让gpt知道如何去调用。available_functions是为了保存函数名和函数的映射关系,方便我们后续通过函数名去调用函数。

  接下来我们实现一个函数,其功能就是给定一个关键词(keyword),返回百度、谷歌、必应三个搜索引擎搜索结果的汇总,这要在之前的函数调用方式下,你必须通过多轮对话获取到所有需要调用的函数,然后将结果汇总后在发给gpt。而在支持了多函数调用后,仅需要一轮对话就可以完成所有的功能,完整的代码如下:

from openai import OpenAI
import json
client = OpenAI(base_url='https://thales.xindoo.xyz/openai/v1/')

def search(keyword):
    messages = [{"role": "user", "content": f"汇总下百度、谷歌、必应三个搜索引擎关于'{keyword}'的结果"}]
    # 发起首次请求,告诉gpt要做什么,已经有哪些函数可以调动 
    response = client.chat.completions.create(
        model="gpt-3.5-turbo-1106",
        messages=messages,
        tools=tools,
        tool_choice="auto", 
    )
    response_message = response.choices[0].message
    tool_calls = response_message.tool_calls
    # 检查是否需要调用函数
    if tool_calls:
        # 解析所有需要调用的函数及参数
        messages.append(response_message)  # 注意这里要将openai的回复也拼接到消息列表里
        # 将所有函数调用的结果拼接到消息列表里
        for tool_call in tool_calls:
            function_name = tool_call.function.name
            function_to_call = available_functions[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,
                }
            ) 
        second_response = client.chat.completions.create(
            model="gpt-3.5-turbo-1106",
            messages=messages,
        )  
        return second_response.choices[0].message.content
    
print(search("xindoo"))

  输出的结果是根据百度、谷歌和必应三个搜索引擎的结果,'xindoo'可能是一个技术博主、后端工程师以及Python爱好者。

这里需要提醒以下两点:

  1. 目前只有gpt-4-1106-preview和gpt-3.5-turbo-1106两个模式支持单词对话同时调用多个模型的,其他模型均不支持。
  2. openAI改变了api中传递function的参数,废弃了 functions和 function_call,改用了tools和tool_choice两个新参数,我猜测是为了未来增加更多的工具支持。

  这里额外说下,上面的三个函数调用是串行调用,如果每个函数都比较耗时的话,会增加整体的调用时长,而在最新的assistant api中增加了并行执行函数的api,这个我们放到下篇文章中讲解。

<think>嗯,用户遇到了Vue中关于个应用实例挂载到同一容器的警告问题,需要解决。首先,我需要回忆一下Vue的基本结构和应用实例的创建方式。Vue 3中,通常使用`createApp`来创建应用实例,然后调用`mount`方法挂载到指定的DOM容器上。 用户提到的警告信息是“Multiple instances of Vue detected”或者“$attrs is readonly”,这可能是因为在同一个容器上次调用了`mount`方法,导致重复挂载应用实例。例如,用户可能在某个组件或代码中不小心次创建了Vue应用,并挂载到同一个DOM元素上,比如`#app`。 接下来,我需要检查可能导致这种情况的原因。引用[1]中的情况提到,当使用Vuetify时,可能因为个Vue实例被创建而导致此问题。这提示用户需要确保在整个应用中只创建一个根实例,并避免重复挂载。 另外,引用[4]和[5]讨论了关于生命周期钩子的问题,比如`mounted`和`created`不执行的情况,这可能与`keep-alive`或重复的钩子函数有关,但用户的问题更直接的是关于应用实例的重复挂载,所以可能需要关注应用初始化的代码部分。 解决方案的第一步应该是检查入口文件,通常是`main.js`或类似的文件,确认是否次调用了`createApp`和`mount`。例如,用户可能在某个条件判断中不小心次执行了挂载操作,或者在动态加载组件时重复初始化应用。 另一个可能性是,用户在开发环境中使用了热重载(HMR),导致模块被重新执行时没有正确销毁之前的实例。这时,可以在挂载前检查是否已经存在实例,如果有,先卸载旧实例再挂载新的。例如,在挂载时保存实例引用,并在重新挂载前调用`unmount`方法。 此外,用户可能在路由或动态组件加载时错误地创建了新的应用实例,而不是复用现有的实例。需要确保在单页面应用(SPA)中,只有一个根实例,并通过路由来管理不同的视图,而不是为每个页面创建新的实例。 总结可能的解决步骤: 1. 确保入口文件中只创建和挂载一次应用实例。 2. 在挂载前检查是否已有实例,存在则先卸载。 3. 避免在组件内部或条件逻辑中重复创建和挂载实例。 4. 检查是否有第三方库或插件导致额外的实例创建。 5. 使用单例模式管理应用实例,确保全局唯一。 现在需要将这些思路整理成结构化的回答,并引用相关的资料来支持建议。例如,引用[1]中的情况说明重复实例的问题,引用[4]提到的生命周期钩子可能影响挂载过程,但主要问题还是应用实例的管理。同时,提供代码示例,展示如何正确销毁旧实例再挂载新实例,以及如何检查DOM容器是否已经被挂载。</think>### 解决Vue应用重复挂载到同一容器的警告 当Vue检测到同一DOM容器被次挂载应用实例时,会触发"Multiple app instances mounted on same container"警告。以下是逐步解决方案: #### 1. 检查应用初始化流程 在入口文件(通常是`main.js`)确保**单例模式**创建应用实例: ```javascript // 正确做法:全局唯一实例 const app = createApp(App) app.mount('#app') // 错误示例:重复创建实例 // createApp(App).mount('#app') // createApp(App).mount('#app') // 触发警告 ``` 引用[1]指出Vuetify等插件可能导致实例问题,需检查第三方库是否正确集成[^1]。 #### 2. 实现挂载前检查 在动态挂载场景下,添加实例状态检测: ```javascript let isMounted = false function initApp() { if (!isMounted) { const app = createApp(App) app.mount('#app') isMounted = true } } ``` #### 3. 处理热重载场景 开发环境下使用模块作用域保存实例引用: ```javascript let vueApp = null if (!vueApp) { vueApp = createApp(App) vueApp.mount('#app') } else { vueApp.unmount() // 先卸载旧实例 vueApp = createApp(App) vueApp.mount('#app') } ``` 引用[5]提到生命周期管理对挂载的影响,确保正确处理卸载流程[^5]。 #### 4. 验证DOM容器唯一性 检查HTML模板是否重复定义挂载点: ```html <!-- index.html --> <div id="app"></div> <!-- 唯一挂载点 --> <!-- 错误示例 --> <div id="app"></div> <div id="app"></div> <!-- 重复定义 --> ``` #### 5. 排查动态组件加载 使用`<component :is>`时避免创建新实例: ```vue <template> <!-- 正确方式:复用现有实例 --> <component :is="currentComponent" /> </template> ``` #### 6. 使用工厂函数封装 对于需要实例的场景(如微前端),显式管理容器: ```javascript function createMicroApp(containerId) { const container = document.createElement('div') container.id = containerId document.body.appendChild(container) return createApp(App).mount(`#${containerId}`) } ``` ###
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

xindoo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值