1. 前言
本文紧张论证从零开始搭建爬虫->向量数据库->LLM大模子知识库过程,文章中不依靠任何爬虫、LangChain、Chat GLM等框架,从最原始角度平常易懂、直观的解读大模子与向量数据库联合过程,给各人提供现阶段热门企业大模子办理方案创建思绪和方向。
如今盛行的中文开源大模子非ChatGLM(智普)、baichuan(百川)等莫属。固然认知本事赶不上ChatGPT 3.5,但是它的开源吸引了广大的AI研究者。
如今大语言模子存在最大的标题在于:
1、研究本钱高,假如搭建一个13B以及以上的模子,全量运行必要24GB以上显存,假如举行量化质量又达不到要求,前期研究就要投入大量本钱而且假如有多个LLM项目并行开辟就会碰到项目组之间抢资源标题;
2、训练本钱高,回报率随机,对于广大举行“炼丹”的“神仙”们都知道,炼丹最大标题在于整理的训练集、训练轮数及各种参数大概导致炼出废丹,而且知识的日益更新,假如要更新知识就要重新训练;
3、胡胡语言(理想),理想就是你问一个标题,它有板有眼儿的给你答复,你不是专业人士大概就会被它的答复误导了。LLM的理想并非真正的理想,而是由其训练方式和数据源所决定的,LLM通过大量的互联网文本数据举行训练,这些数据包罗了各种话题和语境。
以上就是如今LLM模子常见的标题,对于模子的研发者和利用者都是头痛标题。针对企业级AI应用,如今有个各人探索的方案就是向量数据库+LLM大模子联合,办理研究本钱、训练及胡胡语言标题,通过知识库中精确的内容补充数据的不敷导理想。
其原理就是将知识要点存储到向量数据库,在提问时通太过词或大模子对提问内容举行分解,从向量数据库提取出关键内容,然后再将内容喂给LLM模子,从而得到想要的答案,从而实现了AI数据库的可维护性,这个模子可以用于OpenAI API也可以用于LLM私有化模子。
接下来,我们从探索的角度来研究向量数据库+LLM大模子的应用(这里倒霉用LangChain 东西,由于他们封装很深不适当从原理上研究探索)
2. 实现目的
本次目的是搭建一个腾讯云向量数据库LLM知识问答体系:
1、搭建向量数据库(这里利用 腾讯云向量数据库Tencent Cloud VectorDB);
2、开辟知识库收罗及存储工具
(1) 编写爬虫工具,实现知识库数据爬取;
(2) 编写数据存储服务
3、开辟LLM大模子对话功能,并将向量数据库知识库加入到大模子对话中来;
环境准备:
Python:3.10
LLM:ChatGLM 3
运行环境:Windows11 WSL2 Ubuntu22.04
开辟工具:VsCode
3. 开辟爬虫及知识库存储工具
3.1. 环境搭建
创建一个独立的python假造环境,内容存储再venv中
激活venv实行:
- vectorenv\Scripts\activate.bat
复制代码
3.2. 爬虫工具开辟
确定要爬取的URL所在:
https://cloud.tencent.com/document/product/1709
编写Crawling.py爬虫,爬取向量知识库内容
引入依靠包:
- import requests
- import json
- import re
- from bs4 import BeautifulSoup
复制代码
引用依靠:
- pip install bs4
- pip install lxml
复制代码 界说干系变量:
- seed = "https://cloud.tencent.com/document/product/1709"
- baseUrl="https://cloud.tencent.com"
- appendUrlList=[]
- appendDataList = []
复制代码
获取栏目所及子栏目全部URL所在,这里通过textarea的J-qcSideNavListData CSS举行定位,并从文本中得到JSON没形貌信息。
- def getCrawl(seed):
- seeds = []
- seeds.append(seed)
- textdata = requests.get(seed).text
- soup = BeautifulSoup(textdata,'lxml')
- nodes = soup.select("textarea.J-qcSideNavListData")
- jsonObj=json.loads(nodes[0].getText())["list"]
- seeds.append(nodes)
- getChild(jsonObj)
- def getChild(nowObj):
- if nowObj is not None:
- for n in nowObj:
- links= baseUrl+n["link"]
- data={"title":n["title"],"link":links}
- appendUrlList.append(data)
- if n.get("children") is not None:
- getChild(n.get("children"))
复制代码
遍历爬取到的所在信息,来获取指定页面正文内容,这里对正文中的html标签必要去除,否则会有大量干扰内容:
- def crawlData():
- getCrawl(seed)
- for data in appendUrlList:
- url = data["link"]
- print("正在爬取:"+data["title"]+" "+data["link"])
- textdata = requests.get(url).text
- soup = BeautifulSoup(textdata,'lxml')
- nodes = soup.select("div.J-markdown-box")
- if nodes is not None and len(nodes)>0:
- text = nodes[0].get_text()
- text = text[:20000] #这里需要截取长度,不然会出现过长溢出
- stringText = re.sub('\n+', '\n', text)
- data={"url":url,"title":data["title"],"text":stringText}
- appendDataList.append(data)
- return appendDataList
复制代码
至此,知识库动态获取部分完成,比力简单吧!
3.3. 向量知识库存储功能开辟
3.3.1创建腾讯云向量数据库
腾讯云向量数据库如今低设置可免费利用,只必要在控制台搜刮:向量数据库
选择一个你所在迩来地区,点击新建创建一个
创建一个免费的测试版向量数据库
进入实例,开启外网访问:
设置允许访问的IP所在,假如只是测试用那就写0.0.0.0/0,如许全部ip都能访问,也省得多IP网络去研究本身到底哪个外网IP要进白名单
得到外网IP:
获取密钥:
得到这些信息了就可以将信息写入到代码中了。
别的假如你想要查询录入的数据大概创建库和聚集也可以点DMC登录到管理端举行检察:
登录DMC
查询数据
3.3.2开辟向量存储
开始前必要留意:
1.【紧张的事】向量对应的文本字段不要创建索引,会浪费较大的内存,而且没有任何作用。
2.【必须的索引】:主键id、向量字段vector这两个字段如今是固定且必须的,参考下面的例子;
3.【其他索引】:检索时需作为条件查询的字段,比如要按册本的作者举行过滤,这个时间author字段就必要创建索引,
否则无法在查询的时间对author字段举行过滤,不必要过滤的字段无需加索引,会浪费内存;
4.向量数据库支持动态Schema,写入数据时可以写入任何字段,无需提前界说,类似MongoDB.
5.例子中创建一个册本片断的索引,比方册本片断的信息包罗{id,vector,segment,bookName,page},
id为主键必要全局唯一,segment为文本片断,vector为segment的向量,vector字段必要创建向量索引,假如我们在查询的时间要查询指定册本
名称的内容,这个时间必要对bookName创建索引,其他字段没有条件查询的必要,无需创建索引。
6.创建带Embedding的collection必要包管设置的vector索引的维度和Embedding所用模子天生向量维度同等,模子及维度关系:
创建TencentVDB.py文件
引入依靠包
- from Crawling import crawlData
- import tcvectordb
- from tcvectordb.model.collection import Embedding
- from tcvectordb.model.document import Document, Filter, SearchParams
- from tcvectordb.model.enum import FieldType, IndexType, MetricType, EmbeddingModel, ReadConsistency
- from tcvectordb.model.index import Index, VectorIndex, FilterIndex, HNSWParams, IVFFLATParams
复制代码
关闭debug模式
- tcvectordb.debug.DebugEnable = False
复制代码 创建一个class TencentVDB类,内里分块表明寄义:
初始化链接tcvectordb的客户端,干系信息稍后在main传入
- def __init__(self, url: str, username: str, key: str, timeout: int = 30):
- """
- 初始化客户端
- """
- # 创建客户端时可以指定 read_consistency,后续调用 sdk 接口的 read_consistency 将延用该值
- self._client = tcvectordb.VectorDBClient(url=url, username=username, key=key,
- read_consistency=ReadConsistency.EVENTUAL_CONSISTENCY, timeout=timeout)
复制代码
创建数据库和聚集,这里也可以去腾讯云DMC中创建:
创建数据库和聚集
- def create_db_and_collection(self):
- database = 'crawlingdb'
- coll_embedding_name = 'tencent_knowledge'
- # 创建DB--'book'
- db = self._client.create_database(database)
- database_list = self._client.list_databases()
- for db_item in database_list:
- print(db_item.database_name)
- index = Index()
- index.add(VectorIndex('vector', 1024, IndexType.HNSW, MetricType.COSINE, HNSWParams(m=16, efconstruction=200)))
- index.add(FilterIndex('id', FieldType.String, IndexType.PRIMARY_KEY))
- index.add(FilterIndex('title', FieldType.String, IndexType.FILTER))
- ebd = Embedding(vector_field='vector', field='text', model=EmbeddingModel.TEXT2VEC_LARGE_CHINESE)
- # 第二步:创建 Collection
- # 创建支持 Embedding 的 Collection
- db.create_collection(
- name=coll_embedding_name,
- shard=3,
- replicas=0,
- description='爬虫向量数据库实验',
- index=index,
- embedding=ebd,
- timeout=50
- )
复制代码
Embedding可以选择多种

