系列文章目录
GLM-4 (1) - 推理+概览
GLM-4 (2) - RoPE
GLM-4 (3) - GLMBlock
GLM-4 (4) - SelfAttention
GLM-4 (5) - API & Function Calling
GLM-4 (6) - KV Cache / Prefill & Decode
文章目录
前言
我们之前解析了
GLM-4
模型相关的部分,这有助于我们对理解和使用开源大模型。然后,有一些场景对于大模型的性能(比如某个任务的推理准确率)有较高的要求,
10B
以下参数的模型很可能无法胜任。那么就有两条路可以选择:1)收集与任务相关的数据,微调该模型;2)直接使用商业化的
API
服务。由于第一条路收集数据工作量较大,所以调用
API
是个不错的选择,也是本篇要讲述的内容。
大模型并不是万能的,推理的时候也会出错,比如近期的热搜就是大模型比较
9.11
和
9.8
大小的时候变成弱智。但是,大模型有
Function Calling
的加持,调用外部函数,就能有效缓解这些问题。
本文将展示
glm-4
、
gpt-4o
以及
deepseek coder v2
大模型的
API
的调用,以及
Function Calling
的集成。参考链接如下:
zhipuai:https://open.bigmodel.cn/dev/howuse/functioncall
openai:https://learn.microsoft.com/en-us/azure/ai-services/openai/how-to/function-calling?tabs=python
deepseek:https://platform.deepseek.com/usage
微调大模型function calling:https://blog.csdn.net/weixin_40959890/article/details/140137952
一、获取API KEY
想要使用大模型
API
服务,需要去各厂商官网上获取
API KEY
。这比较容易,不再赘述。值得一提的是,
智谱
和
deepseek
只要注册就会有免费的资源包。对于
gpt-4o
,我使用的是
Azure
提供的服务。使用的
API KEY
和模型如下:
ZHIPU_API_KEY ="xxxxx.xxxxx"
DEEPSEEK_API_KEY ="sk-xxxxx"# Azure的服务
OPENAI_API_KEY ="xxxxx"
OPENAI_ENDPOINT ="https://xxxxx"# 系列模型,在前面的效果好一些
ZHIPU_MODELS =["glm-4-0520","glm-4"]
DEEPSEEK_MODELS =["deepseek-coder","deepseek-chat"]
OPENAI_MODELS =["gpt-4o"]
二、API调用 & Function Calling使用
1.引入库
我在类
LLMRequestWithFunctionCalling
中集成了上述几种大模型的
API
调用,且包含了
Function Calling
(这里
deepseek
不支持)。
API
调用可以是SDK调用,也可以是
HTTP
调用。这边在使用
智谱
和
deepseek
模型时,选择了SDK调用,对于
Azure gpt-4o
则采用了
HTTP
调用。
# function_calling.pyclassLLMRequestWithFunctionCalling:"""
在使用llm的时候,使用function calling以满足你的需求
有两种方式实现function calling:
1) function call: https://techcommunity.microsoft.com/t5/ai-azure-ai-services-blog/function-calling-is-now-available-in-azure-openai-service/ba-p/3879241
2)tool call: https://learn.microsoft.com/en-us/azure/ai-services/openai/how-to/function-calling?tabs=python
我们这里使用tool call
"""def__init__(self, system_prompt:str, prompt:str, tools: List[Dict]=None, model:str="glm-4"):# 系统提示
self.system_prompt = system_prompt
# 用户提示
self.prompt = prompt
# tools
self.tools = tools
# # 函数functions# self.functions = [tool.get("function") for tool in tools if tool.get("type") == "function"]# 使用的模型
self.model = model
# 消息
self.messages: List[Dict]=[]# 客户端
self.client: Union[OpenAI, ZhipuAI,None]=None# http请求 -> 这是Azure的gpt-4o的请求方式
self.http_args: Union[Dict[str, Dict],None]=None# 函数名称
self.function_names: List =[]# 初始化
self._init()def_init(self):"""
初始化
"""# client & http_argsassert self.model in ZHIPU_MODELS + DEEPSEEK_MODELS + OPENAI_MODELS
if self.model in ZHIPU_MODELS + DEEPSEEK_MODELS:if self.model in ZHIPU_MODELS:# 使用智谱api
self.client = ZhipuAI(api_key=ZHIPU_API_KEY)# 填写您自己的APIKeyelse:# 使用deepseek api
self.client = OpenAI(api_key=DEEPSEEK_API_KEY, base_url="https://api.deepseek.com")else:# 使用Azure的gpt-4o api
self.http_args ={"headers":{"Content-Type":"application/json","api-key": OPENAI_API_KEY,},"generate_args":{"temperature":0.7,"top_p":0.95,"max_tokens":800}# 这边设置了一些生成参数,当然上面client也是可以设置的}# messages# 这是必备消息
self.messages.append({"role":"system","content": self.system_prompt})
self.messages.append({"role":"user","content": self.prompt})# function name抽取一下for tool in self.tools:
self.function_names.append(tool["function"]["name"])def_llm_request(self,
messages: List[Dict],
tools: Optional[List[Dict]]=None)-> Union[ChatCompletion, Completion, Dict,None]:"""
使用llm api请求
"""if self.client isnotNone:try:
response = self.client.chat.completions.create(
model=self.model,# 模型名称
messages=messages,
stream=False,
tools=tools,# tool_choice="auto")except Exception as e:print("response error")
response =Noneelse:# Azure提供的api服务print()
headers = self.http_args["headers"]
payload ={"messages": messages,}if tools isnotNone:# function call# functions = [tool.get("function") for tool in tools if tool.get("type") == "function"]# payload.update({# "function_call": "auto",# "functions": functions# })# tool call
payload.update({"tools": tools,"tool_choice":"auto"})
payload.update(**self.http_args["generate_args"])# Send requesttry:
response = requests.post(OPENAI_ENDPOINT, headers=headers, json=payload)
response.raise_for_status()# Will raise an HTTPError if the HTTP request returned an unsuccessful status codeexcept requests.RequestException as e:# raise SystemExit(f"Failed to make the request. Error: {e}")print(f"Failed to make the request. Error: {e}")
response =Noneif response isnotNone:# 转成dict, eg:{'choices': [{'content_filter_results': {'hate': {'filtered': False, 'severity': 'safe'}, 'self_harm': {'filtered': False, 'severity': 'safe'}, 'sexual': {'filtered': False, 'severity': 'safe'}, 'violence': {'filtered': False, 'severity': 'safe'}}, 'finish_reason': 'stop', 'index': 0, 'logprobs': None, 'message': {'content': '中国最发达的城市之一是上海。上海是中国的经济、金融、贸易和航运中心。它拥有世界上最繁忙的港口之一,同时也是许多跨国公司在中国的总部所在地。除了上海,北京、深圳和广州也被认为是中国最发达的城市之一。北京是中国的政治和文化中心,深圳是科技和创新的前沿城市,而广州则是重要的贸易和制造业中心。这些城市在经济、教育、医疗、基础设施等多个方面都非常发达。', 'role': 'assistant'}}], 'created': 1721784656, 'id': 'chatcmpl-9oL8Cm6q3LNTJcpP4EePv1MK2tEzj', 'model': 'gpt-4o-2024-05-13', 'object': 'chat.completion', 'prompt_filter_results': [{'prompt_index': 0, 'content_filter_results': {'hate': {'filtered': False, 'severity': 'safe'}, 'self_harm': {'filtered': False, 'severity': 'safe'}, 'sexual': {'filtered': False, 'severity': 'safe'}, 'violence': {'filtered': False, 'severity': 'safe'}}}], 'system_fingerprint': 'fp_abc28019ad', 'usage': {'completion_tokens': 116, 'prompt_tokens': 31, 'total_tokens': 147}}
response = response.json()return response
defexecute(self, use_functions:bool)-> Union[ChatCompletion, Completion, Dict,None]:"""
执行请求,
当use_functions=True时才会进行函数调用
"""ifnot use_functions ornot self.tools:return self._llm_request(self.messages)if self.model in ZHIPU_MODELS:return self._execute_zhipuai()elif self.model in OPENAI_MODELS:return self._execute_openai()else:raise NotImplementedError(f"{self.model} 在此处暂时不支持function calling!")def_parse_function_call_zhipuai(self, response_to_tools):"""
解析智谱的function_call
"""ifnot response_to_tools.choices[0].message.tool_calls:return
tool_call = response_to_tools.choices[0].message.tool_calls[0]
args = tool_call.function.arguments
function_result ={}
name = tool_call.function.name
if name in self.function_names:
function_result =eval(name)(**json.loads(args))# 添加消息
self.messages.append({"role":"tool","content":f"{json.dumps(function_result)}","tool_call_id": tool_call.id})def_parse_function_call_openai(self, response_to_tools):"""
解析openai function_call (http请求)
"""ifnot response_to_tools:# todo: 观察字段return
function_obj = response_to_tools["choices"][0]["message"]["tool_calls"][0]
tool_call_id = function_obj["id"]
function = function_obj["function"]
name = function["name"]
args = function["arguments"]
function_result ={}if name in self.function_names:
function_result =eval(name)(**json.loads(args))# 添加消息
self.messages.append({"role":"tool","content":f"{json.dumps(function_result)}","tool_call_id": tool_call_id
})def_execute_zhipuai(self)-> Union[Completion, Dict,None]:"""
glm系列实现function calling
"""# 调用tools并响应
response_to_tools = self._llm_request(self.messages, tools=self.tools)if response_to_tools isNone:raise Exception("智谱函数调用失败")# {'content': '', 'role': 'assistant', 'tool_calls': [{'id': 'call_202407301506203c337e808ac84619', 'function': {'arguments': '{"date":"2022-01-23","departure":"北京","destination":"广州"}', 'name': 'get_flight_number'}, 'type': 'function', 'index': 0}]}
self.messages.append(response_to_tools.choices[0].message.model_dump())
self._parse_function_call_zhipuai(response_to_tools)# 以上添加了assistant和tool的响应,接下来根据所有message请求
response = self._llm_request(self.messages, tools=self.tools)if response isnotNone:
self.messages.append(response.choices[0].message.model_dump())return response
def_execute_openai(self)-> Union[ChatCompletion, Dict,None]:"""
gpt系列(http请求)实现function calling
"""
response_to_tools = self._llm_request(self.messages, tools=self.tools)if response_to_tools isNone:raise Exception("openai函数调用失败")
self.messages.append(response_to_tools["choices"][0]["message"])
self._parse_function_call_openai(response_to_tools)
response = self._llm_request(self.messages, tools=self.tools)if response isnotNone:
self.messages.append(response["choices"][0]["message"])return response
三、示例
假设这边有两个函数
get_flight_number
和
get_ticket_price
,我们构建了函数如下方的
tools
所示,该结构化数据主要包含函数的名称
name
,函数的描述
description
,函数入参
parameters
。
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"]},}},]defget_flight_number(date:str, departure:str, destination:str):
flight_number ={"北京":{"上海":"1234","广州":"8321",},"上海":{"北京":"1233","广州":"8123",}}return{"flight_number": flight_number[departure][destination]}defget_ticket_price(date:str, flight_number:str):return{"ticket_price":"1000"}
我们来让大模型借助
Function Calling
来查一下航班:
system_prompt ="不要假设或猜测传入函数的参数值。如果用户的描述不明确,请要求用户提供必要信息"
prompt ="帮我查询1月23日,北京到广州的航班"
llmr = LLMRequestWithFunctionCalling(system_prompt, prompt, tools,"gpt-4o")# or glm-4
res = llmr.execute(use_functions=True)print(res)
我测试了
glm-4
以及
gpt-4o
这两个模型,得到
llmr.messages
结果如下:
# ------------------- glm-4 ---------------------[{'role':'system','content':'不要假设或猜测传入函数的参数值。如果用户的描述不明确,请要求用户提供必要信息'},{'role':'user','content':'帮我查询1月23日,北京到广州的航班'},{'content':'','role':'assistant','tool_calls':[{'id':'call_202407301506203c337e808ac84619','function':{'arguments':'{"date":"2022-01-23","departure":"北京","destination":"广州"}','name':'get_flight_number'},'type':'function','index':0}]},{'role':'tool','content':'{"flight_number": "8321"}','tool_call_id':'call_202407301506203c337e808ac84619'},{'content':'根据您的查询,经过API调用,我找到了1月23日从北京到广州的航班号,它是8321。','role':'assistant','tool_calls':None}]# ------------------- gpt-4o ---------------------[{'role':'system','content':'不要假设或猜测传入函数的参数值。如果用户的描述不明确,请要求用户提供必要信息'},{'role':'user','content':'帮我查询1月23日,北京到广州的航班'},{'content':'请稍等,我将为您查询1月23日从北京到广州的航班信息。','role':'assistant','tool_calls':[{'function':{'arguments':'{"departure":"北京","destination":"广州","date":"2024-01-23"}','name':'get_flight_number'},'id':'call_znSa2Quwbwze9xtjRebUgSsI','type':'function'}]},{'role':'tool','content':'{"flight_number": "8321"}','tool_call_id':'call_znSa2Quwbwze9xtjRebUgSsI'},{'content':'查询到1月23日从北京到广州的航班号是8321。您是否需要查询该航班的票价信息?','role':'assistant'}]
成功!
四、其他
Function Calling
功能现在都是使用tools
和tool_choice
参数来实现的,实际上也可以使用functions
和function_call
实现,比如Azure的example和OpenAI的example(这种方式可能比较老旧);- 上面有两个函数,我们在进行
Function Calling
的时候,tool_choice
参数要么是使用默认值,要么就是"auto"
,这意味着让模型自主选择其中的一个函数;如果将其设置为{"name": "your_function_name"}
时,可以强制API
返回特定函数的调用;根据智谱文档所说,目前它只支持默认的"auto"
; - 该博客中提到了
NexusRavenV2-13B
模型感觉也不错。
总结
本文介绍了大模型
API
调用,以及
Function Calling
的使用,适用于推理准确性要求较高的场景。
版权归原作者 戴昊光 所有, 如有侵权,请联系我们删除。