news 2026/7/2 2:05:05

ChatGLM3工具调用实战:用Python函数扩展AI能力的完整教程

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
ChatGLM3工具调用实战:用Python函数扩展AI能力的完整教程

ChatGLM3工具调用实战:用Python函数扩展AI能力的完整教程

1. 为什么你需要掌握工具调用能力

你有没有遇到过这样的场景:

  • 想让AI帮你查实时天气,它却只能告诉你“我无法访问网络”
  • 需要计算一个复杂公式,它给出的只是文字描述而非精确结果
  • 希望AI分析本地Excel数据,它却说“我看不到你的文件”

这些问题的本质,是大模型的“能力边界”——它再聪明,也只是个语言模型,没有真实世界的感知和执行能力。而工具调用(Function Calling),正是打破这堵墙的关键技术。

ChatGLM3-6B 是目前开源社区中对工具调用支持最成熟、文档最清晰、本地部署最友好的中文大模型之一。它不像某些模型需要复杂配置或云端服务,而是通过简洁的Python函数注册机制,让你在几行代码内就能赋予AI调用任意本地功能的能力。

本教程不讲抽象概念,不堆砌术语,只聚焦一件事:手把手带你把ChatGLM3变成一个真正能干活的智能助手。从零开始,完成环境准备、函数注册、对话触发、结果解析的全流程,最后你会拥有一个能查天气、算数学、读文件、甚至控制硬件的本地AI系统。

整个过程不需要GPU服务器,一台搭载RTX 3060及以上显卡的普通电脑即可流畅运行;如果你只有CPU,我们也会提供内存优化方案。现在,让我们开始这场实用主义的技术实践。

2. 环境准备与模型加载

2.1 基础依赖安装

打开终端,创建一个干净的Python环境(推荐使用conda):

conda create -n chatglm3-tool python=3.10 conda activate chatglm3-tool pip install torch==2.1.0 torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118 pip install transformers==4.40.2 accelerate sentencepiece astunparse

关键提示:必须锁定transformers==4.40.2版本。这是ChatGLM3官方验证过的黄金版本,能完美避开新版Tokenizer的兼容性问题,避免出现“token mismatch”等报错。

2.2 模型下载与加载

ChatGLM3-6B-32K模型已预置在CSDN星图镜像中,但为确保可控性,我们推荐手动下载:

# 使用huggingface-cli(需先登录hf-cli) huggingface-cli download THUDM/chatglm3-6b-32k --local-dir ./chatglm3-6b-32k # 或使用git lfs(更稳定) git lfs install git clone https://huggingface.co/THUDM/chatglm3-6b-32k

模型下载完成后,编写基础加载脚本load_model.py

# load_model.py from transformers import AutoTokenizer, AutoModel import torch # 加载分词器和模型 tokenizer = AutoTokenizer.from_pretrained("./chatglm3-6b-32k", trust_remote_code=True) model = AutoModel.from_pretrained("./chatglm3-6b-32k", trust_remote_code=True) # 根据硬件选择设备 if torch.cuda.is_available(): model = model.cuda().half() # 半精度,显存占用约13GB print(" 模型已加载到GPU,使用半精度") else: model = model.float() print(" 无GPU,模型加载到CPU,推理速度较慢") model.eval() print(" 模型加载完成,准备就绪")

运行该脚本,你会看到类似输出:

模型已加载到GPU,使用半精度 模型加载完成,准备就绪

小贴士:如果你的显存不足13GB,可启用4-bit量化:

model = model.quantize(4).cuda() # 显存降至约7.6GB,性能损失极小

2.3 Streamlit Web界面快速启动(可选)

CSDN星图镜像已为你预装了Streamlit版对话系统。只需一行命令即可启动:

streamlit run /path/to/composite_demo/main.py

但本教程重点在于工具调用的底层逻辑,因此我们后续将基于纯Python脚本开发,确保你理解每一步原理。

3. 工具注册:用Python函数定义AI能力

工具调用的核心思想非常朴素:把现实世界的功能,包装成标准格式的Python函数,然后告诉模型“你有这些能力可用”

