Chatglm实现agent控制

news2024/11/26 18:31:25

背景:

这个系列文章,会从LLM搭建应用生态角度来写。从0到1训练一个大的通用的模型对于大部分人和团队来讲是不现实的。重资金,重技术含量、重投入这几个门槛可以把很多团队直接劝退。那么在LLM蓬勃发展的时候我们可以做些什么呢,是否可以围绕LLM来搭建生态,搭建应用市场;针对LLM模型的不足做一些小的设计训练呢。这个其实很像发动机引擎,无论是汽车发动机引擎、柴油发动机引擎还是航空发动机引擎,能造的其实全球也不超过20家。但是围绕着这些引擎,延展出了汽车、农用器械、矿产机械、船舶、海洋、火车、飞机.....一系列的产业链、产业生态,编织出了现代工业的网。同理不从0造LLM引擎,深入的理解引擎原理,基于LLM引擎搭建应用生态是否也是可行呢。这一系列的文章尝试来做这件事情,如何打造LLM的生态,如何把LLM引擎能力引出、如何做各种转接头把LLM能力转成适配的动力。

这篇文章主要会介绍下面3部分:

1.Chatglm转成适配langchian的llm模式

2.搭建定制化的工具集

3.基于llm、工具结合构建有一定自主性Agent

代码实现

model

实现思路,继承langchain.llm.base中的LLM类,重写_call方法、_identifying_params属性类,load_model方法用transformer的AutoModel.from_pretrained来加载chatglm模型,把实例化的model传给LLM的model,chatglm基于prompt生成的逻辑封装在generate_resp方法,LLM的_call方法调用generate_resp获取生成逻辑。
 

### define llm ###

from typing import List, Optional, Mapping, Any
from functools import partial

from langchain.llms.base import LLM
from langchain.callbacks.manager import CallbackManagerForLLMRun
from langchain.callbacks.streaming_stdout import StreamingStdOutCallbackHandler

from transformers import AutoModel, AutoTokenizer


### chatglm-6B llm ###
class ChatGLM(LLM):

    model_path: str
    max_length: int = 2048
    temperature: float = 0.1
    top_p: float = 0.7
    history: List = []
    streaming: bool = True
    model: object = None
    tokenizer: object = None

    @property
    def _llm_type(self) -> str:
        return "chatglm-6B"

    @property
    def _identifying_params(self) -> Mapping[str, Any]:
        """Get the identifying parameters."""
        return {
            "model_path": self.model_path,
            "max_length": self.max_length,
            "temperature": self.temperature,
            "top_p": self.top_p,
            "history": [],
            "streaming": self.streaming
        }

    def _call(
        self,
        prompt: str,
        stop: Optional[List[str]] = None,
        run_manager: Optional[CallbackManagerForLLMRun] = None,
        add_history: bool = False
    ) -> str:
        if self.model is None or self.tokenizer is None:
            raise RuntimeError("Must call `load_model()` to load model and tokenizer!")

        if self.streaming:
            text_callback = partial(StreamingStdOutCallbackHandler().on_llm_new_token, verbose=True)
            resp = self.generate_resp(prompt, text_callback, add_history=add_history)
        else:
            resp = self.generate_resp(self, prompt, add_history=add_history)

        return resp

    def generate_resp(self, prompt, text_callback=None, add_history=True):
        resp = ""
        index = 0
        if text_callback:
            for i, (resp, _) in enumerate(self.model.stream_chat(
                self.tokenizer,
                prompt,
                self.history,
                max_length=self.max_length,
                top_p=self.top_p,
                temperature=self.temperature
            )):
                if add_history:
                    if i == 0:
                        self.history += [[prompt, resp]]
                    else:
                        self.history[-1] = [prompt, resp]
                text_callback(resp[index:])
                index = len(resp)
        else:
            resp, _ = self.model.chat(
                self.tokenizer,
                prompt,
                self.history,
                max_length=self.max_length,
                top_p=self.top_p,
                temperature=self.temperature
            )
            if add_history:
                self.history += [[prompt, resp]]
        return resp

    def load_model(self):
        if self.model is not None or self.tokenizer is not None:
            return
        self.tokenizer = AutoTokenizer.from_pretrained(self.model_path, trust_remote_code=True)
        self.model = AutoModel.from_pretrained(self.model_path, trust_remote_code=True).half().cuda().eval()

        def set_params(self, **kwargs):
        for k, v in kwargs.items():
        if k in self._identifying_params:
        self.k = v

