LLM大语言模型(十五):LangChain的Agent中使用自定义的ChatGLM,且底层调用的是remote的ChatGLM3-6B的HTTP服务

news2025/1/17 21:43:08

背景

本文搭建了一个完整的LangChain的Agent,调用本地启动的ChatGLM3-6B的HTTP server。

为后续的RAG做好了准备。

增加服务端role:observation

ChatGLM3的官方demo:openai_api_demo目录

api_server.py文件

class ChatMessage(BaseModel):
    # role: Literal["user", "assistant", "system", "function"]
    role: Literal["user", "assistant", "system", "function","observation"]
    content: str = None
    name: Optional[str] = None
    function_call: Optional[FunctionCallResponse] = None

修改role列表,增加了“observation”。

这是因为LangChain的Agent执行过程,是ReAct模式,在执行完tool调用后,会生成一个observation角色的消息。

在将LangChain的prompt转换为ChatGLM3的prompt时,也保留了observation角色,但是在服务启动时,接口允许的role却没有observation,会导致接口调用失败。

ChatGLM3-6B 本地HTTP服务启动

参考:

LLM大语言模型(一):ChatGLM3-6B本地部署_llm3 部署-CSDN博客

自定义LLM

自定义LLM内部访问的是HTTP server。

将LangChain Agent的prompt转换为ChatGLM3能识别的prompt。

prompt转换参考:LLM大语言模型(十三):ChatGLM3-6B兼容Langchain的Function Call的一步一步的详细转换过程记录_langchain+chatglm3-CSDN博客

import ast
import requests
import json
from typing import Any, List, Optional
from langchain.llms.base import LLM
from langchain_core.callbacks import CallbackManagerForLLMRun
from output_parse import getFirstMsg,parse_tool


class MyChatGLM(LLM):
    max_token: int = 8192
    # do_sample: bool = False
    do_sample: bool = True
    temperature: float = 0.8
    top_p = 0.8
    tokenizer: object = None
    model: object = None
    history: List = []
    has_search: bool = False
    model_name: str = "chatglm3-6b"
    url: str = "http://localhost:8000/v1/chat/completions"
    tools: List = []

    # def __init__(self):
    #     super().__init__()

    @property
    def _llm_type(self) -> str:
        return "MyChatGLM"


    def _tool_history(self, prompt: str):
            ans = []

            tool_prompts = prompt.split(
                "You have access to the following tools:\n\n")[1].split("\n\nUse a json blob")[0].split("\n")
            tools_json = []

            for tool_desc in tool_prompts:
                name = tool_desc.split(":")[0]
                description = tool_desc.split(", args:")[0].split(":")[1].strip()
                parameters_str = tool_desc.split("args:")[1].strip()
                parameters_dict = ast.literal_eval(parameters_str)
                params_cleaned = {}
                for param, details in parameters_dict.items():
                    params_cleaned[param] = {'description': details['description'], 'type': details['type']}

                tools_json.append({
                    "name": name,
                    "description": description,
                    "parameters": params_cleaned
                })

            ans.append({
                "role": "system",
                "content": "Answer the following questions as best as you can. You have access to the following tools:",
                "tools": tools_json
            })

            dialog_parts = prompt.split("Human: ")
            for part in dialog_parts[1:]:
                if "\nAI: " in part:
                    user_input, ai_response = part.split("\nAI: ")
                    ai_response = ai_response.split("\n")[0]
                else:
                    user_input = part
                    ai_response = None

                ans.append({"role": "user", "content": user_input.strip()})
                if ai_response:
                    ans.append({"role": "assistant", "content": ai_response.strip()})

            query = dialog_parts[-1].split("\n")[0]
            return ans, query

    def _extract_observation(self, prompt: str):
        return_json = prompt.split("Observation: ")[-1].split("\nThought:")[0]
        self.history.append({
            "role": "observation",
            "content": return_json
        })
        return

    def _extract_tool(self):
        if len(self.history[-1]["metadata"]) > 0:
            metadata = self.history[-1]["metadata"]
            content = self.history[-1]["content"]

            lines = content.split('\n')
            for line in lines:
                if 'tool_call(' in line and ')' in line and self.has_search is False:
                    # 获取括号内的字符串
                    params_str = line.split('tool_call(')[-1].split(')')[0]

                    # 解析参数对
                    params_pairs = [param.split("=") for param in params_str.split(",") if "=" in param]
                    params = {pair[0].strip(): pair[1].strip().strip("'\"") for pair in params_pairs}
                    action_json = {
                        "action": metadata,
                        "action_input": params
                    }
                    self.has_search = True
                    print("*****Action*****")
                    print(action_json)
                    print("*****Answer*****")
                    return f"""
Action: 
```
{json.dumps(action_json, ensure_ascii=False)}
```"""
        final_answer_json = {
            "action": "Final Answer",
            "action_input": self.history[-1]["content"]
        }
        self.has_search = False
        return f"""
Action: 
```
{json.dumps(final_answer_json, ensure_ascii=False)}
```"""

    def _call(self, prompt: str, history: List = [], stop: Optional[List[str]] = ["<|user|>"]):
        if not self.has_search:
            self.history, query = self._tool_history(prompt)
            if self.history[0]:
                self.tools = self.history[0]["tools"]
        else:
            self._extract_observation(prompt)
            query = ""
        print(self.history)
        data = {}
        data["model"] = self.model_name
        data["messages"] = self.history
        data["temperature"] = self.temperature
        data["max_tokens"] = self.max_token
        data["tools"] = self.tools
        resp = self.doRequest(data)
        
        msg = {}
        respjson = json.loads(resp)
        if respjson["choices"]:
            if respjson["choices"][0]["finish_reason"] == 'function_call':
                msg["metadata"] = respjson["choices"][0]["message"]["function_call"]["name"]
            else:
                msg["metadata"] = ''
            msg["role"] = "assistant"
            msg["content"] = respjson["choices"][0]["message"]["content"]

        self.history.append(msg)
        print(self.history)
        response = self._extract_tool()
        history.append((prompt, response))
        return response


    def doRequest(self,payload:dict) -> str:
        # 请求头
        headers = {"content-type":"application/json"}
        # json形式,参数用json
        res = requests.post(self.url,json=payload,headers=headers)
        return res.text

