Function Calling案例实战:天气助手

46 阅读8分钟

在工程实践中,大模型不再只是一个“会聊天的接口”,而是需要作为业务系统的一部分,去访问数据库、调用 HTTP API、触发内部服务。 要做到这一点,必须有一层清晰的“调用抽象”,把模型输出安全、可靠地映射为函数调用和系统操作——这层抽象就是 Function Calling,以及在更复杂场景下的 MCP。

1、Function Calling 在大模型中的作用是什么?

从典型的“查天气”场景看,一个简单的问题背后往往包含两类能力:

  • 访问外部数据源(例如实时天气接口);
  • 基于数据生成自然语言响应

这正是 Function Calling 发挥价值的地方:让模型既能理解用户意图,又能驱动底层能力。

  • 扩展模型能力

大模型本身无法直接访问外部世界(数据库、HTTP API、本地程序等),但通过 Function Calling,可以把这些能力“接”到模型身上,例如:

  • 结构化输出

模型不仅输出“文本”,还可以输出结构化参数,作为函数调用的入参。 例如,用户说:“明天北京天气如何?” 模型可以生成一条函数调用:

get_weather(location="北京", date="2025-05-06")

这条调用就是“自然语言 → 结构化参数 → 真正函数执行”的桥梁。

  • 动态决策流程

模型可以根据上下文,自主决定要不要调用函数、调用哪个函数、按什么顺序调用:

一句话总结:Function Calling 是大模型与真实世界交互的“桥梁”,负责把“自然语言意图”转换为“可执行的函数调用”。

2、Function Calling 与 MCP 的区别?

维度Function CallingMCP
定位模型厂商私有接口(如 OpenAI, Qwen)开放协议(类似统一“USB-C 接口”)
扩展性需为每个模型单独适配一次开发,多模型兼容
复杂性适合简单、单次调用任务支持多轮对话、复杂上下文管理
生态依赖依赖特定模型(如 GPT-4、Qwen)跨模型、跨平台(如 Claude、Cursor)
安全性依赖云端 API 密钥更易做本地化部署与数据控制

可以简单理解为:

  • Function Calling更像是“某个模型厂商提供的函数调用接口规范”,偏向模型内部实现
  • MCP更像是“跨模型通用的工具协议”,偏向系统集成与架构层,一次实现,可以被多个模型客户端消费。

3、已经有了 MCP,还需要 Function Calling 吗?

需要,而且会长期共存。

  • 对于简单、原子化任务,直接用 Function Calling 往往更轻量:
  • 优势

可以把 MCP 看作“更通用、更工程化的上层方案”,而 Function Calling 是“大模型底层能力的一部分”,两者并不互斥。

4、CASE:用 Qwen3 + 高德天气 Function Calling 查天气

这一节基于一个完整的示例项目,从“环境与依赖 → 工具实现 → 智能体初始化 → 交互入口(TUI/GUI)”,串起一个可在本地直接运行的 Function Calling + MCP 天气助手。

Step1:环境与依赖准备

在实现任何基于 Function Calling 的应用前,推荐先把依赖和运行环境标准化,确保各端一致可复现。 示例项目的依赖在requirements.txt中已经列出,例如:

dashscope==1.22.1
pandas==2.3.0
qwen_agent==0.0.25
Requests==2.32.4

说明:qwen_agent只需保留一个版本即可,实际安装时建议选择最新稳定版,避免多版本共存带来的依赖冲突。

  • 安装依赖(以 conda / pip 为例)
pip install -r requirements.txt
  • 配置必要环境变量

在 Windows PowerShell 中可以临时设置(当前会话),用于本地开发调试:

$env:DASHSCOPE_API_KEY = "你的DashScopeKey"
$env:AMAP_MAPS_API_KEY = "你的高德Key"

Step2:配置大模型与工具系统(DashScope + Qwen Agent)

这一层主要是把“大模型服务”与“智能体框架”串起来,形成一个可扩展的调用入口。 在示例代码中,首先完成大模型 SDK 和工具框架的基础配置:

