在大模型应用开发中,函数调用(Function Calling)是突破纯文本交互、实现模型与外部工具协同的核心能力。它让大模型不再只做 “语言翻译官”,更能成为 “任务指挥官”—— 识别需要调用的工具、传递正确参数、接收执行结果并给出最终答案。本文将以阿里通义千问(qwen-plus)为例,从基础加法器实现到复杂场景的水果计数,一步步拆解大模型函数调用的完整流程。

一、函数调用核心原理

        函数调用的本质是:开发者预先定义工具(函数)的描述(名称、用途、参数格式),大模型接收用户问题后,先判断是否需要调用工具;若需要,则按照预设格式生成调用指令(指定函数名、传入参数);开发者解析指令后执行函数,再将结果返回给大模型,最终由大模型整合结果给出自然语言回答。

核心流程

  1. 定义工具函数的 JSON 描述(告诉大模型 “有什么工具、怎么用”)
  2. 用户提问,大模型判断是否调用工具并生成调用指令
  3. 解析调用指令,执行本地函数并获取结果
  4. 将函数结果回传给大模型,生成最终回答

二、加法器函数调用

1.定义函数调用的核心函数

封装get_completion函数,包含模型调用逻辑和工具描述定义(重点是tools参数)

def get_completion(messages, model="qwen-plus"):
    """
    调用大模型,支持工具函数调用
    :param messages: 对话历史(system+user+工具调用结果等)
    :param model: 调用的模型,默认qwen-plus
    :return: 大模型的回复对象
    """
    response = client.chat.completions.create(
        model=model,
        messages=messages,
        tools=[  # 定义工具函数的JSON描述(可定义多个)
            {
                "type": "function",
                "function": {
                    "name": "sum",  # 函数名
                    "description": "加法器,计算一组数的和,只能运用于加法操作",  # 函数用途(大模型据此判断是否调用)
                    "parameters": {  # 参数定义(JSON Schema格式)
                        "type": "object",
                        "properties": {
                            "numbers": {"type": "array", "items": {"type": "number"}}  # 数组类型,元素为数字
                        },
                        "required": ["numbers"]  # 必传参数(建议显式声明,提升稳定性)
                    },
                },
            }
        ],
    )
    return response.choices[0].message

2.测试基础加法场景

调用函数计算 1 到 10 的和,验证工具调用逻辑:

# 测试1:计算1-10的和
prompt = "Tell me the sum of 1, 2, 3, 4, 5, 6, 7, 8, 9, 10."
messages = [
    {"role": "system", "content": "你是一个数学家,当需要进行加法操作时调用sum工具"},
    {"role": "user", "content": prompt},
]

# 第一步:获取大模型的工具调用指令
response = get_completion(messages)
messages.append(response)  # 将模型回复加入对话历史
print("=====大模型工具调用指令=====")
print(response)

# 第二步:解析指令并执行sum函数
if response.tool_calls is not None:
    tool_call = response.tool_calls[0]
    if tool_call.function.name == "sum":
        # 解析参数(JSON字符串转字典)
        args = json.loads(tool_call.function.arguments)
        result = sum(args["numbers"])  # 执行加法
        print("=====函数执行结果=====")
        print(result)

        # 第三步:将函数结果回传给大模型
        messages.append(
            {
                "tool_call_id": tool_call.id,  # 关联工具调用ID
                "role": "tool",
                "name": "sum",
                "content": str(result),  # 工具返回结果必须是字符串
            }
        )

        # 第四步:获取大模型的最终自然语言回答
        final_response = get_completion(messages)
        print("=====最终回答=====")
        print(final_response.content)

3.解析

  • tools参数是核心:通过 JSON Schema 定义函数的名称、描述、参数,大模型会根据用户问题和描述判断是否调用该函数

  • tool_calls是模型返回的调用指令:若不为空,说明模型决定调用工具,需解析其中的function.namefunction.arguments

  • 对话历史需完整:每一步的模型回复、工具执行结果都要加入messages,保证模型上下文连贯

三、智能水果计数

        加法场景是纯数字输入,而实际场景中用户问题往往是自然语言(如 “2 个苹果、4 个桃子、3 本书,一共有几个水果?”),需要大模型先识别 “水果类别”、提取数字,再调用加法函数。

1.代码实现

import json
from openai import OpenAI

# 初始化客户端
client = OpenAI(
    api_key="sk-0b717f29b6ee4852a2331cf1ffa30d4f",
    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"}}
                        },
                        "required": ["numbers"]
                    },
                },
            }
        ],
    )
    return response.choices[0].message

# 进阶测试:水果计数(需先筛选水果类别,再求和)
prompt = "桌上有 2 个苹果,四个桃子和 3 本书,一共有几个水果?"
messages = [
    {"role": "system", "content": "你是一个数学家,先识别用户问题中的水果类别,提取对应数量,再调用sum工具计算水果总数;非水果类物品忽略"},
    {"role": "user", "content": prompt},
]

# 第一步:获取模型的工具调用指令(模型会自动筛选水果数量)
response = get_completion(messages)
messages.append(response)
print("=====大模型回复(工具调用指令)=====")
print(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"])
        print("=====sum函数执行结果=====")
        print(result)

        # 第三步:回传工具结果给模型
        messages.append(
            {
                "tool_call_id": tool_call.id,
                "role": "tool",
                "name": "sum",
                "content": str(result),
            }
        )

        # 第四步:获取最终自然语言回答
        final_response = get_completion(messages)
        print("=====最终回答=====")
        print(final_response.content)
else:
    # 模型未调用工具,直接返回回答
    print("=====最终回答=====")
    print(response.content)

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