聊一下大模型的函數(shù)調(diào)用-Function call

你好,我是 shengjk1,多年大廠經(jīng)驗,努力構(gòu)建 通俗易懂的、好玩的編程語言教程。 歡迎關(guān)注!你會有如下收益:

  1. 了解大廠經(jīng)驗
  2. 擁有和大廠相匹配的技術(shù)等 希望看什么,評論或者私信告訴我!

一、 前言

GPT-4 出來后的一大特色就是 Function call,一直想去嘗試一下。后來智普出來了 GLM-4 模型也支持了 Function call,所以就來試一下。
在正式的了解這一塊之前呢,我一直以為 Function call 就是大模型可以執(zhí)行函數(shù)呢,從有了這個概念之后到目前為止一直沒有想明白,大模型是如何執(zhí)行函數(shù),腦海里的想法是:大模型調(diào)用 python 解釋器執(zhí)行。
但研讀了 GLM-4 和 GPT-4 的 Function call 之后,發(fā)現(xiàn)不是這樣的

二、函數(shù)調(diào)用

2.1 Function call 功能

函數(shù)調(diào)用功能可以增強模型推理效果或進行其他外部操作,包括信息檢索、數(shù)據(jù)庫操作、知識圖譜搜索與推理、操作系統(tǒng)、觸發(fā)外部操作等工具調(diào)用場景。

需要注意的是,大模型的 Function call 不會執(zhí)行任何函數(shù)調(diào)用,僅返回調(diào)用函數(shù)所需要的參數(shù)。開發(fā)者可以利用模型輸出的參數(shù)在應(yīng)用中執(zhí)行函數(shù)調(diào)用。

2.2 GLM-4 是如何進行函數(shù)調(diào)用的

假設(shè)我們要創(chuàng)建一個具備查詢航班功能的聊天機器人

2.2.1 定義外部函數(shù)

我們定義如下兩個外部函數(shù)供模型選擇調(diào)用:

    1. 查詢兩地之間某日航班號函數(shù):get_flight_number(departure: str, destination: str, date: str)
    1. 查詢某航班某日票價函數(shù):get_ticket_price(flight_number: str, date: str)
def get_flight_number(date:str , departure:str , destination:str):
    flight_number = {
        "北京":{
            "上海" : "1234",
            "廣州" : "8321",
        },
        "上海":{
            "北京" : "1233",
            "廣州" : "8123",
        }
    }
    return { "flight_number":flight_number[departure][destination] }
def get_ticket_price(date:str , flight_number:str):
    return {"ticket_price": "1000"}

2.2.2 描述函數(shù)功能

為了向模型描述外部函數(shù)庫,需要向 tools 字段傳入可以調(diào)用的函數(shù)列表。參數(shù)如下表:

參數(shù)名稱 類型 是否必填 參數(shù)說明
type String 設(shè)置為function
function Object
name String 函數(shù)名稱
description String 用于描述函數(shù)功能。模型會根據(jù)這段描述決定函數(shù)調(diào)用方式。
parameters Object parameters字段需要傳入一個 Json Schema 對象,以準確地定義函數(shù)所接受的參數(shù)。若調(diào)用函數(shù)時不需要傳入?yún)?shù),省略該參數(shù)即可。
required 指定哪些屬性在數(shù)據(jù)中必須被包含。

樣例:

tools = [
    {
        "type": "function",
        "function": {
            "name": "get_flight_number",
            "description": "根據(jù)始發(fā)地、目的地和日期,查詢對應(yīng)日期的航班號",
            "parameters": {
                "type": "object",
                "properties": {
                    "departure": {
                        "description": "出發(fā)地",
                        "type": "string"
                    },
                    "destination": {
                        "description": "目的地",
                        "type": "string"
                    },
                    "date": {
                        "description": "日期",
                        "type": "string",
                    }
                },
                "required": [ "departure", "destination", "date" ]
            },
        }
    },
    {
        "type": "function",
        "function": {
            "name": "get_ticket_price",
            "description": "查詢某航班在某日的票價",
            "parameters": {
                "type": "object",
                "properties": {
                    "flight_number": {
                        "description": "航班號",
                        "type": "string"
                    },
                    "date": {
                        "description": "日期",
                        "type": "string",
                    }
                },
                "required": [ "flight_number", "date"]
            },
        }
    },
]

2.3 Function call 代碼編寫

函數(shù)描述

