Function Calling 初识
本节定位
很多新人第一次做 LLM 应用时,会把模型当成“万能文本生成器”。
但真正走向实用系统时,你很快会发现:
模型不只要会说,还得会把任务转成可执行动作。
这就是 Function Calling 要解决的问题。
学习目标
- 理解为什么仅靠自然语言输出很难稳定调用工具
- 理解函数 schema、参数、调用结果这几个核心概念
- 看懂一个最小的函数调用闭环
- 知道 Function Calling 最适合什么场景
新人先掌握 / 进阶再理解
如果你是新人,这一节先抓一句话:Function Calling 不是让模型真的去执行代码,而是让模型先输出一份结构化“调用意图”,再由程序检查、执行和返回结果。
如果你已经做过 LLM 应用,可以进一步关注:工具 schema 是否足够清楚,参数校验是否完整,工具失败后怎么重试或降级,调用日志是否能支撑调试和评估。
一、为什么纯文本输出不够?
1.1 一个常见的脆弱做法
假设用户问:
“北京今天多少度?”
你让模型返回一句话:
“我建议调用
get_weather(city='Beijing')”
这看起来像能用,但其实很脆:
- 格式可能不稳定
- 参数名可能乱写
- 城市名可能写成“北京”或“Beijing”
- 甚至可能多输出一堆解释
1.2 真正的问题是什么?
问题不在于模型不会理解任务,而在于:
自然语言太自由,不适合做稳定的程序接口。
程序更喜欢的是:
- 固定字段
- 明确参数
- 可校验结构
这正是 Function Calling 的价值。
二、Function Calling 到底是什么?
2.1 一句话理解
Function Calling = 让模型输出结构化工具调用,而不是随意文本。
它通常包括:
- 调哪个工具
- 传哪些参数
例如:
{
"name": "get_weather",
"arguments": {
"city": "Beijing"
}
}
2.2 这比自由文本强在哪?
因为它更像程序接口,而不是聊天内容。
程序拿到这个结构后,可以:
- 校验字段
- 自动执行
- 失败重试
- 记录日志
也就是说,Function Calling 是在给模型和程序之间搭桥。
三、先看一个最小闭环
3.1 定义两个工具
def get_weather(city):
data = {
"Beijing": {"temperature": 22, "condition": "sunny"},
"Shanghai": {"temperature": 25, "condition": "cloudy"}
}
return data.get(city, {"error": "city_not_found"})
def calculate(expression):
return {"result": eval(expression, {"__builtins__": {}})}
3.2 定义“模型输出”的调用结构
tool_call = {
"name": "get_weather",
"arguments": {
"city": "Beijing"
}
}
print(tool_call)
3.3 真正执行这个调用
def dispatch(call):
if call["name"] == "get_weather":
return get_weather(**call["arguments"])
if call["name"] == "calculate":
return calculate(**call["arguments"])
return {"error": "unknown_tool"}
tool_call = {
"name": "get_weather",
"arguments": {"city": "Beijing"}
}
result = dispatch(tool_call)
print(result)
这就是函数调用闭环的最小版本:
- 识别任务
- 产出结构化调用
- 程序执行
- 拿到结果
四、Schema 是什么?
4.1 Schema 可以理解成“工具说明书”
模型要正确调用工具,必须知道:
- 工具叫什么
- 每个参数叫什么
- 参数是什么类型
- 参数是不是必须传
这就是 schema 的作用。
4.2 一个简单 schema 示例
weather_schema = {
"name": "get_weather",
"description": "查询指定城市天气",
"parameters": {
"city": {
"type": "string",
"description": "城市英文名,例如 Beijing"
}
},
"required": ["city"]
}
print(weather_schema)
schema 不是“装饰文案”,而是在告诉模型和程序:
这个工具允许怎样被调用。
五、为什么参数校验很重要?
5.1 模型不一定总能给对参数
就算模型选对了工具,也可能:
- 漏字段
- 类型不对
- 参数值无效
例如:
bad_call = {
"name": "get_weather",
"arguments": {"city_name": "Beijing"}
}
如果你的程序不校验,就会在执行阶段直接炸掉。
5.2 一个最小校验示例
def validate_weather_call(call):
if call.get("name") != "get_weather":
return False, "wrong_tool"
args = call.get("arguments", {})
if "city" not in args:
return False, "missing_city"
if not isinstance(args["city"], str):
return False, "city_must_be_string"
return True, "ok"
good_call = {"name": "get_weather", "arguments": {"city": "Beijing"}}
bad_call = {"name": "get_weather", "arguments": {"city_name": "Beijing"}}
print(validate_weather_call(good_call))
print(validate_weather_call(bad_call))