从0到1开发DeepSeek天气助手智能体——你以为大模型只会聊天?Function Calling让它“上天入地”

2 阅读13分钟

前言

2025年伊始,科技界的风云人物们——从英伟达的黄仁勋到OpenAI的山姆·奥特曼,再到机器学习领域的泰斗吴恩达不约而同地将目光聚焦于一个关键词:AI Agent(即智能体,若想深入了解,可阅读我的文章《一文读懂2025核心概念 AI Agent:科技巨头都在布局的未来赛道》)。然而,对于AI Agent的前景,持怀疑态度的人可能会问:“大模型只是个能完成问答的概率模型,它哪来的行为能力?又怎能摇身一变成为AI Agent呢?” 这个问题的答案,正隐藏在我们今天要探讨的 Function Calling(函数调用) 技术之中!

一、 什么是大模型的 Function Calling 技术?

Function Calling 是一种让大语言模型能够调用外部函数或工具的技术。简单来说,就是让大模型不仅能理解和生成文本,还能根据用户的需求,调用特定的 API 或工具来完成更复杂的任务。

举个例子:

  • 用户:“帮我订一张明天从北京到上海的机票。”
  • 不具备Function Calling的大模型:回复“好的,我会帮您订票。”,但无法真正执行。
  • 具备 Function Calling 的大模型:可以调用机票预订 API,获取航班信息,并完成订票操作。

二、 Function Calling 和 AI Agent 的关系

AI Agent 是指能够自主感知环境、进行决策和执行动作的智能体。Function Calling 是构建强大 AI Agent 的关键技术之一,它为 AI Agent 提供了以下能力:

  • 连接现实世界:  通过调用外部 API,AI Agent 可以获取实时信息、操作外部系统,从而与现实世界进行交互。
  • 执行复杂任务:  通过组合调用不同的函数,AI Agent 可以完成更复杂、更个性化的任务,例如旅行规划、日程安排等。
  • 提升效率和准确性:  利用外部工具的强大功能,AI Agent 可以更高效、更准确地完成任务,例如数据分析、代码生成等。

从上述分析中可知要开发智能体,必须用到大模型的Function Calling技术。要让大模型调用Function Calling功能,必须提供大模型相应功能的函数。

为了更直观感受大模型Function Calling技术,我们将利用DeepSeek大模型从0到1开发天气助手智能体,可以实时查询天气状态并给我们提供穿衣建议等~

三、心知天气 + Python + DeepSeek开发天气预报智能体

3.1 心知天气注册及API key获取方法

为了能够使用Python代码获得实时的天气情况,我们这里需要用到心知天气的的API:

  1. 打开心知天气的官网,注册登录并点击控制台:

2.png

  1. 在控制台左侧产品管理栏中点击添加产品

3.png

  1. 申请免费版的API,点击左侧免费版,就可以看到API私钥了:

5.png

  1. 利用python requests库调用API获得天气情况(免费版的只能得到天气现象、天气现象代码和气温 3项数据)
请提前安装requests sdk: pip install requests
import requests

url = "https://api.seniverse.com/v3/weather/now.json"

params = {
    "key": "",  # 填写你的私钥
    "location": "北京",  # 你要查询的地区可以用代号,拼音或者汉字,文档在官方下载,这里举例北京
    "language": "zh-Hans",  # 中文简体
    "unit": "c",  # 获取气温
}

response = requests.get(url, params=params)  # 发送get请求
temperature = response.json()  # 接受消息中的json部分
print(temperature['results'][0]['now'])  # 输出接收到的消息进行查看

6.png

  1. 将请求天气的代码封装成可以指定查询地点的函数:
import requests

def get_weather(loc):
    url = "https://api.seniverse.com/v3/weather/now.json"
    params = {
        "key": "", #填写你的私钥
        "location": loc,
        "language": "zh-Hans",
        "unit": "c",
    }
    response = requests.get(url, params=params)
    temperature = response.json()
    return temperature['results'][0]['now']

3.2 DeepSeek API Key注册方法

Function Calling 适用于模型规模大于30B的模型,本次分享我们使用DeepSeek-V3模型。按如下方法注册获得DeepSeek-V3 API Key(Deep-V3 API 访问教程请看文章DeepSeek大模型API实战指南):

  1. 进入DeepSeek官网,点击API 开放平台:

7.png

  1. 注册并充值tokens后(deepseek的tokens还是相当便宜的,10元可以用好久),点击左边栏API Keys生成API Key:

