大语言模型虽然强大,但它有几个天生的"短板":不会算复杂数学、无法获取实时信息、不能直接操作数据库。那怎么办?Function Calling就是解决方案——让模型在需要的时候,主动调用我们写好的函数,把"思考"和"执行"完美结合起来。

这篇文章会通过5个由浅入深的案例,带你彻底搞懂Function Calling。


案例一:最简单的Function Calling——查询天气

先从一个最经典的场景开始:让模型调用天气查询工具。

1.1 定义工具

我们需要告诉模型:有一个叫get_current_weather的工具,当你需要查天气时,就用它。

from openai import OpenAI
import random

client = OpenAI(
    api_key="sk-xxx",
    base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
)

# 定义工具列表(告诉模型有哪些工具可用)
tools = [
    {
        "type": "function",
        "function": {
            "name": "get_current_weather",
            "description": "当你想查询指定城市的天气时非常有用。",
            "parameters": {
                "type": "object",
                "properties": {
                    "location": {
                        "type": "string",
                        "description": "城市或县区,比如北京市、杭州市、余杭区等。",
                    }
                },
                "required": ["location"],
            },
        },
    },
]

# 实现工具函数(这里用随机模拟,实际应该调天气API)
def get_current_weather(arguments):
    weather_conditions = ["晴天", "多云", "雨天"]
    random_weather = random.choice(weather_conditions)
    location = arguments["location"]
    return f"{location}今天是{random_weather}。"

1.2 定义模型调用函数

def get_response(messages):
    completion = client.chat.completions.create(
        model="qwen-plus",
        messages=messages,
        tools=tools,  # 把工具列表传给模型
    )
    return completion

1.3 主流程:判断是否需要调用工具

USER_QUESTION = "合肥天气咋样"
messages = [{"role": "user", "content": USER_QUESTION}]

# 第一次调用模型
response = get_response(messages)
assistant_output = response.choices[0].message

if assistant_output.content is None:
    assistant_output.content = ""

messages.append(assistant_output)

# 判断是否要调用工具
if assistant_output.tool_calls is None:
    print(f"无需调用天气查询工具,直接回复:{assistant_output.content}")
else:
    # 进入工具调用循环
    while assistant_output.tool_calls is not None:
        tool_call = assistant_output.tool_calls[0]
        tool_call_id = tool_call.id
        func_name = tool_call.function.name
        arguments = json.loads(tool_call.function.arguments)

        print(f"正在调用工具 [{func_name}],参数:{arguments}")

        # 执行对应的函数
        if func_name == "get_current_weather":
            tool_result = get_current_weather(arguments)
        else:
            tool_result = f"未知工具:{func_name}"

        print(f"工具返回:{tool_result}")

        # 把工具返回结果加入对话
        tool_message = {
            "role": "tool",
            "tool_call_id": tool_call_id,
            "content": tool_result,
        }
        messages.append(tool_message)

        # 再次调用模型,生成最终回答
        response = get_response(messages)
        assistant_output = response.choices[0].message

        if assistant_output.content is None:
            assistant_output.content = ""

        messages.append(assistant_output)

        if assistant_output.tool_calls is None:
            break

    print(f"助手最终回复:{assistant_output.content}")

运行结果:

正在调用工具 [get_current_weather],参数:{'location': '合肥'}
工具返回:合肥今天是晴天。
助手最终回复:合肥今天是晴天。

案例二:JSON格式提取——让模型帮你整理信息

有时候我们需要模型从自然语言中提取结构化数据,比如从一段话中提取联系人信息。

from openai import OpenAI
import json

client = OpenAI(
    api_key="sk-xxx",
    base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
)

def get_completion(messages, model="qwen-plus"):
    response = client.chat.completions.create(
        model=model,
        messages=messages,
        temperature=0,
        tools=[
            {
                "type": "function",
                "function": {
                    "name": "add_contact",
                    "description": "添加联系人",
                    "parameters": {
                        "type": "object",
                        "properties": {
                            "name": {"type": "string", "description": "联系人姓名"},
                            "address": {"type": "string", "description": "联系人地址"},
                            "tel": {"type": "string", "description": "联系人电话"},
                        },
                    },
                },
            }
        ],
    )
    return response.choices[0].message

