ChatGLM3 tool_registry.py 代码解析
ChatGLM3 tool_registry.py 代码解析
- 0. 背景
- 1. tool_registry.py
0. 背景
学习 ChatGLM3 的项目内容,过程中使用 AI 代码工具,对代码进行解释,帮助自己快速理解代码。这篇文章记录 ChatGLM3 tool_registry.py 的代码解析内容。
1. tool_registry.py
from copy import deepcopy
import inspect
from pprint import pformat
import traceback
from types import GenericAlias
from typing import get_origin, Annotated_TOOL_HOOKS = {}
_TOOL_DESCRIPTIONS = {}
这段代码定义了几个全局变量和导入了一些模块。让我来逐个解释:
-
from copy import deepcopy:从 copy 模块导入 deepcopy 函数,用于深拷贝对象。
-
import inspect:导入 inspect 模块,用于获取对象的信息。
-
from pprint import pformat:从 pprint 模块导入 pformat 函数,用于格式化打印对象。
-
import traceback:导入 traceback 模块,用于打印异常堆栈信息。
-
from types import GenericAlias:从 types 模块导入 GenericAlias 类,用于表示泛型类型。
-
from typing import get_origin, Annotated:从 typing 模块导入 get_origin 和 Annotated 函数,用于获取泛型类型的原始类型和注解信息。
-
_TOOL_HOOKS = {}:定义一个空的全局字典变量 _TOOL_HOOKS,用于存储工具的钩子函数。
-
_TOOL_DESCRIPTIONS = {}:定义一个空的全局字典变量 _TOOL_DESCRIPTIONS,用于存储工具的描述信息。
这段代码的作用可能是为后续的工具注册和存储钩子函数以及描述信息提供了一个全局的数据结构。
def register_tool(func: callable):tool_name = func.__name__tool_description = inspect.getdoc(func).strip()python_params = inspect.signature(func).parameterstool_params = []for name, param in python_params.items():annotation = param.annotationif annotation is inspect.Parameter.empty:raise TypeError(f"Parameter `{name}` missing type annotation")if get_origin(annotation) != Annotated:raise TypeError(f"Annotation type for `{name}` must be typing.Annotated")typ, (description, required) = annotation.__origin__, annotation.__metadata__typ: str = str(typ) if isinstance(typ, GenericAlias) else typ.__name__if not isinstance(description, str):raise TypeError(f"Description for `{name}` must be a string")if not isinstance(required, bool):raise TypeError(f"Required for `{name}` must be a bool")tool_params.append({"name": name,"description": description,"type": typ,"required": required})tool_def = {"name": tool_name,"description": tool_description,"params": tool_params}print("[registered tool] " + pformat(tool_def))_TOOL_HOOKS[tool_name] = func_TOOL_DESCRIPTIONS[tool_name] = tool_defreturn func
这段代码定义了一个名为 register_tool 的函数,该函数接受一个可调用对象 func 作为参数。
以下是代码的详细解析:
- tool_name = func.name:获取传入函数 func 的名称,并将其赋值给变量 tool_name。
- tool_description = inspect.getdoc(func).strip():使用 inspect.getdoc 函数获取传入函数 func 的文档字符串,并去除首尾的空白字符,将结果赋值给变量 tool_description。
- python_params = inspect.signature(func).parameters:使用 - inspect.signature 函数获取传入函数 func 的参数签名,并将其参数信息保存在变量 python_params 中。
- tool_params = []:创建一个空列表 tool_params,用于存储工具的参数信息。
- for name, param in python_params.items()::遍历 python_params 中的每个参数项,其中 name 是参数名,param 是参数对象。
- annotation = param.annotation:获取参数对象的注解,并将其赋值给变量 annotation。
- if annotation is inspect.Parameter.empty::如果注解为空,则表示参数缺少类型注解,抛出 TypeError 异常。
- if get_origin(annotation) != Annotated::如果注解类型不是 typing.Annotated,抛出 TypeError 异常。
- typ, (description, required) = annotation.origin, annotation.metadata:从注解中获取类型和元数据信息,并将其分别赋值给 typ、description 和 required 变量。
- typ: str = str(typ) if isinstance(typ, GenericAlias) else typ.name:将类型转换为字符串,如果类型是 GenericAlias(泛型类型),则将其转换为字符串形式,否则保留类型的名称。
- if not isinstance(description, str)::如果描述不是字符串类型,抛出 TypeError 异常。
- if not isinstance(required, bool)::如果 required 不是布尔类型,抛出 TypeError 异常。
- tool_params.append({…}):将参数的名称、描述、类型和是否必需组成的字典添加到 tool_params 列表中。
- tool_def = {…}:创建一个字典 tool_def,包含工具的名称、描述和参数信息。
- print("[registered tool] " + pformat(tool_def)):打印注册的工具的定义,使用 pformat 函数格式化输出。
- _TOOL_HOOKS[tool_name] = func:将函数 func 添加到全局字典变量 _TOOL_HOOKS 中,键为工具的名称。
- _TOOL_DESCRIPTIONS[tool_name] = tool_def:将工具的定义添加到全局字典变量 _TOOL_DESCRIPTIONS 中,键为工具的名称。
- return func:返回原始的函数 func。
这段代码的作用是将传入的函数作为工具进行注册,并将工具的名称、描述和参数信息存储在全局字典变量中。注册的工具可以通过 _TOOL_HOOKS 全局字典变量进行调用。
def dispatch_tool(tool_name: str, tool_params: dict) -> str:if tool_name not in _TOOL_HOOKS:return f"Tool `{tool_name}` not found. Please use a provided tool."tool_call = _TOOL_HOOKS[tool_name]try:ret = tool_call(**tool_params) except:ret = traceback.format_exc()return str(ret)def get_tools() -> dict:return deepcopy(_TOOL_DESCRIPTIONS)
这段代码定义了两个函数:dispatch_tool 和 get_tools。让我为你逐个解释:
函数 dispatch_tool(tool_name: str, tool_params: dict) -> str:
该函数接受两个参数 tool_name 和 tool_params,并返回一个字符串。
- if tool_name not in _TOOL_HOOKS::检查传入的工具名称 tool_name 是否存在于全局字典变量 _TOOL_HOOKS 中。
如果工具名称不存在,返回一个提示字符串,表示找不到该工具。 - tool_call = _TOOL_HOOKS[tool_name]:从全局字典变量 _TOOL_HOOKS 中获取与工具名称对应的工具函数,并将其赋值给变量 tool_call。
- try::尝试执行工具函数,并捕获可能的异常。
- ret = tool_call(**tool_params):使用传入的参数 tool_params 调用工具函数,并将返回值赋值给变量 ret。这里使用 ** 运算符将 tool_params 字典解包为关键字参数传递给工具函数。
- except::捕获可能的异常。
- ret = traceback.format_exc():如果出现异常,将异常的堆栈信息格式化为字符串,并将其赋值给变量 ret。
- return str(ret):返回结果,无论是工具函数的返回值还是异常堆栈信息,都将转换为字符串并返回。
该函数的作用是根据传入的工具名称和参数调用对应的工具函数,并返回结果或异常信息的字符串表示。
函数 get_tools() -> dict:
该函数不接受任何参数,返回一个字典。
- return deepcopy(_TOOL_DESCRIPTIONS):返回全局字典变量 _TOOL_DESCRIPTIONS 的深拷贝。
该函数的作用是返回全局字典变量 _TOOL_DESCRIPTIONS 的副本,以提供工具的名称、描述和参数信息。
这两个函数一起提供了工具的调度和获取工具信息的功能。dispatch_tool 函数用于调用具体的工具函数,而 get_tools 函数用于获取所有已注册工具的描述信息。
deepcopy: deepcopy 是一个函数,用于创建一个对象的深拷贝。深拷贝是指创建一个新对象,将原始对象的所有元素递归地复制到新对象中,包括嵌套的对象。换句话说,它会创建一个原始对象的完全独立副本,而不仅仅是引用原始对象的内存地址。
深拷贝对于需要完全独立的副本的情况非常有用,尤其是在处理可变对象时。通过深拷贝,可以确保修改一个对象的副本不会影响到原始对象,因为它们是相互独立的。
例如,假设有一个包含嵌套列表和字典的对象 obj,如果直接对 obj 进行赋值操作,那么新对象将只是原始对象的引用,而不是副本。这意味着对新对象的修改也会反映到原始对象中。但是,如果使用 deepcopy 函数创建一个新对象 new_obj,那么 new_obj 将是 obj 的深拷贝副本,对 new_obj 的修改不会影响到 obj。
@register_tool
def random_number_generator(seed: Annotated[int, 'The random seed used by the generator', True], range: Annotated[tuple[int, int], 'The range of the generated numbers', True],
) -> int:"""Generates a random number x, s.t. range[0] <= x < range[1]"""if not isinstance(seed, int):raise TypeError("Seed must be an integer")if not isinstance(range, tuple):raise TypeError("Range must be a tuple")if not isinstance(range[0], int) or not isinstance(range[1], int):raise TypeError("Range must be a tuple of integers")import randomreturn random.Random(seed).randint(*range)
这段代码定义了一个名为 random_number_generator 的函数,并使用 @register_tool 装饰器将其注册为一个工具。
函数接受两个参数 seed 和 range,并返回一个整数。下面是对代码的详细解释:
-
@register_tool:@ 符号是装饰器语法,用于在函数定义之前修饰函数。@register_tool 表示将该函数注册为一个工具。具体工具注册的逻辑在你提供的代码中没有呈现,可以在其他地方找到。
-
def random_number_generator(seed: Annotated[int, ‘The random seed used by the generator’, True], range: Annotated[tuple[int, int], ‘The range of the generated numbers’, True]) -> int::这是函数的定义部分。函数名为 random_number_generator,接受两个参数 seed 和 range,并指定返回类型为整数。
-
“”" Generates a random number x, s.t. range[0] <= x < range[1] “”":这是函数的文档字符串(docstring),用于描述函数的功能。根据文档字符串的描述,该函数生成一个介于 range[0] 和 range[1] 之间的随机整数 x。
-
参数验证部分:在函数体内部,对传入的参数进行验证,确保它们具有正确的类型和值。
- if not isinstance(seed, int)::检查 seed 是否为整数类型,如果不是,则抛出 TypeError 异常,提示 “Seed must be an integer”。
- if not isinstance(range, tuple)::检查 range 是否为元组类型,如果不是,则抛出 TypeError 异常,提示 “Range must be a tuple”。
- if not isinstance(range[0], int) or not isinstance(range[1], int)::检查 range 的元素是否为整数类型,如果不是,则抛出 TypeError 异常,提示 “Range must be a tuple of integers”。
-
import random:导入 Python 标准库中的 random 模块,用于生成随机数。
-
return random.Random(seed).randint(*range):使用 random 模块生成一个随机整数,并将其作为函数的返回值。random.Random(seed) 创建了一个具有指定种子 seed 的随机数生成器对象,然后使用 randint(*range) 方法生成介于 range[0] 和 range[1] 之间的随机整数。
总之,这段代码定义了一个将参数验证和随机数生成结合在一起的函数。它使用装饰器将函数注册为一个工具,并在调用时生成指定范围内的随机整数。
@register_tool
def get_weather(city_name: Annotated[str, 'The name of the city to be queried', True],
) -> str:"""Get the current weather for `city_name`"""if not isinstance(city_name, str):raise TypeError("City name must be a string")key_selection = {"current_condition": ["temp_C", "FeelsLikeC", "humidity", "weatherDesc", "observation_time"],}import requeststry:resp = requests.get(f"https://wttr.in/{city_name}?format=j1")resp.raise_for_status()resp = resp.json()ret = {k: {_v: resp[k][0][_v] for _v in v} for k, v in key_selection.items()}except:import tracebackret = "Error encountered while fetching weather data!\n" + traceback.format_exc() return str(ret)
这段代码定义了一个名为 get_weather 的函数,并使用 @register_tool 装饰器将其注册为一个工具。
函数接受一个参数 city_name,并返回一个字符串。下面是对代码的详细解释:
-
@register_tool:@ 符号是装饰器语法,用于在函数定义之前修饰函数。@register_tool 表示将该函数注册为一个工具。具体工具注册的逻辑在你提供的代码中没有呈现,可以在其他地方找到。
-
def get_weather(city_name: Annotated[str, ‘The name of the city to be queried’, True]) -> str::这是函数的定义部分。函数名为 get_weather,接受一个 city_name 参数,指定返回类型为字符串。
-
“”" Get the current weather for city_name “”":这是函数的文档字符串(docstring),用于描述函数的功能。根据文档字符串的描述,该函数用于获取指定城市的当前天气情况。
-
参数验证部分:在函数体内部,对传入的参数进行验证,确保它们具有正确的类型和值。
- if not isinstance(city_name, str)::检查 city_name 是否为字符串类型,如果不是,则抛出 TypeError 异常,提示 “City name must be a string”。
-
key_selection = {…}:定义了一个字典变量 key_selection,用于存储需要从 API 响应中提取的天气信息的键值选择。该字典的键代表不同的天气信息,而对应的值是一个列表,包含了该天气信息所对应的子键。
-
import requests:导入 Python 第三方库 requests,用于发送 HTTP 请求。
-
try::尝试执行一段代码,并捕获可能的异常。
- resp = requests.get(f"https://wttr.in/{city_name}?format=j1"):使用 requests 发送一个 GET 请求,获取指定城市的天气数据。URL 中的 {city_name} 部分会被替换为实际的城市名称。
- resp.raise_for_status():检查请求的状态码,如果是错误的状态码,将抛出一个异常。
- resp = resp.json():将响应的 JSON 数据解析为 Python 字典,并将其赋值给 resp 变量。
- ret = {…}:根据 key_selection 字典中的键值选择,从响应中提取相应的天气信息,存储在 ret 变量中。这里使用了字典推导式来生成结果。
-
except::捕获可能的异常。
- import traceback:导入 Python 标准库中的 traceback 模块,用于获取异常的堆栈信息。
- ret = “Error encountered while fetching weather data!\n” + traceback.format_exc():如果发生异常,将错误提示信息和堆栈信息拼接成一个字符串,并将其赋值给 ret 变量。
-
return str(ret):返回结果,将结果转换为字符串类型后返回。
总之,这段代码定义了一个用于获取指定城市天气的函数。它使用 requests 库发送 HTTP 请求获取天气数据,并从响应中提取指定的天气信息。如果发生任何异常,它会将错误提示信息和堆栈信息返回。
请注意,这段代码中的 @register_tool 装饰器和 requests 库是额外的依赖项,你可能需要在其他地方找到这些实现或库的定义。
完结!
相关文章:
ChatGLM3 tool_registry.py 代码解析
ChatGLM3 tool_registry.py 代码解析 0. 背景1. tool_registry.py 0. 背景 学习 ChatGLM3 的项目内容,过程中使用 AI 代码工具,对代码进行解释,帮助自己快速理解代码。这篇文章记录 ChatGLM3 tool_registry.py 的代码解析内容。 1. tool_re…...
js实现定时刷新,并设置定时器上限
定时器 在js中,有两种定时器: 倒计时定时器 倒计时定时器,也叫延时定时器或一次性定时器 功能:倒计时多长时间后执行某个动作 语法:setTimeout(function, timeout); 返回值:int类型,当前定时器…...
常用Linux命令
df -h #查看磁盘 kill -9 pid #强制关闭程序 ifconfig #查看网卡信息 last …...
【C++】获取指定点所在屏幕的尺寸
问题 多个显示器时,获取指定点所在的显示器的尺寸。 分析 之前整理过获取屏幕尺寸的方法:https://blog.csdn.net/m0_43605481/article/details/125024500多显示器时,需要用到GetSystemMetrics、EnumDisplayDevices、EnumDisplaySettings函…...
软文发布如何选择对应的媒体
企业做软文推广第一步,就是选择合适的媒体进行投放,然而许多企业不知道如何选择合适的媒体导致推广工作十分被动,无法取得效果,今天媒介盒子就来和大家分享,企业应该如何选择对应的媒体。 一、 媒体类型 根据软文类型…...
Django如何创建表关系,Django的请求声明周期流程图
【1】表与表之间的关系 一对一 左表的一条记录对应右表的一条记录,反之亦然 多对一 左表的一条记录对应右表的多条记录,反之不成立 多对多 左表的一条记录对应右表的多表记录,反之成立 【2】django中创建表关系 class Book(models.Model):t…...
微服务-我对Spring Clound的理解
官网:https://spring.io/projects/spring-cloud 官方说法:Spring Cloud 为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理、服务发现、熔断器、智能路由、微代理、控制总线、一次性令牌、全局锁、领导选举、分布式会话…...
安防监控EasyCVR视频汇聚平台无法接入Ehome5.0是什么原因?该如何解决?
视频云存储/安防监控EasyCVR视频汇聚平台基于云边端智能协同,支持海量视频的轻量化接入与汇聚、转码与处理、全网智能分发、视频集中存储等。安防平台EasyCVR拓展性强,视频能力丰富,具体可实现视频监控直播、视频轮播、视频录像、云存储、回放…...
机器学习——逻辑回归
目录 一、分类问题 监督学习的最主要类型 二分类 多分类 二、Sigmoid函数 三、逻辑回归求解 代价函数推导过程(极大似然估计): 交叉熵损失函数 逻辑回归的代价函数 代价函数最小化——梯度下降: 编辑 正则化 四、逻辑…...
自动驾驶学习笔记(七)——感知融合
#Apollo开发者# 学习课程的传送门如下,当您也准备学习自动驾驶时,可以和我一同前往: 《自动驾驶新人之旅》免费课程—> 传送门 《Apollo Beta宣讲和线下沙龙》免费报名—>传送门 文章目录 前言 感知融合 卡尔曼滤波 融合策略 实…...
【Java0基础学Java第八颗】 -- 继承与多态 -- 多态
8.继承与多态 8.2 多态8.2.1 多态的概念8.2.2 多态实现条件8.2.3 重写8.2.4 向上转型和向下转型8.2.5 向下转型8.2.6 多态的优缺点8.2.7 避免在构造方法中调用重写的方法 8.2 多态 8.2.1 多态的概念 通俗来说就是多种形态,具体点就是去完成某个行为,当…...
玩转ansible之参数调试和文件操作篇
更多IT技术文章,欢迎关注微信公众号“运维之美” 玩转ansible之参数调试和文件操作篇 01 剧本调试和帮助02 使用场景举例 上节我们学习了使用ansible进行软件安装,那么安装完软件后,就需要linux系统和软件配置修改了,对于linux主机…...
JVM虚拟机:垃圾回收器之Parallel Old(老年代)
本文重点 本文将学习老年代的另外一种垃圾回收器Parallel Old(PO),这是一种用于老年代的并行化垃圾回收器,它使用标记整理算法进行垃圾回收。 历史 在1.6之前,新生代使用Parallel Scavenge只能搭配老年代的Serial Old收集器,而…...
Stream流的groupingBy
Stream流的groupingBy 简单使用 业务场景:现在有100个人,这些人都年龄分部在18-30岁之间。现要求把他们按照年龄进行分组 key:年龄 value:数据列表 public void listToMapGroup() {//这里假设通过listStreamService.list();方法…...
如何在不结束tcpdump的情况下复制完整的pcap
tcpdump正在运行的时候,他写入的pcap可能是不完整的,通常我们要结束掉tcpdump才能拿到完整的pcap,否则wireshark打开的时候会提示:The capture file appears to have been cut short in the middle of a packet。这可能是因为tcpd…...
maven POM文件总体配置说明
<project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd "> <!-- 父项目的坐…...
49.批处理命令(1/2)
目录 一批处理。 (1)批处理定义。 (2)常见命令。 (2.1)rem和:: (2.2)echo和。 (2.3)pause。 (2.4)errorlevel。 (…...
react类式组件的生命周期和useEffect实现函数组件生命周期
概念 生命周期是一个组件丛创建,渲染,更新,卸载的过程,无论是vue还是react都具有这个设计概念,也是开发者必须熟练运用的,特别是业务开发,不同的生命周期做不同的事是很重要的. ....多说两句心得,本人是先接触vue的,无论是vue2还是vue3的生命周期,在理解和学习上都会比react更…...
ARM 基础学习记录 / 异常与GIC介绍
GIC概念 念课本(以下内容都是针对"通用中断控制器(GIC)"而言,直接摘录的,有的地方可能不符人类的理解方式): 通用中断控制器(GIC)架构提供了严格的规范&…...
java压缩pdf体积,图片体积
pdf整体进行压缩,图片进行压缩 // 生成主证书的PDF路径 创建一个文件String pdfPath UploadDown.createFile(".pdf");outputStream new FileOutputStream(pdfPath);bufferedOutputStream new BufferedOutputStream(outputStream);writer PdfWriter.getInstance(…...
五年级数学知识边界总结思考-下册
目录 一、背景二、过程1.观察物体小学五年级下册“观察物体”知识点详解:由来、作用与意义**一、知识点核心内容****二、知识点的由来:从生活实践到数学抽象****三、知识的作用:解决实际问题的工具****四、学习的意义:培养核心素养…...
【Go】3、Go语言进阶与依赖管理
前言 本系列文章参考自稀土掘金上的 【字节内部课】公开课,做自我学习总结整理。 Go语言并发编程 Go语言原生支持并发编程,它的核心机制是 Goroutine 协程、Channel 通道,并基于CSP(Communicating Sequential Processes࿰…...
Spring AI与Spring Modulith核心技术解析
Spring AI核心架构解析 Spring AI(https://spring.io/projects/spring-ai)作为Spring生态中的AI集成框架,其核心设计理念是通过模块化架构降低AI应用的开发复杂度。与Python生态中的LangChain/LlamaIndex等工具类似,但特别为多语…...
10-Oracle 23 ai Vector Search 概述和参数
一、Oracle AI Vector Search 概述 企业和个人都在尝试各种AI,使用客户端或是内部自己搭建集成大模型的终端,加速与大型语言模型(LLM)的结合,同时使用检索增强生成(Retrieval Augmented Generation &#…...
Android第十三次面试总结(四大 组件基础)
Activity生命周期和四大启动模式详解 一、Activity 生命周期 Activity 的生命周期由一系列回调方法组成,用于管理其创建、可见性、焦点和销毁过程。以下是核心方法及其调用时机: onCreate() 调用时机:Activity 首次创建时调用。…...
安宝特案例丨Vuzix AR智能眼镜集成专业软件,助力卢森堡医院药房转型,赢得辉瑞创新奖
在Vuzix M400 AR智能眼镜的助力下,卢森堡罗伯特舒曼医院(the Robert Schuman Hospitals, HRS)凭借在无菌制剂生产流程中引入增强现实技术(AR)创新项目,荣获了2024年6月7日由卢森堡医院药剂师协会࿰…...
STM32HAL库USART源代码解析及应用
STM32HAL库USART源代码解析 前言STM32CubeIDE配置串口USART和UART的选择使用模式参数设置GPIO配置DMA配置中断配置硬件流控制使能生成代码解析和使用方法串口初始化__UART_HandleTypeDef结构体浅析HAL库代码实际使用方法使用轮询方式发送使用轮询方式接收使用中断方式发送使用中…...
深度学习之模型压缩三驾马车:模型剪枝、模型量化、知识蒸馏
一、引言 在深度学习中,我们训练出的神经网络往往非常庞大(比如像 ResNet、YOLOv8、Vision Transformer),虽然精度很高,但“太重”了,运行起来很慢,占用内存大,不适合部署到手机、摄…...
GraphRAG优化新思路-开源的ROGRAG框架
目前的如微软开源的GraphRAG的工作流程都较为复杂,难以孤立地评估各个组件的贡献,传统的检索方法在处理复杂推理任务时可能不够有效,特别是在需要理解实体间关系或多跳知识的情况下。先说结论,看完后感觉这个框架性能上不会比Grap…...
CppCon 2015 学习:REFLECTION TECHNIQUES IN C++
关于 Reflection(反射) 这个概念,总结一下: Reflection(反射)是什么? 反射是对类型的自我检查能力(Introspection) 可以查看类的成员变量、成员函数等信息。反射允许枚…...
