Skip to content

大模型 Function Call 的原理及应用

目录

  1. 什么是 Function Call

  2. Function Call 的工作原理

  3. Function Call 的单一函数应用

  4. Function Call 的多个函数应用

  5. Function Call 实现数据库查询应用

什么是 Function Call

2023 年 6 月 13 日 OpenAI 公布了 Function Call(函数调用)功能,该功能指的是在语言模型中集成外部功能或 API 的调用能力,这意味着模型可以在生成文本的过程中调用外部函数或服务,获取额外的数据或执行特定的务。

Function Call 应用基本流程(简化):

image-20250508090219906

Function Call 的功能

Function Call 可以解决大模型什么问题:

01、信息实时性,大模型训练的数据集无法包含最新的信息,如最新的新闻、实时股价等。通过 Function Call,模型可以实时获取最新数据,提供更加时效的服务。

02、数据局限性,模型训练数据虽多但有限,无法覆盖所有领域,如医学、法律等领域的专业咨询,Function Call 允许模型调用外部数据库或 API,获取特定领域的详细信息。

03、功能扩展性,大模型虽然功能强大,但不可能内置所有可能需要的功能。通过 FunctionCall,可以轻松扩展模型能力,如调用外部工具进行复杂计算、数据分析等。

Function Call 工作原理

当没有函数调用 (Function-call) 时候,我们调用 GPT 构建 AI 应用的模式非常简单。

image-20250508090436667

主要步骤:

  1. 用户 (Client) 发请求给我们的服务 (Chat Server)

  2. 我们的服务 (Chat Server) 给 GPT 提示词

  3. 重复执行

当有函数调用 (Function-call) 时候,我们调用 GPT 构建 AI 应用的模式比之前要复杂一些。

image-20250508090451908

主要步骤:

  1. 用户 (Client) 发请求 prompt 以及 functions 给我们的服务 (ChatServer)

  2. GPT 模型根据用户的 prompt,判断是用普通文本还是函数调用的格式响应我们的服务 (Chat Server)

  3. 如果是函数调用格式,那么 Chat Server 就会执行这个函数,并且将结果返回给 GPT

  4. 然后模型使用提供的数据,用连贯的文本响应。返回

注意:大模型的 Function call 不会调用函数,仅返回函数的参数。开发者利用模型输出的参数在应用中调用函数。

思考总结

1、什么是 Function Call?

答案:在语言模型中集成外部功能或 API 的调用能力。

2、LLM 模型是否能够直接运行 function?

答案:不会调用函数,仅返回函数的参数。开发者利用模型输出的参数在应用中调用函数。

单一函数应用

假设我们要创建一个具备查询实时天气的聊天机器人。

基本流程如下:

1、准备工作,申请 API-KEY,配置环境变量。

2、定义 Funtion Tools,定义外部函数,描述函数功能

3、模型应用 Function Call 输入 prompt,模型输出函数参数,然后调用本地函数得到结果,融合消息再次送入模型,得到结果。

第一步:准备工作

模型选择:

国内外支持 Function Call 的模型,如:Deepseek、ChatGPT,智谱 ChatGLM3 等。

此次应用基于 ollama 的 qwen3:1.7b 来实现

第二步:定义 Function Tools

Function Tools

  1. 目的:定义查询天气的外部函数;2.描述函数功能;3.解析模型参数调用函数
  2. 代码路径:./ChatGLM3_FunctionCall/weather/tools.py
  3. 具体代码:一共包含 2 个自定义函数,一个函数功能描述,具体请看下文

自定义 get_current_weather 函数

python
import json
import requests
from langchain_core.tools import tool


@tool
def get_current_weather(location):
    """得到给定地址的当前天气信息"""
    # 读取城市及对应的邮政编码
    str_data = open('./city_code.json', 'r', encoding='utf-8').read()
    json_data = json.loads(str_data)

    city_code = ""
    weather_info = {}
    for item in json_data:
        if location == item["市名"]:
            city_code = item["编码"]

    # 调用天气查询 API 接口
    if city_code:
        weather_url = "http://t.weather.itboy.net/api/weather/city/" + city_code
        response = requests.get(weather_url)
        result1 = eval(response.text)
        print(result1)
        forecast = result1["data"]["forecast"][0]
        print(forecast)
        weather_info = {
            "location": location,
            "high_temperature": forecast["high"],
            "low_temperature": forecast["low"],
            "week": forecast["week"],
            "type": forecast["type"],
        }
    return json.dumps(weather_info, ensure_ascii=False)


