大模型Function Call的原理及其应用
大模型Function Call的应用
Section titled “大模型Function Call的应用”- 了解什么是Function Call
- 理解Function Call工作的原理
- 掌握Function Call开发应用的代码
1 什么是Function Call
Section titled “1 什么是Function Call”2023年6月13日 OpenAI 公布了 Function Call(函数调用) 功能,Function Call 允许开发者向 GPT-4 和 GPT-3.5-turbo 模型描述函数,模型会智能地选择输出一个包含调用这些函数参数的JSON对象。这是一种更可靠地将 GPT 的功能与外部工具和 API 相连接的新方法。
GPT4 及 GPT-3.5-turbo 模型之所以能够使用函数 Function Call 功能,是因为这些模型经过训练,不仅可以检测到何时需要调用函数(根据用户的输入),并且又可以回复符合函数参数的 JSON对象,而不是直接返回常规的文本,简而言之:函数调用使开发者能够更可靠地从模型中获得结构化数据。
那么 Function Call 可以解决大模型什么问题:
- 信息实时性:大模型训练时使用的数据集往往有时间限制,无法包含最新的信息,如最新的新闻、实时股价等。通过Function Call,模型可以实时获取最新数据,提供更加时效的服务。
- 数据局限性:模型的训练数据量庞大但有限,无法覆盖所有可能的查询,如医学、法律等领域的专业咨询。Function Call允许模型调用外部数据库或API,获取特定领域的详细信息。
- 功能扩展性:大模型虽然功能强大,但不可能内置所有可能需要的功能。通过Function Call,可以轻松扩展模型能力,如调用外部工具进行复杂计算、数据分析等。
总的来说,Function Call功能的出现,极大地提升了大型语言模型的实用性和灵活性,使其能够更好地服务于用户的各种需求。
目前支持Function Call功能的模型除了GPT模型外,国内的模型也支持,如:百度文心一言,ChatGLM3-6B、讯飞星火3.0等。
2 Function Call 工作原理
Section titled “2 Function Call 工作原理”接下来,我们通过举例分别对比有无Function Call功能时GPT模型工作流程的差异:
当没有函数调用(funciton-call)时候,我们调用GPT构建AI应用的模式非常简单。
- 主要步骤:
- 用户(client)发请求给我们的服务(chat server)
- 我们的服务(chat server)给GPT提示词
- 重复执行

当有函数调用(funciton-call)时候,我们调用GPT构建AI应用的模式比之前要复杂一些。
- 主要步骤:
- 用户(client)发请求提示词以及可以调用的函数给我们的服务(chat server)
- GPT模型根据用户的提示词,判断是用普通文本还是函数调用的格式响应我们的服务(chat server)
- 如果是函数调用格式,那么Chat Server就会执行这个函数,并且将结果返回给GPT
- 然后模型使用提供的数据,用连贯的文本响应。返回

