AI大模型探索之路-实战篇3:基于私有模型GLM-企业级知识库开发实战

news2024/12/27 14:00:24

文章目录

  • 前言
  • 概述
  • 一、本地知识库核心架构回顾(RAG)
    • 1. 知识数据向量化
    • 2. 知识数据检索返回
  • 二、大模型选择
    • 1. 模型选择标准
    • 2. ChatGLM3-6B
  • 三、Embedding模型选择
  • 四、改造后的技术选型
  • 五、资源准备
    • 1. 安装git-lfs
    • 2. 下载GLM模型
    • 3. 下载Embeding模型
  • 六、代码落地实践
    • 1. Embedding代码改造
    • 2. LLM代码改造
    • 3. 测试运行
  • 总结


前言

在当今信息时代,数据已经成为企业的核心资产之一。对于许多企业而言,信息安全和私密性是至关重要的,因此对外部服务提供的数据接口存在天然的警惕性。因此常规的基于在线大模型接口落地企业知识库项目,很难满足这些企业的安全需求。面对这样的挑战,只有私有化的部署方案才能满足企业需求;

在实战篇2中《AI大模型探索之路-实战篇2:基于CVP架构-企业级知识库实战落地》,设计实现了基于CVP架构的企业知识库。本篇文章中将对企业知识库进行进一步的改造升级,以满足企业安全性方面的需求;利用开源的GLM大模型,进行私有化部署,重新设计落地整个企业级知识库。

概述

在构建企业知识库的过程中,通常会涉及两种类型的大模型API:嵌入向量化模型和LLM语言分析处理模型。为了确保企业的安全性需求得到满足,我们采用开源的GLM大模型进行私有化部署,并重新设计了整个知识库机器人的架构。接下来,我们将深入探讨这一改造升级过程。
在这里插入图片描述

一、本地知识库核心架构回顾(RAG)

1. 知识数据向量化

首先,通过文档加载器加载本地知识库数据,然后使用文本拆分器将大型文档拆分为较小的块,便于后续处理。接着,对拆分的数据块进行Embedding向量化处理,最后将向量化后的数据存储到向量数据库中以便于检索。
在这里插入图片描述

2. 知识数据检索返回

根据用户输入,使用检索器从向量数据库中检索相关的数据块。然后,利用包含问题和检索到的数据的提示,交给ChatModel / LLM(聊天模型/语言生成模型)生成答案。
在这里插入图片描述

二、大模型选择

1. 模型选择标准

1)开源的:需要选择开源的项目方便自主扩展。
2)高性能的:选择各方面性能指标比较好的,能够提升用户体验。
3)可商用的:在不增加额外成本的前提下,保证模型的商用可行性。
4)低成本部署的:部署时要能够以最低成本代价部署运行知识库,降低公司成本开支。
5)中文支持:需要选择对我母语中文支持性比较好的模型,能够更好的解析理解中文语义。

2. ChatGLM3-6B

ChatGLM3-6B 是 ChatGLM3 系列中的开源模型,也是目前各方面性能比较突出的大模型:
1)更强大的基础模型: ChatGLM3-6B 的基础模型 ChatGLM3-6B-Base 采用了更多样的训练数据、更充分的训练步数和更合理的训练策略。在语义、数学、推理、代码、知识等不同角度的数据集上测评显示,* ChatGLM3-6B-Base 具有在 10B 以下的基础模型中最强的性能*。
2)更完整的功能支持: ChatGLM3-6B 采用了全新设计的 Prompt 格式 ,除正常的多轮对话外。同时原生支持工具调用(Function Call)、代码执行(Code Interpreter)和 Agent 任务等复杂场景。
3)更全面的开源序列: 除了对话模型 ChatGLM3-6B 外,还开源了基础模型 ChatGLM3-6B-Base 、长文本对话模型 ChatGLM3-6B-32K 和进一步强化了对于长文本理解能力的 ChatGLM3-6B-128K。

三、Embedding模型选择