可根据现实环境选择必要利用的模子
以上模子摆列代码位置:\venv\Lib\site-packages\tcvectordb\model\enum.py
- BGE_BASE_ZH = ("bge-base-zh", 768)
- M3E_BASE = ("m3e-base", 768)
- TEXT2VEC_LARGE_CHINESE = ("text2vec-large-chinese", 1024)
- E5_LARGE_V2 = ("e5-large-v2", 1024)
- MULTILINGUAL_E5_BASE = ("multilingual-e5-base", 768)
复制代码
调用爬虫并写入数据到向量数据库
- def upsert_data(self):
- # 获取 Collection 对象
- db = self._client.database('book')
- coll = db.collection('book_segments')
- # upsert 写入数据,可能会有一定延迟
- # 1. 支持动态 Schema,除了 id、vector 字段必须写入,可以写入其他任意字段;
- # 2. upsert 会执行覆盖写,若文档 id 已存在,则新数据会直接覆盖原有数据(删除原有数据,再插入新数据)
- data = crawlData()
- docList =[]
- for dd in data:
- docList.append(Document(id=dd["url"],
- text=dd["text"],
- title=dd["title"]))
- coll.upsert(documents=docList,build_index=True)
- print("成功将数据写入腾讯云向量数据库")
复制代码 调用:
- if __name__ == '__main__':
- test_vdb = TencentVDB('http://xxxxxxxx.clb.ap-beijing.tencentclb.com:50000', key='xxxxx', username='root')
- test_vdb.upsert_data()
复制代码 实行后会输出:
假如提示:
code=1, message=There was an error with the embedding: token rate limit reached 阐明收罗内容过多,免费账户限流了,必要删除一些已存储的聚集。
登录检察数据是否入库:
4.开辟LLM模子对话功能
LLM利用ChatGLM3,这个模子我照旧比力喜欢的它的6B速率快,数据的精确性也比力高,我这边举行提问时大概要14G显存,假如显存少可以根据现实环境举行量化大概用autodl 之类云厂商假造化服务,反正搭建一套到验证完也不要10块钱。
引入依靠
创建文件 requirements.txt
- protobuf
- transformers>=4.30.2
- cpm_kernels
- torch>=2.0
- gradio~=3.39
- sentencepiece
- accelerate
- sse-starlette
- streamlit>=1.24.0
- fastapi>=0.104.1
- uvicorn~=0.24.0
- sse_starlette
- loguru~=0.7.2
- streamlit
复制代码 导入LLM依靠
- pip install -r requirements.txt
复制代码
下载ChatGLM3模子,国内下载所在:
https://modelscope.cn/models/ZhipuAI/chatglm3-6b/
https://modelscope.cn/models/ZhipuAI/chatglm3-6b-base/
https://modelscope.cn/models/ZhipuAI/chatglm3-6b-32k/summary
三选一即可,32K紧张是支持长文本内容对话,模子比力大十多个G 最好放到固态硬盘上,能低沉加载时间
编码ChatGLM谈天对话,这里利用streamlit作为谈天对话UI框架
引入依靠包:
- import os
- import streamlit as st
- import torch
- from transformers import AutoModel, AutoTokenizer
复制代码
设定模子位置,我这里代码和THUDM目次在同一位置,也可以利用绝对路径指向下载好的模子文件夹
- MODEL_PATH = os.environ.get('MODEL_PATH', 'THUDM/chatglm3-6b')
- TOKENIZER_PATH = os.environ.get("TOKENIZER_PATH", MODEL_PATH)
复制代码
判断当前处置惩罚器是否Nvidia显卡,大概是否装了cuda驱动,假如没装请参考我这篇文章:
https://blog.csdn.net/cnor/article/details/129170865
举行cuda安装。
- DEVICE = 'cuda' if torch.cuda.is_available() else 'cpu'
复制代码 设置标题
- # 设置页面标题、图标和布局
- st.set_page_config(
- page_title="我的AI知识库",
- page_icon=":robot:",
- layout="wide"
- )
复制代码 获取model,判断利用cuda照旧cpu来盘算
- @st.cache_resource
- def get_model():
- tokenizer = AutoTokenizer.from_pretrained(TOKENIZER_PATH, trust_remote_code=True)
- if 'cuda' in DEVICE: # AMD, NVIDIA GPU can use Half Precision
- model = AutoModel.from_pretrained(MODEL_PATH, trust_remote_code=True).to(DEVICE).eval()
- else: # CPU, Intel GPU and other GPU can use Float16 Precision Only
- model = AutoModel.from_pretrained(MODEL_PATH, trust_remote_code=True).float().to(DEVICE).eval()
- # 多显卡支持,使用下面两行代替上面一行,将num_gpus改为你实际的显卡数量
- # from utils import load_model_on_gpus
- # model = load_model_on_gpus("THUDM/chatglm3-6b", num_gpus=2)
- return tokenizer, model
复制代码 编写页面左侧一些调治开关以及汗青谈天纪录内容处置惩罚,支持汗青内容分析。
- # 加载Chatglm3的model和tokenizer
- tokenizer, model = get_model()
- # 初始化历史记录和past key values
- if "history" not in st.session_state:
- st.session_state.history = []
- if "past_key_values" not in st.session_state:
- st.session_state.past_key_values = None
-
- # 设置max_length、top_p和temperature
- max_length = st.sidebar.slider("max_length", 0, 32768, 8000, step=1)
- top_p = st.sidebar.slider("top_p", 0.0, 1.0, 0.8, step=0.01)
- temperature = st.sidebar.slider("temperature", 0.0, 1.0, 0.8, step=0.01)
- # 清理会话历史
- buttonClean = st.sidebar.button("清理会话历史", key="clean")
- if buttonClean:
- st.session_state.history = []
- st.session_state.past_key_values = None
- if torch.cuda.is_available():
- torch.cuda.empty_cache()
- st.rerun()
- # 渲染聊天历史记录
- for i, message in enumerate(st.session_state.history):
- print(message)
- if message["role"] == "user":
- with st.chat_message(name="user", avatar="user"):
- st.markdown(message["content"])
- else:
- with st.chat_message(name="assistant", avatar="assistant"):
- st.markdown(message["content"])
- # 输入框和输出框
- with st.chat_message(name="user", avatar="user"):
- input_placeholder = st.empty()
- with st.chat_message(name="assistant", avatar="assistant"):
- message_placeholder = st.empty()
复制代码
获取输入内容,利用model.stream_chat将数据内容发给transformers以流的情势打印输出出来。
- # 获取用户输入
- prompt_text = st.chat_input("请输入您的问题")
- # 如果用户输入了内容,则生成回复
- if prompt_text:
- input_placeholder.markdown(prompt_text)
- history = st.session_state.history
- past_key_values = st.session_state.past_key_values
- history = []
- for response, history, past_key_values in model.stream_chat(
- tokenizer,
- prompt_text,
- history,
- past_key_values=past_key_values,
- max_length=max_length,
- top_p=top_p,
- temperature=temperature,
- return_past_key_values=True,
- ):
- # print(response)
- message_placeholder.markdown(response)
- st.session_state.history = history
- st.session_state.past_key_values = past_key_values
复制代码 以上步调已经完成了大模子对话部分,后续将基于上述代码举行增补,完成与腾讯云向量数据库的对接,这里可以选择腾讯云也可以选择用Milvus等。
输出结果:
这时输入向量照旧模子本身原始的明白没有和腾讯云向量数据库干系知识。
5腾讯云向量数据库与LLM大模子联合
在第四步底子上,我们必要举行以下内容增补即可完本钱次大模子知识库开辟
增长腾讯云向量数据库查询功能
- def searchTvdb(txt):
- conn_params = {
- 'url':'http://lb-xxxxx.clb.ap-beijing.tencentclb.com:50000',
- 'key':'xxxxxxxxxxxxxxxxxx',
- 'username':'root',
- 'timeout':20
- }
- vdb_client = tcvectordb.VectorDBClient(
- url=conn_params['url'],
- username=conn_params['username'],
- key=conn_params['key'],
- timeout=conn_params['timeout'],
- )
- db_list = vdb_client.list_databases()
- db = vdb_client.database('crawlingdb')
- coll = db.collection('tencent_knowledge')
- embeddingItems = [txt]
- search_by_text_res = coll.searchByText(embeddingItems=embeddingItems,limit=3, params=SearchParams(ef=100))
- # print_object(search_by_text_res.get('documents'))
- # print(search_by_text_res.get('documents'))
- return search_by_text_res.get('documents')
- def listToString(doc_lists):
- str =""
- for i, docs in enumerate(doc_lists):
- for doc in docs:
- str=str+doc["text"]
- return str
复制代码 用户从向量数据库查询出内容,在内容前部增长一些辅助性形貌,让LLM更容易懂我们对知识库利用的意图。
由于从向量数据库出来的内容比力多,鉴于条件不允许,这里加个开关,对于平常GLM谈天利用汗青对话,利用知识库时关闭汗青对话。
- def on_mode_change():
- mode = st.session_state.dialogue_mode
- text = f"已切换到 {mode} 模式。"
- st.session_state.history = []
- st.session_state.past_key_values = None
- if torch.cuda.is_available():
- torch.cuda.empty_cache()
-
- st.toast(text)
- dialogue_mode = st.sidebar.selectbox("请选择对话模式",
- ["腾讯云知识库对话",
- "正常LLM对话(支持历史)",
- ],
- on_change=on_mode_change,
- key="dialogue_mode",
- )
复制代码
输入部分改为:
- if prompt_text:
- mode = st.session_state.dialogue_mode
- template_data=""
- if mode =="腾讯云知识库对话":
- result = searchTvdb(prompt_text)
- str = listToString(result)
- # print(str)
- template_data = "请按照""+prompt_text+""进行总结,内容是:"+str
- template_data = template_data[:20000]
- else:
- template_data =prompt_text
-
- input_placeholder.markdown(prompt_text)
- history = st.session_state.history
- past_key_values = st.session_state.past_key_values
- history = []
- for response, history, past_key_values in model.stream_chat(
- tokenizer,
- template_data,
- history,
- past_key_values=past_key_values,
- max_length=max_length,
- top_p=top_p,
- temperature=temperature,
- return_past_key_values=True,
- ):
- # print(response)
- message_placeholder.markdown(response)
-
- endString = ""
- # 更新历史记录和past key values
- if mode != "腾讯云知识库对话":
- st.session_state.history = history
- st.session_state.past_key_values = past_key_values
- else:
- for i,doc in enumerate(result[0]):
- # print(doc)
- endString = endString+"\n\n"+doc["title"]+" "+doc["id"]
- response=response+"\n\n参考链接:\n\n\n"+endString
- message_placeholder.markdown(response)
复制代码
终极结果:

封面
总结
从上面的实践得出结论,通过爬虫+向量数据库+LLM构建本身企业私有化AI模子的蹊径是可以通的,LLM模子越是强大,AI对向量数据库中内容长度以及认知度会更强。腾讯云向量数据库的性能表现出众,利用简单,Embedding支持范例丰富,各人可以上手试试。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!qidao123.com:ToB企服之家,中国第一个企服评测及软件市场,开放入驻,技术点评得现金 |