prompt = "帮我寄给陆天宇,地址是合肥市经开区英唐工业园,电话15156028147。"
messages = [
    {"role": "system", "content": "你是一个联系人录入员。"},
    {"role": "user", "content": prompt},
]

response = get_completion(messages)
print("====GPT回复====")
print(response)

# 解析函数参数
args = json.loads(response.tool_calls[0].function.arguments)
print("====提取的信息====")
print(f"姓名:{args['name']}")
print(f"地址:{args['address']}")
print(f"电话:{args['tel']}")

运行结果:

====提取的信息====
姓名:陆天宇
地址:合肥市经开区英唐工业园
电话:15156028147

💡 使用Function Calling来提取结构化数据,比直接用提示词要求输出JSON更稳定可靠!


案例三:多个工具配合——解决复杂问题

现实场景往往需要多个工具配合。比如用户说:“我在合肥英唐工业园附近,想找麦当劳”。这需要先获取坐标,再搜索附近POI。

3.1 定义两个工具

from openai import OpenAI
import json
import requests

client = OpenAI(
    api_key="sk-xxx",
    base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
)

amap_key = "你的高德地图API Key"

tools = [
    {
        "type": "function",
        "function": {
            "name": "get_location_coordinate",
            "description": "根据POI名称,获得POI的经纬度坐标",
            "parameters": {
                "type": "object",
                "properties": {
                    "location": {"type": "string", "description": "POI名称,必须是中文"},
                    "city": {"type": "string", "description": "POI所在的城市名,必须是中文"},
                },
                "required": ["location", "city"],
            },
        },
    },
    {
        "type": "function",
        "function": {
            "name": "search_nearby_pois",
            "description": "搜索给定坐标附近的POI",
            "parameters": {
                "type": "object",
                "properties": {
                    "longitude": {"type": "string", "description": "中心点的经度"},
                    "latitude": {"type": "string", "description": "中心点的纬度"},
                    "keyword": {"type": "string", "description": "目标POI的关键字"},
                },
                "required": ["longitude", "latitude", "keyword"],
            },
        },
    },
]

3.2 实现工具函数

def get_location_coordinate(location, city):
    url = f"https://restapi.amap.com/v5/place/text?key={amap_key}&keywords={location}&region={city}"
    print(f"[请求] 获取坐标: {url}")
    r = requests.get(url)
    result = r.json()
    if "pois" in result and result["pois"]:
        return result["pois"][0]
    return None

def search_nearby_pois(longitude, latitude, keyword):
    url = f"https://restapi.amap.com/v5/place/around?key={amap_key}&keywords={keyword}&location={longitude},{latitude}"
    print(f"[请求] 搜索附近POI: {url}")
    r = requests.get(url)
    result = r.json()
    ans = ""
    if "pois" in result and result["pois"]:
        for i in range(min(3, len(result["pois"]))):
            name = result["pois"][i]["name"]
            address = result["pois"][i]["address"] or "地址未知"
            distance = result["pois"][i]["distance"] or "未知"
            ans += f"{name}\n{address}\n距离:{distance}米\n\n"
    return ans

3.3 主流程:支持多轮工具调用

prompt = "我想在合肥英唐工业园附近吃麦当劳,给我推荐几个"
messages = [
    {"role": "system", "content": "你是一个地图通,你可以找到任何地址。"},
    {"role": "user", "content": prompt},
]

response = get_completion(messages)
messages.append(response)

# 循环处理工具调用
while response.tool_calls is not None:
    for tool_call in response.tool_calls:
        args = json.loads(tool_call.function.arguments)

        if tool_call.function.name == "get_location_coordinate":
            print("Call: get_location_coordinate")
            result = get_location_coordinate(**args)
        elif tool_call.function.name == "search_nearby_pois":
            print("Call: search_nearby_pois")
            result = search_nearby_pois(**args)
        else:
            result = "未知的工具调用"

        messages.append({
            "tool_call_id": tool_call.id,
            "role": "tool",
            "name": tool_call.function.name,
            "content": str(result),
        })

    response = get_completion(messages)
    messages.append(response)

print("=====最终回复=====")
print(response.content)

运行结果(模拟):

Call: get_location_coordinate
[请求] 获取坐标: https://restapi.amap.com/v5/place/text?key=xxx&keywords=英唐工业园&region=合肥
Call: search_nearby_pois
[请求] 搜索附近POI: https://restapi.amap.com/v5/place/around?key=xxx&keywords=麦当劳&location=117.22,31.82
=====最终回复=====
在合肥英唐工业园附近找到以下麦当劳:
1. 麦当劳(合肥经开区店)
   地址:经开区繁华大道与翡翠路交叉口
   距离:约500米

