关于chatglm3 function calling的理解

news2025/3/15 0:23:49

ChatGLM3-6B开源了工具调用,好奇他是怎么实现的,所以写了这个文章记录。

一、测试官方的示例

官方给的示例很简单,只不过给的两个函数 track 和 text-to-speech 没有具体的实现,模型的输出也只是给出了需要调用的函数名和参数。剩下的需要自己去实现..

我更换了tools中的函数:

tools = [
    {
        "name": "go_ahead",
        "description": "小车前进",
        "parameters": {
            "type": "object",
            "properties": {
                "distance": {
                    "description": "前进的距离,单位为米"
                }
            },
            "required": ['distance']
        }
    },
    {
        "name": "back",
        "description": "小车后退",
        "parameters": {
            "type": "object",
            "properties": {
                "distance": {
                    "description": "后退的距离,单位为米"
                }
            },
            "required": ['distance']
        }
    },
    {
        "name": "turn_left",
        "description": "小车左转",
        "parameters": {
            "type": "object",
            "properties": {
                "angle": {
                    "description": "左转角度,单位为°"
                }
            },
            "required": ['angle']
        }
    },
    {
        "name": "turn_right",
        "description": "小车右转",
        "parameters": {
            "type": "object",
            "properties": {
                "angle": {
                    "description": "右转角度,单位为°"
                }
            },
            "required": ['angle']
        }
    }
]

测试下来出现以下问题:

        1. 输入多个操作只能执行一个操作

        2. 会出现输出不存在的函数的情况

        3. 当已有的函数不能实现用户的操作时,会调用已有函数强行输出

二、代码解析

现在让我们来看看具体实现的代码。下载chatglm3-6b权重的时候也会下载modeling_chatglm.py和tokenization_chatglm.py这两个python文件,chatglm3实现function calling也是在这里面实现的。

1. 输入的处理

首先工具调用跟一般的对话的输入差在有一个 system_info ,他是作为history输入到model.chat函数中的。

system_info = {"role": "system", "content": "Answer the following questions as best as you can. You have access to the following tools:", "tools": tools}

我们可以在modeling_chatglm.py文件中找到chat的实现

@torch.inference_mode()
def chat(self, tokenizer, query: str, history: List[Tuple[str, str]] = None, role: str = "user",
            max_length: int = 8192, num_beams=1, do_sample=True, top_p=0.8, temperature=0.8, logits_processor=None,
            **kwargs):
    if history is None:
        history = []
    if logits_processor is None:
        logits_processor = LogitsProcessorList()
    logits_processor.append(InvalidScoreLogitsProcessor())
    gen_kwargs = {"max_length": max_length, "num_beams": num_beams, "do_sample": do_sample, "top_p": top_p,
                    "temperature": temperature, "logits_processor": logits_processor, **kwargs}
    

    inputs = tokenizer.build_chat_input(query, history=history, role=role)


    inputs = inputs.to(self.device)
    eos_token_id = [tokenizer.eos_token_id, tokenizer.get_command("<|user|>"),
                    tokenizer.get_command("<|observation|>")]
    outputs = self.generate(**inputs, **gen_kwargs, eos_token_id=eos_token_id)
    outputs = outputs.tolist()[0][len(inputs["input_ids"][0]):-1]
    response = tokenizer.decode(outputs)
    history.append({"role": role, "content": query})
    response, history = self.process_response(response, history)
    return response, history

在chat函数中,history又被作为参数送到tokenizer.build_chat_input中,然后得到input。

那很明显需要查看tokenizer.build_chat_input的实现,tokenizer.build_chat_input函数在tokenization_chatglm中:

def build_chat_input(self, query, history=None, role="user"):
    if history is None:
        history = []
    input_ids = []
    for item in history:
        content = item["content"]
        if item["role"] == "system" and "tools" in item:


            content = content + "\n" + json.dumps(item["tools"], indent=4, ensure_ascii=False)


        input_ids.extend(self.build_single_message(item["role"], item.get("metadata", ""), content))
    input_ids.extend(self.build_single_message(role, "", query))
    input_ids.extend([self.get_command("<|assistant|>")])
    return self.batch_encode_plus([input_ids], return_tensors="pt", is_split_into_words=True)

根据上面的代码看得出来,他是直接用json.dumps把tools拼接到content中,然后塞给大模型的。

2. 输出的处理

输出的处理在chat函数中的process_response函数

def process_response(self, output, history):
    content = ""
    history = deepcopy(history)
    for response in output.split("<|assistant|>"):
        metadata, content = response.split("\n", maxsplit=1)
        if not metadata.strip():
            content = content.strip()
            history.append({"role": "assistant", "metadata": metadata, "content": content})
            content = content.replace("[[训练时间]]", "2023年")
        else:
            history.append({"role": "assistant", "metadata": metadata, "content": content})
            if history[0]["role"] == "system" and "tools" in history[0]:
                content = "\n".join(content.split("\n")[1:-1])
                def tool_call(**kwargs):
                    return kwargs
                parameters = eval(content)
                content = {"name": metadata.strip(), "parameters": parameters}
            else:
                content = {"name": metadata.strip(), "content": content}
    return content, history

