聊一下大模型的函數調用-Function call

你好,我是 shengjk1,多年大廠經驗,努力構建 通俗易懂的、好玩的編程語言教程。 歡迎關注!你會有如下收益:

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

一、 前言

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

二、函數調用

2.1 Function call 功能

函數調用功能可以增強模型推理效果或進行其他外部操作,包括信息檢索、數據庫操作、知識圖譜搜索與推理、操作系統、觸發外部操作等工具調用場景。

需要注意的是,大模型的 Function call 不會執行任何函數調用,僅返回調用函數所需要的參數。開發者可以利用模型輸出的參數在應用中執行函數調用。

2.2 GLM-4 是如何進行函數調用的

假設我們要創建一個具備查詢航班功能的聊天機器人

2.2.1 定義外部函數

我們定義如下兩個外部函數供模型選擇調用:

    1. 查詢兩地之間某日航班號函數:get_flight_number(departure: str, destination: str, date: str)
    1. 查詢某航班某日票價函數: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 描述函數功能

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

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

樣例:

tools = [
    {
        "type": "function",
        "function": {
            "name": "get_flight_number",
            "description": "根據始發地、目的地和日期,查詢對應日期的航班號",
            "parameters": {
                "type": "object",
                "properties": {
                    "departure": {
                        "description": "出發地",
                        "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 代碼編寫

函數描述

tools = [
    {
        "type": "function",
        "function": {
            "name": "get_flight_number",
            "description": "根據始發地、目的地和日期,查詢對應日期的航班號",
            "parameters": {
                "type": "object",
                "properties": {
                    "departure": {
                        "description": "出發地",
                        "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

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

client = ZhipuAI(api_key='')

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

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

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

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

若將其設置為 {"name": "your_function_name"} 時,可以強制 API 返回特定函數的調用。

還可以通過將 tool_choice 參數設置為 "none" 來強制 API 不返回任何函數的調用。

輸出

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')]

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

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

def parse_function_call(model_response,messages):
    # 處理函數調用結果,根據模型返回參數,調用對應的函數。
    # 調用函數返回結果后構造tool message,再次調用模型,將函數結果輸入模型
    # 模型會將函數調用結果以自然語言格式返回給用戶。
    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",  # 填寫需要調用的模型名稱
            messages=messages,
            tools=tools,
        )
        print(response.choices[0].message)
        messages.append(response.choices[0].message.model_dump())

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

# 清空對話
messages = []

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

response = client.chat.completions.create(
    model="glm-4",  # 填寫需要調用的模型名稱
    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='根據您的要求,我已經查詢到了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": "根據始發地、目的地和日期,查詢對應日期的航班號",
            "parameters": {
                "type": "object",
                "properties": {
                    "departure": {
                        "description": "出發地",
                        "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):
    # 處理函數調用結果,根據模型返回參數,調用對應的函數。
    # 調用函數返回結果后構造tool message,再次調用模型,將函數結果輸入模型
    # 模型會將函數調用結果以自然語言格式返回給用戶。
    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",  # 填寫需要調用的模型名稱
            messages=messages,
            tools=tools,
        )
        print(response.choices[0].message)
        messages.append(response.choices[0].message.model_dump())

# 清空對話
messages = []

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

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

parse_function_call(response,messages)

四、總結

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

整體的過程:大模型 Function call 其實就是通過大模型找到對應的函數,然后再把函數執行后的結果,返回給大模型,最后大模型給出結果。

五、擴展

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

現在有兩個函數的描述:

  


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


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

返回結果

所以 Function call 的技術復雜度有多少,自然一目了然

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容