定义tool

使用LangChain中Tool的方式:继承BaseTool

Tool实现方式对prompt的影响,参考:LLM大语言模型(十四):LangChain中Tool的不同定义方式,对prompt的影响-CSDN博客

class WeatherInput(BaseModel):
    location: str = Field(description="the location need to check the weather")


class Weather(BaseTool):
    name = "weather"
    description = "Use for searching weather at a specific location"
    args_schema: Type[BaseModel] = WeatherInput

    def __init__(self):
        super().__init__()

    def _run(self, location: str) -> dict[str, Any]:
        weather = {
            "temperature": "20度",
            "description": "温度适中",
        }
        return weather

LangChain Agent调用

设置Agent使用了2个tool:Calculator() Weather(),看是否能正确调用。

    # Get the prompt to use - you can modify this!
    prompt = hub.pull("hwchase17/structured-chat-agent")
    prompt.pretty_print()
    tools = [Calculator(),Weather()]
    # Choose the LLM that will drive the agent
    # Only certain models support this
    # Choose the LLM to use
    llm = MyChatGLM()

    # Construct the agent
    agent = create_structured_chat_agent(llm, tools, prompt)
    # Create an agent executor by passing in the agent and tools
    agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True, handle_parsing_errors=True)

    ans = agent_executor.invoke({"input": "北京天气怎么样?"})
    print(ans)

调用结果:

> Entering new AgentExecutor chain...
[{'role': 'system', 'content': 'Answer the following questions as best as you can. You have access to the following tools:', 'tools': [{'name': 'Calculator', 'description': 'Useful for when you need to calculate math problems', 'parameters': {'calculation': {'description': 'calculation to perform', 'type': 'string'}}}, {'name': 'weather', 'description': 'Use for searching weather at a specific location', 'parameters': {'location': {'description': 'the location need to check the weather', 'type': 'string'}}}]}, {'role': 'user', 'content': '北京天气怎么样?\n\n\n (reminder to respond in a JSON blob no matter what)'}]
[{'role': 'system', 'content': 'Answer the following questions as best as you can. You have access to the following tools:', 'tools': [{'name': 'Calculator', 'description': 'Useful for when you need to calculate math problems', 'parameters': {'calculation': {'description': 'calculation to perform', 'type': 'string'}}}, {'name': 'weather', 'description': 'Use for searching weather at a specific location', 'parameters': {'location': {'description': 'the location need to check the weather', 'type': 'string'}}}]}, {'role': 'user', 'content': '北京天气怎么样?\n\n\n (reminder to respond in a JSON blob no matter what)'}, {'metadata': 'weather', 'role': 'assistant', 'content': "weather\n ```python\ntool_call(location='北京')\n```"}]
*****Action*****
{'action': 'weather', 'action_input': {'location': '北京'}}
*****Answer*****

Action:
```
{"action": "weather", "action_input": {"location": "北京"}}
```{'temperature': '20度', 'description': '温度适中'}