需要注意的是,大模型的 Function call 不会执行任何函数调用,仅返回调用函数所需要的参数。开发者可以利用模型输出的参数在应用中执行函数调用。
3 Function Call 实践应用
Section titled “3 Function Call 实践应用”假设我们要创建一个具备查询实时天气的聊天机器人。
3.1 定义外部函数
Section titled “3.1 定义外部函数”- 查询某地的天气函数:get_current_weather(location: str)
def get_current_weather(location): """得到给定地址的当前天气信息""" with open('./cityCode_use.json', 'r') as file: # 使用 json.load() 函数加载 JSON 数据 data = json.load(file) city_code = "" weather_info = {} for loc in data: if location == loc["市名"]: city_code = loc["编码"] if city_code: weather_url = "http://t.weather.itboy.net/api/weather/city/" + city_code response = requests.get(weather_url) result1 = eval(response.text) forecast = result1["data"]["forecast"][0] weather_info = { "location": location, "high_temperature": forecast["high"], "low_temperature": forecast["low"], "week": forecast["week"], "type": forecast["type"], } return json.dumps(weather_info, ensure_ascii=False)3.2 描述函数功能
Section titled “3.2 描述函数功能”- 为了向模型描述外部函数库,需要向 tools 字段传入可以调用的函数列表。参数如下表:
| 参数名称 | 类型 | 是否必填 | 参数说明 |
|---|---|---|---|
| type | String | 是 | 设置为function |
| function | Object | 是 | |
| name | String | 是 | 函数名称 |
| description | String | 是 | 用于描述函数功能,模型会根据这段描述决定函数调用方式。 |
| parameters | Object | 是 | parameters字段需要传入一个Json Schema对象,以准确地定义函数所接受的参数。若调用函数时不需要传入参数,省略该参数即可。 |
| required | 否 | 指定哪些属性在数据中必须被包含。 |
- 样例:
tools = [ { "type": "function", "function": { "name": "get_current_weather", "description": "获取给定位置的当前天气", "parameters": { "type": "object", "properties": { "location": { "type": "string", "description": "城市或区,例如北京、海淀", }, }, "required": ["location"], }, } }]3.3 模型应用Function Call
Section titled “3.3 模型应用Function Call”3.3.1创建 client
Section titled “3.3.1创建 client”- 这里默认使用ZhipuAI,第一次注册免费赠送100万token,没有 key 可以自己去 智普开发平台注册一下。
zhupu_api = "494b*******************************"client = ZhipuAI(api_key=zhupu_ak)3.3.2 模型应用(第一次)
Section titled “3.3.2 模型应用(第一次)”-
第一次模型得到回复时符合函数参数的 JSON对象
-
比如:我们想查询”今天北京的天气如何?“。我们向模型提供这个信息:
def chat_completion_request(messages, tools=None, tool_choice=None, model="glm-4"): try: response = client.chat.completions.create( model=model, messages=messages, tools=tools, tool_choice=tool_choice, ) return response except Exception as e: print("Unable to generate ChatCompletion response") print(f"Exception: {e}") return e
messages = []messages.append({"role": "system","content": "你是一个天气播报小助手,你需要根据用户提供的地址来回答当地的天气情况,如果用户提供的问题具有不确定性,不要自己编造内容,提示用户明确输入"})messages.append({"role": "user", "content": "今天北京的天气如何?"})response=chat_completion_request(messages,tools=tools,tool_choice="auto",model="glm-4")# 模型返回结果:print(response.choices[0].message)# {'content': None, 'role': 'assistant', 'tool_calls':[{'id':'call_8688150014463468290', 'function': {'arguments': '{"location":"北京"}', 'name': 'get_current_weather'}, 'type': 'function'}]}关于 tool_choice 如果不写,则默认情况下模型将决定何时适合使用其中一个函数。
如果要控制模型如何选择函数调用,需要设置 tool_choice 参数。参数默认值为auto,此时模型根据上下文信息自行选择是否返回函数调用。
若将其设置为 {“name”: “your_function_name”} 时,可以强制 API 返回特定函数的调用。
还可以通过将 tool_choice 参数设置为 “none” 来强制 API 不返回任何函数的调用。
- 可以看到此时**模型成功触发对 get_current_weather 函数的应用** 参数为location=北京”。
3.3.3 定义处理 Function call 的函数
Section titled “3.3.3 定义处理 Function call 的函数”- 本质 Function call,就是通过大模型选择函数以及获取函数的参数。然后进行函数的运行
def parse_response(response): response_message = response.choices[0].message # 检测是否需要调用函数 if response_message.tool_calls: # 调用函数 available_functions = { "get_current_weather": get_current_weather, } # only one function test in this example, but you can have multiple function_name = response_message.tool_calls[0].function.name fuction_to_call = available_functions[function_name] function_args = json.loads(response_message.tool_calls[0].function.arguments) function_response = fuction_to_call( location=function_args.get("location"), ) return function_response- 上述parse_response函数得到真实函数运行的结果
3.3.4 模型应用(第二次)
Section titled “3.3.4 模型应用(第二次)”- 基于上下文以及函数返回的结果,重新作为prompt输入模型,得到最终的结果
# extend conversation with assistant's reply messages.append(assistant_message.model_dump()) function_name = response.choices[0].message.tool_calls[0].function.name print(f'function_name--》{function_name}') function_id = response.choices[0].message.tool_calls[0].id print(f'function_id--》{function_id}') function_response = parse_response(response) # extend conversation with function response messages.append( { "role": "tool", "tool_call_id": function_id, "name": function_name, "content": function_response, } ) last_response = chat_completion_request( messages, tools=tools, tool_choice="auto" ) print(f'last_response--》{last_response.choices[0].message}')3.4 Function Call应用完整代码
Section titled “3.4 Function Call应用完整代码”-
该 Function Call 应用目的:创建一个具备查询实时天气的聊天机器人。
-
完整代码包含两个部分:一个main.py、tools.py。另外还包含一个cityCode的json文件,该文件的目的是实现城市名称到城市编码的映射,因为天气接口API是只能基于编码的结果进行查询。
- cityCode_use.json数据
[ { "市名": "北京", "编码": "101010100"},
{ "市名": "昌平", "编码": "101010700"}]- tools.py完整代码
import jsonimport requests
tools = [ { "type": "function", "function": { "name": "get_current_weather", "description": "获取给定位置的当前天气", "parameters": { "type": "object", "properties": { "location": { "type": "string", "description": "城市或区,例如北京、海淀", }, }, "required": ["location"], }, } }]
# todo:1.调用API接口,实现天气查询def get_current_weather(location): """得到给定地址的当前天气信息""" with open('./cityCode_use.json', 'r') as file: # 使用 json.load() 函数加载 JSON 数据 data = json.load(file) city_code = "" weather_info = {} for loc in data: if location == loc["市名"]: city_code = loc["编码"] if city_code: weather_url = "http://t.weather.itboy.net/api/weather/city/" + city_code response = requests.get(weather_url) result1 = eval(response.text) forecast = result1["data"]["forecast"][0] weather_info = { "location": location, "high_temperature": forecast["high"], "low_temperature": forecast["low"], "week": forecast["week"], "type": forecast["type"], } return json.dumps(weather_info, ensure_ascii=False)
# todo: 2.根据模型回复来确定使用工具函数:
def parse_response(response): response_message = response.choices[0].message # 检测是否需要调用函数 if response_message.tool_calls: # 调用函数 available_functions = { "get_current_weather": get_current_weather, } # only one function test in this example, but you can have multiple function_name = response_message.tool_calls[0].function.name fuction_to_call = available_functions[function_name] function_args = json.loads(response_message.tool_calls[0].function.arguments) function_response = fuction_to_call( location=function_args.get("location"), ) return function_response- main.py主函数
import osfrom dotenv import load_dotenv, find_dotenvfrom tools import *from zhipuai import ZhipuAI_ = load_dotenv(find_dotenv()) # 需要创建.env文件,存放zhupu_apizhupu_ak = os.environ['zhupu_api']client = ZhipuAI(api_key=zhupu_ak) # 填写您自己的APIKeyChatGLM = "glm-4"
def chat_completion_request(messages, tools=None, tool_choice=None, model=ChatGLM): try: response = client.chat.completions.create( model=model, messages=messages, tools=tools, tool_choice=tool_choice, ) return response except Exception as e: print("Unable to generate ChatCompletion response") print(f"Exception: {e}") return e
def main(): messages = [] messages.append({"role": "system", "content": "你是一个天气播报小助手,你需要根据用户提供的地址来回答当地的天气情况,如果用户提供的问题具有不确定性,不要自己编造内容,提示用户明确输入"}) messages.append({"role": "user", "content": "今天北京的天气如何"}) print(messages) response = chat_completion_request( messages, tools=tools, tool_choice="auto" ) assistant_message = response.choices[0].message print(f'assistant_message-->{assistant_message}') # extend conversation with assistant's reply messages.append(assistant_message.model_dump()) function_name = response.choices[0].message.tool_calls[0].function.name print(f'function_name--》{function_name}') function_id = response.choices[0].message.tool_calls[0].id print(f'function_id--》{function_id}') function_response = parse_response(response) messages.append( { "role": "tool", "tool_call_id": function_id, "name": function_name, "content": function_response, }
) # extend conversation with function response last_response = chat_completion_request( messages, tools=tools, tool_choice="auto" ) print(f'last_response--》{last_response.choices[0].message}')
if __name__ == '__main__': main()最终结果:last_response—》content=‘根据API调用结果,今天北京的天气情况如下:今天是星期五,北京有小雨,最高温度为21℃,最低温度为17℃。’ role=‘assistant’ tool_calls=None
4 多个Functions的应用实践
Section titled “4 多个Functions的应用实践”-
该 Function Call 应用目的:假设我们要创建一个具备查询航班功能的聊天机器人。
-
完整代码包含三个部分:一个muti_function_zhipu.py、airplane_function_tools.py、muti_utils.py。
-
airplane_function_tools.py
-
定义需要的工具类函数:描述函数功能
tools = [ { "type": "function", "function": { "name": "get_plane_number", "description": "根据始发地、目的地和日期,查询对应日期的航班号", "parameters": { "type": "object", "properties": { "start": { "description": "出发地", "type": "string" }, "end": { "description": "目的地", "type": "string" }, "date": { "description": "日期", "type": "string", } }, "required": ["start", "end", "date"] }, } }, { "type": "function", "function": { "name": "get_ticket_price", "description": "查询某航班在某日的价格", "parameters": { "type": "object", "properties": { "number": { "description": "航班号", "type": "string" }, "date": { "description": "日期", "type": "string", } }, "required": [ "number", "date"] }, } },]- muti_utils.py
- 定义两个外部函数供模型选择调用:查询两地之间某日航班号函数:get_flight_number(departure: str, destination: str, date: str);查询某航班某日票价函数:get_ticket_price(flight_number: str, date: str)
- 定义解析Function Call的函数
import json
def get_plane_number(date, start , end): plane_number = { "北京": { "深圳": "126", "广州": "356", }, "郑州": { "北京": "1123", "天津": "3661", } } return {"date": date, "number": plane_number[start][end]}
def get_ticket_price(date:str , number:str): print(date) print(number) return {"ticket_price": "668"}
def parse_function_call(model_response): '''
:param model_response: 模型返回的结果 :return: 返回函数的结果 ''' function_result = '' 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_plane_number": function_result = get_plane_number(**json.loads(args)) if tool_call.function.name == "get_ticket_price": function_result = get_ticket_price(**json.loads(args)) return function_result- muti_function_zhipu.py
- 主逻辑函数
import json
from zhipuai import ZhipuAIfrom dotenv import load_dotenv, find_dotenvfrom muti_utils import *from airplane_function_tools import *import os_ = load_dotenv(find_dotenv())# 获取环境变量 ZhiPu_API_KEYzhupu_ak = os.environ['zhupu_api']client = ZhipuAI(api_key=zhupu_ak) # 填写您自己的APIKeyChatGLM = "glm-4"
def chat_completion_request(messages, tools=None, tool_choice=None, model=ChatGLM): try: response = client.chat.completions.create( model=model, messages=messages, tools=tools, tool_choice=tool_choice, ) return response except Exception as e: print("Unable to generate ChatCompletion response") print(f"Exception: {e}") return e
def main(): messages = [] messages.append({"role": "system", "content": "现在你是一个航班查询助手,将根据用户问题提供答案,但是不要假设或猜测传入函数的参数值。如果用户的描述不明确,请要求用户提供必要信息 "}) messages.append({"role": "user", "content": "帮我查询2024年4月2日,郑州到北京的航班的票价"}) # 1.得到第一次回复:调用:get_plane_number函数 first_response = chat_completion_request( messages, tools=tools, tool_choice="auto")
assistant_message1 = first_response.choices[0].message print(f'assistant_message1-->{assistant_message1}') # 2. 将第一次得到的模型回复结果加入messages messages.append(first_response.choices[0].message.model_dump()) # 3. 第一次得到函数的结果 first_function = parse_function_call(model_response=first_response) print(f'first_function--》{first_function}') tool_call = first_response.choices[0].message.tool_calls[0] # 4. 将函数的结果添加到messages中,继续送入模型问答
messages.append({"role": "tool", "tool_call_id": tool_call.id, "content": str(json.dumps(first_function))}) # 5. 第二次调用模型 print(messages) second_response = chat_completion_request( messages, tools=tools, tool_choice="auto") print(f'second_response--》{second_response.choices[0].message}') # 6. 将第二次得到函数结果加入信息中 messages.append(second_response.choices[0].message.model_dump()) second_function = parse_function_call(model_response=second_response) print(f'second_function--》{second_function}') tool2_call = second_response.choices[0].message.tool_calls[0] # 4. 将函数的结果添加到messages中,继续送入模型问答 messages.append({"role": "tool", "tool_call_id": tool2_call.id, "content": str(json.dumps(second_function))})
last_response = chat_completion_request( messages, tools=tools, tool_choice="auto") print(f'last_response--》{last_response.choices[0].message}')if __name__ == '__main__': main()5 使用Function Call功能应用Sql查询
Section titled “5 使用Function Call功能应用Sql查询”- 该 Function Call 应用目的:假设我们要创建一个具备sql语句查询的聊天机器人。
- 完整代码包含三个部分:一个sql_function_tools.py、sql_zhipu.py。
- sql_function_tools.py
- 定义需要的工具类函数:描述函数功能
- 定义表的结构
- 定义表查询的函数
- 定义解析function call函数
import jsonimport requestsimport osimport pymysqlfrom dotenv import load_dotenv, find_dotenv
# todo: 1.描述数据库表结构(单一个表格)database_schema_string = """ CREATE TABLE `emp` ( `empno` int DEFAULT NULL, --员工编号, 默认为空 `ename` varchar(50) DEFAULT NULL, --员工姓名, 默认为空 `job` varchar(50) DEFAULT NULL,--员工工作, 默认为空 `mgr` int DEFAULT NULL,--员工领导, 默认为空 `hiredate` date DEFAULT NULL,--员工入职日期, 默认为空 `sal` int DEFAULT NULL,--员工的月薪, 默认为空 `comm` int DEFAULT NULL,--员工年终奖, 默认为空 `deptno` int DEFAULT NULL,--员工部分编号, 默认为空)"""
# todo: 2.描述数据库表结构(多个表格)database_schema_string1 = """CREATE TABLE `emp` ( `empno` int DEFAULT NULL, --员工编号, 默认为空 `ename` varchar(50) DEFAULT NULL, --员工姓名, 默认为空 `job` varchar(50) DEFAULT NULL,--员工工作, 默认为空 `mgr` int DEFAULT NULL,--员工领导, 默认为空 `hiredate` date DEFAULT NULL,--员工入职日期, 默认为空 `sal` int DEFAULT NULL,--员工的月薪, 默认为空 `comm` int DEFAULT NULL,--员工年终奖, 默认为空 `deptno` int DEFAULT NULL,--员工部分编号, 默认为空);
CREATE TABLE `DEPT` ( `DEPTNO` int NOT NULL, -- 部门编码, 默认为空 `DNAME` varchar(14) DEFAULT NULL,--部门名称, 默认为空 `LOC` varchar(13) DEFAULT NULL,--地点, 默认为空 PRIMARY KEY (`DEPTNO`));
"""tools = [ { "type": "function", "function": { "name": "ask_database", "description": "使用此函数回答业务问题,要求输出是一个SQL查询语句", "parameters": { "type": "object", "properties": { "query": { "type": "string", "description": f"SQL查询提取信息以回答用户的问题。" f"SQL应该使用以下数据库模式编写:{database_schema_string1}" f"查询应该以纯文本返回,而不是JSON。" f"查询应该只包含MySQL支持的语法。", } }, "required": ["query"], }, } }]
# todo:1.连接数据库,进行sql语句的查询
def ask_database(query): """连接数据库,进行查询""" # 1.连接到 MySQL 数据库 print("进入函数内部") conn = pymysql.connect( host='localhost', port=3306, user='', password='密码', database='数据库名称', charset='utf8mb4', # 指定游标类,返回结果为字典 ) # 2. 创建游标 cursor = conn.cursor() print(f'开始测试') # 3. 执行sql语句测试 # 示例:执行 SQL 查询 # sql = "SELECT * FROM emp" print(f'query--》{query}') cursor.execute(query) # 4. 获取查询结果 result = cursor.fetchall() # 5.关闭游标 cursor.close() # 6.关闭连接 conn.close() return result
# # todo: 2.根据模型回复来确定使用工具函数:
def parse_response(response): response_message = response.choices[0].message # 检测是否需要调用函数 if response_message.tool_calls: # 调用函数 available_functions = { "ask_database": ask_database } # only one function test in this example, but you can have multiple function_name = response_message.tool_calls[0].function.name fuction_to_call = available_functions[function_name] function_args = json.loads(response_message.tool_calls[0].function.arguments) function_response = fuction_to_call( query=function_args.get("query"), ) return function_response
if __name__ == '__main__': query = "select count(*) from emp" a = ask_database(query) print(a)- sql_zhipu.py
- 主逻辑函数
from zhipuai import ZhipuAIfrom dotenv import load_dotenv, find_dotenvfrom sql_function_tools import *import os_ = load_dotenv(find_dotenv())# 获取环境变量 ZhiPu_API_KEYzhupu_ak = os.environ['zhupu_api']client = ZhipuAI(api_key=zhupu_ak) # 填写您自己的APIKeyChatGLM = "glm-4"
def chat_completion_request(messages, tools=None, tool_choice=None, model=ChatGLM): try: response = client.chat.completions.create( model=model, messages=messages, tools=tools, tool_choice=tool_choice, ) return response except Exception as e: print("Unable to generate ChatCompletion response") print(f"Exception: {e}") return e
def main(): messages = [] messages.append({"role": "system", "content": "通过针对业务数据库生成 SQL 查询来回答用户的问题"}) messages.append({"role": "user", "content": "查询一下最高工资的员工姓名及对应的工资"}) response = chat_completion_request( messages, tools=tools, tool_choice="auto" ) assistant_message = response.choices[0].message print(f'assistant_message1-->{assistant_message}') function_name = response.choices[0].message.tool_calls[0].function.name function_id = response.choices[0].message.tool_calls[0].id function_response = parse_response(response) print(f'assistant_message.model_dump()-->{assistant_message.model_dump()}') messages.append(assistant_message.model_dump()) # extend conversation with assistant's reply messages.append( { "role": "tool", "tool_call_id": function_id, "name": function_name, "content": str(function_response), }
) # extend conversation with function response print(f'messages-->{messages}') last_response = chat_completion_request( messages, tools=tools, tool_choice="auto" ) print(f'last_response--》{last_response}') print(f'last_response--》{last_response.choices[0].message}')if __name__ == '__main__': main()6 小节总结
Section titled “6 小节总结”本章节介绍了大模型 Function call 功能的基本概念和使用方法,包括定义外部函数、描述函数功能、代码编写等。