一、什么是函數調用功能
幾個月前OpenAI官方發布了其API的函數調用功能(Function calling),?在 API 調用中,您可以描述函數,并讓模型智能地選擇輸出包含調用一個或多個函數的參數的 JSON 對象。API函數“ChatCompletion”?雖然不會實際調用該函數;但是模型會生成這些基于函數參數的JSON對象,您可以使用它來調用代碼中的實際函數。
也就是說當用戶和ChatGPT對話的過程中需要調用某些外部的函數或者API時,我們可以讓ChatGPT生成調用外部函數所需的參數,然后我們再使用這些參數再去實際的調用外部函數,目前OpenAl 對 gpt-3.5-turbo-0613 和 gpt-4-0613 模型進行了微調,使它們具備了以下函數調用功能:
1. 接受額外的參數,用戶可以通過這些參數傳入函數的描述。
2. 如果相關,則返回要使用的函數的名稱,以及帶有適當輸入參數的 JSON 對象。
二,如何實現OpenAI的函數調用功能
在實現OpenAI的函數調用功能之前,我們先定義一個外部函數,當用戶和ChatGPT對話時,ChatGPT會自動判斷是否需要調用外部函數,當需要調用外部函數時ChatGPT會返回調用函數的json對象給用戶:
import json# 查詢天氣的模擬函數示例
# 在生產中,這可能是您的后端 API 或外部 API
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)
這里我們定義一個外部函數get_current_weather,他用來查詢特定城市的天氣情況,并返回一個jons對象作為查詢結果。接下來我們需要定義一個該函數的描述對象,該描述對象后面會作為參數傳遞給ChatGPT:
#函數描述對象
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"],},}
]
下面我們來說明一下函數描述對象的主要成員:
- name: 外部函數名稱如get_current_weather
- description:外部函數功能的描述
- parameters:外部函數的參數集
- parameters-type:外部函數的參數集的類型
- properties:外部函數的具體參數集
- location:具體的外部函數的參數
- location-type:外部函數的參數的類型
- location-description:外部函數的參數的描述
- unit:具體的外部函數的參數
- unit-type:外部函數的參數的類型
- enum:外部函數的參數的枚舉值
- required:必填的參數
這里我們生成了一個外部函數的描述對象,該描述對象會告訴ChatGPT該外部函數的作用,以及我們需要在恰當的時候來調用該函數,至于什么時候才是“恰當的時候”這需要由ChatGPT根據用戶對話的上下文來判斷。接下來我們向ChatGPT詢問一個關于天氣的問題:
import openai
openai.api_key = "XXXXXXXXX"messages = [{"role": "user","content": "上海的天氣怎么樣?"}
]response = openai.ChatCompletion.create(model="gpt-3.5-turbo-1106",messages=messages,functions=functions
)print(response)
這里我們向ChatGPT提出了關于天氣的問題:“上海的天氣怎么樣?”, 從ChatGPT的返回結果中我們看到"function_call",這告訴我們接下來我們該調用外部函數了,同時ChatGPT還返回了調用外部函數的參數location和unit,以及所需調用的外部函數名:get_current_weather,有意思的是這里返回的unit為“celsius”即攝氏度而非美國使用的"fahrenheit(華氏度)", 這似乎說明ChatGPT知道中國使用攝氏度作為溫度的單位,下面我們詢問一下美國城市的天氣:
messages = [{"role": "user","content": "What's the weather like in Boston?" #波士頓 的天氣怎么樣?}
]response = openai.ChatCompletion.create(model="gpt-3.5-turbo-1106",messages=messages,functions=functions
)print(response)
這里我們用英語詢問了美國城市波士頓的天氣情況,從ChatGPT的返回結果中我們看到arguments中只包含了location,而沒有包含unit, 而在我們的外部函數get_current_weather中unit為非必填參數,它有一個默認值為:unit="fahrenheit",因此在實際調用外部函數時我們只需將chatgpt返回結果中的arguments中取出對應的參數然后傳遞給外部函數即可,接下來我們從ChatGPT的返回結果中獲取參數來實際調用外部函數get_current_weather:
args = json.loads(response_message["function_call"]["arguments"])
result=get_current_weather(args)
print(result)
?接下來我們來測試一下ChatGPT能否準確識別何時該調用外部函數,下面我們會對ChatGPT發送一個簡單的問候語:hi,? 當Chatgpt收到該問候語時不應該觸發函數調用功能:
messages = [{"role": "user","content": "hi!",}
]response = openai.ChatCompletion.create(model="gpt-3.5-turbo-1106",messages=messages,functions=functions,
)print(response)
從上面的chatgpt的返回結果中我們看到不存在先前的“function_call"內容即沒有生成外部函數的調用參數,這說明此時我們不需要調用外部函數。
三、設置OPAI API的默認參數
openai的API函數ChatCompletion.create中存在一個function_call的參數,該參數的默認值為“auto”即讓模型自己來選擇是否需要調用外部函數:
messages = [{"role": "user","content": "hi!",}
]
response = openai.ChatCompletion.create(model="gpt-3.5-turbo-1106",messages=messages,functions=functions,function_call="auto",
)
print(response)
上面我們在openai.ChatCompletion.create的方法中加入了function_call="auto",意思是讓模型根據上下文來確定是否調用外部函數,我們看到當我們向ChatGPT打招呼時,如輸入“hi”時 ,chatgpt的返回結果中沒有“function_call”的內容。這說明ChatGPT知道此時不應該調用外部函數。
messages = [{"role": "user","content": "hi!",}
]
response = openai.ChatCompletion.create(model="gpt-3.5-turbo-1106",messages=messages,functions=functions,function_call="none",#禁止調用外部函數
)
print(response)
上面當我們將function_cal設置為"none"時(即禁止chatGPT調用外部函數),chatGPT的返回結果中也不會出現“function_call”的內容。
messages = [{"role": "user","content": "What's the weather in Boston?",}
]
response = openai.ChatCompletion.create(model="gpt-3.5-turbo-1106",messages=messages,functions=functions,function_call="none", #禁止調用外部函數
)
print(response)
在上面的代碼中我們向ChatGPT詢問了波士頓的天氣,但是我們設置了function_call="none",也就是說雖然我們詢問了波士頓的天氣情況,但我們卻禁止chatgpt調用外部函數,從chatgpt的返回結果中我們看到仍然沒有“function_cal”的相關內容。
下面我們設置chatgpt強制調用外部函數,看看會發生上面情況:
messages = [{"role": "user","content": "hi!",}
]
response = openai.ChatCompletion.create(model="gpt-3.5-turbo-1106",messages=messages,functions=functions,function_call={"name": "get_current_weather"},#強制調用外部函數
)
print(response)
在上面的代碼中我們在?ChatCompletion.create中設置了function_call={"name": "get_current_weather"}意思是讓chatgpt強制生成調用get_current_weather函數的參數,但是我們向chatgpt發送的用戶消息卻是:hi!, 這時會讓chatgpt產生困惑,因為用戶消息中沒有有關詢問天氣的內容,但是卻要強制chatgpt去生成外部函數的調用參數,所以在chatgpt的返回結果中function_call中的arguments中給出了一個隨機的location:San Francisco,CA。
下面我們向chatgpt詢問波士頓的天氣,并且讓chatgpt強制調用get_current_weather,看看會發生什么情況:
messages = [{"role": "user","content": "What's the weather like in Boston!",}
]
response = openai.ChatCompletion.create(model="gpt-3.5-turbo-1106",messages=messages,functions=functions,function_call={"name": "get_current_weather"}, #強制調用外部函數
)
print(response)
從上面的chatgpt的返回結果中我們看到了“function_call”中的內容。這說明只要我們設置了chatgpt強制指定了外部調用函數時,它總會生成相應的函數參數。
四、外部函數的調用結果的應用
上面我們讓chatgpt來判斷是否應該調用外部函數,并且讓chatgpt返回了調用外部函數的參數,接下來我們要做的是用chatgpt提供的參數去實際調用外部函數,并將外部函數的返回結果再喂給chatgpt,這樣做的目的是讓chatgpt來匯總所有的信息并產生最終對用戶友好的返回信息。
#整合chatgpt的返回結果
messages.append(response["choices"][0]["message"])
#從chatgpt的返回結果中獲取外部函數的調用參數
args = json.loads(response["choices"][0]["message"]['function_call']['arguments'])
#調用外部函數
observation = get_current_weather(args)messages.append({"role": "function","name": "get_current_weather","content": observation, #外部函數的返回結果}
)response = openai.ChatCompletion.create(model="gpt-3.5-turbo-0613",messages=messages,
)
print(response)
?這里我們看到ChatGPT最終返回了一個非常友好的回復,該回復是在外部函數調用結果的基礎上經過整理后得到的。
五、關于token統計
我們知道chatgpt的API是通過token來收費的,這里我們在使用chatgpt的函數調用功能時我們創建了一個函數描述對象functions,因此functions也會作為上下文的一部分被統計token數,下面我們去掉ChatCompletion.create中的functions和function_call這兩個參數看看最后chatgpt返回的總的token數是多少:
messages = [{"role": "user","content": "What's the weather like in Boston!",}
]
response = openai.ChatCompletion.create(model="gpt-3.5-turbo-1106",messages=messages
)
print(response)
從上面的返回結果中我們看到當我們去掉了ChatCompletion.create中的functions和function_call這兩個參數時,總token數為48,而先前的總token數為99,這說明外部函數描述對象functions被統計了token數。