案例四:让模型操作数据库——SQL生成与执行

这个案例展示了如何让大模型根据自然语言生成SQL并执行查询。

4.1 定义数据库表结构

import sqlite3

database_schema_string = """
CREATE TABLE orders (
    id INT PRIMARY KEY NOT NULL,
    student_id STR NOT NULL,
    paper_id STR NOT NULL,
    mark DECIMAL(10,3) NOT NULL,
    graduate_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
"""

# 创建内存数据库
conn = sqlite3.connect(":memory:")
cursor = conn.cursor()
cursor.execute(database_schema_string)

# 插入测试数据
mock_data = [
    (1, "Tom", "A", 91.00, "2021-10-12 "),
    (2, "Lucy", "B", 87.50, "2022-10-16 "),
    (3, "Jack", "C", 90.25, "2023-10-17 "),
    (4, "Paul", "D", 86.75, "2024-10-20 "),
    (5, "Bob", "E", 55.00, "2025-10-28 "),
]

for record in mock_data:
    cursor.execute(
        "INSERT INTO orders (id, student_id, paper_id, mark, graduate_time) VALUES (?, ?, ?, ?, ?)",
        record,
    )
conn.commit()

4.2 定义工具并调用

def ask_database(query):
    cursor.execute(query)
    records = cursor.fetchall()
    return records

prompt = "哪个学生毕业时间最晚?在什么时候?"
messages = [
    {"role": "system", "content": "基于 order 表回答用户问题"},
    {"role": "user", "content": prompt},
]

response = get_sql_completion(messages)
messages.append(response)

if response.tool_calls is not None:
    tool_call = response.tool_calls[0]
    if tool_call.function.name == "ask_database":
        args = json.loads(tool_call.function.arguments)
        print("====生成的SQL====")
        print(args["query"])

        result = ask_database(args["query"])
        print("====查询结果====")
        print(result)

        messages.append({
            "tool_call_id": tool_call.id,
            "role": "tool",
            "name": "ask_database",
            "content": str(result),
        })

        response = get_sql_completion(messages)
        print("====最终回复====")
        print(response.content)

conn.close()

运行结果:

====生成的SQL====
SELECT student_id, graduate_time FROM orders ORDER BY graduate_time DESC LIMIT 1;
====查询结果====
[('Bob', '2025-10-28 ')]
====最终回复====
毕业时间最晚的学生是Bob,毕业时间为2025年10月28日。

案例五:简单加法器——让模型调用计算工具

最后一个案例演示如何让模型调用加法工具进行计算。

from openai import OpenAI
import json
from math import *

client = OpenAI(
    api_key="sk-xxx",
    base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
)

def get_completion(messages, model="qwen-plus"):
    response = client.chat.completions.create(
        model=model,
        messages=messages,
        tools=[
            {
                "type": "function",
                "function": {
                    "name": "sum",
                    "description": "加法器,计算一组数的和,只能运用于加法操作",
                    "parameters": {
                        "type": "object",
                        "properties": {
                            "numbers": {"type": "array", "items": {"type": "number"}}
                        },
                    },
                },
            }
        ],
    )
    return response.choices[0].message

prompt = "桌上有 2 个苹果,四个桃子和 3 本书,一共有几个水果?"
messages = [
    {"role": "system", "content": "你是一个数学家,当需要进行加法操作时调用sum工具"},
    {"role": "user", "content": prompt},
]

response = get_completion(messages)
messages.append(response)

if response.tool_calls is not None:
    tool_call = response.tool_calls[0]
    if tool_call.function.name == "sum":
        args = json.loads(tool_call.function.arguments)
        result = sum(args["numbers"])  # 调用Python内置sum函数
        print("=====函数返回=====")
        print(result)

        messages.append({
            "tool_call_id": tool_call.id,
            "role": "tool",
            "name": "sum",
            "content": str(result),
        })

        print("=====最终回复=====")
        print(get_completion(messages).content)

运行结果:

=====函数返回=====
6
=====最终回复=====
桌上有2个苹果和4个桃子,一共是6个水果。

更多推荐