我们要选择一个开源免费的、中文支持性比较好的,场景合适的,各方面排名靠前的嵌入模型。
MTEB排行榜是衡量文本嵌入模型在各种嵌入任务上性能的重要基准;可从排行榜中选相应的Enbedding模型;
在这里插入图片描述

本次从中选择使用比较广泛的 bge-large-zh-v1.5 (同样也是智谱AI的开源模型)
在这里插入图片描述

四、改造后的技术选型

1)LLM模型:ChatGLM3-6B
2)Embedding模型:bge-large-zh-v1.5
3)应用开发框架:LangChain
4)向量数据库:FAISS/pinecone/Milvus
5)WEB框架:streamlit/gradio

五、资源准备

将 Hugging Face Hub 上的预训练模型,下载到本地使用更方便,性能更快。

1. 安装git-lfs

1)需要先安装Git LFS ,Ubuntu系统操作命令:

curl -s https://packagecloud.io/install/repositories/github/git-lfs/script.deb.sh | sudo bash
sudo apt-get install git-lfs

在这里插入图片描述

Centos命令参考:

curl -s https://packagecloud.io/install/repositories/github/git-lfs/script.rpm.sh | sudo bash
sudo yum install git-lfs

2)执行:git lfs install

在这里插入图片描述

2. 下载GLM模型

下载使用huggingface上的大模型

git clone https://huggingface.co/THUDM/chatglm3-6b

在这里插入图片描述

注意:下载后记得和huggingface上的文件列表对比,看看是否有缺失,缺少了单独下载补全。

3. 下载Embeding模型

下载huggingface上的词嵌入模型

git clone https://huggingface.co/BAAI/bge-large-zh-v1.5

在这里插入图片描述

注意:下载后记得和huggingface上的文件列表对比,看看是否有缺失,缺少了单独下载补全。

对比后发现果然少了pytorch_model.bin文件
在这里插入图片描述

手动单独下载pytorch_model.bin文件

wget https://huggingface.co/BAAI/bge-large-zh-v1.5/resolve/main/pytorch_model.bin

结果下载超时了😅😅😅
在这里插入图片描述

后发现国内gitee上有一个hf-models; 专门放的hugginface的模型。😄
在这里插入图片描述

改用gitee地址下载

git clone https://gitee.com/hf-models/bge-large-zh-v1.5.git

果然皇天不负苦心人。。。。😀
在这里插入图片描述

六、代码落地实践

1. Embedding代码改造

将原来的openai嵌入模型

def get_openaiEmbedding_model():
    return OpenAIEmbeddings(openai_api_key=Keys.OPENAI_API_KEY,
                            openai_api_base=Keys.OPENAI_API_BASE)

改造替换为私有嵌入模型:

# 私有嵌入模型部署
embedding_model_dict = {
    #"text2vec3": "shibing624/text2vec-base-chinese",
    #"baaibge": "BAAI/bge-large-zh-v1.5",
    #"text2vec3": "/root/autodl-tmp/text2vec-base-chinese",
    "baaibge": "/root/autodl-tmp/bge-large-zh-v1.5",
}

def get_embedding_model(model_name="baaibge"):
    """
    加载embedding模型
    :param model_name:
    :return:
    """
    encode_kwargs = {"normalize_embeddings": False}
    model_kwargs = {"device": "cuda:0"}
    print(embedding_model_dict[model_name])
    return HuggingFaceEmbeddings(
        model_name=embedding_model_dict[model_name],
        model_kwargs=model_kwargs,
        encode_kwargs=encode_kwargs
    )

2. LLM代码改造

将原来的OpenAI的LLM模型

def get_openai_model():
    llm_model = ChatOpenAI(openai_api_key=Keys.OPENAI_API_KEY,
                           model_name=Keys.MODEL_NAME,
                           openai_api_base=Keys.OPENAI_API_BASE,
                           temperature=0)
    return llm_model

改造为GLM的LLM模型

def get_chatglm3_6b_model(model_path=keys.Keys.CHATGLM3_MODEL_PATH):
    MODEL_PATH = os.environ.get('MODEL_PATH', model_path)
    llm = chatglm3()
    llm.load_model(model_name_or_path=MODEL_PATH)
    return llm