[{'role': 'system', 'content': 'Answer the following questions as best as you can. You have access to the following tools:', 'tools': [{'name': 'Calculator', 'description': 'Useful for when you need to calculate math problems', 'parameters': {'calculation': {'description': 'calculation to perform', 'type': 'string'}}}, {'name': 'weather', 'description': 'Use for searching weather at a specific location', 'parameters': {'location': {'description': 'the location need to check the weather', 'type': 'string'}}}]}, {'role': 'user', 'content': '北京天气怎么样?\n\n\n (reminder to respond in a JSON blob no matter what)'}, {'metadata': 'weather', 'role': 'assistant', 'content': "weather\n ```python\ntool_call(location='北京')\n```"}, {'role': 'observation', 'content': "{'temperature': '20度', 'description': '温度适中'}"}]
[{'role': 'system', 'content': 'Answer the following questions as best as you can. You have access to the following tools:', 'tools': [{'name': 'Calculator', 'description': 'Useful for when you need to calculate math problems', 'parameters': {'calculation': {'description': 'calculation to perform', 'type': 'string'}}}, {'name': 'weather', 'description': 'Use for searching weather at a specific location', 'parameters': {'location': {'description': 'the location need to check the weather', 'type': 'string'}}}]}, {'role': 'user', 'content': '北京天气怎么样?\n\n\n (reminder to respond in a JSON blob no matter what)'}, {'metadata': 'weather', 'role': 'assistant', 'content': "weather\n ```python\ntool_call(location='北京')\n```"}, {'role': 'observation', 'content': "{'temperature': '20度', 'description': '温度适中'}"}, {'metadata': '', 'role': 'assistant', 'content': '根据最新的气象数据 
,北京的天气情况如下:温度为20度,天气状况适中。'}]

Action:
```
{"action": "Final Answer", "action_input": "根据最新的气象数据,北京的天气情况如下:温度为20度,天气状况适中。"}
```

> Finished chain.
{'input': '北京天气怎么样?', 'output': '根据最新的气象数据,北京的天气情况如下:温度为20度,天气状况适中。'}

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

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

相关文章

英语学习笔记9——How are you today?

How are you today? 你好吗&#xff1f; 词汇 Vocabulary well adj. 好的 n. 井 fine adj. 美好的 两个方面&#xff1a;天气、身体。 搭配&#xff1a;a fine day 晴朗的一天    It’s a fine day today. 今天很晴朗。 good adj. 好的 口语偏多 搭配&#xff1a;Good jo…

【Python技术】使用akshare、pandas高效复盘每日涨停板行业分析

作为一个程序员宝爸&#xff0c;每天的时间很宝贵&#xff0c;工作之余除了辅导孩子作业&#xff0c;就是补充睡眠。 怎么快速高效的进行当天A股涨停板的复盘&#xff0c;便于第二天的跟踪。这里简单写个示例&#xff0c; 获取当天连涨数排序&#xff0c;以及所属行业排序。 …

服务器数据恢复—RAID5磁盘阵列两块盘离线的数据恢复过程

服务器故障&#xff1a; 服务器中有一组由多块硬盘组建的raid5磁盘阵列&#xff0c;服务器阵列中2块硬盘先后掉线导致服务器崩溃。 服务器数据恢复过程&#xff1a; 1、将故障服务器中所有磁盘编号后取出&#xff0c;由硬件工程师对掉线的两块磁盘进行物理故障检测&#xff0c…

AGI 时代,Rust与Python谁是未来的语言?

随着 Rust 在开发者社区中越来越受欢迎&#xff0c;有必要问一下&#xff0c;Rust 会取代 Python 吗&#xff1f;哪一款最适合您&#xff1f;而且&#xff0c;你应该开始学习 Rust 吗&#xff1f;本文将对 Rust 与 Python 进行全面比较。读完本文后&#xff0c;你将对是否要开始…

uniapp:抖音PK进度条(nvue)

nvue中,仿抖音PK进度条效果, <template><view class="index" :style="{width:windowWidth+px,height:index_windowHeight+px,paddingTop:windowTop+px}"><view class="pk"><text class="pk_jindu_left_val fsz-24 …

基于SSM框架弹幕视频网站

采用技术 基于SSM框架弹幕视频网站的设计与实现~ 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;SpringMVCMyBatis 工具&#xff1a;IDEA/Ecilpse、Navicat、Maven 页面展示效果 前台首页 首页 登录 视频信息 商品信息 个人信息 用户模块 我…

ntfs文件系统的优势 NTFS文件系统的特性有哪些 ntfs和fat32有什么区别 苹果电脑怎么管理硬盘

对于数码科技宅在新购得磁盘之后&#xff0c;出于某种原因会在新的磁盘安装操作系统。在安装操作系统时&#xff0c;首先要对磁盘进行分区和格式化&#xff0c;而在此过程中&#xff0c;操作者们需要选择文件系统。文件系统也决定了之后操作的流程程度&#xff0c;一般文件系统…

图像处理的一些操作(3)

图像处理 13.创建主窗口与子图13.1导入模块 加载图片13.2创建窗口13.3创建子图数组 14.定义png图像文件路径15.提取指定帧图像16.图像旋转17.伽马值校正18.检查图像对比度19.强度缩放20 . 绘制直方图20.三通道彩色直方图21.算子21.1Sobel22.2 prewitt 22.滤波器23.绘制图形23.1…

python实现背单词程序

欢迎关注我👆,收藏下次不迷路┗|`O′|┛ 嗷~~ 目录 一.前言 二.代码 三.使用 四.分析 一.前言 背单词是学习英语的一个重要环节,它有很多好处,以下是其中一些主要的好处: 提高词汇量

数据结构之单链表的基本操作

目录 一.定义一个单链表 二.实现基本操作 1&#xff09;链表的打印 2&#xff09;链表的尾插 3&#xff09;链表的头插 4&#xff09;链表的尾删 5&#xff09;链表的头删 6&#xff09; 链表的查找 7&#xff09;在指定位置之前插入数据 8&#xff09;在指定位置之…

内网安全综合管理系统是什么 | 好用的内网安全管理系统有哪些

内网安全综合管理系统是指一种集成终端管理、网络管理、内容管理、资产管理等功能的综合性安全管理系统。它主要对内网上的主机进行统一安全管理&#xff0c;包括对网络主机用户操作实施监督控制&#xff0c;并对主机中的安全软件&#xff08;如主机入侵监测系统、主机防火墙和…

国内首发 | CSA大中华区启动《AI安全产业图谱(2024)》调研

在人工智能&#xff08;AI&#xff09;技术的快速发展浪潮中&#xff0c;AI安全已成为全球关注的焦点。为应对AI安全带来的挑战&#xff0c;确保AI技术的健康发展&#xff0c;全球范围内的研究机构、企业和技术社区都在积极探索解决方案。 在这一背景下&#xff0c;CSA大中华区…

git 常用命令 git怎么撤销命令 持续更新中!!!!

基本流程 # 拉取仓库 git clone 仓库地址 # 拉取最新版本 git pull # 本地提交 git add . git commit -m "本次提交信息&#xff01;" # 推送上云 git push分支 # 创建分支 git checkout -b cart # 删除本机的分支 git branch -d cart # 切换分支 本地切换到主分支…

将本地托管模型与 Elastic AI Assistant 结合使用的好处

作者&#xff1a;来自 Elastic James Spiteri, Dhrumil Patel 当今公共部门组织利用生成式人工智能解决安全挑战的一种方式。 凭借其筛选大量数据以发现异常模式的能力&#xff0c;生成式人工智能现在在帮助团队保护其组织免受网络威胁方面发挥着关键作用。 它还可以帮助安全专…

LayaAir引擎全面支持淘宝小游戏、小程序、小部件的发布

在最新的3.1版本和2.13版本中&#xff0c;LayaAir引擎已经全面支持了淘宝小游戏、小程序和小部件的开发和发布。这一重大更新&#xff0c;标志着LayaAir引擎与电商巨头阿里巴巴旗下的淘宝平台形成生态合作&#xff0c;在为广大开发者提供更加强大、高效的跨平台开发工具和解决方…

train_gpt2_fp32.cu

源程序 llm.c/test_gpt2_fp32.cu at master karpathy/llm.c (github.com) #include <stdio.h> #include <stdlib.h> #include <math.h> #include <time.h> #include <assert.h> #include <float.h> #include <string.h> #include…

06.命令的组合使用

命令的组合使用 1.查询当前整个系统每个进程的线程数 我们经常遇到这样的问题&#xff0c;比如某台服务器的CPU 使用率飙升&#xff0c;通过top命令查看是某个程序&#xff08;例如java&#xff09;占用的cpu比较大&#xff0c;现在需要查询java各个进程下的线程数情况。可以通…

各种依赖注入和分层解耦

分层解耦 三层架构 controller:控制层&#xff0c;接收前端发送的请求&#xff0c;对请求进行处理&#xff0c;并响应数据 service:业务逻辑层&#xff0c;处理具体业务的逻辑 dao:数据访问&#xff0c;负责数据访问操作&#xff0c;包括数据的增、删、改、查 流程为&…

初阶C语言(8) - 实用的调试技巧

1. 什么是bug? bug 是计算机领域专业术语&#xff0c;是计算机在硬件、软件、协议和系统安全策略上存在的缺陷&#xff0c;攻击者能够在未授权情况下访问的危害&#xff0c;世界最早的一批程序设计师之一&#xff0c;美国的葛丽丝霍波在调试设备时出现故障&#xff0c;拆开继电…

MySQL——变量的定义与使用

新建链接&#xff0c;自带world数据库&#xff0c;里面自带city表格。 DQL # MySQL变量的定义与使用 #1、不允许数字作为开头 #2、只能用_或$符号&#xff0c;不允许使用其他符号 #3、不允许使用关键字或保留字 set userName小可爱; select userName; #标识符只影响当前查询#…