Function Calling(函数调用)是LLM 工程化、AI 智能体的核心基石。
如果大模型是大脑,那 Function Calling 就是让大脑「指挥手脚干活」的标准协议——它规定了大模型如何描述工具、如何输出调用指令、程序如何执行、如何回传结果。
一、Function Calling 是什么?
一句话定义:
Function Calling 是一套固定的 JSON 数据协议,让大模型以结构化格式告诉程序:我需要调用哪个函数、传什么参数。
它的核心分工:
- LLM:理解意图 → 生成规范的调用格式
- 程序:解析格式 → 执行函数 → 返回结果
- LLM:整合结果 → 生成最终回答
二、Function Calling 标准格式拆解
Function Calling两套核心格式:
- 工具定义格式(告诉 LLM 有什么函数可用)
- 模型返回格式(LLM 告诉程序要调用什么)
2.1 格式1:工具定义格式
传给大模型的格式,描述函数的名字、功能、参数。
完整标准结构
[ { "type": "function", "function": { "name": "函数名称(英文,程序中真实存在)", "description": "函数功能描述(LLM靠这个判断是否调用)", "parameters": { "type": "object", "properties": { "参数1": { "type": "参数类型(string/number/boolean)", "description": "参数说明" }, "参数2": { "type": "string", "description": "参数说明" } }, "required": ["必填参数1"] } } } ]字段 | 作用 | 必须? |
| 函数名,程序与模型的唯一对应标识 | ✅ 必须 |
| 功能描述,LLM 调用的依据 | ✅ 必须(写不清楚就调用失败) |
| 参数定义容器 | ✅ 必须 |
| 所有参数的详细描述 | ✅ 必须 |
| 声明哪些参数必填 | ✅ 推荐 |
2.2 格式2:模型返回格式
LLM 接收到工具描述后,需要调用时,会返回固定 JSON 格式:
{ "role": "assistant", "content": null, "tool_calls": [ { "function": { "name": "调用的函数名", "arguments": "{\"参数1\":\"值1\",\"参数2\":\"值2\"}" } } ] }关键字段
name:模型选择的函数arguments:模型生成的参数(JSON 字符串)
三、Function Calling 怎么用?
5 步法:
- 定义工具函数(写业务代码:计算器/查天气/查数据库)
- 封装为标准 FC 格式(上文的工具格式)
- 传给 LLM,让模型知道可用工具
- LLM 返回调用指令,程序解析并执行函数
- 把结果回传给 LLM,生成最终答案
四、JSON 工具封装
1. 手动封装 JSON
步骤 1:定义原始函数
# 原始业务函数 def calculator(num1: float, num2: float, operator: str) -> str: if operator == "+": return f"结果:{num1+num2}" if operator == "-": return f"结果:{num1-num2}" if operator == "*": return f"结果:{num1*num2}" if operator == "/": return f"结果:{num1/num2}" return "无效运算符"步骤 2:严格按照标准封装 JSON
# 手动封装:计算器工具 JSON tool_json = [ { "type": "function", "function": { "name": "calculator", # 对应函数名 "description": "计算器工具,支持加减乘除计算", # 功能描述 "parameters": { "type": "object", "properties": { "num1": { "type": "number", "description": "第一个计算数字" }, "num2": { "type": "number", "description": "第二个计算数字" }, "operator": { "type": "string", "description": "运算符,支持+ - * /" } }, "required": ["num1", "num2", "operator"] # 必填参数 } } } ]步骤 3:多工具合并封装
如果有多个工具,直接放在数组里即可:
# 双工具封装:计算器 + 天气查询 tools_json = [ # 计算器工具 { "type": "function", "function": {"name": "calculator", "...": "..."} }, # 天气查询工具 { "type": "function", "function": {"name": "weather_query", "...": "..."} } ]步骤 4:原生调用验证(无框架)
直接把 JSON 传给模型,完成调用。
4.2 LangChain 自动封装
核心原理:函数与JSON 映射关系
Python 函数元素 | 自动封装为 JSON 字段 |
函数名 |
|
函数注释(docstring) |
|
参数类型注解 |
|
参数说明 |
|
查看 LangChain 自动生成的 JSON
这是最直观的验证方式,运行代码就能看到封装结果:
from langchain.tools import tool # 1. 定义工具函数 @tool def calculator(num1: float, num2: float, operator: str) -> str: """计算器工具,支持加减乘除计算""" if operator == "+": return f"结果:{num1+num2}" return "计算失败" # 2. 打印自动封装的 JSON 格式 print("===== LangChain 自动封装的工具 JSON =====") print(calculator.args_schema.model_json_schema())===== LangChain 自动封装的工具 JSON ===== {'description': '计算器工具,支持加减乘除计算', 'properties': {'num1': {'title': 'Num1', 'type': 'number'}, 'num2': {'title': 'Num2', 'type': 'number'}, 'operator': {'title': 'Operator', 'type': 'string'}}, 'required': ['num1', 'num2', 'operator'], 'title': 'calculator', 'type': 'object'}五、实战代码(可直接运行)
用双工具(计算器+天气查询)完整演示,框架使用 LangChain,自动处理格式,无需手写 JSON。
1. 完整可执行代码
from langchain_openai import ChatOpenAI from langchain.tools import tool from langchain.agents import create_tool_calling_agent, AgentExecutor from langchain.prompts import ChatPromptTemplate import os from dotenv import load_dotenv # 加载配置 load_dotenv() # ===================== 1. 初始化模型 ===================== llm = ChatOpenAI( model="qwen3.5-flash", api_key=os.getenv("DASHSCOPE_API_KEY"), base_url="https://dashscope.aliyuncs.com/compatible-mode/v1", temperature=0.1, ) # ===================== 2. 定义工具(自动封装FC格式) ===================== # 工具1:计算器 @tool def calculator(num1: float, num2: float, operator: str) -> str: """ 计算器工具,执行加减乘除 参数: num1: 数字1 num2: 数字2 operator: 运算符(+ - * /) """ if operator == "+": return f"结果:{num1+num2}" if operator == "-": return f"结果:{num1-num2}" if operator == "*": return f"结果:{num1*num2}" if operator == "/": return f"结果:{num1/num2}" return "无效运算符" # 工具2:天气查询 @tool def weather_query(city: str) -> str: """ 查询城市天气 city: 城市名称 """ data = {"北京":"晴 25℃","上海":"多云 28℃","广州":"小雨 26℃"} return data.get(city, "暂无数据") # 工具列表 tools = [calculator, weather_query] # ===================== 3. 构建调用流程 ===================== prompt = ChatPromptTemplate.from_messages([ ("system", "你是工具调用助手,必须严格按格式调用工具"), ("human", "{input}"), ("placeholder", "{agent_scratchpad}"), ]) # 创建智能体(自动处理FC格式) agent = create_tool_calling_agent(llm, tools, prompt) executor = AgentExecutor( agent=agent, tools=tools, verbose=True, # 打印完整格式日志 max_iterations=3 ) # ===================== 4. 执行测试 ===================== if __name__ == "__main__": # 测试1:计算 print("=== 测试1:10乘以6.5 ===") print(executor.invoke({"input": "10乘以6.5等于多少?"})["output"]) # 测试2:天气 print("\n=== 测试2:查询北京天气 ===") print(executor.invoke({"input": "北京今天天气怎么样?"})["output"])3. 运行日志
=== 测试1:10乘以6.5 === > Entering new AgentExecutor chain... Invoking: `calculator` with `{'num1': 10, 'num2': 6.5, 'operator': '*'}` 结果:65.010乘以6.5等于65。 > Finished chain. 10乘以6.5等于65。 === 测试2:查询北京天气 === > Entering new AgentExecutor chain... Invoking: `weather_query` with `{'city': '北京'}` 晴 25℃北京今天天气晴朗,气温25℃。 > Finished chain. 北京今天天气晴朗,气温25℃。五、Function Calling 用法核心规则
1. 工具描述决定调用成功率
LLM 不看代码,只看描述!
描述越清晰,调用越准确。
2. 参数类型必须严格定义
数字、字符串不能混淆,否则格式解析失败。
3. 必须限制调用次数
防止无限循环,生产环境必加max_iterations。
六、总结
- Function Calling = 一套固定 JSON 协议
- 两个核心格式:工具定义格式 + 模型返回格式
- 5步固定用法:定义工具→封装格式→传给LLM→执行→回传
- 工程落地:工具描述是关键,框架自动处理格式