LLM大语言模型四(function calling 大模型通往外部世界的桥梁)

109 阅读7分钟

1.引言

大模型的发展越来越进化,尤其是DeepSeek发布以来,可谓是AI界的拼多多,给产业界带来极致性价比,让大模型成本进一步普惠化。大模型的发展,从生产力工具的维度去看主要有两个方面:

  • 创造增量需求
  • 现有成本替代

你看总结起来,其实就是一句话:降本增效提质。但事实上,当下不管是大模型,还是Agent从技术,从需求上来看,发展都是不及业界预期的,在应用形态上还主要在于知识型工具层面,还远没有达到生产力工具的层面。

这也是大模型本身固有的一些局限,比如:

  • 没有记忆能力,上下文窗口限制
  • 知识更新不及时,新旧知识分不清
  • 针对专业领域,不保障给出可靠的答案
  • 难以和外部已有系统灵活交互

这里我们聚焦第四点:不能和外部已有系统灵活交互。这就要了命了,计算机世界发展那么多年,已经积累了那么多成套成体系的生产力工具系统,不可能因为大模型而从0到1重构一遍,成本太高了!

必须要有某种结合的方式。一方面发挥大模型的推理,理解,生成等智能化能力;一方面要能利用好现有系统能力。要解决这个问题,open ai在23年提出了function calling,给大模型找到了一条通往外部世界的渠道。

你看,就大模型而言,open ai代表了事实上的标准,从最初提出大模型技术:rest api +客户端库的形态,再到function calling。

那么,今天我试图通过一篇文章,让你搞懂function calling的本质。

2.案例

2.1.场景描述

我想要开发一个天气智能助手,输入某个城市名称,能够知道该城市当前气温等相关天气状况。要实现这个智能助手,需要借助两个平台:

关于这两个平台,你需要去注册,拿到相应的密钥key,才能访问相关api服务。具体操作,我就不展开了,都比较简单。心知天气平台,登录以后,找到控制台,添加产品,拿到私钥,像这样:

image.png

deepseek注册登录,需要充值,创建api key即可:

image.png

2.2.环境准备

在开发中,需要使用到相关第三方库,通过requests库访问外部api,通过openai 库访问大模型服务,需要安装这两个库:

pip install requests openai

你也可以像我这样,通过requirements.txt文件,管理依赖库:

image.png

然后通过以下命令安装

pip install -r requirements.txt 

2.3.示例代码

2.3.1.创建访问天气服务工具

心知天气平台,提供了丰富气象api服务,文档地址:seniverse.yuque.com/hyper_data/…

image.png

这里,我通过天气实况api完成我们的智能助手工具

image.png

通过requests库,封装api工具:

import requests

def get_weather(loc):
    """查询实时天气函数"""
    url = "https://api.seniverse.com/v3/weather/now.json"
    params = {
        "key": "",  # 填写你的私钥
        "location": "广州",  # 你要查询的地区可以用代号,拼音或者汉字,文档在官方下载,这里举例广州
        "language": "zh-Hans",  # 中文简体
        "unit": "c",  # 获取气温
    }

    response = requests.get(url, params=params)
    temperature = response.json()
    return temperature["results"][0]["now"]

# 执行入口
if __name__ == "__main__":
    print(get_weather("广州"))

image.png

2.3.2.访问大模型服务

好了,天气服务工具已经有了,来封装访问大模型服务

# 大模型部分内容
    from openai import OpenAI
    import json

    client = OpenAI(
        api_key="你的deepseek 密钥",
        base_url="https://api.deepseek.com"
    )
    messages = [
        {"role": "user", "content": "请帮我查询广州地区今日天气情况"}
    ]
    #只输入问题,不提供函数输入测试
    response = client.chat.completions.create(
            model="deepseek-chat",
            messages=messages
        )
    print(response.choices[0].message.content)

image.png

不使用工具,直接问大模型实时性问题,你看它是无能为力的,deepseek还算可以,给我们提出了寻找答案的建议和方案。

2.3.3.整合工具和大模型

要在大模型服务中整合function calling,需要对工具进行一些元数据描述,这很重要!大模型在推理过程中,在理解问题的基础上,来决定是否要调用工具

# 为大模型声明描述函数,是一个字典dict
    get_weather_function = {
        "name": "get_weather",
        "description": "查询即时天气函数,根据输入的城市名称,查询对应城市的实时天气",
        "parameters": {
            "type": "object",
            "properties": {
                "loc": {
                    "type": "string",
                    "description": "城市名称",
                },
            },
            "required": ["loc"],
        },
    }

    tools = [
        {
            "type": "function",
            "function": get_weather_function,
        }
    ]

    available_functions = {
        "get_weather": get_weather,
    }