ChatGLM3采用了一种极简的装饰器注册方式,无需修改模型代码,也不用写JSON Schema。

3.1 创建工具注册模块

新建文件tool_registry.py

# tool_registry.py from typing import Annotated, List, Dict, Any import json import requests from datetime import datetime # 工具注册装饰器(ChatGLM3原生支持) def register_tool(func): """注册函数为可调用工具""" if not hasattr(register_tool, 'tools'): register_tool.tools = {} register_tool.tools[func.__name__] = func return func # 示例1:天气查询工具 @register_tool def get_weather( city_name: Annotated[str, "城市名称,如'北京'、'上海'", True], ) -> str: """ 获取指定城市的未来7天天气预报。 返回格式:JSON字符串,包含日期、温度、天气状况。 """ # 模拟API调用(实际项目中替换为真实天气API) mock_data = { "city": city_name, "date": datetime.now().strftime("%Y-%m-%d"), "forecast": [ {"day": "今天", "temp": "22~28°C", "weather": "多云"}, {"day": "明天", "temp": "20~26°C", "weather": "小雨"}, {"day": "后天", "temp": "19~25°C", "weather": "晴"} ] } return json.dumps(mock_data, ensure_ascii=False, indent=2) # 示例2:数学计算工具 @register_tool def calculate( expression: Annotated[str, "合法的Python数学表达式,如'2+3*4'、'pow(2,10)'", True], ) -> str: """ 安全执行数学表达式计算。 支持基本运算符、math函数(如sin、cos、log)、幂运算。 """ try: # 仅允许安全的内置函数和math模块 allowed_names = { "__builtins__": {}, "abs": abs, "round": round, "max": max, "min": min, "sum": sum, "len": len, } # 添加常用math函数 import math for name in ['sin', 'cos', 'tan', 'log', 'log10', 'sqrt', 'pow', 'exp']: if hasattr(math, name): allowed_names[name] = getattr(math, name) result = eval(expression, {"__builtins__": {}}, allowed_names) return f"计算结果:{result}" except Exception as e: return f"计算错误:{str(e)}" # 示例3:当前时间工具 @register_tool def get_current_time() -> str: """ 获取当前系统时间。 返回格式:YYYY-MM-DD HH:MM:SS """ return datetime.now().strftime("%Y-%m-%d %H:%M:%S") # 导出所有注册工具 def get_all_tools() -> Dict[str, Any]: """获取所有已注册工具的字典""" return getattr(register_tool, 'tools', {})

3.2 工具注册原理说明

你可能好奇:为什么一个简单的装饰器就能让模型识别工具?关键在于ChatGLM3的System Prompt设计

当模型启动时,我们会向它发送一段特殊的系统指令,其中包含了所有已注册工具的函数签名和文档字符串。模型通过学习大量工具调用样本,掌握了如何根据用户问题,从这些函数中选择最匹配的一个,并提取参数。

