GLM-4 (5) - API & Function Calling

系列文章目录

GLM-4 (1) - 推理+概览
GLM-4 (2) - RoPE
GLM-4 (3) - GLMBlock
GLM-4 (4) - SelfAttention
GLM-4 (5) - API & Function Calling



前言

我们之前解析了GLM-4模型相关的部分,这有助于我们对理解和使用开源大模型。然后,有一些场景对于大模型的性能(比如某个任务的推理准确率)有较高的要求,10B以下参数的模型很可能无法胜任。那么就有两条路可以选择:1)收集与任务相关的数据,微调该模型;2)直接使用商业化的API服务。由于第一条路收集数据工作量较大,所以调用API是个不错的选择,也是本篇要讲述的内容。
大模型并不是万能的,推理的时候也会出错,比如近期的热搜就是大模型比较9.119.8大小的时候变成弱智。但是,大模型有Function Calling的加持,调用外部函数,就能有效缓解这些问题。
本文将展示glm-4gpt-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.py
class LLMRequestWithFunctionCalling:
    """
    在使用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_args
        assert 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)  # 填写您自己的APIKey
            else:
                # 使用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 is not None:
            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 = None
        else:
            # Azure提供的api服务
            print()
            headers = self.http_args["headers"]
            payload = {
                "messages": messages,
            }

            if tools is not None:
                # 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 request
            try:
                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 code
            except requests.RequestException as e:
                # raise SystemExit(f"Failed to make the request. Error: {e}")
                print(f"Failed to make the request. Error: {e}")
                response = None
            if response is not None:
                # 转成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

    def execute(self, use_functions: bool) -> Union[ChatCompletion, Completion, Dict, None]:
        """
        执行请求,
        当use_functions=True时才会进行函数调用
        """
        if not use_functions or not 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
        """
        if not 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请求)
        """
        if not 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 is None:
            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 is not None:
            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 is None:
            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 is not None:
            self.messages.append(response["choices"][0]["message"])
        return response

三、示例

假设这边有两个函数get_flight_numberget_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"]
            },
        }
    },
]


def get_flight_number(date: str, departure: str, destination: str):
    flight_number = {
        "北京": {
            "上海": "1234",
            "广州": "8321",
        },
        "上海": {
            "北京": "1233",
            "广州": "8123",
        }
    }
    return {"flight_number": flight_number[departure][destination]}


def get_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功能现在都是使用toolstool_choice参数来实现的,实际上也可以使用functionsfunction_call实现,比如Azure的exampleOpenAI的example(这种方式可能比较老旧);
  • 上面有两个函数,我们在进行Function Calling的时候,tool_choice参数要么是使用默认值,要么就是"auto",这意味着让模型自主选择其中的一个函数;如果将其设置为 {"name": "your_function_name"}时,可以强制API返回特定函数的调用;根据智谱文档所说,目前它只支持默认的"auto"
  • 该博客中提到了NexusRavenV2-13B模型感觉也不错。

总结

本文介绍了大模型API调用,以及Function Calling的使用,适用于推理准确性要求较高的场景。

  • 28
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
GLM-4V是一种大型模型,它的全称是Generalized Linear Model-4V。GLM-4V是一种广义线性模型,它是基于广义线性模型(Generalized Linear Model, GLM)的扩展和改进。 GLM-4V的原理如下: 1. 广义线性模型(GLM):GLM是一种统计模型,用于建立因变量与自变量之间的关系。它通过将线性回归模型与非线性函数相结合,可以处理不满足正态分布假设的数据。GLM的基本假设是,因变量的分布可以通过一个链接函数与自变量的线性组合相关联。 2. 四个"V":GLM-4V中的四个"V"代表了四个重要的概念,分别是Variation、Variance、Value和Validation。 - Variation(变异性):GLM-4V关注因变量的变异性,通过分析因变量的变异程度来确定模型的拟合程度。 - Variance(方差):GLM-4V考虑了因变量的方差,通过对方差进行建模,可以更好地描述因变量的分布特征。 - Value(价值):GLM-4V关注因变量的价值,通过对因变量的价值进行建模,可以更好地理解因变量对自变量的响应。 - Validation(验证):GLM-4V通过验证模型的拟合程度和预测能力,来评估模型的有效性和可靠性。 3. 模型构建:GLM-4V的模型构建包括以下几个步骤: - 数据准备:包括数据清洗、变量选择和数据转换等。 - 模型选择:选择适当的链接函数和误差分布族,并确定自变量的形式。 - 参数估计:使用最大似然估计或广义最小二乘法等方法,估计模型的参数。 - 模型诊断:对模型进行诊断,检验模型的拟合程度和假设条件是否满足。 - 模型评估:通过交叉验证等方法,评估模型的预测能力和稳定性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值