GLM-4-9B-Chat-1M函数调用功能详解:自定义工具集成实战
GLM-4-9B-Chat-1M函数调用功能详解:自定义工具集成实战
想象一下,你正在开发一个智能客服系统,用户问:“帮我查一下北京明天飞上海的航班,然后看看天气怎么样,再推荐几个附近的酒店。”
传统的大模型可能只能回答“我无法查询实时信息”,或者给你一段编造的航班信息。但如果你能让大模型真正“动手”去调用查询接口、获取真实数据、再综合分析给出建议,那体验就完全不一样了。
这就是函数调用(Function Call)的魅力所在。它让大模型从一个“只会说”的聊天机器人,变成了一个“能动手”的智能助手。今天,我们就来深入聊聊GLM-4-9B-Chat-1M这个支持百万上下文的开源模型,看看它的函数调用功能到底有多强大,以及如何用它来构建真正实用的自动化工作流。
1. 函数调用:让大模型从“说”到“做”
简单来说,函数调用就是让大模型学会“使用工具”。你告诉模型:“我这里有几个工具(函数),分别是查天气、查航班、订酒店……”,然后当用户提出需求时,模型会自己判断:“哦,这个需求需要调用查天气和查航班这两个工具”,接着它会把调用这些工具所需的参数整理好,交给你去执行。
GLM-4-9B-Chat-1M在这方面做得相当不错。它不仅能理解复杂的工具描述,还能在长达100万token的上下文里记住你定义的所有工具,然后精准地选择该用哪个、该怎么用。
1.1 一个简单的例子:查天气
我们先来看个最简单的例子,感受一下函数调用是怎么工作的。
假设我们有一个查询天气的函数:
def get_weather(city: str, date: str) -> str:
"""查询指定城市在指定日期的天气情况
Args:
city: 城市名称,如“北京”、“上海”
date: 日期,格式为“YYYY-MM-DD”
Returns:
天气情况的描述字符串
"""
# 这里应该是调用真实天气API的代码
# 为了演示,我们返回一个模拟结果
return f"{city}在{date}的天气:晴,温度20-28°C"
现在,我们把这个函数“教”给GLM-4-9B-Chat-1M。当用户问“北京明天天气怎么样?”时,模型不会直接编造一个答案,而是会输出类似这样的内容:
{
"function": "get_weather",
"arguments": {
"city": "北京",
"date": "2024-12-25"
}
}
看到没?模型识别出了需要调用get_weather函数,并且准确地提取出了“北京”作为城市参数。至于日期,模型根据“明天”这个描述,自动推算出了具体的日期值。
你拿到这个调用请求后,去执行真正的get_weather函数(调用真实天气API),拿到结果后再交给模型,模型会把这个结果组织成自然语言回复给用户:“北京明天(12月25日)天气晴朗,温度在20到28度之间,适合外出。”
这个过程听起来简单,但背后有几个关键点:
- 模型要能理解工具的功能:从函数描述中知道这个工具是干什么的
- 模型要能提取正确的参数:从用户问题中提取出城市、日期等信息
- 模型要能处理工具返回的结果:把API返回的原始数据转换成用户能看懂的话
GLM-4-9B-Chat-1M在这几个方面都表现得很扎实,特别是它的长上下文能力,意味着你可以一次性定义几十个甚至上百个工具,模型都能记住并在需要时准确调用。
2. 实战:构建一个智能旅行助手
光说不练假把式,我们用一个实际的例子来展示函数调用的威力。我们来构建一个智能旅行助手,它能帮用户处理旅行中的各种需求:查航班、看天气、找酒店、算汇率等等。
2.1 定义我们的工具集
首先,我们需要定义助手能用到的所有工具。为了简化,我们先实现四个核心功能:
# 工具函数定义
import json
from datetime import datetime, timedelta
import random
def search_flights(departure: str, arrival: str, date: str) -> dict:
"""搜索航班信息
Args:
departure: 出发城市
arrival: 到达城市
date: 出发日期,格式为“YYYY-MM-DD”
Returns:
航班信息的字典
"""
# 模拟航班数据
flights = [
{
"flight_no": "CA1501",
"departure_time": "08:00",
"arrival_time": "10:30",
"airline": "中国国际航空",
"price": 1200
},
{
"flight_no": "MU5101",
"departure_time": "10:30",
"arrival_time": "13:00",
"airline": "中国东方航空",
"price": 1100
},
{
"flight_no": "CZ3101",
"departure_time": "14:00",
"arrival_time": "16:30",
"airline": "中国南方航空",
"price": 1050
}
]
return {
"departure": departure,
"arrival": arrival,
"date": date,
"flights": flights[:2] # 返回前两个航班
}
def get_weather_forecast(city: str, days: int = 3) -> dict:
"""获取城市多日天气预报
Args:
city: 城市名称
days: 预报天数,默认3天
Returns:
天气预报信息
"""
# 模拟天气数据
weather_types = ["晴", "多云", "阴", "小雨", "中雨", "雷阵雨"]
forecast = []
today = datetime.now()
for i in range(days):
date = (today + timedelta(days=i)).strftime("%Y-%m-%d")
forecast.append({
"date": date,
"weather": random.choice(weather_types),
"temp_low": random.randint(15, 20),
"temp_high": random.randint(25, 32),
"humidity": random.randint(50, 90)
})
return {
"city": city,
"forecast": forecast
}
def find_hotels(city: str, check_in: str, check_out: str, guests: int = 2) -> dict:
"""查找酒店信息
Args:
city: 城市名称
check_in: 入住日期
check_out: 离店日期
guests: 入住人数,默认2人
Returns:
酒店信息列表
"""
hotels = [
{
"name": f"{city}国际大酒店",
"star": 5,
"price": 800,
"address": f"{city}市中心人民广场1号",
"rating": 4.8
},
{
"name": f"{city}商务快捷酒店",
"star": 3,
"price": 300,
"address": f"{city}火车站附近",
"rating": 4.2
},
{
"name": f"{city}特色民宿",
"star": 4,
"price": 500,
"address": f"{city}老城区文化街",
"rating": 4.6
}
]
return {
"city": city,
"check_in": check_in,
"check_out": check_out,
"guests": guests,
"hotels": hotels
}
def currency_conversion(amount: float, from_currency: str, to_currency: str) -> dict:
"""货币兑换计算
Args:
amount: 金额
from_currency: 源货币,如“USD”、“CNY”
to_currency: 目标货币
Returns:
兑换结果
"""
# 模拟汇率(非实时)
exchange_rates = {
"USD": {"CNY": 7.2, "EUR": 0.92, "JPY": 150},
"CNY": {"USD": 0.14, "EUR": 0.13, "JPY": 21},
"EUR": {"USD": 1.09, "CNY": 7.8, "JPY": 163},
"JPY": {"USD": 0.0067, "CNY": 0.048, "EUR": 0.0061}
}
if from_currency in exchange_rates and to_currency in exchange_rates[from_currency]:
rate = exchange_rates[from_currency][to_currency]
converted = amount * rate
return {
"amount": amount,
"from_currency": from_currency,
"to_currency": to_currency,
"exchange_rate": rate,
"converted_amount": round(converted, 2)
}
else:
return {
"error": f"不支持{from_currency}到{to_currency}的兑换"
}
2.2 准备工具描述给模型
接下来,我们需要把这些工具的描述准备好,让GLM-4-9B-Chat-1M知道每个工具是干什么的、需要什么参数。这是函数调用的关键一步:
# 工具描述(模型需要知道的信息)
tools = [
{
"type": "function",
"function": {
"name": "search_flights",
"description": "搜索指定日期、出发地和目的地的航班信息",
"parameters": {
"type": "object",
"properties": {
"departure": {
"type": "string",
"description": "出发城市,如'北京'、'上海'"
},
"arrival": {
"type": "string",
"description": "到达城市,如'广州'、'深圳'"
},
"date": {
"type": "string",
"description": "出发日期,格式为YYYY-MM-DD"
}
},
"required": ["departure", "arrival", "date"]
}
}
},
{
"type": "function",
"function": {
"name": "get_weather_forecast",
"description": "获取城市的多日天气预报",
"parameters": {
"type": "object",
"properties": {
"city": {
"type": "string",
"description": "城市名称"
},
"days": {
"type": "integer",
"description": "预报天数,默认3天",
"default": 3
}
},
"required": ["city"]
}
}
},
{
"type": "function",
"function": {
"name": "find_hotels",
"description": "查找指定城市的酒店信息",
"parameters": {
"type": "object",
"properties": {
"city": {
"type": "string",
"description": "城市名称"
},
"check_in": {
"type": "string",
"description": "入住日期,格式为YYYY-MM-DD"
},
"check_out": {
"type": "string",
"description": "离店日期,格式为YYYY-MM-DD"
},
"guests": {
"type": "integer",
"description": "入住人数,默认2人",
"default": 2
}
},
"required": ["city", "check_in", "check_out"]
}
}
},
{
"type": "function",
"function": {
"name": "currency_conversion",
"description": "货币兑换计算",
"parameters": {
"type": "object",
"properties": {
"amount": {
"type": "number",
"description": "要兑换的金额"
},
"from_currency": {
"type": "string",
"description": "源货币代码,如USD、CNY、EUR"
},
"to_currency": {
"type": "string",
"description": "目标货币代码"
}
},
"required": ["amount", "from_currency", "to_currency"]
}
}
}
]
注意看这些描述,它们都是用自然语言写的,清楚地说明了每个函数是干什么的、需要哪些参数、参数是什么类型。GLM-4-9B-Chat-1M就是靠这些描述来理解工具的用途。
2.3 与模型交互:完整的对话流程
现在,让我们看看如何与模型进行完整的函数调用对话。这里我用一个简化的示例来展示整个流程:
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer
# 初始化模型和分词器
device = "cuda" if torch.cuda.is_available() else "cpu"
model_name = "THUDM/glm-4-9b-chat-1m"
tokenizer = AutoTokenizer.from_pretrained(model_name, trust_remote_code=True)
model = AutoModelForCausalLM.from_pretrained(
model_name,
torch_dtype=torch.bfloat16,
low_cpu_mem_usage=True,
trust_remote_code=True
).to(device).eval()
def chat_with_tools(user_input, conversation_history=None):
"""与模型进行带工具调用的对话"""
if conversation_history is None:
conversation_history = []
# 添加用户输入到历史
conversation_history.append({"role": "user", "content": user_input})
# 准备消息格式,包含工具描述
messages = [
{
"role": "system",
"content": "你是一个智能旅行助手,可以帮用户查询航班、天气、酒店和货币兑换。"
}
]
# 添加工具描述
messages.append({
"role": "system",
"content": f"你可以使用以下工具:\n{json.dumps(tools, ensure_ascii=False, indent=2)}"
})
# 添加对话历史
messages.extend(conversation_history)
# 编码输入
inputs = tokenizer.apply_chat_template(
messages,
add_generation_prompt=True,
tokenize=True,
return_tensors="pt",
return_dict=True
)
inputs = inputs.to(device)
# 生成回复
with torch.no_grad():
outputs = model.generate(
**inputs,
max_new_tokens=500,
do_sample=True,
temperature=0.7,
top_p=0.9
)
# 解码输出
response = tokenizer.decode(outputs[0], skip_special_tokens=True)
# 提取模型回复(去掉输入部分)
prompt_length = len(tokenizer.decode(inputs['input_ids'][0], skip_special_tokens=True))
model_reply = response[prompt_length:].strip()
return model_reply
# 示例对话
if __name__ == "__main__":
# 用户:我想去上海旅行,帮我查一下明天的航班和天气
user_query = "我想去上海旅行,帮我查一下明天的航班和天气"
print(f"用户: {user_query}")
print("-" * 50)
# 第一次交互:模型可能会请求更多信息或直接调用工具
reply = chat_with_tools(user_query)
print(f"助手: {reply}")
# 在实际应用中,你需要解析模型的回复
# 如果回复中包含工具调用请求,就执行对应的函数
# 然后把函数结果再交给模型,让模型生成最终回复
在实际运行中,当用户提出“我想去上海旅行,帮我查一下明天的航班和天气”这样的复合请求时,GLM-4-9B-Chat-1M很可能会输出一个结构化的工具调用请求,比如同时请求search_flights和get_weather_forecast两个函数。
2.4 处理复杂多轮对话
真正的旅行规划往往需要多轮对话。比如用户先说“查北京到上海的航班”,然后又说“那天的天气怎么样?”,接着问“附近有什么酒店?”。GLM-4-9B-Chat-1M的百万上下文在这里就派上大用场了,它能记住整个对话历史,包括之前调用过的工具和返回的结果。
def handle_complex_conversation():
"""处理多轮复杂对话示例"""
history = []
# 第一轮:查询航班
user1 = "帮我查一下北京到上海明天早上的航班"
reply1 = chat_with_tools(user1, history)
history.append({"role": "user", "content": user1})
history.append({"role": "assistant", "content": reply1})
print(f"用户: {user1}")
print(f"助手: {reply1}")
print("-" * 50)
# 第二轮:基于上文的查询(模型记得刚才查的是北京到上海)
user2 = "那天的天气怎么样?"
reply2 = chat_with_tools(user2, history)
history.append({"role": "user", "content": user2})
history.append({"role": "assistant", "content": reply2})
print(f"用户: {user2}")
print(f"助手: {reply2}")
print("-" * 50)
# 第三轮:更具体的需求
user3 = "找到酒店后,帮我算一下1000美元换成人民币是多少"
reply3 = chat_with_tools(user3, history)
print(f"用户: {user3}")
print(f"助手: {reply3}")
在这个例子中,当用户问“那天的天气怎么样?”时,模型能根据上下文理解“那天”指的是北京到上海航班的日期,并且知道要查询的是上海的天气(因为目的地是上海)。这种上下文理解能力对于构建流畅的对话体验至关重要。
3. 高级技巧:让函数调用更智能
基本的函数调用已经很有用了,但如果我们想让助手更智能,还需要一些进阶技巧。
3.1 参数自动补全和验证
有时候用户说的话不够具体,比如“查一下航班”,没说出发地、目的地、日期。好的模型应该能主动询问缺失的信息:
def smart_parameter_handling(user_input):
"""智能处理参数缺失的情况"""
# 分析用户输入,提取已有信息
# 如果发现缺失关键参数,让模型生成追问
# 例如用户说“查航班”,模型可以回复:
# “请问您要从哪个城市出发,去哪个城市,什么时间出发呢?”
# 或者更智能一点,如果上下文中有相关信息,可以自动补全
# 比如用户之前说过“我要去上海”,那么当他说“查一下航班”时
# 模型可以自动把目的地设为上海,只追问出发地和时间
pass
GLM-4-9B-Chat-1M在这方面表现不错,它能够根据工具描述中的required字段,判断哪些参数是必须的,然后在用户没提供时主动询问。
3.2 多工具协同工作
真正的智能助手应该能同时协调多个工具。比如用户说“规划一个三天的北京旅行”,助手应该能自动:
- 查航班(往返)
- 查三天的天气
- 找酒店
- 推荐景点(如果定义了这样的工具)
- 估算总花费
def plan_trip(destination: str, days: int, budget: float):
"""旅行规划工作流"""
# 这是一个高级函数,内部会调用多个工具
# 1. 查天气
weather = get_weather_forecast(destination, days)
# 2. 查航班(假设从用户所在城市出发)
flights = search_flights("用户所在城市", destination, "出发日期")
# 3. 找酒店
hotels = find_hotels(destination, "入住日期", "离店日期")
# 4. 计算预算
total_cost = flights['flights'][0]['price'] + hotels['hotels'][0]['price'] * days
# 5. 组织所有信息,生成完整报告
return {
"destination": destination,
"duration": days,
"weather_summary": weather,
"flight_options": flights,
"hotel_options": hotels,
"estimated_cost": total_cost,
"within_budget": total_cost <= budget
}
你可以把这个plan_trip函数也作为一个工具暴露给模型,这样用户一句话就能触发完整的工作流。
3.3 错误处理和降级方案
工具调用可能会失败(API出错、网络问题等),好的系统应该有错误处理机制:
def robust_tool_call(tool_name, arguments):
"""健壮的工具调用"""
try:
# 尝试调用工具
result = call_tool(tool_name, arguments)
# 检查结果是否合理
if is_result_valid(result):
return {"success": True, "data": result}
else:
return {"success": False, "error": "工具返回了无效结果"}
except Exception as e:
# 记录错误
log_error(f"工具{tool_name}调用失败: {str(e)}")
# 提供降级方案
fallback_result = get_fallback_data(tool_name, arguments)
return {"success": False, "error": str(e), "fallback": fallback_result}
当工具调用失败时,你可以把错误信息交给模型,让模型生成得体的回复,比如“抱歉,暂时无法查询航班信息,您可以稍后再试或直接访问航空公司官网查询。”
4. 实际应用场景
函数调用功能在实际工作中有很多应用场景,远不止旅行助手这么简单。
4.1 企业内部助手
想象一个公司内部助手,员工可以问:
- “帮我查一下张三上个月的考勤记录” → 调用HR系统接口
- “这个季度的销售数据怎么样?” → 调用BI系统接口
- “给我预约明天下午2点的会议室” → 调用日历系统接口
- “创建一份关于项目进展的周报” → 调用文档模板+数据填充
4.2 电商客服机器人
顾客可以问:
- “我订单123456的物流到哪了?” → 调用物流查询接口
- “这款手机有现货吗?” → 调用库存查询接口
- “帮我推荐一款适合玩游戏的笔记本电脑” → 调用产品推荐算法
- “我要退货,怎么操作?” → 调用退货流程指南生成
4.3 数据分析助手
数据分析师可以问:
- “统计一下上周的用户活跃度” → 调用数据查询+统计函数
- “画一个销售额随时间变化的趋势图” → 调用图表生成函数
- “预测下个月的用户增长” → 调用预测模型
- “对比一下A产品和B产品的用户留存” → 调用对比分析函数
4.4 开发助手
程序员可以问:
- “帮我写一个用户登录的API” → 调用代码生成函数
- “检查这段代码有没有安全漏洞” → 调用代码分析工具
- “把这份JSON转换成TypeScript接口定义” → 调用格式转换函数
- “部署这个应用到测试环境” → 调用部署脚本
5. 性能考虑和最佳实践
虽然GLM-4-9B-Chat-1M的函数调用功能很强大,但在实际使用时还是需要注意一些性能问题。
5.1 上下文长度管理
百万上下文很强大,但也不是无限的。如果你的工具描述非常详细,或者对话历史很长,还是会占不少token。建议:
- 精简工具描述:在保证清晰的前提下,尽量用简洁的语言描述工具功能
- 选择性记忆:不是所有对话历史都需要永远记住,可以定期清理
- 工具分组:如果工具很多,可以按场景分组,需要时再加载对应的工具集
5.2 响应速度优化
函数调用涉及多次交互(用户→模型→工具→模型→用户),可能会影响响应速度:
- 并行调用:如果模型请求多个工具,且工具之间没有依赖关系,可以并行调用
- 缓存结果:对于一些不常变的数据(如城市信息、产品目录),可以缓存工具结果
- 超时设置:给工具调用设置合理的超时时间,避免长时间等待
5.3 安全性考虑
让模型调用外部工具是有安全风险的,需要注意:
- 权限控制:不同用户只能调用其有权限的工具
- 输入验证:即使模型提取了参数,在执行前也要再次验证
- 输出过滤:工具返回的结果可能包含敏感信息,需要适当过滤
- 审计日志:记录所有的工具调用,便于追踪和审计
5.4 错误处理和用户体验
工具调用可能失败,要做好错误处理:
- 友好的错误信息:不要直接把技术错误抛给用户
- 重试机制:对于临时性错误(如网络超时),可以自动重试
- 降级方案:主工具失败时,提供简化的替代方案
- 进度反馈:如果工具执行时间较长,要给用户进度反馈
6. 总结
GLM-4-9B-Chat-1M的函数调用功能确实让人眼前一亮。它不只是简单地把用户问题映射到API调用,而是真正理解用户意图、处理复杂上下文、协调多个工具完成复杂任务。
从我们的实践来看,这个模型在工具理解的准确性、参数提取的精确度、以及多轮对话的连贯性上都表现不错。特别是它的长上下文能力,让它可以处理非常复杂的多步骤任务,而不需要开发者做太多额外的上下文管理。
当然,要构建一个真正好用的智能助手,光有强大的模型还不够。你还需要设计合理的工具架构、处理各种边界情况、优化用户体验。但有了GLM-4-9B-Chat-1M这样的模型作为基础,这些工作就变得容易多了。
如果你正在考虑为你的产品或服务添加智能助手功能,或者想自动化一些重复性的工作流程,GLM-4-9B-Chat-1M的函数调用功能绝对值得一试。从简单的查询助手到复杂的业务流程自动化,它都能提供很好的支持。
实际用下来,部署和集成过程比想象中要顺畅。文档比较清晰,社区也比较活跃,遇到问题基本都能找到解决方案。对于有一定开发经验的团队来说,一到两周时间就能搭建起一个可用的原型,然后可以根据实际反馈逐步完善。
获取更多AI镜像
想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。
更多推荐

所有评论(0)