8.png

  1. 利用python openai库访问deepseek (这里openai库定义的是请求数据格式,并不是说deepseek是基于openai构造的`)
# 请提前安装openai sdk: pip install openai

from openai import OpenAI

client = OpenAI(api_key="你创建的api key", base_url="https://api.deepseek.com")

response = client.chat.completions.create(
    model="deepseek-chat", # 指定deepseek-chat, deepseek-chat对应deepseek-v3, deepseek-reasoner对应deepseek-r1
    messages=[
        {"role": "system", "content": "You are a helpful assistant"}, #指定系统背景
        {"role": "user", "content": "Hello"}, #指定用户提问
    ],
    stream=False
)

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

9.png

3.3 Function Calling准备: 让大模型理解函数

准备好外部函数之后,非常重要的一步是将外部函数的信息以某种形式传输给大模型,让大模型理解函数的作用。大模型需要特定的字典格式对函数进行完整描述, 字典描述包括:

  • name:函数名称字符串
  • description: 描述函数功能的字符串,大模型选择函数的核心依据
  • parameters: 函数参数, 要求遵照JSON Schema格式输入,JSON Schema格式请参照JSON Schema格式详解

对于上面的get_weather函数, 我们创建如下字典对其完整描述:

get_weather_function = {
    'name': 'get_weather',
    'description': '查询即时天气函数,根据输入的城市名称,查询对应城市的实时天气',
    'parameters': {
        'type': 'object',
        'properties': { #参数说明
            'loc': {
                'description': '城市名称',
                'type': 'string'
            }
        },
        'required': ['loc']  #必备参数
    }
}

完成对get_weather函数描述后,还需要将其加入tools列表,用于告知大模型可以使用哪些函数以及这些函数对应的描述,并在可用函数对象中记录一下:

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

available_functions = {
    "get_weather": get_weather,
}

3.4 Function calling 功能实现

完成一系列基础准备工作之后,接下来尝试与DeepSeek-V3大模型对话调用Function calling功能(分步教程代码在 codecopy.cn/post/ir801w ,完整优化代码在codecopy.cn/post/c80rrk ):

  1. 实例化客户端并创建如下messages
# 实例化客户端
client = OpenAI(api_key=你的api_key, 
                base_url="https://api.deepseek.com")

messages=[
    {"role": "user", "content": "请帮我查询北京地区今日天气情况"}
]
  1. 测试一下如果只输入问题不输入外部函数,模型是不知道天气结果的,只会告诉我们如何获得实时天气
response = client.chat.completions.create(
        model="deepseek-chat",
        messages=messages 
    )
print(response.choices[0].message.content)

10.png

  1. 接下来尝试将函数相关信息输入给Chat模型,需要额外设置两个参数,首先是tools参数, 用于申明外部函数库, 也就是我们上面定义的tools列表对象。其次是可选参数tool_choice参数,该参数用于控制模型对函数的选取,默认值为auto, 表示会根据用户提问自动选择要执行函数,若想让模型在本次执行特定函数不要自行挑选,需要给tool_choice参数赋予{"name":"functionname"}值,这时大模型就会从tools列表中选取函数名为functionname的函数执行。这里我们考验一下模型的智能性,让模型自动挑选函数来执行:
response = client.chat.completions.create(
    model="deepseek-chat",  
    messages=[
        {"role": "user", "content": "请帮我查询北京地区今日天气情况"}
    ],
    tools=tools,
)

print(response.choices[0].message)

观察现在response返回的结果, 我们发现message中的content变为空字符串, 增加了一个tool_calls的list, 如图红框所示,该list就包含了当前调用外部函数的全部信息:

11.png

我们输出一下toll_calls列表项中的function内容,可以看到大模型自动帮我们选择了要执行的函数get_weather,并告诉我们要传递的参数{loc:北京}。,

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

12.png

  1. 下一步将大模型生成的函数参数输入大模型选择的函数并执行(注意大模型不会帮我们自动调用函数,它只会帮我们选择要调用的函数以及生成函数参数),通过上面定义的available_functions对象找到具体的函数,并将大模型返回的参数传入(这里 ** 是一种便捷的参数传递方法,该方法会将字典中的每个key对应的value传输到同名参数位中),可以看到天气函数成功执行:
# 获取函数名称
function_name = response_message.tool_calls[0].function.name

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

# 获得执行函数所需参数
function_args = json.loads(response_message.tool_calls[0].function.arguments)

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

print(function_response)

13.png

  1. 在调用天气函数得到天气情况后,将天气结果传入mesages列表中并发送给大模型,让大模型理解上下文。函数执行结果的messagetool_message类型(这部分有点绕,可以看整体对于message类型有疑问的请看我的文章DeepSeek大模型API实战指南, 里面有详细的参数指南)。

首先将大模型关于选择函数的回复response_message内容解析后传入messages列表中

print(response_message.model_dump())
messages.append(response_message.model_dump()) 

解析结果如下:

{
	'content': '',
	'refusal': None,
	'role': 'assistant',
	'annotations': None,
	'audio': None,
	'function_call': None,
	'tool_calls': [{
		'id': 'call_0_8feaa367-c274-4c84-830f-13b49358a231',
		'function': {
			'arguments': '{"loc":"北京"}',
			'name': 'get_weather'
		},
		'type': 'function',
		'index': 0
	}]
}

然后再将函数执行结果作为tool_message并与response_message关联后传入messages列表中:

messages.append({
    "role": "tool",
    "content": json.dumps(function_response), # 将回复的字典转化为json字符串
    "tool_call_id": response_message.tool_calls[0].id # 将函数执行结果作为tool_message添加到messages中, 并关联返回执行函数内容的id
})
  1. 接下来,再次调用Chat模型来围绕messages进行回答。需要注意的是,此时不再需要向模型重复提问,只需要简单的将我们已经准备好的messages传入Chat模型即可:
second_response = client.chat.completions.create(
    model="deepseek-chat",
    messages=messages)

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

下面看大模型的输出结果,很明显大模型接收到了函数执行的结果,并进一步处理得到输出,同时天气和气温的输出也是正确的,这样我们就基于function calling技术完成一个简单的智能体了!

14.png

3.5 代码优化

以上步骤详细描述了Fucntion Calling的技术细节,执行流程图如下:

1.png

开发一个智能体需要将上面流程串起来,下一步我们编写一个能够自动执行外部函数调用的Chat智能体函数, 参数messages为输入到Chat模型的messages参数对象, 参数api_key为调用模型的API-KEY ,参数tools设置为包含全部外部函数的列表对象, 参数model默认为deepseek-chat , 该函数返回结果为大模型根据function calling内容的回复, 函数的具体代码如下:

def run_conv(messages,
             api_key,
             tools=None,
             functions_list=None,
             model="deepseek-chat"):
    user_messages = messages

    client = OpenAI(api_key=api_key,
                    base_url="https://api.deepseek.com")

    # 如果没有外部函数库,则执行普通的对话任务
    if tools == None:
        response = client.chat.completions.create(
            model=model,
            messages=user_messages
        )
        final_response = response.choices[0].message.content

    # 若存在外部函数库,则需要灵活选取外部函数并进行回答
    else:
        # 创建外部函数库字典
        available_functions = {func.__name__: func for func in functions_list}

        # 创建包含用户问题的message
        messages = user_messages

        # first response
        response = client.chat.completions.create(
            model=model,
            messages=user_messages,
            tools=tools,
        )
        response_message = response.choices[0].message

        # 获取函数名
        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(**function_args)

        # messages中拼接first response消息
        user_messages.append(response_message.model_dump())

        # messages中拼接外部函数输出结果
        user_messages.append(
            {
                "role": "tool",
                "content": json.dumps(function_response),
                "tool_call_id": response_message.tool_calls[0].id
            }
        )

        # 第二次调用模型
        second_response = client.chat.completions.create(
            model=model,
            messages=user_messages)

        # 获取最终结果
        final_response = second_response.choices[0].message.content

    return final_response

以上函数的流程就十分清晰啦,调用该函数测试一下结果~

ds_api_key = '你的api key'
messages = [{"role": "user", "content": "请问上海今天天气如何?"}]
get_weather_function = {
    'name': 'get_weather',
    'description': '查询即时天气函数,根据输入的城市名称,查询对应城市的实时天气',
    'parameters': {
        'type': 'object',
        'properties': {  # 参数说明
            'loc': {
                'description': '城市名称',
                'type': 'string'
            }
        },
        'required': ['loc']  # 必备参数
    }
}
tools = [
    {
        "type": "function",
        "function": get_weather_function
    }
]
final_response = run_conv(messages=messages,
         api_key=ds_api_key,
         tools=tools,
         functions_list=[get_weather])
print(final_response)

15.png

四、总结与展望

本文我们详细讲解了大模型 function calling 技术并基于该技术开发了天气智能体。Function Calling技术是AI Agent实现的关键,它让大模型不再只是简单的聊天回复,更可以"上天入地”完成各种各样的事。

然而在开发过程中我们也发现,function calling 技术开发过程冗长,需要编写相应的能力函数,有没有什么办法可以做到函数复用或简化开发呢,这就需要用到2025年最流行的Agent开发技术——MCP协议,什么是MCP协议呢?我们下一篇文章给大家分享~

感兴趣大家可关注微信公众号:大模型真好玩,工作开发中的大模型经验、教程和工具免费分享,大家快来看看吧~