Langchain-Chatchat项目:5.1-ChatGLM3-6B工具调用
在语义、数学、推理、代码、知识等不同角度的数据集上测评显示,ChatGLM3-6B-Base 具有在10B以下的基础模型中最强的性能。ChatGLM3-6B采用了全新设计的Prompt格式,除正常的多轮对话外。同时原生支持工具调用(Function Call)、代码执行(Code Interpreter)和Agent任务等复杂场景。本文主要通过天气查询例子介绍了在tool_registry.py中注册新的工具来增强模型能力。
可以直接调用LangChain自带的工具(比如,ArXiv),也可以调用自定义的工具。LangChain自带的部分工具[2],如下所示:

一.自定义天气查询工具
1.Weather类
可以参考Tool/Weather.py以及Tool/Weather.yaml文件,继承BaseTool类,重载_run()方法,如下所示:
class Weather(BaseTool): # 天气查询工具
name = "weather"
description = "Use for searching weather at a specific location"
def __init__(self):
super().__init__()
def get_weather(self, location):
api_key = os.environ["SENIVERSE_KEY"]
url = f"https://api.seniverse.com/v3/weather/now.json?key={api_key}&location={location}&language=zh-Hans&unit=c"
response = requests.get(url)
if response.status_code == 200:
data = response.json()
weather = {
"temperature": data["results"][0]["now"]["temperature"],
"description": data["results"][0]["now"]["text"],
}
return weather
else:
raise Exception(
f"Failed to retrieve weather: {response.status_code}")
def _run(self, para: str) -> str:
return self.get_weather(para)
2.weather.yaml文件
weather.yaml文件内容,如下所示:
name: weather
description: Search the current weather of a city
parameters:
type: object
properties:
city:
type: string
description: City name
required:
- city
二.自定义天气查询工具调用
自定义天气查询工具调用,在main.py中导入Weather工具。如下所示:
run_tool([Weather()], llm, [
"今天北京天气怎么样?",
"What's the weather like in Shanghai today",
])
其中,run_tool()函数实现如下所示:
def run_tool(tools, llm, prompt_chain: List[str]):
loaded_tolls = [] # 用于存储加载的工具
for tool in tools: # 逐个加载工具
if isinstance(tool, str):
loaded_tolls.append(load_tools([tool], llm=llm)[0]) # load_tools返回的是一个列表
else:
loaded_tolls.append(tool) # 如果是自定义的工具,直接添加到列表中
agent = initialize_agent( # 初始化agent
loaded_tolls, llm,
agent=AgentType.STRUCTURED_CHAT_ZERO_SHOT_REACT_DESCRIPTION, # agent类型:使用结构化聊天的agent
verbose=True,
handle_parsing_errors=True
)
for prompt in prompt_chain: # 逐个输入prompt
agent.run(prompt)
1.load_tools()函数
根据工具名字加载相应的工具,如下所示:
def load_tools(
tool_names: List[str],
llm: Optional[BaseLanguageModel] = None,
callbacks: Callbacks = None,
**kwargs: Any,
) -> List[BaseTool]:
2.initialize_agent()函数
根据工具列表和LLM加载一个agent executor,如下所示:
def initialize_agent(
tools: Sequence[BaseTool],
llm: BaseLanguageModel,
agent: Optional[AgentType] = None,
callback_manager: Optional[BaseCallbackManager] = None,
agent_path: Optional[str] = None,
agent_kwargs: Optional[dict] = None,
*,
tags: Optional[Sequence[str]] = None,
**kwargs: Any,
) -> AgentExecutor:
其中,agent默认为AgentType.ZERO_SHOT_REACT_DESCRIPTION。本文中使用为AgentType.STRUCTURED_CHAT_ZERO_SHOT_REACT_DESCRIPTION,一种为聊天模型优化的zero-shot react agent,该agent能够调用具有多个输入的工具。
3.run()函数
执行链的便捷方法,这个方法与Chain.__call__之间的主要区别在于,这个方法期望将输入直接作为位置参数或关键字参数传递,而Chain.__call__期望一个包含所有输入的单一输入字典。如下所示:
def run(
self,
*args: Any,
callbacks: Callbacks = None,
tags: Optional[List[str]] = None,
metadata: Optional[Dict[str, Any]] = None,
**kwargs: Any,
) -> Any:

