ChatGLM3训练数据更多样、训练步数更充分、训练策略更合理,在语义、数学、推理、代码、知识等不同角度的数据集上表现出色,目前在44个中英文公开数据集测试国内第一。
I-ChatGLM3的功能变化
1.全新的多消息模式
从一问一答的对话模式转为多角色问答模型,如下图
2.史诗级的更新点 – Function Calling
2023年6月Function Calling功能首次由OpenAI宣布在Chat Completion模型中进行使用,代表着Chat模型不再需要LangChain框架就可以直接在模型内部调用外部工具API,从而更便捷的使用LLM,进行AI Agent的开发。
LangChain框架
LangChain是一个用于开发由语言模型驱动的应用程序的框架。
主要功能:
- 调用语言模型
- 将不同的数据源接到语言模型的交互中
- 允许语言模型与运行环境交互
提供的模块:
- Modules:支持的模型类型和集成
- Prompt:提示词管理、优化和序列
- Memory:在链/代理调用之间持续存在的状态
- Indexes:加载、查询和更新外部数据的接口和集成
- Chain:结构化的调用序列
- Agents:链,其中LLM在给定高级指令和一组工具的情况下,反复决定操作,执行操作并观察结果,直到高级指令完成
- Callbacks:回调允许记录和流式传输任何链的中间步骤,从而轻松观察、调试和评估应用程序内部
基于LangChain+ChatGLM的本地知识库问答实现原理流程图
Function Calling功能可以弥补LLM的一些缺点,比如实时信息的缺乏、特定领域能力,使得能够进一步利用LLM的逻辑推理能力,可以将问题进行分解处理,解决问题能力更加强大。
基于Function Calling可以开发什么AI Agent
简单类AI Agent项目:
- Function Calling + OpenWeather:智能查询天气信息的应用
- Function Calling + 谷歌邮箱API:智能收发邮件的应用
进阶类AI Agent项目:
- Function Calling + 谷歌搜索API + 爬虫:智能搜索问答机器人
- Function Calling + pymysql + MySQL:定制化SQL代码解释器
- Function Calling + python:代码解释器(code interpreter)
- Function Calling + python+算法库:机器学习/深度学习助手
II-ChatGLM3的使用
类OpenAI Chat模型的调用方法
ChatGLM3模型提供了基于openai框架的调用使用方法,对于使用OpenAI API创建的应用而言,想要将基础模型更改为ChatGLM3,只需要将model参数修改为‘chatglm3-6b’即可。一个统一的调用方式对开发者来说是重大利好的。
import openai
if __name__ == "__main__":
openai.api_base = "http://localhost:8000/v1"
openai.api_key = "none"
for chunk in openai.ChatCompletion.create(
model = "chatglm3-6b",
messages = [
{"role": "user", "content": "你好"}
],
stream = True
):
if hasatter(chunk.choices[0].delta, "content"):
print(chunk.choices[0].delta.content, end="", flush=True)
1.ChatGLM3调用流程及返回结果说明
response_glm3 = openai.ChatCompletion.create(
model = "chatglm3-6b",
messages = [
{"role": "user", "content": "你好"}
]
)
print(response_glm3['choices'][0]['message']['content'])
# 输出为:你好!我是人工智能助手ChatGLM3-6B,很高兴见到你,欢迎问我任何问题。
- messages
:基本构成元素为字典的列表,每一个字典都代表一条独立的消息。role表示某条消息的作者,content表示消息的具体内容。
- choices
:返回答案是一个列表,通过参数的设置,可以返回不止一条答案。
返回结果response_glm3是高度结构化的json对象,其结构如下图所示
- metadata
:为调用的工具名。(Code Interpreter固定为interpreter。)
2.messages参数结构及功能解释
messages是一种用于描述ChatCompletion模型与用户之间通信信息的高级抽象,从表示形式上来看,一个messages是一个列表,包含多个字典,每一个字典都代表一条独立的消息。role表示某条消息的作者,content表示消息的具体内容。
- role
:某条消息的作者。角色身份:system(系统提示词,指明模型扮演的角色信息),user(用户输入),assistant(模型回复),observation(工具调用、代码执行结果)。
- content
:消息的具体内容。
为LLM模型指定某种虚拟身份,更便于激活模型能力。设置身份的方法主要有两种:
-
一是在
content
中直接假设response_glm3 = openai.ChatCompletion.create( model = "chatglm3-6b", messages = [ {"role": "user", "content": "假设你是一名资深的大学教授,请问机器学习是什么?"} ] )
-
二是使用system role
response_glm3 = openai.ChatCompletion.create( model = "chatglm3-6b", messages = [ {"role": "system", "content": "假设你是一名资深的大学教授。"}, {"role": "user", "content": "请问机器学习是什么?"} ] )
III-ChatGLM3的Function Calling功能及示例
Function Calling功能的本质是让LLM调用外部函数,即Chat模型不再仅仅根据自身的数据库知识进行回答,而是可以额外挂载一个函数库,根据用户提问进行函数库检索,根据实际需求调用外部函数并获取函数运行结果,再基于函数运行结果进行回答。基本过程如下图所示:
智能查询天气信息的应用
1.查询天气函数实现(获取查询天气工具的API key,如OpenWeather)
import requests
def get_weather(loc):
"""
查询即时天气预报
param loc:当前需要查询城市的名称,中国城市要用对应城市的英文名称。
:return:OpenWeather API查询即时天气的结果,返回结果对象类型为解析之后的JSON格式对象。
"""
# step1 构建请求
URL = "https://api.openweathermaporg/data/2.5/weather"
# step2 设置查询参数
params = {
"q": loc,
"appid": open_weather_key, # 获取的API key
"units": "metric", # 使用摄氏度而不是华氏度
"lang": "zh_cn" # 输出语言为简体中文
}
# step3 发送get请求
response = requests.get(URL, params=params)
# step4 解析响应
data = response.json()
return json.dumps(data)
# 测试函数是否能使用
weather = get_weather("Hangzhou")
print(weather)
正常运行时,上述输出如下图所示
最后需要创建get_weather函数的functions参数
weather_api_spec = [
{
'name': 'get_weather',
'description': '输入城市名称,查询即时天气函数。',
'parameters': {
'type': 'object',
'properties': {
'loc': {
'description': '城市名称,中国城市要用对应城市的英文名称。',
'type': 'string',
'required': True
}
}
}
}
]
2.实时查询天气的Function Calling实现
将get_weather函数放入函数库(写入functions_list函数列表),然后启用外链函数库之后的多伦对话机器人chat_with_model,查看对话效果。下面对整个对话流程进行简要的说明。
第一步:构建消息内容
# 设定LLM身份
system_info = {
"role": "system",
"content": "Answer the following questions a best as you can. You have access to the following tools:",
"tools": weather_api_spec
}
# 用户提问消息内容
messages = [
system_info,
{
"role": "user",
"content": "请问今天北京天气如何?"
}
]
第二步:首次调用LLM
# 第一次调用LLM
first_response = openai.ChatCompletion.create(
model = "chatglm3-6b",
messages = messages,
return_function_call = True
)
first_response_message = first_response['choices'][0]['message']
print(first_response_message)
输出结果如下图所示
这表明想要获得问题的答案需要调用外部函数。
对first_response_message内容进行解析
function_call = json.loads(first_response_message.content)
print(function_call)
输出结果如下图所示
该解析结果中包含了需要调用的外部函数的名称以及所需参数。
第三步:外部函数调用
# 获取函数名及参数
function_name = function_call["name"]
function_args = function_call["parameters"]
# 获取外部函数
functions_list = [get_weather]
available_functions = {func.__name__: func for func in functions_list}
function_to_call = available_functions[function_name]
# 运行外部函数
function_response = function_to_call(**function_args)
print(function_response)
输出结果如下图所示
得到的是外部函数运行的结果,接下来需要再一次使用LLM对上述结果进行解析并输出最初问题的答案。
第四步:第二次调用LLM
# messages中拼接first_response_message消息
messages.append(first_response_message)
# messages中拼接外部函数输出结果
messages.append(
{
"role": "observation", # 角色为observation,表示为外部函数发的消息
"name": function_name,
"content": function_response
}
)
# 第二次调用LLM
second_response = openai.ChatCompletion.create(
model = "chatglm3-6b",
messages = messages
)
final_response = second_response['choices'][0]['message']['content']
print(final_response)
输出结果如下图所示
3.函数封装
对上述过程进行函数封装,实现自动执行外部函数的LLM对话功能。
def run_conv_glm(messages, functions_list=None, functions=None, model="chatglm3-6b", return_function_call = True):
"""
能够自动执行外部函数调用的Chat对话模型
:param messages:必要参数,输入到对话模型的messages参数对象
:param functions_list:可选参数,默认为None,可以设置为包含全部外部函数的函数列表
:param functions:外部函数的说明
:param model:可选参数,LLM模型,默认为chatglm3-6b
:return:
"""
def is_json_string(s):
try:
json_object = json.loads(s)
except ValueError as e:
retrun False
return True
user_messages = messages
# 如果没有外部函数库,则执行普通的对话任务
if functions_list == None:
response = openai.ChatCompletion.create(
model = model,
messages = user_messages
)
response_message = response['choices'][0]['message']
final_response = response_message['content']
# 有外部函数库
else:
# 创建外部函数的system_message
system_info = {
"role": "system",
"content": "Answer the following questions a best as you can. You have access to the following tools:",
"tools": functions
}
# 创建外部函数库字典
available_functions = {func.__name__: func for func in functions_list}
# 创建包含用户问题的messages
messages = system_info + user_messages
# first response
first_response = openai.ChatCompletion.create(
model = "chatglm3-6b",
messages = messages,
return_function_call = return_function_call
)
first_response_message = first_response['choices'][0]['message']
# 判断返回结果是否为json格式对象,即是否需要调用外部函数
if is_json_string(first_response_message['content']): # 需要调用
function_call = json.loads(first_response_message.content)
# 获取函数名及参数
function_name = function_call["name"]
function_args = function_call["parameters"]
# 获取外部函数
function_to_call = available_functions[function_name]
# 运行外部函数
function_response = function_to_call(**function_args)
# messages中拼接first_response_message消息
messages.append(first_response_message)
# messages中拼接外部函数输出结果
messages.append(
{
"role": "observation", # 角色为observation,表示为外部函数发的消息
"name": function_name,
"content": function_response
}
)
# 第二次调用LLM
second_response = openai.ChatCompletion.create(
model = "chatglm3-6b",
messages = messages
)
final_response = second_response['choices'][0]['message']['content']
else:
final_response = first_response_message['content']
return final_response
上述实现为单轮对话,下面基于上述函数实现多轮对话函数。
def chat_with_glm(messages, functions_list=None, functions=None, model="chatglm3-6b", return_function_call = True):
messages = [{"role": "system", "content": question}]
while True:
answer = run_conv_glm(messages,
functions_list=None,
functions=None,
model="chatglm3-6b",
return_function_call = True
)
print(f"模型回答:{answer}")
question = input("您还有其他问题吗?(输入退出以结束对话)")
if question == "退出":
break
message.append({"role": "assistant", "content": answer})
message.append({"role": "user", "content": question})
del messages
多轮对话功能测试如下图所示