AI 文摘

大模型工具调用(functioncall)原理及实现





作者: 吃果冻不吐果冻皮 来源: 吃果冻不吐果冻皮

【点击】加入大模型技术交流群

原文:https://zhuanlan.zhihu.com/p/663770472

ChatGLM3地址:https://github.com/THUDM/ChatGLM3/tree/mainM3/tree/main

function call原理

今天ChatGLM3发布了,介绍说原生支持工具调用(听起来很厉害吧!),其实就是openai提供的Function Call功能。使用Function Call功能时,你需要定义(并不是真的写程序去定义一个函数,而仅仅是用文字来描述一个函数)一些function(需要指定函数名,函数用途的描述,参数名,参数描述),传给LLM,当用户输入一个问题时,LLM通过文本分析是否需要调用某一个function,如果需要调用,那么LLM返回一个json,json包括需要调用的function名,需要输入到function的参数名,以及参数值。总而言之,function call帮我们做了两件事情:1.判断是否要调用某个预定义的函数。2.如果要调用,从用户输入的文本里提取出函数所需要的函数值。

通过查询天气举个例子,这块我用openai的funciton call进行演示,chatglm3的权重还没有下完hhh:

首先我们定义一些function,如下所示,我只定义了一个查询天气的函数get_current_weather,我们需要对这个函数进行简单的描述,如“获取今天的天气”。这个函数有两个参数,分别是地点location和时间time,也需要进行文本描述。LLM根据函数描述,参数描述以及用户的输入,来决定是不是要调用这个funciton。

func = {  
        "name": "get_current_weather",  
        "description": "获取今天的天气",  
        "parameters": {  
            "type": "object",  
            "properties": {  
                "location": {  
                    "type": "string",  
                    "description": "获取天气情况的城市或者国家,比如北京、东京、新加坡"  
                },  
                "time": {  
                    "type": "string",  
                    "description": "时间信息"  
                },  
  
            },  
            "required": ["location", "time"]  
        }  
    }

当我们在openai的请求里添加了funtions相关的字段,他会增加一个判断是否需要调用function的环节。

以下以向ChatGPT输入“10月27日北京天气怎么样”为例:

请求里没有functions字段得到的结果如下,他会告诉你一大段答案(应该是假的),就是走Chatgpt正常的回答。

根据天气预报,10月27日北京的天气预计为晴到多云,气温较低。最高气温约为16摄氏度,最低气温约为4摄氏度。需要注意保暖措施,适时添衣物。', message=AIMessage(content='根据天气预报,10月27日北京的天气预计为晴到多云,气温较低。最高气温约为16摄氏度,最低气温约为4摄氏度。需要注意保暖措施,适时添衣物。'

请求里如果有functions字段,返回了一个json,并帮我们从输入文本里抽取了get_current_weather所需要的location和time的函数值(因为chatgpt训练数据截至到2021年,它还帮忙脑补了个年份hhh)。

 {'name': 'get_current_weather', 'arguments': '{\n  "location": "北京",\n  "time": "2021-10-27"\n}'}

本质上ChatGPT不就是在帮我们在做个文本结构化嘛,这个有啥用呢?我们知道,ChatGPT并不能帮我们真正的去查询天气,但是我们可以真的用python去定义一个get_current_weather函数,在这个函数里,编写程序(调用查询天气的工具包)去实现查询天气的功能,函数的输入就用ChatGPT返回的json里的内容,相当于我们通过编程帮ChatGPT提供了一个查询天气的工具,因此也可以管function call叫做“工具调用”。get_current_weather返回天气信息给ChatGPT,ChatGPT再回答给用户。整体流程如下图所示。我们可以定义各种功能的函数帮ChatGPT完成任务,比如查询网页,算数等,因此工具调用对于agent来说非常重要。

chatglm3 function call实现原理

把chatglm2和chatglm3模型文件diff一下就能发现,代码改动很少,增加了几个新的token,增加一些处理function字符串的逻辑。chatglm3官方function call示例如下,和chatgpt 定义的function结构几乎就是一致的。将包含函数描述的system_info 变量通过history传入到LLM的chat接口里。

tools = [  
    {  
        "name": "track",  
        "description": "追踪指定股票的实时价格",  
        "parameters": {  
            "type": "object",  
            "properties": {  
                "symbol": {  
                    "description": "需要追踪的股票代码"  
                }  
            },  
            "required": ['symbol']  
        }  
    },  
    {  
        "name": "text-to-speech",  
        "description": "将文本转换为语音",  
        "parameters": {  
            "type": "object",  
            "properties": {  
                "text": {  
                    "description": "需要转换成语音的文本"  
                },  
                "voice": {  
                    "description": "要使用的语音类型(男声、女声等)"  
                },  
                "speed": {  
                    "description": "语音的速度(快、中等、慢等)"  
                }  
            },  
            "required": ['text']  
        }  
    }  
]  
system_info = {"role": "system", "content": "Answer the following questions as best as you can. You have access to the following tools:", "tools": tools}  
  
history = [system_info]  
query = "帮我查询股票10111的价格"  
response, history = model.chat(tokenizer, query, history=history)  
print(response)

接下来,chatglm3的chat接口调用了tokenization_chatglm.py里边的build_chat_input函数,处理history的内容,具体代码如下。嗯,就是直接把json格式的函数描述(存在item[“tools”]里)直接转换成了字符串,然后和其他输入一并送入LLM,就是这么简单。归根到底function call能力就是在prompt上边做了手脚。那为什么chatglm3比别的模型更加强调自己工具调用能力强呢?当然是微调时候有function call格式的数据啦!


def build_chat_input(self, query, history=None, role="user"):
        if history is None:
            history = []
        input_ids = []
        for item in history:
            content = item["content"]
            if item["role"] == "system" and "tools" in item:
                content = content + "\n" + json.dumps(item["tools"], indent=4, ensure_ascii=False)
            input_ids.extend(self.build_single_message(item["role"], item.get("metadata", ""), content))
        input_ids.extend(self.build_single_message(role, "", query))
        input_ids.extend([self.get_command("<|assistant|>")])
        return self.batch_encode_plus([input_ids], return_tensors="pt", is_split_into_words=True)

历史文章: 2024年1月大模型文章集锦

更多AI工具,参考Github-AiBard123国内AiBard123

可关注我们的公众号:每天AI新工具