什么是Function Call
????????簡(jiǎn)單來說,就是大模型函數(shù)調(diào)用,不是你直接調(diào)用大模型函數(shù),而是你告訴大模型一個(gè)函數(shù),大模型根據(jù)你喂給它的數(shù)據(jù)和參數(shù)執(zhí)行函數(shù)調(diào)用返回給你想要的函數(shù)執(zhí)行結(jié)果。
????????你可以借助大模型的自然語(yǔ)言理解能力實(shí)現(xiàn)自然語(yǔ)言的函數(shù)調(diào)用。大模型這一能力大大增加了私有定制模型的擴(kuò)展性!
為什么有Function Call
- 大模型的缺陷:
- 有所不知
a.訓(xùn)練數(shù)據(jù)不可能什么都有;垂直、非公開數(shù)據(jù)必有欠缺。
b.不知道最新信息;大模型的訓(xùn)練周期很長(zhǎng),且更新一次耗資巨大,還有越訓(xùn)越傻的風(fēng)險(xiǎn)。所以它不可能實(shí)時(shí)訓(xùn)練。- 沒有"真邏輯"
它表現(xiàn)出的邏輯、推理,是訓(xùn)練文本的統(tǒng)計(jì)規(guī)律,而不是真正的邏輯,所以有幻覺。
- 大模型的優(yōu)勢(shì):
- 強(qiáng)大的語(yǔ)義泛化能力,能根據(jù)所說的話,識(shí)別出意圖
- 希望通過大模型來做更多的事,而不僅是簡(jiǎn)單的搜索和閑聊
因?yàn)椋捍竽P托枰B接真實(shí)世界,并對(duì)接真邏輯系統(tǒng)。
Function Call 技術(shù)可以把大模型和業(yè)務(wù)系統(tǒng)連接,實(shí)現(xiàn)更豐富的功能
Function call工作流程
- 用戶提出functions需求: 用戶用自然語(yǔ)言描述他們想要完成的任務(wù)。
- LLM理解意圖: 大模型分析用戶的語(yǔ)言,理解其意圖,并將其轉(zhuǎn)化為結(jié)構(gòu)化的請(qǐng)求。
- 選擇合適的function: 根據(jù)用戶的需求,從預(yù)先定義好的functions中選擇合適的function。
- 參數(shù)轉(zhuǎn)換與執(zhí)行: 將用戶的需求轉(zhuǎn)化為function能夠理解的參數(shù),并調(diào)用外部工具執(zhí)行操作。
- 結(jié)果處理與呈現(xiàn): 接收外部工具返回的結(jié)果, 由LLM將其轉(zhuǎn)化為用戶友好的自然語(yǔ)言回應(yīng)。
對(duì)大模型的要求
- 好的語(yǔ)義理解能力,能夠正確識(shí)別是否調(diào)用function、調(diào)用哪個(gè)function 以及對(duì)應(yīng)參數(shù);
- 對(duì)于缺失的參數(shù)要能主動(dòng)提問;
langchain實(shí)現(xiàn)
????????當(dāng)今大部分模型廠商都有支持function call的模型,需要選擇對(duì)應(yīng)的正確模型,定義好模型廠商對(duì)應(yīng)的functions書寫規(guī)范,function描述的必備要素:
- 函數(shù)名
- 函數(shù)的功能描述
- 函數(shù)的請(qǐng)求參數(shù)說明
- 函數(shù)的響應(yīng)參數(shù)說明(可選)
實(shí)例如下:
1. 定義functions
functions = [
{
"type":"function",
"function":{
"name": "get_current_speed",
"description": "Gets current speed of a given car",
"parameters": {
"type": "object",
"properties": {
"car_name": {
"type": "string",
"description": "The name of the car"
}
},
"required": ["car_name"]
}
}
},
{
"type":"function",
"function":{
"name": "get_interior_temperature",
"description": "Gets the interior temperature of a given car",
"parameters": {
"type": "object",
"properties": {
"car_name": {
"type": "string",
"description": "The name of the car"
}
},
"required": ["car_name"]
}
}
},
{
"type":"function",
"function":{
"name": "get_remaining_battery",
"description": "Gets the remaining battery of a given car",
"parameters": {
"type": "object",
"properties": {
"car_name": {
"type": "string",
"description": "The name of the car"
}
},
"required": ["car_name"]
}
}
}
]
2. 創(chuàng)建llm client
import os
import openai
import numpy as np
import json
import tenacity
from openai import OpenAI
client = OpenAI(
api_key='sk-xxxxxxxxxxxx',
base_url='https://api.chatanywhere.tech/v1'
)
GPT_MODEL = "gpt-3.5-turbo"
3. 定義Api調(diào)用接口
def generate_response_with_function_call(messages):
try:
response = client.chat.completions.create(
model= GPT_MODEL,
messages= messages,
max_tokens=100, # 生成的最大token數(shù)
temperature=0.7, # 生成的隨機(jī)性
tools=functions, # 定義的函數(shù)調(diào)用規(guī)范
tool_choice="auto",
)
return response.choices[0].message
except Exception as e:
print(f"Error generating response: {str(e)}")
return None
4. 調(diào)用函數(shù)及結(jié)果處理
- 封裝messages
messages= [
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "Car A 當(dāng)前速度是多少"}
]
- 定義本地function
def get_current_speed(car_name):
//調(diào)用其他接口來獲取speed
def get_interior_temperature(car_name):
//調(diào)用其他接口來獲取interior temperature
def get_remaining_battery(car_name):
//調(diào)用其他接口來獲取remaining battery
- 執(zhí)行Api調(diào)用及結(jié)果處理
first_response_message = generate_response_with_function_call(messages)
????????第一次請(qǐng)求大模型接口后,會(huì)從定義的functions里面輸出對(duì)應(yīng)的function信息:
//獲取function name
response_function_name = first_response_message.tool_calls[0].function.name
//獲取function arguments
response_function_argu = json.loads(first_response_message.tool_calls[0].function.arguments)
????????根據(jù)function name及arguments選擇調(diào)用對(duì)應(yīng)的本地function來獲取到當(dāng)前結(jié)果:
# 定義一個(gè)函數(shù)來基于大模型的調(diào)用結(jié)果,來執(zhí)行函數(shù)
def call_function(function_name, arguments):
if function_name == "get_interior_temperature":
car_name = arguments.get("car_name")
return get_interior_temperature(car_name)
elif function_name == "get_current_speed":
car_name = arguments.get("car_name")
return get_current_speed(car_name)
elif function_name == "get_remaining_battery":
car_name = arguments.get("car_name")
return get_remaining_battery(car_name)
return "Function not found"
# 執(zhí)行本地函數(shù)
function_response = call_function(response_function_name, response_function_argu)
????????將獲得的函數(shù)執(zhí)行結(jié)果補(bǔ)充進(jìn)messages,再次調(diào)用API:
messages.append(first_response_message)
messages.append({
"role":"tool",
"content":function_response,
"tool_call_id":response_message.tool_calls[0].id,
})
second_response = generate_response_with_function_call(messages)
# 讀取二次調(diào)用得到的content
final_message = second_response.content
print("最終輸出: " + str(final_message))
最終輸出: Car A當(dāng)前車速為70.0km/h
????????經(jīng)過兩次大模型的API調(diào)用,得到了合理的結(jié)果;
- 第一次大模型調(diào)用:根據(jù)用戶的query及定義的functions,得到對(duì)應(yīng)的function name及arguments
- 第二次大模型調(diào)用:結(jié)合外部請(qǐng)求結(jié)果,大模型進(jìn)行總結(jié)給出合理的回答