llm = ChatGLM(model_path="../ChatGLM2-6B/llm_model/models--THUDM--chatglm2-6b/snapshots/8eb45c842594b8473f291d0f94e7bbe86ffc67d8")
llm.load_model()

Tool集合实现

tool集合包括了两层:工具api层,这一层主要是实现工具的逻辑层功能;业务调用工具逻辑层,这一层主要就是对工具能力根据实际业务来适配的策略逻辑层。下面我们会通过一个搜索API根据各种业务需要来封不同工具集来做示意。再强调一遍这部分只是做个示意具体的其他工具集合的实现会在后面文章介绍,比如:向量知识库、只是图谱、可视化工具、摘要抽取、意图理解、文本生成图片、图片内容理解....

搜索工具API层实现代码,Rapid来实现联网搜索即时信息。

import requests

#这个RapidAPIKey,各位自己去注册下就有了
RapidAPIKey = ""

class DeepSearch:
    def search(query: str = ""):
        query = query.strip()

        if query == "":
            return ""

        if RapidAPIKey == "":
            return "请配置你的 RapidAPIKey"

        url = "https://bing-web-search1.p.rapidapi.com/search"

        querystring = {"q": query,
                "mkt":"zh-cn","textDecorations":"false","setLang":"CN","safeSearch":"Off","textFormat":"Raw"}

        headers = {
            "Accept": "application/json",
            "X-BingApis-SDK": "true",
            "X-RapidAPI-Key": RapidAPIKey,
            "X-RapidAPI-Host": "bing-web-search1.p.rapidapi.com"
        }

        response = requests.get(url, headers=headers, params=querystring)

        data_list = response.json()['value']

        if len(data_list) == 0:
            return ""
        else:
            result_arr = []
            result_str = ""
            count_index = 0
            for i in range(6):
                item = data_list[i]
                title = item["name"]
                description = item["description"]
                item_str = f"{title}: {description}"
                result_arr = result_arr + [item_str]

            result_str = "\n".join(result_arr)
            return result_str

业务调用工具逻辑层,对搜索API结合业务需要用LLMchain和llm能力来做检索前理解、检索后信息整合。Tool的实现继承langchain.tools的BaseTool来实现,通过回调方式来实现把搜索API能力即成到后续Agent里面。


from langchain.tools import BaseTool
from langchain.callbacks.manager import (
    AsyncCallbackManagerForToolRun,
    CallbackManagerForToolRun,
)

class functional_Tool(BaseTool):
    name: str = ""
    description: str = ""
    url: str = ""

    def _call_func(self, query):
        raise NotImplementedError("subclass needs to overwrite this method")

    def _run(
            self,
            query: str,
            run_manager: Optional[CallbackManagerForToolRun] = None,
    ) -> str:
        return self._call_func(query)

    async def _arun(
            self,
            query: str,
            run_manager: Optional[AsyncCallbackManagerForToolRun] = None,
    ) -> str:
        raise NotImplementedError("APITool does not support async")
        


        
class Product_knowledge_Tool(functional_Tool):
    llm: BaseLanguageModel

    # tool description
    name = "生成文案,产品卖点查询"
    description = "用户输入的是生成内容问题,并且没有描述产品具体卖点无法给出答案,可以用互联网检索查询来获取相关信息"
    
    # QA params
    qa_template = """
    请根据下面信息```{text}```,回答问题:{query}
    """
    prompt = PromptTemplate.from_template(qa_template)
    llm_chain: LLMChain = None
    '''
    def _run(self, query: str, run_manager: Optional[CallbackManagerForToolRun] = None):
        self.get_llm_chain()
        context = DeepSearch.search(query = query)
        resp = self.llm_chain.predict(text=context, query=query)
        return resp
    '''
    
    def _call_func(self, query) -> str:
        self.get_llm_chain()
        context = DeepSearch.search(query = query)
        resp = self.llm_chain.predict(text=context, query=query)
        return resp
    

    def get_llm_chain(self):
        if not self.llm_chain:
            self.llm_chain = LLMChain(llm=self.llm, prompt=self.prompt)
            
            
