大模型应用开发教程09 | Function Calling 原理与实践(工具调用)
·
理论部分
前面我们做的事情,本质上都是:给模型一段输入,让模型生成一段输出。
但当我们希望模型“帮我们做事”时,仅靠生成文本往往不够:
- 我们想要实时数据(天气、股价、数据库查询结果),模型本身拿不到
- 我们想要模型触发动作(发请求、查文件、执行计算),文本并不能直接执行
Function Calling(工具调用)就是把“模型输出”升级为“可执行的结构化意图”:
- 我们把可用工具(函数)及参数规则告诉模型
- 模型根据用户问题,决定要不要调用工具、调用哪个工具、传什么参数
- 我们在代码里执行真实函数,把函数结果回传给模型
- 模型基于工具结果生成最终回答
从工程视角看,这里最关键的是:模型只负责决定“调用什么 + 参数是什么”,真正执行仍然由我们的程序掌控。这使得工具调用更可控、更可验证,也更容易接入业务系统。
一个完整的工具调用链路里,会出现三类关键信息:
- Tool Definition(工具描述):函数名、用途描述、参数 schema(类型、枚举、必填字段)
- Tool Call(工具调用请求):模型生成的结构化调用意图(函数名 + arguments)
- Tool Result(工具执行结果):我们执行函数后的返回值(通常是 JSON 字符串或可解析文本)
实践部分
本案例做什么
我们实现一个“智能天气助手”,让模型在需要时自动调用 get_current_weather 工具:
- 用户提问(比如“北京天气怎么样?”)
- 模型决定调用工具,并给出参数(location、unit)
- 我们执行函数(这里用模拟数据代替真实天气 API)
- 把工具返回结果喂回模型,生成最终答复
主要代码
本篇使用项目脚本:src/4.1_weather_agent.py
import os
import json
from dotenv import load_dotenv
from openai import OpenAI
# 加载环境变量
load_dotenv()
client = OpenAI(
api_key=os.getenv("ZHIPUAI_API_KEY"),
base_url=os.getenv("ZHIPUAI_BASE_URL")
)
# === 1. 定义实际的工具函数 ===
# 这里我们用模拟数据,实际项目中可以调用真实的天气 API
def get_current_weather(location, unit="celsius"):
"""获取指定城市的当前天气"""
print(f"DEBUG: 正在查询 {location} 的天气...")
if "北京" in location:
return json.dumps({"location": "北京", "temperature": "22", "unit": unit, "description": "晴朗"})
elif "上海" in location:
return json.dumps({"location": "上海", "temperature": "25", "unit": unit, "description": "多云"})
elif "广州" in location:
return json.dumps({"location": "广州", "temperature": "30", "unit": unit, "description": "雷阵雨"})
else:
return json.dumps({"location": location, "temperature": "unknown"})
# === 2. 定义工具描述 (Tool Definitions) ===
# 告诉 LLM 有哪些工具可用,以及参数格式
tools = [
{
"type": "function",
"function": {
"name": "get_current_weather",
"description": "获取指定城市的当前天气",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "城市名称,如:北京、上海",
},
"unit": {
"type": "string",
"enum": ["celsius", "fahrenheit"],
"description": "温度单位,默认为摄氏度"
},
},
"required": ["location"],
},
},
}
]
# === 3. 对话主循环 ===
def run_conversation():
print("=== 🌦️ 智能天气助手 (Function Calling Demo) ===")
print("试着问我:'北京天气怎么样?' 或 '上海和广州哪里更热?' (输入 q 退出)")
messages = [] # 维护上下文
while True:
user_input = input("\n👤 你: ")
if user_input.lower() in ['q', 'quit']:
break
# 将用户消息加入历史
messages.append({"role": "user", "content": user_input})
# 第一次调用模型:查看是否需要调用工具
response = client.chat.completions.create(
model="glm-4-flash", # 确保模型支持 Function Calling
messages=messages,
tools=tools,
tool_choice="auto", # 让模型自动决定是否调用工具
)
response_message = response.choices[0].message
tool_calls = response_message.tool_calls
# 如果模型决定调用工具
if tool_calls:
# 必须先把模型的这条回复(包含工具调用请求)加入历史
messages.append(response_message)
print(f"🤖 AI 决定调用工具: {len(tool_calls)} 个请求")
# 建立函数名到实际函数的映射
available_functions = {
"get_current_weather": get_current_weather,
}
# 遍历所有工具调用请求
for tool_call in tool_calls:
function_name = tool_call.function.name
function_to_call = available_functions[function_name]
function_args = json.loads(tool_call.function.arguments)
# 执行真正的函数
function_response = function_to_call(
location=function_args.get("location"),
unit=function_args.get("unit"),
)
print(f" --> 调用 {function_name}({function_args})")
print(f" <-- 返回 {function_response}")
# 将工具执行结果作为 tool 类型的消息加入历史
messages.append(
{
"tool_call_id": tool_call.id,
"role": "tool",
"name": function_name,
"content": function_response,
}
)
# 第二次调用模型:让模型根据工具结果生成最终回答
print("🤖 AI 正在根据工具结果生成回答...")
final_response = client.chat.completions.create(
model="glm-4-flash",
messages=messages,
)
ai_reply = final_response.choices[0].message.content
print(f"🤖 AI: {ai_reply}")
# 把最终回答也加入历史
messages.append({"role": "assistant", "content": ai_reply})
else:
# 不需要调用工具,直接回复
ai_reply = response_message.content
print(f"🤖 AI: {ai_reply}")
messages.append({"role": "assistant", "content": ai_reply})
if __name__ == "__main__":
run_conversation()
运行方式
在项目根目录执行:
python3 src/4.1_weather_agent.py
运行结果示例
我们可以输入一个需要实时信息的问题,例如:
- “北京天气怎么样?”
- “上海和广州哪里更热?”
运行时会看到类似输出(模型的措辞可能不同,但工具调用链路一致):
=== 🌦️ 智能天气助手 (Function Calling Demo) ===
试着问我:'北京天气怎么样?' 或 '上海和广州哪里更热?' (输入 q 退出)
👤 你: 上海和广州哪里更热?
🤖 AI 决定调用工具: 2 个请求
DEBUG: 正在查询 上海 的天气...
--> 调用 get_current_weather({'location': '上海', 'unit': 'celsius'})
<-- 返回 {"location": "上海", "temperature": "25", "unit": "celsius", "description": "多云"}
DEBUG: 正在查询 广州 的天气...
--> 调用 get_current_weather({'location': '广州', 'unit': 'celsius'})
<-- 返回 {"location": "广州", "temperature": "30", "unit": "celsius", "description": "雷阵雨"}
🤖 AI 正在根据工具结果生成回答...
🤖 AI: ...
总结
这一篇我们跑通了 Function Calling 的完整闭环:模型先决定是否调用工具以及工具参数,我们负责执行真实函数并把结果回传,最后再由模型整合工具结果生成最终回答。
更多推荐
所有评论(0)