本地化部署 私有化大语言模型
- 本地化部署 私有化大语言模型
- Anaconda 环境
- 搭建运行
- 代码概述
- 环境配置
- 安装依赖
- CUDA 环境配置
- 系统设计与实现
- 文件处理与加载
- 文档索引构建
- 模型加载与推理
- 文件上传与索引更新
- 实时对话与文档检索
- Gradio 前端设计
- 主要功能
- 完整代码
- 功能说明
- 运行示例
- 文件上传
- 资料库对话
- 模型切换
- 模型对话
- 项目文件介绍
- 执行命令合集
本地化部署 私有化大语言模型
本文介绍了如何使用 Gradio 构建一个基于深度学习的文件上传与知识库查询系统。通过结合 FAISS 和 Sentence-Transformers,系统可以高效地处理和检索大量文档,并基于用户的问题生成回答。通过 Gradio 提供了一个简单直观的用户界面,便于用户上传文件和与智能助理进行对话。
Anaconda 环境
搭建运行
创建 Anaconda 新环境
打开 Terminal(终端)
打开工程位置:cd D:\Unity\ChatGLM\PrivateAgent\Private-Agent (这是我的位置)
下载环境依赖:pip install -r requirements.txt
下面是 requirements.txt 完整代码
# basic requirementsprotobuf>=3.20.0
transformers>=4.41.0
tokenizers>=0.19.1
cpm_kernels>=1.0.11
torch>=2.5.1
gradio>=5.6.0
sentencepiece>=0.2.0
sentence_transformers>=3.3.1
accelerate>=1.1.1
streamlit>=1.40.1
fastapi>=0.115.5
loguru~=0.7.2
mdtex2html>=1.3.0
latex2mathml>=3.77.0
jupyter_client>=8.6.3
faiss-cpu>=1.9.0
peft>=0.13.2
pandas>=2.2.3
python-docx>=1.1.2
python-pptx>=1.0.2
openpyxl>=3.1.5
pymupdf>=1.24.14
frontend>=0.0.3
tools>=0.1.9
这是下载完的样子
下载 CUDA:
方法 1 :pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118
方法 2 :conda install pytorch torchvision torchaudio pytorch-cuda=11.8 -c pytorch -c nvidia
这是 CUDA 下载好的样子
运行试试:python web_demo_Agent.py
可用性为 True 就是加载上了,我的显卡是 RTX 4080 16G显存
我这里加载的模型是:ChatGlm3-6B和ChatGlm3-6B-32K
到这里就可以访问语言模型了
代码概述
本系统基于以下几项关键技术:Gradio:构建交互式Web界面的Python库。
深度学习模型:使用 transformers 和 peft 库加载和应用预训练的自然语言处理模型,如ChatGLM-6B。
FAISS:用于高效地进行相似度检索和文档索引。
Sentence-Transformers:将文档转换为向量形式,以便于在FAISS中进行高效检索。
多种文件格式支持:支持 .txt, .pdf, .docx, .pptx, .xlsx 等多种文件格式的上传与处理。
环境配置
安装依赖
确保安装所需的库:
gradio、torch、sentence-transformers、faiss-cpu、pandas、python-docx
python-pptx、openpyxl、PyMuPDF
大家如果创建完 requirements.txt 文件的话,直接执行 pip install -r requirements.txt 就行。
CUDA 环境配置
如果你使用GPU加速,确保CUDA环境已经配置好。可以通过以下代码检查CUDA的可用性:import torch
print("CUDA 可用性:", torch.cuda.is_available())
print("当前设备数量:", torch.cuda.device_count())
if torch.cuda.is_available():print("设备名称:", torch.cuda.get_device_name(0))
系统设计与实现
文件处理与加载
系统支持从多种文件格式中加载文本数据,包括 .txt, .pdf, .docx, .pptx, .xlsx 等。
每种文件格式的加载都通过不同的库来解析:.txt 文件使用标准的 Python 文件操作读取。
.pdf 使用 PyMuPDF 解析文本。
.docx 使用 python-docx 解析段落。
.pptx 使用 python-pptx 解析幻灯片内容。
.xlsx 使用 pandas 读取 Excel 文件内容。
这些文件的文本内容随后会进行清洗,去除无用空白和换行,并添加到文档集合中。
文档索引构建
为了在大量文档中快速进行检索,系统使用 FAISS 来构建文档的向量索引。
通过 Sentence-Transformers 模型将文档转化为向量,并将这些向量添加到FAISS索引中。from sentence_transformers import SentenceTransformer
import faiss
import numpy as npembedder = SentenceTransformer('paraphrase-multilingual-MiniLM-L12-v2')
# 向量索引添加
def build_index(docs):embeddings = embedder.encode(docs)dimension = embeddings.shape[1]index = faiss.IndexFlatL2(dimension)index.add(np.array(embeddings))return index
模型加载与推理
本系统支持加载多个深度学习模型,使用 transformers 库来加载预训练的 Causal LM 模型
(例如 ChatGLM-6B)。在用户提出问题时,系统会根据当前选择的模型生成回复。from transformers import AutoTokenizer, AutoModelForCausalLM
# 模型加载方法
def load_model_and_tokenizer(model_dir):model = AutoModelForCausalLM.from_pretrained(model_dir)tokenizer = AutoTokenizer.from_pretrained(model_dir)return model, tokenizer
文件上传与索引更新
用户上传文件后,系统会将其内容提取并增量更新文档索引,确保检索到最新的资料。import shutil
# 文件更新方法
def upload_file(file_path):if not os.path.exists(file_path):raise FileNotFoundError(f"文件 {file_path} 不存在。")shutil.copy(file_path, './reference_docs/')new_documents = load_documents_from_folder('./reference_docs')incrementally_update_index(new_documents)
实时对话与文档检索
用户在聊天过程中输入问题,系统会根据问题生成上下文,并从已加载的文档中检索相关内容。
然后,将检索到的文档与问题一起传递给语言模型生成回答。# 资料库问答
def search_docs(query, top_k=3):query_vector = embedder.encode([query])distances, indices = index.search(query_vector, top_k)return [documents[i] for i in indices[0]]
Gradio 前端设计
前端使用 Gradio 来实现交互界面,用户可以通过文本框输入问题,上传文件,选择不同的模型
调整生成文本的参数(如温度、最大生成长度等)。import gradio as gr# Gradio UI 构建
with gr.Blocks() as demo:user_input = gr.Textbox(show_label=False, placeholder="请输入您的问题...")submit_btn = gr.Button("发送")chatbot = gr.Chatbot(value=[{"role": "assistant", "content": "您好,我是您的智能助理!"}])submit_btn.click(predict, inputs=[user_input], outputs=[chatbot])
主要功能
1. 上传文件:用户可以上传 .txt, .pdf, .docx, .pptx, .xlsx 文件系统会自动处理并更新知识库索引。
2. 文档检索:用户提出问题时,系统会从文档库中检索相关资料,并结合用户问题生成合理的回答。
3. 模型切换:支持动态切换不同的深度学习模型,用户可以根据需求选择不同的推理模型。
4. 实时对话:系统会根据历史对话生成新的回答,并提供流式响应。
完整代码
import os
import gradio as gr
import torch
from threading import Thread
from typing import Union, Tuple
from pathlib import Path
from peft import AutoPeftModelForCausalLM, PeftModelForCausalLM
from transformers import (AutoModelForCausalLM,AutoTokenizer,PreTrainedModel,PreTrainedTokenizer,PreTrainedTokenizerFast,StoppingCriteria,StoppingCriteriaList,TextIteratorStreamer
)
import numpy as np
from sentence_transformers import SentenceTransformer
import faiss
import pandas as pd
import docx
import pptx
import fitz # PyMuPDF for PDF reading
from openpyxl import load_workbook
import logging
import shutil
import asyncio
import gc
from transformers import AutoModelForCausalLM, AutoTokenizerModelType = Union[PreTrainedModel, PeftModelForCausalLM]
TokenizerType = Union[PreTrainedTokenizer, PreTrainedTokenizerFast]# 默认模型路径列表,用户可以根据需要添加更多模型路径
MODEL_PATHS = {'ChatGLM3-6B': 'model/ChatGlm3-6B','ChatGlm3-6B-32K': 'model/ChatGlm3-6B-32K'
}TOKENIZER_PATH = os.environ.get("TOKENIZER_PATH", '')# 固定的资料路径
REFERENCE_FOLDER = './reference_docs' # 指定参考资料文件夹路径# 默认的招呼语
DEFAULT_GREETING = "您好,我是您的智能助理,有什么可以帮助您的吗?"# 加载身份描述文件
IDENTITY_FILE = './reference_docs/identity.txt'# 检查 CUDA 是否可用
print("CUDA 可用性:", torch.cuda.is_available())
print("当前设备数量:", torch.cuda.device_count())
if torch.cuda.is_available():print("设备名称:", torch.cuda.get_device_name(0))# 加载 SentenceTransformer 嵌入模型
embedder = SentenceTransformer('paraphrase-multilingual-MiniLM-L12-v2')# 初始化全局变量
documents = [] # 存储所有加载的文档内容
index = None # FAISS 索引对象# 处理和规范化文件路径
def _resolve_path(path: Union[str, Path]) -> Path:return Path(path).expanduser().resolve()# 模型加载函数
def load_model_and_tokenizer(model_dir: Union[str, Path], trust_remote_code: bool = True) -> Tuple[AutoModelForCausalLM, AutoTokenizer]:model_dir = Path(model_dir).resolve() # 解析路径if (model_dir / 'adapter_config.json').exists():model = AutoPeftModelForCausalLM.from_pretrained(model_dir, trust_remote_code=trust_remote_code, device_map='auto')tokenizer_dir = model.peft_config['default'].base_model_name_or_pathelse:model = AutoModelForCausalLM.from_pretrained(model_dir, trust_remote_code=trust_remote_code, device_map='auto')tokenizer_dir = model_dir# 加载 tokenizertokenizer = AutoTokenizer.from_pretrained(tokenizer_dir, trust_remote_code=trust_remote_code)return model, tokenizer# 初始加载默认模型
current_model_name = 'ChatGLM3-6B'# model token 加载
model, tokenizer = load_model_and_tokenizer(MODEL_PATHS[current_model_name], trust_remote_code=True)# 将模型量化到 INT8(仅在模型支持量化时有效)
#model = torch.quantization.quantize_dynamic(
# model, # 量化的模型
# {torch.nn.Linear}, # 量化的层类型,通常是 Linear 层
# dtype=torch.qint8 # 量化为 INT8
#)
# 转换为半精度(FP16),如果需要
# model = model.half()
# 评估模式
model = model.cuda().eval()# 量化为 INT8、转为 FP16、将模型转移到 GPU、设置为评估模式
# model = model.quantize(8).half().cuda().eval()# Token 停止
class StopOnTokens(StoppingCriteria):def __call__(self, input_ids: torch.LongTensor, scores: torch.FloatTensor, **kwargs) -> bool:stop_ids = [0, 2]for stop_id in stop_ids:if input_ids[0][-1] == stop_id:return Truereturn False# 文本解析函数
def parse_text(text):lines = text.split("\n")lines = [line for line in lines if line != ""]count = 0for i, line in enumerate(lines):if "```" in line:count += 1items = line.split('`')if count % 2 == 1:lines[i] = f'<pre><code class="language-{items[-1]}">'else:lines[i] = f'<br></code></pre>'else:if i > 0:if count % 2 == 1:line = line.replace("`", "\`")line = line.replace("<", "<")line = line.replace(">", ">")line = line.replace(" ", " ")line = line.replace("*", "*")line = line.replace("_", "_")line = line.replace("-", "-")line = line.replace(".", ".")line = line.replace("!", "!")line = line.replace("(", "(")line = line.replace(")", ")")line = line.replace("$", "$")lines[i] = "<br>" + linetext = "".join(lines)return text# 数据清理函数
def clean_text(text):"""清洗文本,去除多余的空格和换行"""if not text or not isinstance(text, str):print("无效文本,跳过处理")return Nonecleaned = " ".join(text.split()).strip()# print(f"清理前: {text[:50]}... 清理后: {cleaned[:50]}...")return cleaned if len(cleaned) > 5 else None # 最小长度为5# 索引构建
def build_index(docs):global indexif not docs or len(docs) == 0:print("没有文档可以索引。")returnembeddings = embedder.encode(docs)dimension = embeddings.shape[1]if index is None:index = faiss.IndexFlatL2(dimension)else:index.reset()index.add(np.array(embeddings))print(f"索引已构建,共包含 {index.ntotal} 个文档。")# 资料加载
def load_documents_from_folder(folder_path):"""从文件夹加载所有文档内容,支持递归子文件夹"""contents = []for root, _, files in os.walk(folder_path):for file in files:file_path = os.path.join(root, file)ext = os.path.splitext(file_path)[-1].lower()try:if ext == ".txt":with open(file_path, "r", encoding="utf-8") as f:content = clean_text(f.read())if content:contents.append(content)print(f"成功加载TXT文件: {file_path}")else:print(f"TXT文件内容为空或被过滤: {file_path}")elif ext == ".pdf":with fitz.Document(file_path) as doc:for page in doc:content = clean_text(page.get_text())if content:contents.append(content)print(f"成功加载PDF文件: {file_path}")elif ext == ".docx":doc_file = docx.Document(file_path)for para in doc_file.paragraphs:content = clean_text(para.text)if content:contents.append(content)print(f"成功加载DOCX文件: {file_path}")elif ext == ".pptx":prs = pptx.Presentation(file_path)for slide in prs.slides:for shape in slide.shapes:if hasattr(shape, "text"):content = clean_text(shape.text)if content:contents.append(content)print(f"成功加载PPTX文件: {file_path}")elif ext in [".xls", ".xlsx"]:xls = pd.ExcelFile(file_path)for sheet_name in xls.sheet_names:df = xls.parse(sheet_name)for col in df.columns:content = clean_text(df[col].dropna().astype(str).str.cat(sep='\n'))if content:contents.append(content)print(f"成功加载EXCEL文件: {file_path}")except Exception as e:logging.error(f"无法加载文件 {file_path},错误信息:{e}", exc_info=True)print(f"加载文档数量: {len(contents)}")return contents# 资料初始化
def initialize_reference_documents():"""初始化加载参考资料文件夹内的所有文档"""global documents, indexif not os.path.exists(REFERENCE_FOLDER):os.makedirs(REFERENCE_FOLDER)# 加载文档documents = load_documents_from_folder(REFERENCE_FOLDER)# 如果文档加载成功,构建索引if documents:build_index(documents)print(f"初始化完成,共加载了 {len(documents)} 个文档。")else:print("未找到任何文档,请检查参考文件夹。")return len(documents)# 文件刷新
def refresh_documents(chat_history):"""刷新文件夹内容"""global documents, indextry:documents = load_documents_from_folder(REFERENCE_FOLDER)build_index(documents)message = f"文档已刷新,共索引了 {len(documents)} 个文档。"chat_history.append({"role": "assistant", "content": message})return chat_historyexcept Exception as e:chat_history.append({"role": "assistant", "content": f"刷新资料库失败:{e}"})return chat_history# 增量更新索引的函数
def incrementally_update_index(new_documents):global documents, index# 仅将新文档的嵌入向量添加到索引new_embeddings = [embedder.encode([doc]) for doc in new_documents] # 假设你有一个 `embedder`index.add(np.array(new_embeddings))print(f"成功更新索引,添加了 {len(new_documents)} 个文档的嵌入向量。")# 修改文件上传函数,使用增量索引更新
def upload_file(file_path, chat_history):global documents, indexprint(f"开始上传文件:{file_path}")if not file_path:chat_history.append({"role": "assistant", "content": "没有选择文件,请重新上传!"})return chat_historyif not os.path.exists(file_path):chat_history.append({"role": "assistant", "content": "文件不存在,请检查文件路径!"})return chat_historytry:file_name = os.path.basename(file_path)save_path = os.path.join(REFERENCE_FOLDER, file_name)if not os.path.exists(REFERENCE_FOLDER):os.makedirs(REFERENCE_FOLDER)if os.path.exists(save_path):chat_history.append({"role": "assistant", "content": f"文件 {file_name} 已存在,正在重命名..."})base_name, ext = os.path.splitext(file_name)counter = 1while os.path.exists(save_path):new_file_name = f"{base_name}_{counter}{ext}"save_path = os.path.join(REFERENCE_FOLDER, new_file_name)counter += 1shutil.copy(file_path, save_path)print(f"文件已复制到 {save_path},大小为 {os.path.getsize(save_path)} 字节")# 加载新文件并增量更新索引new_documents = load_documents_from_folder(REFERENCE_FOLDER)incrementally_update_index(new_documents)chat_history.append({"role": "assistant", "content": f"文件 {file_name} 上传成功并已保存!"})return chat_historyexcept FileNotFoundError:chat_history.append({"role": "assistant", "content": "文件没有找到,请检查文件路径!"})except PermissionError:chat_history.append({"role": "assistant", "content": "没有权限访问文件,请检查文件权限!"})except Exception as e:chat_history.append({"role": "assistant", "content": f"上传过程中发生错误:{e}"})# 资料库检索
def search_docs(query, top_k=3, distance_threshold=15.0):"""从资料库检索相关文档"""if not index or not documents:print("索引或文档未初始化。")return ["还没有加载文件。请初始化或刷新参考文件夹。"]query_vector = embedder.encode([query])distances, indices = index.search(query_vector, top_k)print(f"检索结果索引: {indices}, 距离: {distances}")results = []for i, dist in zip(indices[0], distances[0]):if i < len(documents):results.append((documents[i], dist))if not results:print("未找到任何相关内容。")return ["未找到与查询相关的内容,请确保资料库中有相关内容。"]results = sorted(results, key=lambda x: x[1])filtered_results = [doc for doc, dist in results if dist < distance_threshold]if not filtered_results:print("距离超过阈值,返回最近的文档。")return [doc for doc, _ in results[:top_k]]return filtered_results# 读取 identity.txt 的内容
def load_identity(file_path):if not os.path.exists(file_path):print(f"身份文件未找到:{file_path}")return "我是您的智能助理,很高兴为您服务。"try:with open(file_path, 'r', encoding='utf-8') as f:identity_content = f.read()return identity_content.strip()except Exception as e:print(f"无法读取身份文件:{e}")return "我是您的智能助理,很高兴为您服务。"# 身份文件 读取
identity_content = load_identity(IDENTITY_FILE)# 模型预测
def predict(chat_history, max_length, top_p, temperature):"""处理用户输入,生成回答"""stop = StopOnTokens()global identity_contentif not chat_history:# 如果 chat_history 为空,提示用户输入chat_history = [{"role": "assistant", "content": "请先输入您的问题。"}]yield chat_history, chat_historyreturn# 获取用户的最新输入user_input = chat_history[-1]['content']# 检索相关文档related_docs = search_docs(user_input, top_k=3, distance_threshold=15.0)# 确保 related_docs 格式正确if not related_docs:related_docs = ["未找到相关资料"]if "未找到" in related_docs[0]:# 更新 chat_history,添加助手的回复chat_history.append({"role": "assistant", "content": related_docs[0]})yield chat_history, chat_historyreturn# 构建上下文并限制长度context = "\n".join(related_docs[:3]) # 仅使用前3个相关文档if len(context) > 1000:context = context[:1000] + "..."# 将身份描述添加到上下文if identity_content:identity_prompt = f"助手的自我介绍:\n{identity_content}\n\n"else:identity_prompt = ""# 构造完整输入full_input = f"{identity_prompt}以下是相关资料内容:\n{context}\n\n问题:{user_input}\n回答:"messages = chat_history[:-1] # 不包含最后一个用户消息messages.append({"role": "user", "content": full_input})# 添加一个空的助手回复到 chat_historychat_history.append({"role": "assistant", "content": ""})print("\n\n====conversation====\n", messages)# 生成模型输入model_inputs = tokenizer.apply_chat_template(messages,add_generation_prompt=True,tokenize=True,return_tensors="pt").to(next(model.parameters()).device)streamer = TextIteratorStreamer(tokenizer, timeout=60, skip_prompt=True, skip_special_tokens=True)generate_kwargs = {"input_ids": model_inputs,"streamer": streamer,"max_new_tokens": max_length,"do_sample": True,"top_p": top_p,"temperature": temperature,"stopping_criteria": StoppingCriteriaList([stop]),"repetition_penalty": 1.2,}# 启动生成线程from concurrent.futures import ThreadPoolExecutorexecutor = ThreadPoolExecutor(max_workers=1)future = executor.submit(model.generate, **generate_kwargs)# 实时返回生成的内容partial_response = ""for new_token in streamer:try:if new_token != '':partial_response += new_tokenchat_history[-1]['content'] = partial_responseyield chat_history, chat_historyexcept Exception as e:print(f"流式生成时出错:{e}")break# 模型切换
def update_model( model_name):global model, tokenizer, current_model_namecurrent_model_name = model_name return [{"role": "assistant", "content": f"模型切换中:{current_model_name}"}] # 清空缓存并卸载模型
def clear_previous_model():global model, tokenizerif model:del modelif tokenizer:del tokenizertorch.cuda.empty_cache() # 清理 GPU 显存import gcgc.collect() # 强制垃圾回收# 同步模型切换
def update_modelswit(model_name):clear_previous_model() # 清空上一个模型global model, tokenizercurrent_model_name = model_name# 加载新模型model, tokenizer = load_model_and_tokenizer(MODEL_PATHS[model_name], trust_remote_code=True)model = model.to('cuda') # 将模型移动到 GPUmodel = model.eval() # 设置为推理模式# 初始化参考资料initialize_reference_documents()current_model_name = model_name# 返回模型切换后的状态return [{"role": "assistant", "content": f"模型已切换为:{current_model_name}"}]# 清空对话
def reset_state():return [{"role": "assistant", "content": DEFAULT_GREETING}], [{"role": "assistant", "content": DEFAULT_GREETING}]# 初始化参考资料
initialize_reference_documents()# 构建 UI
with gr.Blocks() as demo:# 自定义按钮的样式 #FF7617gr.HTML("""<style>.my-btn {background-color: #F26D14 !important;color: white !important;border-radius: 5px !important;font-size: 16px !important;padding: 10px 20px !important;}.my-btn:hover {background-color: #E15B10 !important;}.my-btn:active {background-color: #D14A00 !important;}.submitBtn {background-color: #F26D14 !important;color: white !important;border-radius: 5px !important;font-size: 16px !important;padding: 10px 20px !important;}.submitBtn:hover {background-color: #E15B10 !important;}.submitBtn:active {background-color: #D14A00 !important;}</style>""")gr.HTML(f"""<h1 align="center">Maddie 私有化智能体</h1><h3 align="center">欢迎使用智能知识库,请上传文档并提出问题!</h3>""")chatbot = gr.Chatbot(value=[{"role": "assistant", "content": DEFAULT_GREETING}], type="messages")with gr.Row():with gr.Column(scale=4): # 保持原比例,输入框和对话历史不变# 输入框user_input = gr.Textbox(show_label=False, placeholder="输入您的问题...", lines=5, # 保持原来行数container=False,elem_id="user_input_box") # 发送按钮和上传按钮保持原大小submitBtn = gr.Button("发送",elem_classes=["submitBtn"], elem_id="submit_btn") with gr.Column(scale=1): # 文件上传组件,减小宽度file_upload = gr.File(label="上传文件", file_types=['.txt', '.pdf', '.docx', '.xlsx', '.pptx'], type="filepath", elem_id="file_upload", scale=0.7 # 缩小文件上传组件的宽度)upload_button = gr.Button("上传文件",elem_classes=["my-btn"])# refresh_button = gr.Button("刷新资料库")with gr.Column(scale=1): # 右侧的控件部分保持较小比例model_selector = gr.Dropdown(list(MODEL_PATHS.keys()), label="选择模型", # value=current_model_name,scale=0.8 # 窄一点的下拉框)modelswitch = gr.Button("模型切换",elem_classes=["my-btn"], size="sm")max_length = gr.Slider(0, 32768, value=8192, step=1.0, label="最大长度", interactive=True)top_p = gr.Slider(0, 1, value=0.8, step=0.01, label="Top P", interactive=True)temperature = gr.Slider(0.01, 1, value=0.6, step=0.01, label="温度", interactive=True)emptyBtn = gr.Button("清空对话",elem_classes=["my-btn"])# 初始对话历史history = gr.State([{"role": "assistant", "content": DEFAULT_GREETING}])# 示例消息解析逻辑def parse_text(text):return text.strip() # 简单解析,可以按需扩展# 发送消息逻辑def send_message(input_text, chat_history):chat_history.append({"role": "user", "content": parse_text(input_text)})return "", chat_historyuser_input.submit(send_message,inputs=[user_input, history],outputs=[user_input, chatbot])# 点击按钮发送消息submitBtn.click(send_message,inputs=[user_input, history],outputs=[user_input, history]).then(predict,inputs=[history, max_length, top_p, temperature],outputs=[chatbot, history])# 清空对话emptyBtn.click(reset_state, outputs=[chatbot, history], queue=False)# 模型选择model_selector.change(update_model, inputs=[model_selector], outputs=[chatbot])# 模型切换modelswitch.click(update_modelswit, inputs=[model_selector], outputs=[chatbot])# 上传文件并刷新资料库upload_button.click(lambda file_path, chat_history: (upload_file(file_path, chat_history) # 上传文件) if file_path else (chat_history # 如果没有选择文件,返回当前对话),inputs=[file_upload, chatbot],outputs=[chatbot]).then(refresh_documents, # 上传成功后刷新资料库inputs=[chatbot],outputs=[chatbot])# 监听回车事件 (Enter)gr.HTML("""<script>window.addEventListener('load', function() {// 获取用户输入框和按钮元素const inputBox = document.querySelector("#user_input_box"); // 通过 ID 获取输入框const submitButton = document.querySelector("#submit_btn"); // 通过 ID 获取按钮// 监听回车键事件inputBox.addEventListener("keydown", function(event) {// 检查是否按下了回车键if (event.key === "Enter") {event.preventDefault(); // 阻止默认的换行行为submitButton.click(); // 模拟点击提交按钮}});});</script>""")# 刷新资料库# refresh_button.click(refresh_documents, inputs=[chatbot], outputs=[chatbot])# 启动服务
demo.queue()
demo.launch(server_name="127.0.0.1", server_port=7870, inbrowser=True, share=True)
功能说明
运行示例
运行成功后在浏览器键入:127.0.0.1:7870 可以打开测试网页,当然也会自动打开。
文件上传
点击上传文件可以选择你要添加进资料库中的文件。
点击上传文件可以更新资料库以及索引
如果成功就会显示这样的提示。
资料库对话
正常问答就可以访问资料库。
模型切换
点击模型切换下拉菜单 可以查看模型列表。目前是手动添加。
如需更改注意环境兼容性以及在代码中重新配置模型路径。# 默认模型路径列表,用户可以根据需要添加更多模型路径
MODEL_PATHS = {'ChatGLM3-6B': 'model/ChatGlm3-6B','ChatGlm3-6B-32K': 'model/ChatGlm3-6B-32K'
}
选择好模型之后,对话框会提示正在切换的模型。
点击模型切换按钮后台会显示加载模型进度。
模型加载完毕也会有相关提示。
模型对话
目前切换完模型之后,资料库索引有一点问题。Gradio 对话显示时好时坏的,暂时还没找到原因。
后期有时间再补充吧,如果哪位大佬修改好了可以踢我就一脚,我去观摩观摩。
项目文件介绍
model:存放模型文件的文件夹。
reference_docs:资料库文件夹,所有参考资料都在这里。
static:用户生成的文档或数据比如结果文件、日志文件、分析报告等,基本上用不到。
PrivateAgent.py:就是执行文件。
requirements.txt:依赖加载文件。
requirements02.txt:依赖加载文件备份。
执行命令.txt:放了一些常用的命令。
执行命令合集
//打开项目盘
D://打开根目录
cd D:\Unity\ChatGLM\PrivateAgent\Private-Agent//依赖安装
pip install -r requirements.txt//卸载当前版本的 PyTorch:
pip uninstall torch torchvision torchaudio -y//安装 CUDA
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118
conda install pytorch torchvision torchaudio pytorch-cuda=11.8 -c pytorch -c nvidia//运行程序
python PrivateAgent.py//查看 Python 版本
python --version//显卡 CUDA 版本查询
nvidia-smi//查看当前平台支持的版本
pip debug --verbose//查看 **依赖 版本 例如:pip show pytorch
pip show **
暂时先这样吧,如果实在看不明白就留言,看到我会回复的。希望这个教程对您有帮助!
路漫漫其修远兮,与君共勉。