class Actor_knowledge_Tool(functional_Tool):
    llm: BaseLanguageModel

    # tool description
    name = "生成文案,人群画像查询"
    description = "用户输入的是内容生成问题,并且没有描述人群画像和人群的喜好无法给出答案,可以用互联网检索查询来获取相关信息"
    
    # QA params
    qa_template = """
    请根据下面信息```{text}```,回答问题:{query}
    """
    prompt = PromptTemplate.from_template(qa_template)
    llm_chain: LLMChain = None
    '''
    def _run(self, query: str, run_manager: Optional[CallbackManagerForToolRun] = None):
        self.get_llm_chain()
        context = DeepSearch.search(query = query)
        resp = self.llm_chain.predict(text=context, query=query)
        return resp
    '''
    
    def _call_func(self, query) -> str:
        self.get_llm_chain()
        context = DeepSearch.search(query = query)
        resp = self.llm_chain.predict(text=context, query=query)
        return resp
    

    def get_llm_chain(self):
        if not self.llm_chain:
            self.llm_chain = LLMChain(llm=self.llm, prompt=self.prompt)
            
class Search_www_Tool(functional_Tool):
    llm: BaseLanguageModel

    # tool description
    name = "互联网检索查询"
    description = "用户输入的问题是一些常识,实事问题,无法直接给出答案,可以用互联网检索查询来获取相关信息"
    
    # QA params
    qa_template = """
    请根据下面信息```{text}```,回答问题:{query}
    """
    prompt = PromptTemplate.from_template(qa_template)
    llm_chain: LLMChain = None
    '''
    def _run(self, query: str, run_manager: Optional[CallbackManagerForToolRun] = None):
        self.get_llm_chain()
        context = DeepSearch.search(query = query)
        resp = self.llm_chain.predict(text=context, query=query)
        return resp
    '''
    
    def _call_func(self, query) -> str:
        self.get_llm_chain()
        context = DeepSearch.search(query = query)
        resp = self.llm_chain.predict(text=context, query=query)
        return resp
    

    def get_llm_chain(self):
        if not self.llm_chain:
            self.llm_chain = LLMChain(llm=self.llm, prompt=self.prompt)

agent

agent这部分的代码逻辑就是:

1.把封装好的chatglm的llm控制引擎即成进来作为控制和管理核心

2.把toolset融合进来,根据llm控制引擎需要扩展能力,plan方法的AgentAction实现

3.根据业务需来设计引擎控制逻辑,这部分在下面代码对应的就是通过intent_template来写策略,通过choose_tools来实现根据用户输入选择调用tool

from typing import List, Tuple, Any, Union
from langchain.schema import AgentAction, AgentFinish
from langchain.agents import BaseSingleActionAgent
from langchain import LLMChain, PromptTemplate
from langchain.base_language import BaseLanguageModel


class IntentAgent(BaseSingleActionAgent):
    tools: List
    llm: BaseLanguageModel
    intent_template: str = """
    现在有一些意图,类别为{intents},你的任务是根据用户的query内容找到最匹配的意图类;回复的意图类别必须在提供的类别中,并且必须按格式回复:“意图类别:<>”。
    
    举例:
    问题:请给年轻用户生成10条金融产品营销文案?
    意图类别:产品卖点查询
    
    问题:世界最高峰?
    意图类别:互联网检索查询

    问题:“{query}”
    """
    prompt = PromptTemplate.from_template(intent_template)
    llm_chain: LLMChain = None

    def get_llm_chain(self):
        if not self.llm_chain:
            self.llm_chain = LLMChain(llm=self.llm, prompt=self.prompt)

    def choose_tools(self, query) -> List[str]:
        self.get_llm_chain()
        tool_names = [tool.name for tool in self.tools]
        tool_descr = [tool.name + ":" + tool.description for tool in self.tools]
        resp = self.llm_chain.predict(intents=tool_names, query=query)
        select_tools = [(name, resp.index(name)) for name in tool_names if name in resp]
        select_tools.sort(key=lambda x:x[1])
        return [x[0] for x in select_tools]

    @property
    def input_keys(self):
        return ["input"]

    def plan(
            self, intermediate_steps: List[Tuple[AgentAction, str]], **kwargs: Any
    ) -> Union[AgentAction, AgentFinish]:
        # only for single tool
        tool_name = self.choose_tools(kwargs["input"])[0]
        return AgentAction(tool=tool_name, tool_input=kwargs["input"], log="")
        
    async def aplan(
            self, intermediate_steps: List[Tuple[AgentAction, str]], **kwargs: Any
    ) -> Union[List[AgentAction], AgentFinish]:
        raise NotImplementedError("IntentAgent does not support async")

自定义例子

上面部分的代码,已经把围绕llm引擎打造toolset的基本框架搭建好了。当然世实际的业务应用还需要做亿点点细节开发。下面部分代码就是用一个例子来测试下框架效果。
 

from langchain.agents import AgentExecutor
from custom_search import DeepSearch

