前不久 ChatGPT進行了一波重大更新,其中一個殺手級特性就是引入了函數調用功能。
開發者隻需要描述函數功能,函數參數和傳回值,ChatGPT就會根據需要發起調用請求。
目前 gpt-3.5-turbo-0613 和 gpt-4-0613 這兩個模型都支援函數調用,也就是普通使用者也能使用。
我們來仔細看看這個功能是如何使用,能帶來什麼變革?
其工作流程如下:
- 程式中實作一系列提供給ChatGPT的函數;
- 将這些函數放到一個json數組中,并且描述清楚每個函數的名字、功能、參數、傳回參數等;
- 向ChatGPT發起提問,同時将上述函數數組,提供給ChatGPT;
- ChatGPT根據問題情況,來決定是否要調用函數。如果需要,則将相應的函數和調用參數傳回給使用者程式;
- 使用者程式根據調用請求,執行相應的函數;
- 使用者将函數傳回值,添加到消息中,再次調用ChatGPT;
- ChatGPT根據這個傳回值,再重新歸納為自然語言,傳回給使用者。
上述過程,其中 3..6 是可以循環的,也就是說,完成一個響應,可能需要多輪的函數調用。
以下通過執行個體講解:
import openai
import json
openai.api_key = "sk-[your key]"
# 先實作一個功能函數,此處為虛假函數,固定傳回一個天氣狀況
# 在實際産品中,此處可以使用使用連網功能,調用網絡函數,或者操作具體的硬體裝置
def get_current_weather(location, unit="fahrenheit"):
"""輸入一個地區,傳回當地的天氣"""
weather_info = {
"location": location,
"temperature": "72",
"unit": unit,
"forecast": ["sunny", "windy"],
}
return json.dumps(weather_info)
def run_conversation():
# Step 1: 調用chatGPT進行一次對話,并傳入函數描述
# 具體的使用者問題
messages = [{"role": "user", "content": "What's the weather like in Boston?"}]
# 定義一系列可供chatGPT使用的函數
functions = [
{
"name": "get_current_weather", # 函數名
"description": "Get the current weather in a given location", # 函數描述,chatGPT根據描述判斷,是否要使用此函數幫忙解決問題
"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"],
},
}
]
# 向chatGPT發起請求
response = openai.ChatCompletion.create(
model="gpt-3.5-turbo-0613", # 使用新的模型
messages=messages,
functions=functions, # 函數清單
function_call="auto", # auto is default, but we'll be explicit
)
print("first call:")
print(response)
response_message = response["choices"][0]["message"]
# Step 2: 從傳回的消息中finish_reason的值來判斷,
# chatGPT的應答是函數調用請求(function_call),或者是最終結果(stop)
if response_message.get("function_call"):
# Step 3: 如果是函數請求,則根據請求參考,調用相應的函數
# "function_call": {
# "name": "get_current_weather",
# "arguments": "{\n \"location\": \"Boston, MA\"\n}"
# }
available_functions = {
"get_current_weather": get_current_weather,
} # only one function in this example, but you can have multiple
function_name = response_message["function_call"]["name"]
fuction_to_call = available_functions[function_name]
function_args = json.loads(response_message["function_call"]["arguments"])
# 調用本地 get_current_weather 函數
function_response = fuction_to_call(
location=function_args.get("location"),
unit=function_args.get("unit"),
)
# Step 4: 将本地函數的傳回值,合并到消息裡,然後再一次調用chatGPT
messages.append(response_message) # extend conversation with assistant's reply
messages.append(
{
"role": "function",
"name": function_name,
"content": function_response,
}
) # extend conversation with function response
second_response = openai.ChatCompletion.create(
model="gpt-3.5-turbo-0613",
messages=messages,
) # get a new response from GPT where it can see the function response
# 傳回問題的最終結果
return second_response
print(run_conversation())
第一次調用 openai.ChatCompletion.create 傳回的結果:
第二次調用 openai.ChatCompletion.create 傳回的結果:
在第一次響應資料可以看到,傳回的 finish_reson 值為 function_call,表明這并不是最終結果,而是一個函數調用的請求。這時使用者程式需要主動調用相應的函數。
第二次傳回時, finish_reason 為 stop,表示傳回的是最終結果。
再來看一個例子,讓ChatGPT通過SQL語句,查詢使用者音樂資料:
functions = [
{
"name": "ask_database",
"description": "Use this function to answer user questions about music. Output should be a fully formed SQL query.",
"parameters": {
"type": "object",
"properties": {
"query": {
"type": "string",
"description": f"""
SQL query extracting info to answer the user's question.
SQL should be written using this database schema:
{database_schema_string}
The query should be returned in plain text, not in JSON.
""",
}
},
"required": ["query"],
},
}
]
def ask_database(conn, query):
"""Function to query SQLite database with a provided SQL query."""
try:
results = str(conn.execute(query).fetchall())
except Exception as e:
results = f"query failed with error: {e}"
return results
def execute_function_call(message):
if message["function_call"]["name"] == "ask_database":
query = json.loads(message["function_call"]["arguments"])["query"]
results = ask_database(conn, query)
else:
results = f"Error: function {message['function_call']['name']} does not exist"
return results
OpenAI提供的這個方式,與目前流行的LangChain其實是相似,隻是經由ChatGPT官方微調後的函數調用功能,會更加穩定。
而由LangChain調用外部tool,則沒那麼穩定,而且還需要花額外的token與ChatGPT描述及溝通,成本相對高一些。
通過函數調用,我們增強了ChatGPT的能力,不僅限于連網能力,還包括數學能力,程式設計能力等。
甚至讓chatGPT通過函數調用,直接控制機器人,是不是很魔幻?
函數調用官方文檔
https://platform.openai.com/docs/guides/gpt/function-calling
更多例子
https://github.com/openai/openai-cookbook/blob/main/examples/How_to_call_functions_with_chat_models.ipynb
最近也看到免費的chatgpt入口推薦給大家,感興趣的拿走:
免費的 chatgpt:https://ai.y-p.cc/chat/
AI工具導航網站:https://ai.y-p.cc
#人工智能##一起來玩chatgpt##AI應用##為什麼chatGPT這麼火爆#