4.结果分析
结果输出,如下所示:
> Entering new AgentExecutor chain...
======
======
Action:
``
{"action": "weather", "action_input": "北京"}
``
Observation: {'temperature': '20', 'description': '晴'}
Thought:======
======
Action:
``
{"action": "Final Answer", "action_input": "根据查询结果,北京今天的天气是晴,气温为20℃。"}
``
> Finished chain.
> Entering new AgentExecutor chain...
======
======
Action:
``
{"action": "weather", "action_input": "Shanghai"}
``
Observation: {'temperature': '20', 'description': '晴'}
Thought:======
======
Action:
``
{"action": "Final Answer", "action_input": "根据最新的天气数据,今天上海的天气情况是晴朗的,气温为20℃。"}
``
> Finished chain.
刚开始的时候没有找到识别实体city的地方,后面调试ChatGLM3/langchain_demo/ChatGLM3.py->_call()时发现了一个巨长的prompt,这不就是zero-prompt(AgentType.STRUCTURED_CHAT_ZERO_SHOT_REACT_DESCRIPTION)吗?顺便吐槽下LangChain的代码真的不好调试。


三.注册工具增强LLM能力
1.注册工具
可以通过在tool_registry.py中注册新的工具来增强模型的能力。只需要使用@register_tool装饰函数即可完成注册。对于工具声明,函数名称即为工具的名称,函数docstring即为工具的说明;对于工具的参数,使用Annotated[typ: type, description: str, required: bool]标注参数的类型、描述和是否必须。将get_weather()函数进行注册,如下所示:
@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 requests
try:
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 traceback
ret = "Error encountered while fetching weather data!\n" + traceback.format_exc()
return str(ret)
具体工具注册实现方式@register_tool装饰函数,如下所示:
def register_tool(func: callable): # 注册工具
tool_name = func.__name__ # 工具名
tool_description = inspect.getdoc(func).strip() # 工具描述
python_params = inspect.signature(func).parameters # 工具参数
tool_params = [] # 工具参数描述
for name, param in python_params.items(): # 遍历参数
annotation = param.annotation # 参数注解
if annotation is inspect.Parameter.empty:
raise TypeError(f"Parameter `{name}` missing type annotation") # 参数缺少注解
if get_origin(annotation) != Annotated: # 参数注解不是Annotated
raise TypeError(f"Annotation type for `{name}` must be typing.Annotated") # 参数注解必须是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_def # 添加工具定义
return func
2.调用工具
参考文件ChatGLM3/tool_using/openai_api_demo.py,如下所示:
def main():
messages = [ # 对话信息
system_info,
{
"role": "user",
"content": "帮我查询北京的天气怎么样",
}
]
response = openai.ChatCompletion.create( # 调用OpenAI API
model="chatglm3",
messages=messages,
temperature=0,
return_function_call=True
)
function_call = json.loads(response.choices[0].message.content) # 获取函数调用信息
logger.info(f"Function Call Response: {function_call}") # 打印函数调用信息
tool_response = dispatch_tool(function_call["name"], function_call["parameters"]) # 调用函数
logger.info(f"Tool Call Response: {tool_response}") # 打印函数调用结果
messages = response.choices[0].history # 获取历史对话信息
messages.append(
{
"role": "observation",
"content": tool_response, # 调用函数返回结果
}
)
response = openai.ChatCompletion.create( # 调用OpenAI API
model="chatglm3",
messages=messages,
temperature=0,
)
logger.info(response.choices[0].message.content) # 打印对话结果
参考文献:
[1]https://github.com/THUDM/ChatGLM3/tree/main
[2]https://python.langchain.com/docs/integrations/tools
Langchain-Chatchat项目:5.1-ChatGLM3-6B工具调用的更多相关文章
- 项目乱码 GBK转UTF-8工具
项目乱码 GBK转UTF-8工具 链接:http://pan.baidu.com/s/1pLw1mMB 密码:rj6c
- 项目经验分享——Java常用工具类集合 转
http://blog.csdn.net/xyw591238/article/details/51678525 写在前面 本文涉及的工具类部分是自己编写,另一部分是在项目里收集的.工具类涉及数 ...
- react 前端项目技术选型、开发工具、周边生态
react 前端项目技术选型.开发工具.周边生态 声明:这不是一篇介绍 React 基础知识的文章,需要熟悉 React 相关知识 主架构:react, react-router, redux, re ...
- spring项目中 通过自定义applicationContext工具类获取到applicationContext上下文对象
spring项目在服务器启动的时候 spring容器中就已经被创建好了各种对象,在我们需要使用的时候可以进行调用. 工具类代码如下 import org.springframework.beans.B ...
- So easy Webservice 3.使用HttpClient工具调用Webservice接口
首先,看看webservice服务调用演示: a) 登录http://www.webxml.com.cn b) 单击手机查询服务 c) 选择要调用的方法 例如: getMobileCodeInfo 输 ...
- 在maven项目中 配置代理对象远程调用crm
1 在maven项目中配置代理对象远程调用crm 1.1 在项目的pom.xml中引入CXF的依赖 <dependency> <groupId>org.apache.cxf&l ...
- arcgis js 之 渔网工具(调用地图服务)
arcgis js 之 渔网工具(调用地图服务) 原理: 简历不同级别的网渔网图层,设置显示比例尺.然后发布服务,使用MapImageLayer接收. 过程: 1.在arcmap中用创建渔网工具将不同 ...
- [C#项目开源] MongoDB 可视化管理工具 (2011年10月-至今)
正文 该项目从2011年10月开始开发,知道现在已经有整整5年了.MongoDB也从一开始的大红大紫到现在趋于平淡. MongoCola这个工具在一开始定位的时候只是一个Windows版本的工具,期间 ...
- .NET项目工程生成一份项目帮助文档chm--Sandcastle工具
Sandcastle的,由Microsoft创建的,是从创建MSDN风格的文档中使用的工具.NET程序集和关联的XML注释文件.目前的版本是 2010年6月发布.这是命令行并没有GUI前端,项目管理功 ...
- 在Visual Studio 2010中进行“项目重命名”的有效工具
地址:http://www.cnblogs.com/dudu/archive/2011/12/11/visual_studio_2010_project_rename.html 提示:这个工具一次 r ...
随机推荐
- Java实现数组去重复的18种写法
说明 数组(含List)去重复在日常工作中经常遇到,很多时候用到Set数据结构,但有时候我们需要针对数据进行干预,这时候就需要用其他的实现方式了.以下列出各种的去重方式,基本含括了所有情况. 源码下载 ...
- Java服务刚启动时,一小波接口超时排查全过程
原创:扣钉日记(微信公众号ID:codelogs),欢迎分享,非公众号转载保留此声明. 简介 我们组有一个流量较大的Java服务,每次发代码时,服务都会有一小波接口超时,之前简单分析过,发现这些超时的 ...
- 学习lspci:总线
00:00.0 Host bridge 总线地址 00:00.0 是指PCI总线上的第一个设备,也称为根复杂性总线.在PCI架构中,每个设备都有唯一的总线地址,由域号.总线号.设备号和功能号组成.其中 ...
- PostgreSql: 安装与链接
环境介绍 使用宝塔面板,在阿里云中安装PostgreSql,并使用DataGrip在本地进行链接 postgresql 配置 安装postgresql 在宝塔中安装postgresql 管理器 在此处 ...
- 手动安装vur-router并引用
安装并引用 安装 npm install vue-router 引用 步骤一:在src路径下,创建router文件夹, 其下创建index.js // router/index.js import V ...
- Yunfly 一款高效、性能优异的 node.js web 框架
介绍 Yunfly 一款高性能 Node.js WEB 框架, 使用 Typescript 构建我们的应用. 使用 Koa2 做为 HTTP 底层框架, 使用 routing-controllers ...
- 【教程】青少年CTF机器人使用教程
前言 本期教程适用于版本号为2.0.1-Beta的青少年CTF机器人,其他版本可能与当前版本不同. 由于之前版本的机器人重构,所以我们细化了本次的机器人逻辑,并且对机器人的功能进行了一些升级. 机器人 ...
- 从原理聊JVM(四):JVM中的方法调用原理
1 引言 多态是Java语言极为重要的一个特性,可以说是Java语言动态性的根本,那么线程执行一个方法时到底在内存中经历了什么,JVM又是如何确定方法执行版本的呢? 2 栈帧 JVM中由栈帧存储方法的 ...
- 三维模型OSGB格式轻量化技术在大规模场景的加载和渲染的作用分析
三维模型OSGB格式轻量化技术在大规模场景的加载和渲染的作用分析 在移动设备上,大规模场景的加载和渲染是一个不容忽视的问题.对于OSGB格式轻量化处理来说,大规模场景的加载和渲染也是其中一项重要的任务 ...
- springboot下载文件 范围下载
springboot下载文件 范围下载 关键词:springboot,download,Range,Content-Range,Content-Length,http code 206 Partial ...