import os
import requests
import dashscope
from qwen_agent.agents import Assistant
from qwen_agent.gui import WebUI
from qwen_agent.tools.base import BaseTool, register_tool

# 配置 DashScope
dashscope.api_key = os.getenv("DASHSCOPE_API_KEY")
print("dashscope.api_key:" + dashscope.api_key)
dashscope.timeout = 30  # 设置超时时间为 30 秒
  • 使用qwen_agent作为代理框架,负责:

在后面的初始化里,会通过Assistant把模型配置和工具列表串起来。

Step3:实现天气查询工具(Function Calling + 高德 API)

这一层对应“能力封装”,把具体的天气查询逻辑封装成一个可被模型安全调用的工具。 示例中通过继承BaseTool+@register_tool,实现了一个完整的天气查询工具WeatherTool,供 Qwen 调用:

@register_tool('get_current_weather')
class WeatherTool(BaseTool):
    """
    天气查询工具,通过高德地图API查询指定位置的天气情况。
    """
    description = '获取指定位置的当前天气情况'
    parameters = [{
        'name': 'location',
        'type': 'string',
        'description': '城市名称,例如:北京',
        'required': True
    }, {
        'name': 'adcode',
        'type': 'string',
        'description': '城市编码,例如:110000(北京)',
        'required': False
    }]

    def call(self, params: str, **kwargs) -> str:
        import json
        args = json.loads(params)
        location = args['location']
        adcode = args.get('adcode', None)

        return self.get_weather_from_gaode(location, adcode)
  • 关键点

真正访问高德天气 API 的逻辑在get_weather_from_gaode中:

    def get_weather_from_gaode(self, location: str, adcode: str = None) -> str:
        """调用高德地图API查询天气"""
        gaode_api_key = os.getenv("AMAP_MAPS_API_KEY")  # 高德API Key
        base_url = "https://restapi.amap.com/v3/weather/weatherInfo"

        params = {
            "key": gaode_api_key,
            "city": adcode if adcode else location,
            "extensions": "base",  # 可改为 "all" 获取预报
        }

        try:
            response = requests.get(base_url, params=params)
            if response.status_code == 200:
                data = response.json()
                if data.get('status') == '1'and data.get('lives'):
                    weather_info = data['lives'][0]
                    result = (
                        f"天气查询结果:\n"
                        f"城市:{weather_info.get('city')}\n"
                        f"天气:{weather_info.get('weather')}\n"
                        f"温度:{weather_info.get('temperature')}°C\n"
                        f"风向:{weather_info.get('winddirection')}\n"
                        f"风力:{weather_info.get('windpower')}\n"
                        f"湿度:{weather_info.get('humidity')}%\n"
                        f"发布时间:{weather_info.get('reporttime')}"
                    )
                    return result
                else:
                    returnf"获取天气信息失败:{data.get('info', '未知错误')}"
            else:
                returnf"请求失败:HTTP状态码 {response.status_code}"
        except Exception as e:
            returnf"获取天气信息出错:{str(e)}"

这里做了几件事:

  • 从环境变量读取高德AMAP_MAPS_API_KEY
  • 调用实时天气接口/v3/weather/weatherInfo
  • 对返回 JSON 进行检查与格式化,生成用户可读的中文天气描述。

Step4:初始化助手服务(Qwen Agent + MCP 集成)

init_agent_service这一层可以理解为“Agent 装配层”,把模型配置、工具列表(包括自定义工具 + MCP Server)组装成一个真正可对话、可扩展的“天气助手”:

def init_agent_service():
    """初始化助手服务"""
    llm_cfg = {
        'model': 'qwen-turbo-2025-04-28',
        'timeout': 30,
        'retry_count': 3,
    }

    tools = [
        'get_current_weather',  # 上面注册的天气查询工具
        {
            "mcpServers": {
                "amap-maps": {
                    "command": "npx",
                    "args": [
                        "-y",
                        "@amap/amap-maps-mcp-server"
                    ],
                    "env": {
                        # 从环境变量中读取高德地图 API Key
                        "AMAP_MAPS_API_KEY": os.getenv("AMAP_MAPS_API_KEY", "")
                    }
                }
            }
        }
    ]
    try:
        bot = Assistant(
            llm=llm_cfg,
            name='天气助手',
            description='天气助手,查询天气',
            system_message="你是一名有用的助手",
            function_list=tools,  # 增加天气工具 + MCP
        )
        print("助手初始化成功!")
        return bot
    except Exception as e:
        print(f"助手初始化失败: {str(e)}")
        raise
  • 亮点