调用大模型服务,使用工具:

# 提供函数输入测试
response = client.chat.completions.create(
        model="deepseek-chat",
        messages=messages,
        tools=tools
    )

response_message = response.choices[0].message
print(response_message)
print(response_message.tool_calls[0].function)

# 调用函数
function_call = response_message.tool_calls[0].function
# 获取函数名称
function_name = function_call.name

# 获得对应函数对象
function_to_call = available_functions[function_name]

# 获得执行函数所需参数
function_args = json.loads(function_call.arguments)

# 执行函数
function_response = function_to_call(**function_args)

# 输出函数调用结果
print(function_response)

# 综合消息输入大模型,做进一步理解处理
# 大模型函数选择消息
messages.append(response_message.model_dump())
# 追加函数调用结果消息
messages.append({
    "role": "tool",
    "content": json.dumps(function_response), # 将回复的字典转化为json字符串
    "tool_call_id": response_message.tool_calls[0].id # 将函数执行结果作为tool_message添加到messages中, 并关联返回执行函数内容的id
})

# 终结调用大模型
second_response = client.chat.completions.create(
    model="deepseek-chat",
    messages=messages)

print(second_response.choices[0].message.content)

2.3.4.完整代码

import requests

def get_weather(loc):
    """查询实时天气函数"""
    url = "https://api.seniverse.com/v3/weather/now.json"
    params = {
        "key": "",  # 填写你的私钥
        "location": "广州",  # 你要查询的地区可以用代号,拼音或者汉字,文档在官方下载,这里举例广州
        "language": "zh-Hans",  # 中文简体
        "unit": "c",  # 获取气温
    }

    response = requests.get(url, params=params)
    temperature = response.json()
    return temperature["results"][0]["now"]

# 执行入口
if __name__ == "__main__":
    # print(get_weather("广州"))
    # 大模型部分内容
    from openai import OpenAI
    import json

    client = OpenAI(
        api_key="你的deepseek 密钥",
        base_url="https://api.deepseek.com"
    )
    messages = [
        {"role": "user", "content": "请帮我查询广州地区今日天气情况"}
    ]

    # 为大模型声明描述函数,是一个字典dict
    get_weather_function = {
        "name": "get_weather",
        "description": "查询即时天气函数,根据输入的城市名称,查询对应城市的实时天气",
        "parameters": {
            "type": "object",
            "properties": {
                "loc": {
                    "type": "string",
                    "description": "城市名称",
                },
            },
            "required": ["loc"],
        },
    }

    tools = [
        {
            "type": "function",
            "function": get_weather_function,
        }
    ]

    available_functions = {
        "get_weather": get_weather,
    }

    # 提供函数输入测试
    response = client.chat.completions.create(
        model="deepseek-chat",
        messages=messages,
        tools=tools
    )

    response_message = response.choices[0].message
    print(response_message)
    print(response_message.tool_calls[0].function)

    # 调用函数
    function_call = response_message.tool_calls[0].function
    # 获取函数名称
    function_name = function_call.name

    # 获得对应函数对象
    function_to_call = available_functions[function_name]

    # 获得执行函数所需参数
    function_args = json.loads(function_call.arguments)

    # 执行函数
    function_response = function_to_call(**function_args)

    # 输出函数调用结果
    print(function_response)

    # 综合消息输入大模型,做进一步理解处理
    # 大模型函数选择消息
    messages.append(response_message.model_dump())
    # 追加函数调用结果消息
    messages.append({
        "role": "tool",
        "content": json.dumps(function_response),  # 将回复的字典转化为json字符串
        "tool_call_id": response_message.tool_calls[0].id  # 将函数执行结果作为tool_message添加到messages中, 并关联返回执行函数内容的id
    })

    # 终结调用大模型
    second_response = client.chat.completions.create(
        model="deepseek-chat",
        messages=messages)

    print(second_response.choices[0].message.content)

2.3.5.运行结果

image.png

你看整个执行过程:

  • 当我们需要大模型回答关于实时天气的问题:请帮我查询广州地区今日天气情况
  • 大模型在理解问题的基础上,发现需要使用外部工具:get_weather
  • 通过外部工具调用,获取天气数据,将工具执行结果,与原问题一并综合给到大模型
  • 大模型给出最终结果

重要要关注的事情,大模型并不会调用工具,它只会帮助我们选择工具,工具调用要由外部程序控制,大模型返回选择工具信息

image.png

在需要调用工具的场景中,大模型返回信息中有一个tool_calls信息,它是一个list,说明可以选择多个工具。