if __name__ == '__main__':
    print(get_current_weather('昌平'))
json
[
  {
    "市名": "北京",
    "编码": "101010100"
  },
  {
    "市名": "海淀区",
    "编码": "101010200"
  },
  {
    "市名": "朝阳区",
    "编码": "101010300"
  },
  {
    "市名": "顺义区",
    "编码": "101010400"
  },
  {
    "市名": "怀柔区",
    "编码": "101010500"
  },
  {
    "市名": "昌平",
    "编码": "101010700"
  }
]

第三步:模型应用 Function

Function 应用

  • 目的:1.实现大模型 Function Call 的功能应用

  • 代码路径:./weather/main.py

  • 具体代码:一共包含 2 个函数:调用模型函数和主逻辑函数。如下文:

模型应用 Function Call

主逻辑函数 main:

python
import json

from langchain_core.messages import HumanMessage, SystemMessage
from langchain_ollama import ChatOllama
from tools import get_current_weather

llm = ChatOllama(model="qwen3:1.7b")

llm_tools = llm.bind_tools([get_current_weather])

messages = [
    SystemMessage("""你是一个天气播报小助手,你需要根据用户提供的地址来回答当地的天气情况,
        如果用户提供的问题具有不确定性,不要自己编造内容,提示用户明确输入"""),
    HumanMessage('今天北京的天气如何')
]

output = llm_tools.invoke(messages)
# 打印工具调用明细
print("工具调用明细:", output)
tool_load = json.dumps(output.tool_calls, indent=4, ensure_ascii=False)
print('调用的工具为:\n', tool_load)

# 创建新的消息列表
messages.append(output)

# 添加可以调用的 function call 信息
available_tools = {"get_current_weather": get_current_weather}
for tool_call in output.tool_calls:
    select_tool = available_tools[tool_call["name"]]
    tool_message = select_tool.invoke(tool_call)
    messages.append(tool_message)

second_output = llm_tools.invoke(messages)
for message in second_output:
    print(message)
    # print(json.dumps(message.model_dump(), indent=4, ensure_ascii=False))
print(second_output.content)
  • 结果展示:

    今天北京的天气晴朗,高温 27℃,低温 17℃,星期四。

思考总结

1、Function Call 单一函数应用流程?

答案:

  1. 定义函数:函数可以是真实的外部 API 或工具,也可以是模拟函数。
  2. 提供函数定义给模型:将定义好的函数作为参数 (tools) 提供给模型。
  3. 模型生成函数调用 JSON:包含要调用的函数名称和参数值。
  4. 后端系统执行函数:后端系统根据 JSON 中的内容,调用相应的函数结果。
  5. 将函数结果返回给模型:函数返回结果给模型,作为模型的额外上下文信息。
  6. 模型生成最终响应:模型综合原始查询、函数调用结果等信息,生成最终输出。

多个函数应用

chat 方式实现

python
import json
from langchain_core.tools import tool
from langchain.chat_models import init_chat_model
from langchain_core.messages import HumanMessage

from langchain_ollama import ChatOllama


@tool
def add(a: int, b: int) -> int:
    """Adds two numbers together."""
    return a + b


@tool
def subtract(a: int, b: int) -> int:
    """Subtracts two numbers."""
    return a - b


llm = ChatOllama(model='qwen3:1.7b')

llm_tools = llm.bind_tools([add, subtract])

query = "12 和 15 相差多少?"

messages = [
    HumanMessage(query)
]

output = llm_tools.invoke(messages)
# 打印工具调用明细
print("工具调用明细:", output)
tool_load = json.dumps(output.tool_calls, indent=4)
print('调用的工具为:\n', tool_load)

# 创建新的消息列表
messages.append(output)