tools = [Search_www_Tool(llm = llm), Actor_knowledge_Tool(llm=llm),Search_www_Tool(llm = llm),Product_knowledge_Tool(llm=llm)]

agent = IntentAgent(tools=tools, llm=llm)

agent_exec = AgentExecutor.from_agent_and_tools(agent=agent, tools=tools, verbose=True, max_iterations=1)
agent_exec.run("生成10条描述金融产品卖点的营销文案")
agent_exec.run("四川高考分数线?")

小结:

介绍了围绕LLM引擎打造贴合实际应用生态的重要意义。给出了一个围绕chatglm搭建LLM引擎,围绕chatglm引擎用搜索api打造toolset,通过prompt+LLMchain方式来实现控制策略、实现toolset包括业务逻辑的框架。

1.围绕LLM引擎打造业务生态的意义重大,产业价值和可拓展空间可能会大于LLM引擎本身

2.通过langchain框架,打造chatglm的应用生态框架

3.给出了一个实际测试例子

代码实现在github:https://github.com/liangwq/Chatglm_lora_multi-gpu/tree/main/APP_example/chatglm_agent

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

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

相关文章

C语言程序设计——字符、字符串、内存函数

一、长度不受限的字符串函数 1. strlen size_t strlen(const char* str); 功能&#xff1a;求字符串长度 &#xff08;1&#xff09;字符串以\0作为结束标志&#xff0c;strlen函数返回的是在字符串中\0之前出现的字符个数&#xff08;不包含\0&#xff09;。 &#xff08…

【每日运维】大文件的分割与合并

产生背景 特殊单位需要将文件刻盘带入&#xff0c;并且刻盘有大小限制&#xff0c;所以有了这个需求 推荐方法 个人电脑上使用 split 命令指定大小分割Linux 服务上使用 cat 命令进行合并使用 md5sum 命令校验包的完整性 方法演示 需要将一个按照100M分割后刻盘导入 在个…

数据结构 ~ 树

什么是树 - tree 一种分层数据的抽象模型&#xff1b; 如&#xff1a;DOM、级联选择、树形控件&#xff0c;js 中没有树 可以用 Object 构建树&#xff1a; const tree {val: a,children: [{val: a-1,children: [{val: a-1-1,children: []}]},{val: a-2,children: [{val: a…

mapbox绘制多边形

1、实现效果 请忽略马赛克 2、实现思路 绘制一个填充的多边形&#xff0c;再描个边框。 3、实现代码 绘制多边形函数 drawPolygon() {map.addSource(maine, {type: geojson,data: https://asc-test1.oss-cn-beijing.aliyuncs.com/2023/07/05/45f6bd80f2f34d79b3e457b31ec5d…

云原生网关如何实现安全防护能力

作者&#xff1a;刘晓瑞(钰诚) 云原生网关&#xff1a;将安全、流量和微服务三合一 作为面向南北向的公网网关&#xff0c;使用 Waf 防护异常流量是很常规的需求&#xff0c;而且随着互联网环境变得越来越复杂&#xff0c;用户对防护的诉求是持续增强的&#xff0c;常规做法是…

需要我怎么帮你?关于维护Nutsdb开源社区的思考

背景 近来有人问我打算怎么继续维护Nutsdb社区&#xff0c;他们当中不乏有开源项目的狂热爱好者&#xff0c;发起了好几个几千star的开源项目&#xff0c;也有对Nutsdb感兴趣的&#xff0c;想来问问后续的计划。这不禁让我回想到从刚开始参与这个项目到最近这段时间一个人维护…

三维重建的工作

文章目录 一、北京&#xff1a;二、广州&#xff1a;三、深圳&#xff1a; 一、北京&#xff1a; 链接 三维重建技术是自动驾驶领域4D真值数据生成的核心基础能力。融合LiDAR、Camera、IMU、轮速计等传感器数据像BlockNeRF一样重建城市级别逼真精细的三维场景&#xff0c;将…

ArcGIS Pro 矢量数据的空间校正

GIS 数据通常来自多个源。当数据源之间出现不一致时,有时需要执行额外的工作以将新数据集与其余数据进行整合。相对于基础数据而言,一些数据会在几何上发生变形或旋转。 在编辑环境中,空间校正提供用于对齐和整合数据的交互式方法。 空间校正可执行的一些任务包括:将数据…

SQLite数据库安装