这里需要注意一点,chatglm3-6b应该是有针对工具调用进行训练,输出的结果很稳定,基本上都是下面的结构:

'turn_right\n```python\ntool_call(angle=30)\n```'

第一行是调用的函数名,然后下面是执行函数的代码(代码中函数名统一为tool_call)。再通过split('\n')得到代码,eval执行tool_call函数得到函数的变量字典,然后返回字典如下:

{'name': 'turn_right', 'parameters': {'angle': 30}}

3.  openai_api_demo.py

官方还给出了openai_api_demo.py这个文件,他实现了完整的 输入自然语言->得到函数和函数参数->执行函数 这一套流程。虽然不知道为什么没有在readme中写出来

openai_api_demo.py主要依靠tool_register.py下的get_tools和dispatch_tool

1. register_tool用于注册函数,它接受一个可调用对象 func 作为参数。该函数将 func 注册为一个工具,并返回 func 本身。

2. dispatch_tool用于执行函数,它接受一个函数名和函数参数,返回函数的返回。

三、尝试在百川上实现

6点到了先下班,明天再说

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1337035.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

w16php系列之基础数组

一、索引数组 概念 索引数组 是指键名为整数的数组。默认情况下&#xff0c;索引数组的键名是从0开始&#xff0c;并依次递增。它主要适用于利用位置&#xff08;0、1、2……&#xff09;来标识数组元素的情况。另外&#xff0c;索引数组的键名也可以自己指定 示例代码 <…

网络调优,部署内网备份冗余和负载分担---实验

目录 网络调优&#xff0c;部署内网备份冗余和负载分担---实验 拓扑 需求 配置步骤&#xff1a; 配置命令: 网络调优&#xff0c;部署内网备份冗余和负载分担---实验 拓扑 需求 主机获取IP地址&#xff0c;访问WEB服务器&#xff0c;WEB服务器网关在SW5上SW5作为VLAN10,V…

【实施】共享目录防火墙

文章目录 一、共享文件1.1 为什么需要配置文件夹共享功能1.2 配置文件夹共享功能1.3 访问共享文件1.4 普通共享和高级共享 二、安装JDK和Tomcat2.1 安装JDK2.2 安装Tomcat 三、防火墙配置 一、共享文件 1.1 为什么需要配置文件夹共享功能 我们在工作和生活中经常有需要将自己…

2024年五个大胆的网络安全预测

纵观历史&#xff0c;网络安全行业不断对攻击者的转变以及运营现实的变化做出反应&#xff0c;新的一年也不例外。随着人工智能和云计算等新技术改变网络安全格局&#xff0c;攻击者和防御者之间的猫捉老鼠游戏肯定会加剧&#xff0c;并且会变得更加复杂。 再加上其他因素&…

带你学C语言~指针(3)

目录 ✍0.前言 &#x1f680;1.字符指针变量 &#x1f685;2.数组指针变量 &#x1f431;‍&#x1f3cd;2.1.数组指针变量是什么 &#x1f431;‍&#x1f3cd;2.2数组指针变量怎么初始化 &#x1f6a2;3.二维数组传参的本质 &#x1f680;4.函数指针变量 ✈4.1函数指…

15个主流设计灵感网站,激发你的创作灵感!

即时设计 即时设计是一种强大的云设计工具&#xff0c;已成为许多设计师、产品经理和开发人员的首选工具之一。即时设计用户可以使用内置的工具和功能快速创建和编辑设计&#xff0c;或与其他用户共享和合作。此外&#xff0c;即时设计还有一个丰富的资源社区&#xff0c;为用…

制造业数字化转型的核心不止是技术

一、制造业的数字化转型意味着什么&#xff1f; 在当今的制造业领域&#xff0c;数字化转型意味着通过集成数字技术来增强传统的制造方法、产品和劳动力的过程。这些技术包括一系列创新&#xff0c;如自动化软件、电子商务系统、传感器、工业机器人等。 二、制造业数字化转型的…

ubuntu 安装apisix -亲测可用

官方未提供在ubuntu系统中安装apisix的方式&#xff0c;似乎只能通过源码方式安装&#xff0c;但是并不推荐&#xff0c;非常容易失败&#xff0c; 具体操作方式如下&#xff1a; ubuntu和Debian其实类似的&#xff0c;可使用DEB方式安装&#xff0c;如下截图 注意&#xff1…

22000mAh 电池,这款国产新机来了场「续航」震撼