# 添加可以调用的 function call 信息
available_tools = {"add": add, "subtract": subtract}
for tool_call in output.tool_calls:
    select_tool = available_tools[tool_call["name"]]
    tool_message = select_tool.invoke(tool_call)
    messages.append(tool_message)

second_output = llm_tools.invoke(messages)
for message in second_output:
    print(message)
    # print(json.dumps(message.model_dump(), indent=4, ensure_ascii=False))
print(second_output.content)

agent 方式实现

python
from langchain.agents import AgentType, initialize_agent
from langchain_ollama import ChatOllama
from langchain_openai import ChatOpenAI
from langchain.tools import Tool, StructuredTool
from typing import Optional
from langchain.chat_models import init_chat_model


# 定义工具函数
def search_api(query: str) -> str:
    """模拟 API 调用"""
    return f"结果:{query}"


def add_two_numbers(a: int, b: int) -> int:
    """模拟两个数字相加"""
    return a + b


# 创建工具实例
tools = [
    Tool(
        name="SearchAPI",
        func=search_api,
        description="用于查询实时信息"
    ),

    # StructuredTool 来处理多参数工具:
    StructuredTool.from_function(
        func=add_two_numbers,
        name="add_two_numbers",
        description="用于计算两个数的和",
    )
]

llm = ChatOllama(model='qwen3:1.7b')

agent = initialize_agent(
    tools=tools,
    llm=llm,
    agent=AgentType.STRUCTURED_CHAT_ZERO_SHOT_REACT_DESCRIPTION,
    verbose=True
)

# 执行查询
response = agent.invoke("123 和 456 相加是多少?")
print(response)

Function Call 实现数据库查询

第一步:构建脚本 db.py

  1. 定义查询数据库的函数;
  2. 描述函数功能 tools;
  3. 解析模型参数调用函数
python
from sqlalchemy import create_engine, Column, Integer, String, ForeignKey
from sqlalchemy.orm import sessionmaker, declarative_base

# 数据库连接配置
DATABASE_URI = "mysql+pymysql://windows:Zhengxin...123456@localhost:3306/llm_sql"

# 创建数据库引擎
engine = create_engine(DATABASE_URI)

# 创建会话
Session = sessionmaker(bind=engine)
session = Session()

# 定义基类
Base = declarative_base()


# 定义用户表
class User(Base):
    __tablename__ = 'users'
    id = Column(Integer, primary_key=True)
    name = Column(String(50))
    gender = Column(String(10))
    age = Column(Integer)
    department_id = Column(Integer, ForeignKey('departments.id'))


# 定义部门表
class Department(Base):
    __tablename__ = 'departments'
    id = Column(Integer, primary_key=True)
    fid = Column(Integer)
    name = Column(String(50))


if __name__ == '__main__':
    # 创建表
    Base.metadata.create_all(engine)

    # 插入一些测试数据
    session.add_all([
        Department(id=1, fid=None, name="HR"),
        Department(id=2, fid=1, name="Recruitment"),
        User(id=1, name="Alice", gender="Female", age=28, department_id=1),
        User(id=2, name="Bob", gender="Male", age=32, department_id=2),
    ])

    session.commit()

第二步:实现查询

shell
from langchain_deepseek import ChatDeepSeek
from langchain_community.agent_toolkits.sql.base import create_sql_agent
from langchain_community.agent_toolkits.sql.toolkit import SQLDatabaseToolkit
from langchain_community.utilities import SQLDatabase
from langchain.agents.agent_types import AgentType

from db import DATABASE_URI

# 创建数据库对象
db = SQLDatabase.from_uri(DATABASE_URI)

llm = ChatDeepSeek(model='deepseek-chat')

# 创建 SQL 工具套件
toolkit = SQLDatabaseToolkit(db=db, llm=llm)

# 创建 SQL 代理执行器
agent_executor = create_sql_agent(
    llm=llm,
    toolkit=toolkit,
    verbose=True,
    agent_type=AgentType.ZERO_SHOT_REACT_DESCRIPTION,
)

# 执行查询
query = "SELECT * FROM users WHERE age > 30"
ret = agent_executor.invoke(query)
print(ret)

# 更复杂的问答
question = "列出所有年龄大于 30 岁的员工及其所在部门名称"
ret = agent_executor.invoke(question)
print(ret)