chatglm3说明:由于langchain中暂未集成ChatGLM,因此需要自己封装一个ChatGLM的类

import json
from langchain.llms.base import LLM
from transformers import AutoTokenizer, AutoModel, AutoConfig
from typing import List, Optional
import os,yaml

from models.utils import tool_config_from_file


class chatglm3(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 = []
    tool_names: List = []
    has_search: bool = False

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

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

    def load_model(self, model_name_or_path=None):
        model_config = AutoConfig.from_pretrained(
            model_name_or_path,
            trust_remote_code=True
        )
        self.tokenizer = AutoTokenizer.from_pretrained(
            model_name_or_path,
            trust_remote_code=True
        )
        self.model = AutoModel.from_pretrained(
            model_name_or_path, config=model_config, trust_remote_code=True
        ).half().cuda()

    # def tool_config_from_file(tool_name, directory="../Tool/"):
    #     """search tool yaml and return json format"""
    #     for filename in os.listdir(directory):
    #         if filename.endswith('.yaml') and tool_name in filename:
    #             file_path = os.path.join(directory, filename)
    #             with open(file_path, encoding='utf-8') as f:
    #                 return yaml.safe_load(f)
    #     return None

    def _tool_history(self, prompt: str):
        ans = []
        tool_prompts = prompt.split(
            "You have access to the following tools:\n\n")[0].split("\n\nUse a json blob")[0].split("\n")

        tool_names = [tool.split(":")[0] for tool in tool_prompts]
        self.tool_names = tool_names
        tools_json = []
        for i, tool in enumerate(tool_names):
            tool_config = tool_config_from_file(tool)
            if tool_config:
                tools_json.append(tool_config)
            else:
                ValueError(
                    f"Tool {tool} config not found! It's description is {tool_prompts[i]}"
                )

        ans.append({
            "role": "system",
            "content": "Answer the following questions as best as you can. You have access to the following tools:",
            "tools": tools_json
        })
        query = f"""{prompt.split("Human: ")[-1].strip()}"""
        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"]
            if "tool_call" in content:
                for tool in self.tool_names:
                    if tool in metadata:
                        input_para = content.split("='")[-1].split("'")[0]
                        action_json = {
                            "action": tool,
                            "action_input": input_para
                        }
                        self.has_search = True
                        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|>"]):
        print("======")
        print(prompt)
        print("======")
        if not self.has_search:
            self.history, query = self._tool_history(prompt)
        else:
            self._extract_observation(prompt)
            query = ""
        # print("======")
        # print(history)
        # print("======")
        _, self.history = self.model.chat(
            self.tokenizer,
            query,
            history=self.history,
            do_sample=self.do_sample,
            max_length=self.max_token,
            temperature=self.temperature,
        )
        response = self._extract_tool()
        history.append((prompt, response))
        return response

3. 测试运行

在knowledge_base_v2 下运行:streamlit run knowledge_chatbot.py
在这里插入图片描述

上传知识库,再进行对话测试
在这里插入图片描述


总结

通过私有化部署的企业知识库项目已经成功实践落地。在未来的学习中,我们将进一步探索如何优化整个架构,例如利用微调技术改善知识库性能,优化Prompt的设计,集成更强大的外挂工具以满足特殊业务需求,以及如何加强大模型应用的安全性,包括加入模型审查流程等。

👉系列篇章:AI大模型探索之路-实战篇2:基于CVP架构-企业级知识库实战落地
🔖更多专栏系列文章:AIGC-AI大模型探索之路

文章若有瑕疵,恳请不吝赐教;若有所触动或助益,还望各位老铁多多关注并给予支持。

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

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

相关文章

Java、Spring、Dubbo三者SPI机制原理与区别

Java、Spring、Dubbo三者SPI机制原理与区别 什么是SPI SPI全称为Service Provider Interface&#xff0c;是一种动态替换发现的机制&#xff0c;一种解耦非常优秀的思想&#xff0c;SPI可以很灵活的让接口和实现分离&#xff0c;让api提供者只提供接口&#xff0c;第三方来实…

【嵌入式】keil5安装(同时兼容C51和STM32)

最近在开发STM32的时候&#xff0c;安装Keil5&#xff0c;遇到STM32和C51的共存的问题&#xff0c;在网上找了很多方法&#xff0c;又遇到一些bug&#xff0c;最终还是弄好了。因此将处理的过程记录下来&#xff0c;希望对遇到相同问题的朋友一些启发。 1、下载安装包 Keil P…

判断水仙花数(C语言)

一、N-S流程图&#xff1b; 二、运行结果&#xff1b; 三、源代码&#xff1b; # define _CRT_SECURE_NO_WARNINGS # include <stdio.h>int main() {//初始化变量值&#xff1b;int n 0;int b 0;int s 0;int g 0;int m 0;//提示用户&#xff1b;printf("请输入…

贪吃蛇游戏实现(VS编译环境)

贪吃蛇游戏 &#x1f955;个人主页&#xff1a;开敲&#x1f349; &#x1f525;所属专栏&#xff1a;C语言&#x1f353; &#x1f33c;文章目录&#x1f33c; 0. 前言 1. 游戏背景 2. 实现后游戏画面展示 3. 技术要求 4. Win32 API介绍 4.1 Win32 API 4.2 控制台程序 4.…

开启农业新篇章:山海鲸智慧农业解决方案全面解析

在农业领域&#xff0c;数字化转型已经成为推动农业发展的重要力量。山海鲸&#xff0c;作为农业科技创新的引领者&#xff0c;推出了全新的智慧农业解决方案&#xff0c;通过运用先进的物联网、大数据和人工智能等技术&#xff0c;为农业生产提供智能化、精准化的管理服务&…

c++模拟实现list——详讲双链表--链表

在C语言中我们已经模拟实现了list&#xff0c;现在对比c看看二者的区别 双链表————详讲 个人博客主页&#xff1a; 个人主页 个人码云 码云代码 文章目录 目录 文章目录 ​编辑 前言 一、list是什么&#xff1f; 二、list的使用 三、模拟实现list和搭建list的结构 1.节点结…

孩子用什么样的灯对眼睛没有伤害?分享五款防近视护眼台灯

随着生活条件逐渐提升&#xff0c;对台灯的需求也越来越大&#xff0c;不管在生活中还是工作中&#xff0c;灯具是必不可少的照明工具了&#xff0c;尤其是对于学生而言。很多家长都在寻找孩子用什么样的灯对眼睛没有伤害&#xff1f;建议最好选择一款合格、专业的护眼台灯&…

SpringBootWeb请求

文章目录 前言一、Postman介绍 二、简单参数三、实体参数四、数组集合参数五、日期参数六、JSON参数七、路径参数 前言 在上一篇文章中&#xff0c;已经基于SpringBoot的方式开发一个web应用&#xff0c;浏览器发起请求 /hello 后 &#xff0c;给浏览器返回字符串 “Hello Wor…

STM32之HAL开发——FSMC—扩展外部SRAM

SRAM读写时序 对SRAM进行读写数据时&#xff0c;它各个信号线的时序流程如下图 &#xff08;图一&#xff09;SRAM的读时序 &#xff08;图二&#xff09;SRAM的写时序 流程解释 主机使用地址信号线发出要访问的存储器目标地址&#xff1b;控制片选信号CS1#及CS2#使能存储器…

力扣HOT100 - 25. K 个一组翻转链表

解题思路&#xff1a; class Solution {public ListNode reverseKGroup(ListNode head, int k) {ListNode dum new ListNode(0, head);ListNode pre dum;ListNode end dum;while (end.next ! null) {for (int i 0; i < k && end ! null; i) {end end.next;}if …

生成式AI产品图谱全览:投资人、产品经理必备指南

以下是生成式AI产品图谱的核心要点&#xff0c;供投资人、产品经理等关注生成性AI领域的专业人士参考&#xff1a; 技术领域细分&#xff1a;依据AI技术所处理的媒介类型进行划分&#xff0c;包括文本处理、代码生成、图像处理、语音识别、视频分析、3D模型构建、音乐创作和游戏…

深度学习项目设置超参数 parser or dictionary

见惯了parser 有的人却是用字典读的&#xff1a; 将配置文件config.yaml读取到一个dictionary类型的变量cfg中&#xff0c; 后面出现了这样的语句&#xff1a;cfg["trainer"].get("sup_only_epoch", 1): 意思是&#xff1a;在config.yaml文件里key为trai…

Java客户端如何直接调用es的API

Java客户端如何直接调用es的API 一. 问题二. withJson 前言 这是我在这个网站整理的笔记,有错误的地方请指出&#xff0c;关注我&#xff0c;接下来还会持续更新。 作者&#xff1a;神的孩子都在歌唱 一. 问题 今天做项目的时候&#xff0c;想要直接通过java客户端调用es的api…

PHP反序列化漏洞原理(附带pikachu靶场演示)

1.反序列化概念 序列化:是将变量转换为可保存或传输的字符串的过程;实现函数是serialize()反序列化:就是在适当的时候把这个字符串再转化成原来的变量使用&#xff0c;就是序列化的逆过程。实现函数是unserialize() 直白一点就是&#xff1a;序列化是把对象转换成字节流&#…

基于java+springboot+vue实现的图书借还管理系统小程序(文末源码+Lw+ppt)23-1

摘 要 随着社会的发展&#xff0c;图书借还的管理形势越来越严峻。越来越多的借阅者利用互联网获得信息&#xff0c;但图书借还信息量大。为了方便借阅者更好的获得本图书借还信息&#xff0c;因此&#xff0c;设计一种安全高效的“共享书角”图书借还管理系统极为重要。 为…

python安装pytorch@FreeBSD

先上结论&#xff0c;最后在conda下安装成功了&#xff01; PyTorch是一个开源的人工智能深度学习框架&#xff0c;由Facebook人工智能研究院&#xff08;FAIR&#xff09;基于Torch库开发并维护。PyTorch提供了一个高效、灵活且易于使用的工具集&#xff0c;用于构建和训练深…

Matlab进阶绘图第51期—带填充等高线的三维特征渲染散点图

带填充等高线的三维特征渲染散点图是填充等高线图与特征渲染三维散点图的组合。 其中&#xff0c;填充等高线图与特征渲染的三维散点图的颜色用于表示同一个特征。 由于填充等高线图无遮挡但不直观&#xff0c;特征渲染的三维散点图直观但有遮挡&#xff0c;而将二者组合&…

【MySQL探索之旅】多表查询

&#x1f4da;博客主页&#xff1a;爱敲代码的小杨. ✨专栏&#xff1a;《Java SE语法》 | 《数据结构与算法》 | 《C生万物》 |《MySQL探索之旅》 |《Web世界探险家》 ❤️感谢大家点赞&#x1f44d;&#x1f3fb;收藏⭐评论✍&#x1f3fb;&#xff0c;您的三连就是我持续更…

快速排序题目SelectK问题(力扣75.颜色分类、力扣215.数组中的第K个最大元素、面试题17.14最小K个数)

力扣75.颜色分类 给定一个包含红色、白色和蓝色、共 n 个元素的数组 nums &#xff0c;原地对它们进行排序&#xff0c;使得相同颜色的元素相邻&#xff0c;并按照红色、白色、蓝色顺序排列。 我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。 必须在不使用库内置的 sor…

使用Nexus搭建npm私服库

优质博文&#xff1a;IT-BLOG-CN 【1】下载nexus http://www.sonatype.com/download-oss-sonatype解压到本地即可&#xff1b; 【2】打开nexus-3.2.0-01-win64\nexus-3.2.0-01\bin&#xff1b;打开cmd&#xff08;必须使用cmd&#xff09; 执行nexus.exe /run&#xff1b;需要使…