安装方式一&#xff1a; sudi apt-get install sqlite 安装方式二&#xff1a; https://www.sqlite.org/download.html 1. 把下载的文件 sqlite-autoconf-3420000.tar.gz 上传到开发板 2. tar xvf sqlite-autoconf-3420000.tar.gz 解压 3. cd sqlite-autoconf-3420000 进入…

[Mysql] 索引失效的情况详解~

一条查询语句走了索引和没走索引的查询效率是非常大的&#xff0c;但有很多情况导致我们的索引失去效果。这里总结一下常见的索引失效的情况~ 数据准备 我们准备一张简单的学生来做演示。 CREATE TABLE student (id int NOT NULL COMMENT id,name varchar(255) COLLATE utf8…

基于weka手工实现支持向量机smo算法

关于svm机器学习模型&#xff0c;我主要学习的是周志华老师的西瓜书&#xff08;《机器学习》&#xff09;&#xff1b; 但是西瓜书中对于参数优化&#xff08;即&#xff1a;Sequential Minimal Optimization&#xff0c;smo算法&#xff09;部分讲解的十分简略&#xff0c;看…

Linux运维面试题(三)之shell编程类

Linux运维面试题&#xff08;三&#xff09;之shell编程类 文本截取有一个b.txt文本&#xff0c;要求将所有域名截取出来&#xff0c;并统计重复域名出现的次数统计当前服务器正在连接的IP地址&#xff0c;并按连接次数排序&#xff08;cut不能以空格做分隔符&#xff09; 随机…

Vis相关的期刊会议

中国计算机学会推荐国际学术会议和期刊目录 文档&#xff0c; 下载 link&#xff1a;CCF推荐国际学术刊物目录-中国计算机学会 一.可视化方向文章 1.IEEE VIS&#xff0c;是由 IEEE Visualization and Graphics Technical Committee(VGTC) 主办的数据可视化领域的顶级会议&a…

VMware桥接模式无法识别英特尔AX200无线网卡解决办法

1.先到英特尔网站下载最新驱动&#xff0c;更新网卡驱动适用于 Intel 无线网络卡的 Windows 10 和 Windows 11* Wi-Fi 驱动程序 2.到控制面板查看无线网卡属性是否有下图组件 没有的话&#xff0c;依次操作 安装---服务---添加---从磁盘安装----浏览--进入VMware安装目录&…

Linux系统的目录结构

目录 一、Linux系统使用注意 1、Linux严格区分大小写 2、Linux文件"扩展名" 3、Linux中所有内容以文件形式保存 4、Linux中存储设备都必须在挂载之后才能使用 二、目录结构 1、Linux分区与Windows分区 2、Linux系统文件架构 3、Linux系统的文件目录用途 一、…

Docker【安装与基本使用】

【1】Docker的安装 注意&#xff1a;如果之前安装过docker其他版本&#xff0c;请删除干净。 docker-01 10.0.0.51 2G docker-02 10.0.0.52 2G docker-01 [rootdocker-01 ~]# cp -rp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime cp: overwrite ‘/etc/localtime’? …

(中等)LeetCode146 LRU 缓存 Java

本题有两种实现操作&#xff0c;需要使用到一个哈希表和一个双向链表。在Java语言中&#xff0c;有一种结合了哈希表和双向链表的数据结构&#xff0c;LinkedHashMap import java.util.LinkedHashMap; import java.util.Map;class LRUCache extends LinkedHashMap<Integer,…

网络运维能转型到系统运维吗?

很多网工处于刚起步的初级阶段&#xff0c;各大公司有此专职&#xff0c;但重视或重要程度不高&#xff0c;可替代性强&#xff1b;小公司更多是由其它岗位来兼顾做这一块工作&#xff0c;没有专职&#xff0c;也不可能做得深入。 现在开始学习入门会有一些困难&#xff0c;不…

【深度学习】日常笔记13

前向传播&#xff08;forward propagation或forward pass&#xff09;指的是&#xff1a;按顺序&#xff08;从输⼊层到输出层&#xff09;计算和存储神经⽹络中每层的结果。 绘制计算图有助于我们可视化计算中操作符和变量的依赖关系。下图是与上述简单⽹络相对应的计算图&…

【C语言】深入了解分支和循环语句

&#x1f341; 博客主页:江池俊的博客 &#x1f341;收录专栏&#xff1a;C语言——探索高效编程的基石 &#x1f341; 如果觉得博主的文章还不错的话&#xff0c;请点赞&#x1f44d;收藏&#x1f31f; 三连支持一下博主&#x1f49e; &#x1f4ab;“每一天都是一个全新的机会…