在数据隐私至关重要的时代,建立自己的本地语言模型 (LLM)为公司和个人提供了至关重要的解决方案。本教程旨在指导您完成使用Ollama 、 Python 3和ChromaDB创建自定义聊天机器人的过程,所有这些都托管在您的系统本地。以下是您需要本教程的主要原因:
本教程将帮助您构建一个强大且安全的本地聊天机器人,以满足您的需求,同时不影响隐私或控制。
检索增强生成 (RAG)是一种先进的技术,它结合了信息检索和文本生成的优势,可以创建更准确、更符合上下文的响应。下面详细介绍了 RAG 的工作原理及其优势:
RAG 是一种混合模型,它通过整合外部知识库或文档存储来增强语言模型的功能。该过程涉及两个主要部分:
通过使用 Ollama、Python 和 ChromaDB 等工具设置本地 RAG 应用程序,您可以享受高级语言模型的好处,同时保持对数据和自定义选项的控制。
运行大型语言模型 (LLM)(如检索增强生成 (RAG) 中使用的模型)需要强大的计算能力。在这些模型中,实现高效处理和嵌入数据的关键组件之一是图形处理单元 (GPU)。以下是 GPU 对这项任务至关重要的原因以及它们如何影响本地 LLM 设置的性能:
GPU 是一种专用处理器,旨在加速图像和视频的渲染。与针对顺序处理任务进行优化的中央处理器 (CPU) 不同,GPU 擅长并行处理。这使得它们特别适合机器学习和深度学习模型所需的复杂数学计算。
设置本地 LLM 时,GPU 的选择会显著影响性能。以下是一些需要考虑的因素:
投资高性能 GPU 对于在本地运行 LLM 模型至关重要。它可确保更快的数据处理、高效的模型训练和快速的响应生成,从而使您的本地 RAG 应用程序更加强大和可靠。通过利用 GPU 的强大功能,您可以充分实现托管自己的自定义聊天机器人的好处,这些聊天机器人可根据您的特定需求和数据隐私要求量身定制。
在进行设置之前,请确保您已满足以下先决条件:
要安装和设置我们的 Python 3 环境,请按照以下步骤操作:在您的机器上下载并设置 Python 3。然后确保您的 Python 3 安装并成功运行:
$ python3 --version # Python 3.11.7
为您的项目创建一个文件夹,例如local-rag
:
$ mkdir local-rag $ cd local-rag
创建名为venv
虚拟环境:
$ python3 -m venv venv
激活虚拟环境:
$ source venv/bin/activate # Windows # venv\Scripts\activate
使用 pip 安装 ChromaDB:
$ pip install --q chromadb
安装 Langchain 工具以便与您的模型无缝协作:
$ pip install --q unstructured langchain langchain-text-splitters $ pip install --q "unstructured[all-docs]"
安装 Flask 以将您的应用作为 HTTP 服务提供:
$ pip install --q flask
要安装 Ollama,请按照以下步骤操作:前往Ollama 下载页面,然后下载适用于您的操作系统的安装程序。通过运行以下命令验证您的 Ollama 安装:
$ ollama --version # ollama version is 0.1.47
提取您需要的 LLM 模型。例如,要使用 Mistral 模型:
$ ollama pull mistral
拉取文本嵌入模型。例如,要使用 Nomic Embed Text 模型:
$ ollama pull nomic-embed-text
然后运行你的 Ollama 模型:
$ ollama serve
现在您已经使用 Python、Ollama、ChromaDB 和其他依赖项设置了环境,现在是时候构建自定义本地 RAG 应用了。在本节中,我们将介绍实际的 Python 代码,并概述如何构建应用程序。
app.py
这是 Flask 应用程序的主文件。它定义了将文件嵌入到矢量数据库以及从模型中检索响应的路由。
import os from dotenv import load_dotenv load_dotenv() from flask import Flask, request, jsonify from embed import embed from query import query from get_vector_db import get_vector_db TEMP_FOLDER = os.getenv('TEMP_FOLDER', './_temp') os.makedirs(TEMP_FOLDER, exist_ok=True) app = Flask(__name__) @app.route('/embed', methods=['POST']) def route_embed(): if 'file' not in request.files: return jsonify({"error": "No file part"}), 400 file = request.files['file'] if file.filename == '': return jsonify({"error": "No selected file"}), 400 embedded = embed(file) if embedded: return jsonify({"message": "File embedded successfully"}), 200 return jsonify({"error": "File embedded unsuccessfully"}), 400 @app.route('/query', methods=['POST']) def route_query(): data = request.get_json() response = query(data.get('query')) if response: return jsonify({"message": response}), 200 return jsonify({"error": "Something went wrong"}), 400 if __name__ == '__main__': app.run(host="0.0.0.0", port=8080, debug=True)
embed.py
该模块处理嵌入过程,包括保存上传的文件、加载和分割数据以及将文档添加到矢量数据库。
import os from datetime import datetime from werkzeug.utils import secure_filename from langchain_community.document_loaders import UnstructuredPDFLoader from langchain_text_splitters import RecursiveCharacterTextSplitter from get_vector_db import get_vector_db TEMP_FOLDER = os.getenv('TEMP_FOLDER', './_temp') # Function to check if the uploaded file is allowed (only PDF files) def allowed_file(filename): return '.' in filename and filename.rsplit('.', 1)[1].lower() in {'pdf'} # Function to save the uploaded file to the temporary folder def save_file(file): # Save the uploaded file with a secure filename and return the file path ct = datetime.now() ts = ct.timestamp() filename = str(ts) + "_" + secure_filename(file.filename) file_path = os.path.join(TEMP_FOLDER, filename) file.save(file_path) return file_path # Function to load and split the data from the PDF file def load_and_split_data(file_path): # Load the PDF file and split the data into chunks loader = UnstructuredPDFLoader(file_path=file_path) data = loader.load() text_splitter = RecursiveCharacterTextSplitter(chunk_size=7500, chunk_overlap=100) chunks = text_splitter.split_documents(data) return chunks # Main function to handle the embedding process def embed(file): # Check if the file is valid, save it, load and split the data, add to the database, and remove the temporary file if file.filename != '' and file and allowed_file(file.filename): file_path = save_file(file) chunks = load_and_split_data(file_path) db = get_vector_db() db.add_documents(chunks) db.persist() os.remove(file_path) return True return False
query.py
该模块通过生成查询的多个版本、检索相关文档并根据上下文提供答案来处理用户查询。
import os from langchain_community.chat_models import ChatOllama from langchain.prompts import ChatPromptTemplate, PromptTemplate from langchain_core.output_parsers import StrOutputParser from langchain_core.runnables import RunnablePassthrough from langchain.retrievers.multi_query import MultiQueryRetriever from get_vector_db import get_vector_db LLM_MODEL = os.getenv('LLM_MODEL', 'mistral') # Function to get the prompt templates for generating alternative questions and answering based on context def get_prompt(): QUERY_PROMPT = PromptTemplate( input_variables=["question"], template="""You are an AI language model assistant. Your task is to generate five different versions of the given user question to retrieve relevant documents from a vector database. By generating multiple perspectives on the user question, your goal is to help the user overcome some of the limitations of the distance-based similarity search. Provide these alternative questions separated by newlines. Original question: {question}""", ) template = """Answer the question based ONLY on the following context: {context} Question: {question} """ prompt = ChatPromptTemplate.from_template(template) return QUERY_PROMPT, prompt # Main function to handle the query process def query(input): if input: # Initialize the language model with the specified model name llm = ChatOllama(model=LLM_MODEL) # Get the vector database instance db = get_vector_db() # Get the prompt templates QUERY_PROMPT, prompt = get_prompt() # Set up the retriever to generate multiple queries using the language model and the query prompt retriever = MultiQueryRetriever.from_llm( db.as_retriever(), llm, prompt=QUERY_PROMPT ) # Define the processing chain to retrieve context, generate the answer, and parse the output chain = ( {"context": retriever, "question": RunnablePassthrough()} | prompt | llm | StrOutputParser() ) response = chain.invoke(input) return response return None
get_vector_db.py
该模块初始化并返回用于存储和检索文档嵌入的向量数据库实例。
import os from langchain_community.embeddings import OllamaEmbeddings from langchain_community.vectorstores.chroma import Chroma CHROMA_PATH = os.getenv('CHROMA_PATH', 'chroma') COLLECTION_NAME = os.getenv('COLLECTION_NAME', 'local-rag') TEXT_EMBEDDING_MODEL = os.getenv('TEXT_EMBEDDING_MODEL', 'nomic-embed-text') def get_vector_db(): embedding = OllamaEmbeddings(model=TEXT_EMBEDDING_MODEL,show_progress=True) db = Chroma( collection_name=COLLECTION_NAME, persist_directory=CHROMA_PATH, embedding_function=embedding ) return db
创建.env
文件来存储您的环境变量:
TEMP_FOLDER = './_temp' CHROMA_PATH = 'chroma' COLLECTION_NAME = 'local-rag' LLM_MODEL = 'mistral' TEXT_EMBEDDING_MODEL = 'nomic-embed-text'
运行app.py
文件来启动你的应用服务器:
$ python3 app.py
服务器运行后,您可以开始向以下端点发出请求:
$ curl --request POST \ --url http://localhost:8080/embed \ --header 'Content-Type: multipart/form-data' \ --form file=@/Users/nassermaronie/Documents/Nasser-resume.pdf # Response { "message": "File embedded successfully" }
$ curl --request POST \ --url http://localhost:8080/query \ --header 'Content-Type: application/json' \ --data '{ "query": "Who is Nasser?" }' # Response { "message": "Nasser Maronie is a Full Stack Developer with experience in web and mobile app development. He has worked as a Lead Full Stack Engineer at Ulventech, a Senior Full Stack Engineer at Speedoc, a Senior Frontend Engineer at Irvins, and a Software Engineer at Tokopedia. His tech stacks include Typescript, ReactJS, VueJS, React Native, NodeJS, PHP, Golang, Python, MySQL, PostgresQL, MongoDB, Redis, AWS, Firebase, and Supabase. He has a Bachelor's degree in Information System from Universitas Amikom Yogyakarta." }
按照这些说明,您可以根据自己的需求使用 Python、Ollama 和 ChromaDB 有效地运行自定义本地 RAG 应用并与之交互。根据需要调整和扩展功能以增强应用程序的功能。
通过利用本地部署的功能,您不仅可以保护敏感信息,还可以优化性能和响应能力。无论您是增强客户互动还是简化内部流程,本地部署的 RAG 应用程序都具有灵活性和稳健性,可以适应和满足您的需求。
https://github.com/firstpersoncode/local-rag
祝你编码愉快!