这一步完成后,我们就拥有了一个具备天气查询能力的“智能体对象”bot

Step5:终端交互模式(app_tui)

命令行模式适合开发和调试阶段,便于观察原始输入输出和日志。 可以直接使用app_tui在终端中与助手连续对话:

def app_tui():
    """终端交互模式

    提供命令行交互界面,支持:
    - 连续对话
    - 文件输入
    - 实时响应
    """
    try:
        # 初始化助手
        bot = init_agent_service()

        # 对话历史
        messages = []
        whileTrue:
            try:
                # 获取用户输入
                query = input('user question: ')
                # 获取可选的文件输入
                file = input('file url (press enter if no file): ').strip()

                # 输入验证
                ifnot query:
                    print('user question cannot be empty!')
                    continue

                # 构建消息
                ifnot file:
                    messages.append({'role': 'user', 'content': query})
                else:
                    messages.append({'role': 'user', 'content': [{'text': query}, {'file': file}]})

                print("正在处理您的请求...")
                # 运行助手并处理响应(流式)
                response = []
                for response in bot.run(messages):
                    print('bot response:', response)
                messages.extend(response)
            except Exception as e:
                print(f"处理请求时出错: {str(e)}")
                print("请重试或输入新的问题")
    except Exception as e:
        print(f"启动终端模式失败: {str(e)}")
  • 支持连续对话:messages里累计上下文;
  • 支持简单的“文本 + 文件 URL”混合输入;
  • bot.run(messages)内部会自动处理 Function Calling / MCP 调用,无需手动拼接tool_calls

Step6:GUI 图形界面模式(app_gui)

在实际 demo、内部培训或与非技术角色协作时,Web GUI 能显著降低使用门槛。 示例中的app_gui使用WebUI快速构建了一个 Web 聊天界面:

def app_gui():
    """图形界面模式,提供 Web 图形界面"""
    try:
        print("正在启动 Web 界面...")
        # 初始化助手
        bot = init_agent_service()
        # 配置聊天界面,列举典型提问
        chatbot_config = {
            'prompt.suggestions': [
                '北京今天的天气怎么样?',
            ]
        }
        print("Web 界面准备就绪,正在启动服务...")
        # 启动 Web 界面
        WebUI(
            bot,
            chatbot_config=chatbot_config
        ).run()
    except Exception as e:
        print(f"启动 Web 界面失败: {str(e)}")
        print("请检查网络连接和 API Key 配置")

配合入口:

if __name__ == '__main__':
    # 运行模式选择
    app_gui()          # 图形界面模式(默认)

运行方式非常简单:配置好依赖和环境变量后,直接运行该示例脚本即可。 启动后终端会提示本地地址(如http://127.0.0.1:7860),在浏览器中打开即可开始用中文对话查天气。

img_1.jpg

小结:从 Function Calling 底层能力到可交付的天气助手

从工程视角看,这个示例覆盖了一个典型 Agent 应用的完整链路:

  • 依赖和运行环境:通过requirements.txt与环境变量约定,确保可复现性与密钥安全;
  • 领域工具封装:使用WeatherTool封装高德天气 API,统一参数与返回格式;
  • Agent 装配与集成:在初始化阶段组合 Qwen 模型、自定义工具与 MCP Server;
  • 多种交互入口:同时支持终端app_tui与 Web GUIapp_gui,便于调试与演示。

相比很多只停留在“单次 Function 调用”的示例,这个案例更接近真实项目形态, 展示了如何把Function Calling + MCP + Agent 框架落在一个可以直接运行、可扩展的 AI 天气助手上,为后续接更多业务系统打下基础。