tools = [
    {
        "type": "function",
        "function": {
            "name": "get_flight_number",
            "description": "根據(jù)始發(fā)地、目的地和日期,查詢對應(yīng)日期的航班號",
            "parameters": {
                "type": "object",
                "properties": {
                    "departure": {
                        "description": "出發(fā)地",
                        "type": "string"
                    },
                    "destination": {
                        "description": "目的地",
                        "type": "string"
                    },
                    "date": {
                        "description": "日期",
                        "type": "string",
                    }
                },
                "required": [ "departure", "destination", "date" ]
            },
        }
    },
    {
        "type": "function",
        "function": {
            "name": "get_ticket_price",
            "description": "查詢某航班在某日的票價",
            "parameters": {
                "type": "object",
                "properties": {
                    "flight_number": {
                        "description": "航班號",
                        "type": "string"
                    },
                    "date": {
                        "description": "日期",
                        "type": "string",
                    }
                },
                "required": [ "flight_number", "date"]
            },
        }
    },
]

創(chuàng)建 client

沒有 key 可以自己去 智普開發(fā)平臺注冊一下,目前注冊送 100萬 token

client = ZhipuAI(api_key='')

請求模型,這一塊僅僅是一個中間步驟
我們想查詢2024年1月20日從北京前往上海的航班。我們向模型提供這個信息:

messages = []
messages.append({"role": "user", "content": "幫我查詢從2024年1月20日,從北京出發(fā)前往上海的航班"})
response = client.chat.completions.create(
    model="glm-4",  # 填寫需要調(diào)用的模型名稱
    messages=messages,
    tools=tools,
    tool_choice='auto'
)
print(response.choices[0].message)
messages.append(response.choices[0].message.model_dump())

關(guān)于 tool_choice 如果不寫,則默認情況下模型將決定何時適合使用其中一個函數(shù)。

如果要控制模型如何選擇函數(shù)調(diào)用,需要設(shè)置 tool_choice 參數(shù)。參數(shù)默認值為auto,此時模型根據(jù)上下文信息自行選擇是否返回函數(shù)調(diào)用。

若將其設(shè)置為 {"name": "your_function_name"} 時,可以強制 API 返回特定函數(shù)的調(diào)用。

還可以通過將 tool_choice 參數(shù)設(shè)置為 "none" 來強制 API 不返回任何函數(shù)的調(diào)用。

輸出

content=None role='assistant' tool_calls=[CompletionMessageToolCall(id='call_8495942909317716104', function=Function(arguments='{"date":"2024-01-20","departure":"北京","destination":"上海"}', name='get_flight_number'), type='function')]

可以看到此時模型成功觸發(fā)對 get_flight_number 函數(shù)的調(diào)用 參數(shù)為:date="2024-01-20",departure="北京",destination="上海"

定義處理 Function call 的函數(shù),這參數(shù)高潮,其實所謂的 Function call,就是通過大模型選擇函數(shù)以及獲取函數(shù)的參數(shù)

def parse_function_call(model_response,messages):
    # 處理函數(shù)調(diào)用結(jié)果,根據(jù)模型返回參數(shù),調(diào)用對應(yīng)的函數(shù)。
    # 調(diào)用函數(shù)返回結(jié)果后構(gòu)造tool message,再次調(diào)用模型,將函數(shù)結(jié)果輸入模型
    # 模型會將函數(shù)調(diào)用結(jié)果以自然語言格式返回給用戶。
    if model_response.choices[0].message.tool_calls:
        tool_call = model_response.choices[0].message.tool_calls[0]
        args = tool_call.function.arguments
        function_result = {}
        if tool_call.function.name == "get_flight_number":
            function_result = get_flight_number(**json.loads(args))
        if tool_call.function.name == "get_ticket_price":
            function_result = get_ticket_price(**json.loads(args))
        messages.append({
            "role": "tool",
            "content": f"{json.dumps(function_result)}",
            "tool_call_id":tool_call.id
        })
        response = client.chat.completions.create(
            model="glm-4",  # 填寫需要調(diào)用的模型名稱
            messages=messages,
            tools=tools,
        )
        print(response.choices[0].message)
        messages.append(response.choices[0].message.model_dump())

請求模型獲取最終結(jié)果
查詢北京到廣州的航班:

# 清空對話
messages = []

messages.append({"role": "system", "content": "不要假設(shè)或猜測傳入函數(shù)的參數(shù)值。如果用戶的描述不明確,請要求用戶提供必要信息"})
messages.append({"role": "user", "content": "幫我查詢1月23日,北京到廣州的航班"})

response = client.chat.completions.create(
    model="glm-4",  # 填寫需要調(diào)用的模型名稱
    messages=messages,
    tools=tools,
)
print(response.choices[0].message)
messages.append(response.choices[0].message.model_dump())

parse_function_call(response,messages)

返回

content=None role='assistant' tool_calls=[CompletionMessageToolCall(id='call_8282666790542042140', function=Function(arguments='{"date":"2023-01-23","departure":"北京","destination":"廣州"}', name='get_flight_number'), type='function')]
content='根據(jù)您的要求,我已經(jīng)查詢到了1月23日從北京到廣州的航班號,航班號為8321。' role='assistant' tool_calls=None

查詢票價也是同理

三、完整代碼

