2024年您产品必备的10大AI API推荐
大模型Function Call功能深度解析:使用API接口提升语言模型智能化的实战指南
在近年来,人工智能领域取得了飞速的进展,大型语言模型在自然语言处理(NLP)任务中表现尤为突出。然而,即使是最先进的模型也有其局限性,比如处理实时信息的能力不足。为了解决这些局限,Function Call功能的引入成为了一个关键创新。
本文将以ChatGLM-6B为例,探讨Function Call功能如何通过API接口提升语言模型的智能化水平。
先看看效果:
1. 什么是Function Call
Function Call功能指的是语言模型在生成回答时,通过调用外部API接口来获取数据或执行操作。这种功能不仅扩展了模型的应用场景,还提升了其智能化水平。通过与外部系统的互动,模型可以实时获取最新的信息、执行复杂任务,并提供更加精准和实用的回答。
后续内容将以ChatGLM-6B为例来介绍Function Call。
2. Function Call的应用场景
Function Call功能在多个实际应用场景中发挥重要作用,包括但不限于:
- 信息检索: 获取实时新闻、天气、交通等数据。
- 任务执行: 执行特定操作,如日程安排、计算任务。
- 数据查询: 访问数据库或在线服务获取详细信息。
3. 如何让模型具备Function Call
要让模型具备Function Call功能,需要通过设计和训练包含API调用示例的数据集,使模型学习如何在适当的上下文中生成API调用指令。模型训练时结合这些示例进行定制化训练,并通过强化学习优化调用策略。确保数据的一致性和实时更新,以适应API的变化,同时收集用户反馈进行持续改进,从而实现模型对API调用的准确生成和有效处理。
基于Function Call的ChatGLM-6B对话格式:
<|system|>
Answer the following questions as best as you can. You have access to the following tools:
[
{
"name": "get_current_weather",
"description": "Get the current weather in a given location",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "The city and state, e.g. San Francisco, CA",
},
"unit": {"type": "string"},
},
"required": ["location"],
},
}
]
<|user|>
今天北京的天气怎么样?
<|assistant|>
好的,让我们来查看今天的天气
<|assistant|>get_current_weather
tool_call(location="beijing", unit="celsius")
<|observation|>
{"temperature": 22}
<|assistant|>
根据查询结果,今天北京的气温为 22 摄氏度。
上面对话格式说明:
4. ChatGLM-6B介绍
ChatGLM3 是智谱AI和清华大学 KEG 实验室联合发布的对话预训练模型。ChatGLM3-6B 是 ChatGLM3 系列中的开源模型,在保留了前两代模型对话流畅、部署门槛低等众多优秀特性的基础上,ChatGLM3-6B 引入了如下特性:
- 更强大的基础模型:ChatGLM3-6B 的基础模型 ChatGLM3-6B-Base 采用了更多样的训练数据、更充分的训练步数和更合理的训练策略。在语义、数学、推理、代码、知识等不同角度的数据集上测评显示,* ChatGLM3-6B-Base 具有在 10B 以下的基础模型中最强的性能*。
- 更完整的功能支持:ChatGLM3-6B 采用了全新设计的 Prompt 格式 ,除正常的多轮对话外。同时原生支持工具调用(Function Call)、代码执行(Code Interpreter)和 Agent 任务等复杂场景。
- 更全面的开源序列:除了对话模型 ChatGLM3-6B 外,还开源了基础模型 ChatGLM3-6B-Base 、长文本对话模型 ChatGLM3-6B-32K 和进一步强化了对于长文本理解能力的 ChatGLM3-6B-128K。以上所有权重对学术研究完全开放 ,在填写 问卷 进行登记后亦允许免费商业使用。
注意:目前只有 ChatGLM3-6B 模型支持工具调用,而 ChatGLM3-6B-Base 和 ChatGLM3-6B-32K 模型不支持。
5. ChatGLM-6B中Function Call如何实现
5.1. 模型架构
a. 多模态预训练
- 描述: ChatGLM-6B 在多模态预训练阶段学习了丰富的语言和对话模式。这使得模型能够处理各种任务,包括自然语言处理和工具调用。
- 实现方式: 模型通过在大规模数据集上进行预训练,获取了处理不同类型输入的能力,包括调用外部工具的需求。
5.2. 工具调用机制
a. 工具集成
- 描述: ChatGLM-6B 在训练过程中集成了工具调用功能。这意味着模型能够识别何时需要调用工具,并生成相应的请求。
- 实现方式: 模型通过训练数据中包含的工具调用示例,学习如何根据用户问题生成调用工具的请求。
b. 工具调用接口
- 描述: 模型利用预定义的工具接口进行调用。这些接口定义了工具的功能和参数要求。
- 实现方式: 通过在模型推理过程中,将用户问题映射到具体的工具调用请求。工具接口的定义帮助模型知道如何构造请求。
5.3. 训练数据和策略
a. 多任务学习
- 描述: 在训练过程中,ChatGLM-6B 接受了多任务学习,涉及生成对话、回答问题和工具调用等任务。
- 实现方式: 训练数据中包括不同任务的示例,包括需要调用工具的场景。模型通过这些示例学习如何在适当的情况下触发工具调用。
b. 示例学习
- 描述: 模型使用了包含工具调用的示例数据来学习如何在实际应用中使用工具。
- 实现方式: 这些示例展示了从用户输入到工具调用的完整流程,帮助模型理解如何将用户请求转化为工具调用请求。
5.4. 对话管理和上下文理解
a. 上下文感知
- 描述: ChatGLM-6B 能够理解对话上下文,这有助于决定是否调用工具以及如何调用。
- 实现方式: 模型使用上下文信息来判断用户的具体需求,从而决定是否需要工具调用。
b. 决策策略
- 描述: 在推理阶段,模型使用决策策略来确定是否调用工具。
- 实现方式: 模型根据用户输入的特征(如关键词、意图)决定是否触发工具调用,并生成相应的调用请求。
总之,在ChatGLM-6B中,函数/工具调用(Function Call)通过以下步骤实现:
- 模型架构: 利用多模态预训练来处理语言和工具调用。
- 工具调用机制: 通过集成工具接口和生成工具调用请求。
- 训练数据和策略: 使用多任务学习和工具调用示例来训练模型。
- 对话管理和上下文理解: 利用上下文信息和决策策略来决定是否调用工具。
6. 代码实现
本部分将通过代码来实现ChatGLM-6B调用天气接口来获取查询地的天气情况。
ChatGLM-6B模型权重下载地址(下面任选一个):
- HuggingFace:https://huggingface.co/THUDM/chatglm3-6b
- ModelScope: https://modelscope.cn/models/ZhipuAI/chatglm3-6b
代码包括两个部分: 工具定义代码和ChatGLM-6B调用工具代码。由于我将ChatGLM-6B权重下载到了本地,具体路径为:/root/autodl-tmp/chatglm3-6b提示:将下面代码路径/root/autodl-tmp/chatglm3-6b替换为你的路径。工具定义代码(tool_register.py)
"""
这段代码是工具注册的部分,通过注册工具,让模型实现工具调用
"""
import inspect
import traceback
from copy import deepcopy
from pprint import pformat
from types import GenericAlias
from typing import get_origin, Annotated
_TOOL_HOOKS = {}
_TOOL_DESCRIPTIONS = {}
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:
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
}
_TOOL_HOOKS[tool_name] = func
_TOOL_DESCRIPTIONS[tool_name] = tool_def
return func
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)
# tools Definitions
@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 random
return random.Random(seed).randint(*range)
@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)
if __name__ == "__main__":
# print(dispatch_tool("random_number_generator", {"seed": 2024, "range":(1, 10)}))
print(get_tools())
大模型调用工具代码:
"""
This demo script is designed for interacting with the ChatGLM3-6B in Function, to show Function Call capabilities.
"""
import os
import platform
from transformers import AutoTokenizer, AutoModel
from tool_register import get_tools, dispatch_tool
import json
tools = get_tools()
print(tools)
MODEL_PATH = os.environ.get('MODEL_PATH', '/root/autodl-tmp/chatglm3-6b')
TOKENIZER_PATH = os.environ.get("TOKENIZER_PATH", MODEL_PATH)
tokenizer = AutoTokenizer.from_pretrained(TOKENIZER_PATH, trust_remote_code=True)
model = AutoModel.from_pretrained(MODEL_PATH, trust_remote_code=True, device_map="auto").eval()
os_name = platform.system()
clear_command = 'cls' if os_name == 'Windows' else 'clear'
stop_stream = False
tools = get_tools()
system_item = {
"role": "system",
"content": "Answer the following questions as best as you can. You have access to the following tools:",
"tools": tools
}
def main():
past_key_values, history = None, [system_item]
role = "user"
global stop_stream
print("欢迎使用 ChatGLM3-6B 模型,输入内容即可进行对话,clear 清空对话历史,stop 终止程序")
while True:
query = input("\n用户:") if role == "user" else query_rt
if query.strip() == "stop":
break
if query.strip() == "clear":
past_key_values, history = None, [system_item]
role = "user"
os.system(clear_command)
print("欢迎使用 ChatGLM3-6B 模型,输入内容即可进行对话,clear 清空对话历史,stop 终止程序")
continue
print("\nChatGLM:", end="")
# 目前 ChatGLM3-6B 的工具调用只支持通过 chat 方法,不支持 stream_chat 方法。
response, history = model.chat(tokenizer, query, history=history, role=role)
print(response, end="", flush=True)
# 这里 role="observation" 表示输入的是工具调用的返回值而不是用户输入,不能省略。
if isinstance(response, dict):
name = response['name']
param = response['parameters']
print(f"开始调用API:{name}", end="")
rt = dispatch_tool(name, param)
query_rt = json.dumps(rt, ensure_ascii=False)
role = "observation"
else:
role = "user"
print(response, end="", flush=True)
if __name__ == "__main__":
main()
执行结果:
7. 结论
Function Call功能通过API接口的引入,显著提升了语言模型的智能化水平。以ChatGLM-6B为例,模型通过多模态预训练、工具接口集成、请求生成和结果整合等步骤,实现了在对话中智能地调用外部工具。这种功能不仅提升了模型处理实时数据的能力,还扩展了其应用范围,增强了用户交互体验。未来,随着技术的进一步发展和挑战的解决,Function Call功能将在语言模型的智能化进程中发挥更大的作用,为用户提供更为精准和实用的服务。
文章转自微信公众号@大厂小僧
、