见惯了主流智能手机&#xff0c;是时候上一波离谱新机震撼了。 三防手机这一细分类型&#xff0c;咱们普通用户可能接触得比较少&#xff1b; 但对于极限运动、野外探险爱好者来说&#xff0c;这玩意儿可是关键时候能救命的必备神器。 在真正严苛环境面前&#xff0c;性能啥的…

《Vue2.X 进阶知识点》- 防 ElementUI Divider 分割线

前言 使用 el-divider 背景为白色是没问题的。 但当背景换成其它颜色&#xff0c;问题就出现了&#xff01;&#xff01; 仔细看原来是两层&#xff0c;默认背景色是白色。 想着把背景色改为透明应该能用&#xff0c;结果发现背面是一条实线&#xff0c;难怪要用白色遮挡…不符…

使用LLaMA-Factory微调ChatGLM3

1、创建虚拟环境 略 2、部署LLaMA-Factory &#xff08;1&#xff09;下载LLaMA-Factory https://github.com/hiyouga/LLaMA-Factory &#xff08;2&#xff09;安装依赖 pip3 install -r requirements.txt&#xff08;3&#xff09;启动LLaMA-Factory的web页面 CUDA_VI…

HarmonyOS4.0系统性深入开发05ArkTS卡片运行机制

ArkTS卡片运行机制 实现原理 图1 ArkTS卡片实现原理 卡片使用方&#xff1a;显示卡片内容的宿主应用&#xff0c;控制卡片在宿主中展示的位置&#xff0c;当前仅系统应用可以作为卡片使用方。卡片提供方&#xff1a;提供卡片显示内容的应用&#xff0c;控制卡片的显示内容、…

ElasticSearch 聚合统计

聚合统计 度量聚合&#xff1a;求字段的平均值&#xff0c;最小值&#xff0c;最大值&#xff0c;总和等 桶聚合&#xff1a;将文档分成不同的桶&#xff0c;桶的划分可以根据字段的值&#xff0c;范围&#xff0c;日期间隔 管道聚合&#xff1a;在桶聚合的结果上执行进一步计…

【MYSQL】MYSQL 的学习教程(七)之 慢 SQL 优化思路

1. 慢 SQL 优化思路 慢查询日志记录慢 SQLexplain 分析 SQL 的执行计划profile 分析执行耗时Optimizer Trace 分析详情确定问题并采用相应的措施 1. 慢查询日志记录慢 SQL 如何定位慢SQL呢&#xff1f; 我们可以通过 慢查询日志 来查看慢 SQL。 ①&#xff1a;开启慢查询日志…

围栏中心点

后端返回的数据格式是 [{height: 0,lat: 30.864277169098443,lng:114.35252972024682}{height: 1,lat: 30.864277169098443,lng:114.35252972024682}.........]我们要转换成 33.00494857612568,112.53886564762979;33.00307854503083,112.53728973842954;33.00170296814311,11…

labuladong日常刷题-递归魔法 | LeetCode 206反转链表 92反转链表-ii

递归魔法 LeetCode 206 反转链表 2023.12.26 题目链接labuladong讲解[链接] ListNode* reverseList(ListNode* head) {//递归退出条件if(head NULL || head->next NULL)return head;//递归ListNode* last reverseList(head->next);//处理head->next->next …

腾讯云轻量服务器4核8G12M有三年优惠价表

腾讯云轻量服务器4核8G12M有三年优惠价吗&#xff1f;有&#xff0c;但是不怎么优势&#xff0c;相对于云轻量2核2G4M带宽三年价格是540元、2核4G5M带宽3年优惠价756元&#xff0c;4核8G12M轻量应用服务器三年价格是5292元&#xff0c;怎么样&#xff1f;还想买吗&#xff1f;阿…

9.独立看门狗IWDG窗口看门狗WWDG编码思路

前言&#xff1a; 看门狗是维护系统稳定性的一向技术&#xff0c;可以让代码跑飞及时复位&#xff0c;在产品中非常常用&#xff0c;俗话说&#xff0c;重启能解决90%的问题&#xff0c;作为产品来说&#xff0c;你总不能因为一次bug就让程序卡死不动了&#xff0c;肯定要试着重…

微信小程序云开发-下载云存储中的文件

一、前言 很多时候我们需要实现用户在客户端下载服务端的文件&#xff08;图片、视频、pdf等&#xff09;到用户本地并保存起来&#xff0c;小程序也经常需要实现这样的需求。 在传统服务器开发下网上已经有很多关于小程序下载服务端文件的资料了&#xff0c;但是基于云开发的…

【滑动窗口】LeetCode:30串联所有单词的子串

作者推荐 【二叉树】【单调双向队列】LeetCode239:滑动窗口最大值 相关知识点 滑动窗口 题目 给定一个字符串 s 和一个字符串数组 words。 words 中所有字符串 长度相同。 s 中的 串联子串 是指一个包含 words 中所有字符串以任意顺序排列连接起来的子串。 例如&#xff0…