def get_flight_number(date:str , departure:str , destination:str):
    flight_number = {
        "北京":{
            "上海" : "1234",
            "廣州" : "8321",
        },
        "上海":{
            "北京" : "1233",
            "廣州" : "8123",
        }
    }
    return { "flight_number":flight_number[departure][destination] }
def get_ticket_price(date:str , flight_number:str):
    return {"ticket_price": "1000"}
    


tools = [
    {
        "type": "function",
        "function": {
            "name": "get_flight_number",
            "description": "根據(jù)始發(fā)地、目的地和日期,查詢對應(yīng)日期的航班號",
            "parameters": {
                "type": "object",
                "properties": {
                    "departure": {
                        "description": "出發(fā)地",
                        "type": "string"
                    },
                    "destination": {
                        "description": "目的地",
                        "type": "string"
                    },
                    "date": {
                        "description": "日期",
                        "type": "string",
                    }
                },
                "required": [ "departure", "destination", "date" ]
            },
        }
    },
    {
        "type": "function",
        "function": {
            "name": "get_ticket_price",
            "description": "查詢某航班在某日的票價",
            "parameters": {
                "type": "object",
                "properties": {
                    "flight_number": {
                        "description": "航班號",
                        "type": "string"
                    },
                    "date": {
                        "description": "日期",
                        "type": "string",
                    }
                },
                "required": [ "flight_number", "date"]
            },
        }
    },
]

client = ZhipuAI(api_key='')


def parse_function_call(model_response,messages):
    # 處理函數(shù)調(diào)用結(jié)果,根據(jù)模型返回參數(shù),調(diào)用對應(yīng)的函數(shù)。
    # 調(diào)用函數(shù)返回結(jié)果后構(gòu)造tool message,再次調(diào)用模型,將函數(shù)結(jié)果輸入模型
    # 模型會將函數(shù)調(diào)用結(jié)果以自然語言格式返回給用戶。
    if model_response.choices[0].message.tool_calls:
        tool_call = model_response.choices[0].message.tool_calls[0]
        args = tool_call.function.arguments
        function_result = {}
        if tool_call.function.name == "get_flight_number":
            function_result = get_flight_number(**json.loads(args))
        if tool_call.function.name == "get_ticket_price":
            function_result = get_ticket_price(**json.loads(args))
        messages.append({
            "role": "tool",
            "content": f"{json.dumps(function_result)}",
            "tool_call_id":tool_call.id
        })
        response = client.chat.completions.create(
            model="glm-4",  # 填寫需要調(diào)用的模型名稱
            messages=messages,
            tools=tools,
        )
        print(response.choices[0].message)
        messages.append(response.choices[0].message.model_dump())

# 清空對話
messages = []

messages.append({"role": "system", "content": "不要假設(shè)或猜測傳入函數(shù)的參數(shù)值。如果用戶的描述不明確,請要求用戶提供必要信息"})
messages.append({"role": "user", "content": "幫我查詢1月23日,北京到廣州的航班"})

response = client.chat.completions.create(
    model="glm-4",  # 填寫需要調(diào)用的模型名稱
    messages=messages,
    tools=tools,
)
print(response.choices[0].message)
messages.append(response.choices[0].message.model_dump())

parse_function_call(response,messages)

四、總結(jié)

本文介紹了大模型 Function call 功能的基本概念和使用方法,包括定義外部函數(shù)、描述函數(shù)功能、代碼編寫等。同時,文章還提到了如果沒有 Function call,類似的問題也可以通過其他方式解決。本文適合初學(xué)者了解大模型 Function call 功能。

整體的過程:大模型 Function call 其實就是通過大模型找到對應(yīng)的函數(shù),然后再把函數(shù)執(zhí)行后的結(jié)果,返回給大模型,最后大模型給出結(jié)果。

五、擴展

如果沒有 Fcuntion call,類似的問題能解決嗎?當(dāng)然可以。
這里呢,我們葫蘆AI終身免費使用GPT-4來看一下效果
我們輸入 promot

現(xiàn)在有兩個函數(shù)的描述:

  


{
        "type": "function",
        "function": {
            "name": "get_flight_number",
            "description": "根據(jù)始發(fā)地、目的地和日期,查詢對應(yīng)日期的航班號",
            "parameters": 
                "type": "object",
                "properties": {
                    "departure": {
                        "description": "出發(fā)地",
                        "type": "string"
                    },
                    "destination": {
                        "description": "目的地",
                        "type": "string"
                    },
                    "date": {
                        "description": "日期",
                        "type": "string",
                    }
                },
                "required": [ "departure", "destination", "date" ]
            },
        }
    },
  


依據(jù)上述兩個函數(shù)的描述,我想查一下從北京到天津 20240322 的航班,請問該選擇哪個函數(shù),并以 json 的格式返回函數(shù)對應(yīng)的參數(shù)

返回結(jié)果

所以 Function call 的技術(shù)復(fù)雜度有多少,自然一目了然

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容