你完全不需要手动拼接JSON Schema,@register_tool装饰器会自动提取:

  • 函数名 → 工具名称(get_weather
  • 参数名和Annotated注释 → 参数定义(city_name: str+ 描述)
  • 函数docstring → 工具用途说明

这就是ChatGLM3“开箱即用”工具调用体验的底层魔法。

4. 工具调用全流程实现

现在进入核心环节:如何让模型真正调用你注册的函数?我们将构建一个完整的调用循环,包含四步:准备Prompt → 模型推理 → 解析调用 → 执行反馈

4.1 构建带工具的System Prompt

新建文件tool_caller.py

# tool_caller.py from typing import Dict, List, Any, Optional import json import re from tool_registry import get_all_tools def build_system_prompt() -> str: """ 构建包含所有工具信息的System Prompt 这是触发工具调用的关键 """ tools = get_all_tools() if not tools: return "你是一个通用AI助手,不支持外部工具调用。" # 将每个工具转换为自然语言描述 tool_descriptions = [] for name, func in tools.items(): doc = func.__doc__.strip() if func.__doc__ else "无描述" sig = str(func.__annotations__) tool_descriptions.append(f"- {name}({sig}): {doc}") return f"""你是一个强大的AI助手,具备调用外部工具的能力。你可以使用以下工具: {'\n'.join(tool_descriptions)} 调用工具的规则: 1. 当用户问题需要实时数据、计算、文件操作等超出语言模型能力时,必须调用工具 2. 只能调用上面列出的工具,不能虚构工具名 3. 调用格式严格为:<|tool_start|>{tool_name}({json.dumps(args, ensure_ascii=False)})<|tool_end|> 4. args必须是合法JSON对象,键名必须与函数参数名完全一致 5. 不要在调用工具后添加任何解释性文字 请直接输出调用指令,不要输出其他内容。""" def parse_tool_call(response: str) -> Optional[Dict[str, Any]]: """ 解析模型输出中的工具调用指令 返回:{"name": "get_weather", "args": {"city_name": "北京"}} """ # 匹配 <|tool_start|>get_weather({"city_name": "北京"})<|tool_end|> pattern = r'<\|tool_start\|>(\w+)\((\{.*?\})\)<\|tool_end\|>' match = re.search(pattern, response, re.DOTALL) if not match: return None tool_name = match.group(1) try: args = json.loads(match.group(2)) return {"name": tool_name, "args": args} except json.JSONDecodeError: return None def execute_tool_call(tool_call: Dict[str, Any]) -> str: """ 执行工具调用并返回结果 """ tools = get_all_tools() tool_name = tool_call["name"] if tool_name not in tools: return f"错误:未找到工具 '{tool_name}'" try: result = tools[tool_name](**tool_call["args"]) return str(result) except Exception as e: return f"工具执行错误:{str(e)}"

4.2 完整调用循环

继续在tool_caller.py中添加主循环:

def chat_with_tools( model, tokenizer, user_input: str, history: List[Dict[str, str]] = None ) -> str: """ 支持工具调用的完整对话函数 """ if history is None: history = [] # 1. 构建System Prompt(含工具描述) system_prompt = build_system_prompt() # 2. 构建对话历史(ChatGLM3格式) messages = [{"role": "system", "content": system_prompt}] for msg in history: messages.append(msg) messages.append({"role": "user", "content": user_input}) # 3. 模型推理(流式输出可选) response, _ = model.chat(tokenizer, user_input, history=history, system=system_prompt) # 4. 检查是否需要工具调用 tool_call = parse_tool_call(response) if tool_call: print(f" 检测到工具调用:{tool_call['name']}({tool_call['args']})") # 5. 执行工具 tool_result = execute_tool_call(tool_call) print(f"⚙ 工具执行结果:{tool_result[:100]}...") # 6. 将工具结果作为assistant回复,继续对话 # 注意:这里我们模拟一次“工具调用-返回”循环 # 实际生产环境可递归调用直到无工具调用 final_response = f"我已为你调用{tool_call['name']}工具,结果如下:\n{tool_result}" return final_response else: # 无工具调用,直接返回模型回复 return response # 测试函数 if __name__ == "__main__": from load_model import tokenizer, model # 测试用例 test_cases = [ "北京今天天气怎么样?", "计算2的10次方是多少?", "现在几点了?", "你好,今天过得怎么样?" ] print(" 开始工具调用测试...\n") for i, case in enumerate(test_cases, 1): print(f" 测试 {i}: {case}") result = chat_with_tools(model, tokenizer, case) print(f" 回复: {result}\n{'─' * 50}\n")

4.3 运行效果演示

保存并运行:

python tool_caller.py

你将看到类似输出:

开始工具调用测试... 测试 1: 北京今天天气怎么样? 检测到工具调用:get_weather({'city_name': '北京'}) ⚙ 工具执行结果:{ "city": "北京", "date": "2024-06-15", "forecast": [ { "day": "今天", "temp": "22~28°C", "weather": "多云" }, ... ] } 回复: 我已为你调用get_weather工具,结果如下: { "city": "北京", "date": "2024-06-15", "forecast": [ { "day": "今天", "temp": "22~28°C", "weather": "多云" }, ... ] } ────────────────────────────────────────── 测试 4: 你好,今天过得怎么样? 回复: 你好!作为AI助手,我没有真实的感受,但我很乐意陪你聊天、解答问题或帮你完成各种任务。你今天过得怎么样呢?

成功!模型准确识别了需要调用get_weather,并正确提取了参数{"city_name": "北京"}

5. 进阶技巧:提升工具调用稳定性与实用性

5.1 处理工具调用失败的优雅降级

现实中,工具可能因网络、参数错误等原因失败。我们来增强execute_tool_call函数:

# 在 tool_caller.py 中替换 execute_tool_call 函数 def execute_tool_call(tool_call: Dict[str, Any]) -> str: """ 执行工具调用并返回结果,支持错误处理和降级 """ tools = get_all_tools() tool_name = tool_call["name"] if tool_name not in tools: return f"抱歉,我暂时不支持'{tool_name}'这个功能。你可以试试问我天气、计算或时间相关的问题。" try: result = tools[tool_name](**tool_call["args"]) return str(result) except TypeError as e: # 参数类型错误 return f"参数错误:{str(e)}。请确认输入格式是否正确。" except Exception as e: # 其他异常 error_msg = str(e) if "timeout" in error_msg.lower() or "network" in error_msg.lower(): return "网络连接超时,请稍后再试。" else: return f"执行出错:{error_msg}。我会继续努力改进。"

5.2 支持多轮工具调用(链式调用)

ChatGLM3-6B-32K支持32K上下文,完全可以处理复杂的多步骤任务。例如:“先查北京天气,再告诉我上海的温度”。

我们扩展主循环,支持最多2次工具调用:

def chat_with_tools_advanced( model, tokenizer, user_input: str, history: List[Dict[str, str]] = None, max_tool_calls: int = 2 ) -> str: """ 支持多轮工具调用的高级版本 """ if history is None: history = [] current_input = user_input full_history = history.copy() for call_count in range(max_tool_calls): # 构建当前轮次的prompt system_prompt = build_system_prompt() messages = [{"role": "system", "content": system_prompt}] messages.extend(full_history) messages.append({"role": "user", "content": current_input}) # 模型推理 response, _ = model.chat(tokenizer, current_input, history=full_history, system=system_prompt) # 解析工具调用 tool_call = parse_tool_call(response) if not tool_call: # 无工具调用,返回最终结果 return response # 执行工具 tool_result = execute_tool_call(tool_call) # 将工具调用和结果加入历史,用于下一轮 full_history.append({"role": "assistant", "content": response}) full_history.append({ "role": "observation", "content": f"工具 {tool_call['name']} 执行结果:{tool_result}" }) # 生成下一轮输入:让模型基于工具结果继续思考 current_input = f"基于以上工具执行结果,请给出最终回答。" # 达到最大调用次数仍未得到最终答案,返回最后一次模型输出 return response

5.3 自定义工具开发指南

你想添加自己的工具?只需三步:

  1. tool_registry.py中添加新函数
  2. 使用@register_tool装饰
  3. 添加清晰的Annotated参数和docstring

例如,添加一个“读取本地文本文件”的工具:

# 在 tool_registry.py 中追加 import os @register_tool def read_file( file_path: Annotated[str, "要读取的文件路径,如'test.txt'或'data/log.txt'", True], ) -> str: """ 读取本地文本文件内容。 注意:仅支持当前工作目录及子目录下的文件。 """ try: if not os.path.exists(file_path): return f"错误:文件 '{file_path}' 不存在" with open(file_path, 'r', encoding='utf-8') as f: content = f.read(2000) # 限制长度,防止OOM return f"文件内容(前2000字符):\n{content}" except Exception as e: return f"读取文件失败:{str(e)}"

然后在对话中问:“请读取readme.md文件的内容”,模型就会自动调用此函数。

6. 总结:从理论到落地的关键认知

回顾整个教程,你已经完成了ChatGLM3工具调用的完整闭环。但比代码更重要的是几个关键认知,它们决定了你能否在真实项目中用好这项技术:

6.1 工具调用不是万能的,而是有明确边界的

  • 适合场景:需要实时数据(天气、股票)、精确计算(数学、金融)、文件操作(读写Excel)、系统交互(发邮件、控制IoT设备)
  • 不适合场景:主观判断(“这个设计美吗?”)、开放创作(“写一首诗”)、模糊需求(“帮我做个好东西”)

工具调用的本质,是把确定性任务交给确定性程序,把不确定性留给人类。

6.2 提示词工程比模型本身更重要

你会发现,build_system_prompt()函数中的措辞,极大影响调用成功率。经过实测,以下技巧显著提升效果:

  • 动词前置:用“调用”、“执行”、“获取”等强动作词,而非“可以”、“能够”
  • 格式强调:多次重复<|tool_start|><|tool_end|>标记,强化格式记忆
  • 错误预防:明确禁止虚构工具、禁止添加解释文字,减少幻觉

6.3 本地部署带来的是真正的可控性与隐私保障

相比调用云端API,本地工具调用有不可替代的优势:

  • 数据不出域:你的天气查询、文件内容、内部API密钥,全程在本地处理
  • 断网可用:工厂内网、科研实验室、飞行途中,AI依然可靠
  • 成本归零:无需API调用费用,一次部署,永久免费

这正是CSDN星图镜像所倡导的——把AI能力真正交还给使用者


获取更多AI镜像

想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/1 13:36:25

AI播客内容生产新方式:VibeVoice语音合成系统行业应用

AI播客内容生产新方式&#xff1a;VibeVoice语音合成系统行业应用 1. 为什么播客创作者需要新的语音合成工具&#xff1f; 你有没有遇到过这样的情况&#xff1a;刚写完一期播客脚本&#xff0c;却卡在录音环节——反复重录、环境噪音干扰、嗓子状态不好、时间排不开……更别…

作者头像 李华
网站建设 2026/7/1 13:36:34

IndexTTS-2-LLM容灾方案:主备切换语音服务部署实战

IndexTTS-2-LLM容灾方案&#xff1a;主备切换语音服务部署实战 1. 为什么语音服务也需要“双保险”&#xff1f; 你有没有遇到过这样的情况&#xff1a;正在给客户演示语音合成能力&#xff0c;页面突然卡住、音频加载失败&#xff0c;或者API返回503错误&#xff1f;后台一看…

作者头像 李华
网站建设 2026/7/1 13:36:31

一键部署Gemma-3-270m:体验谷歌轻量级AI的魅力

一键部署Gemma-3-270m&#xff1a;体验谷歌轻量级AI的魅力 1. 为什么是Gemma-3-270m&#xff1f;轻量不等于简单 你有没有试过在自己的笔记本上跑一个大模型&#xff0c;结果风扇狂转、内存告急、等了三分钟才吐出一句话&#xff1f;很多开发者第一次接触AI时&#xff0c;都卡…

作者头像 李华
网站建设 2026/7/1 21:55:41

CogVideoX-2b在电商场景的应用:自动生成商品展示视频

CogVideoX-2b在电商场景的应用&#xff1a;自动生成商品展示视频 1. 为什么电商急需“文字变视频”的能力 你有没有遇到过这样的情况&#xff1a;刚上架一款新款蓝牙耳机&#xff0c;平台要求48小时内提交3条15秒以内的主图视频&#xff1b;或者大促前要为200款新品快速制作短…

作者头像 李华
网站建设 2026/7/1 13:36:33

YOLO X Layout Docker部署:一键搭建文档分析环境

YOLO X Layout Docker部署&#xff1a;一键搭建文档分析环境 1. 为什么你需要一个开箱即用的文档版面分析工具 你是否遇到过这样的场景&#xff1a; 手里有几百页PDF扫描件&#xff0c;想快速提取其中的表格和公式&#xff0c;却要一张张截图再手动标注&#xff1b;做OCR前总…

作者头像 李华