-
MacBook本地AI知识库部署计划 前言 【主要诉求】
我现在用的是macbook电脑,我现在需要搭建我个人的本地AI知识库,我要达成的步骤如下:
在本地部署deepseek模型,要求能把模型跑起来
将我的知识库(txt文件格式)和模型关联起来,采用rag模型的方式,后续每次我问deepseek问题,他都可以先去访问一遍我的知识库。
我的知识库会补充内容的,我希望每次deepseek问的问题都可以从最新的知识库进行读取
【补充信息】
我的macbook是48GB内存,M3的芯片,可用存储空间100G,你可以先部署最小参数的模型,先把流程跑通久好
不限制版本,轻量级就好,我要先把流程跑通
我的知识库大概有6万多个字,200多个文档
有python编程经验,但没有机器学习的经验,所以关于机器学习的步骤你要讲究的详细一点
允许一定的延迟
概述 本文档提供了在MacBook上部署DeepSeek模型并实现RAG(检索增强生成)功能的详细步骤,使模型能够访问并利用本地txt文件知识库。
系统需求
硬件 :MacBook M3芯片,48GB内存,100GB可用存储空间
操作系统 :macOS
知识库 :约6万多字,200多个txt文档
部署架构 我们将采用以下架构来实现本地AI知识库:
模型部署 :使用Ollama在本地部署DeepSeek-R1模型(7B或14B版本)
知识库处理 :使用LangChain框架处理txt文件并创建向量存储
RAG实现 :通过检索增强生成技术将模型与知识库集成
动态更新机制 :设计一个能够检测和处理知识库更新的机制
详细部署步骤 1. 安装必要软件 1.1 安装Ollama Ollama是一个轻量级的工具,用于在本地运行大型语言模型。
1.2 安装Docker Desktop(可选,用于Open WebUI) 如果需要图形化界面,可以安装Docker Desktop和Open WebUI。
1.3 安装Python依赖 1 2 3 4 5 6 python3 -m venv ~/ai_knowledge_base_env source ~/ai_knowledge_base_env/bin/activatepip install langchain langchain_community faiss-cpu sentence-transformers streamlit
2. 部署DeepSeek模型 2.1 下载并运行DeepSeek模型 基于您的硬件配置(M3芯片,48GB内存),我们推荐使用DeepSeek-R1的7B或14B模型。
1 2 3 4 5 6 7 8 ollama run deepseek-r1:7b
2.2 验证模型部署 1 2 ollama run deepseek-r1:7b "请简要介绍一下你自己"
3. 知识库集成 3.1 创建项目目录结构 1 mkdir -p ~/ai_knowledge_base/{knowledge,scripts,app}
3.2 处理txt文件知识库 创建一个Python脚本来处理txt文件并生成向量存储:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 import osfrom langchain_community.document_loaders import TextLoader, DirectoryLoaderfrom langchain.text_splitter import RecursiveCharacterTextSplitterfrom langchain_community.embeddings import HuggingFaceEmbeddingsfrom langchain_community.vectorstores import FAISSimport datetimeknowledge_dir = os.path.expanduser("~/ai_knowledge_base/knowledge" ) vector_store_path = os.path.expanduser("~/ai_knowledge_base/vector_store" ) loader = DirectoryLoader(knowledge_dir, glob="**/*.txt" , loader_cls=TextLoader) documents = loader.load() print (f"加载了 {len (documents)} 个文档" )text_splitter = RecursiveCharacterTextSplitter( chunk_size=1000 , chunk_overlap=200 , length_function=len , ) chunks = text_splitter.split_documents(documents) print (f"创建了 {len (chunks)} 个文本块" )embeddings = HuggingFaceEmbeddings(model_name="sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2" ) vector_store = FAISS.from_documents(chunks, embeddings) timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S" ) save_path = f"{vector_store_path} _{timestamp} " vector_store.save_local(save_path) latest_path = f"{vector_store_path} _latest" if os.path.exists(latest_path): os.remove(latest_path) os.symlink(save_path, latest_path) print (f"向量存储已保存到 {save_path} " )print (f"最新版本链接: {latest_path} " )
3.3 创建RAG应用 创建一个Python脚本来实现RAG功能:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 import osfrom langchain_community.llms import Ollamafrom langchain_community.embeddings import HuggingFaceEmbeddingsfrom langchain_community.vectorstores import FAISSfrom langchain.prompts import PromptTemplatefrom langchain.chains import RetrievalQAvector_store_path = os.path.expanduser("~/ai_knowledge_base/vector_store_latest" ) embeddings = HuggingFaceEmbeddings(model_name="sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2" ) vector_store = FAISS.load_local(vector_store_path, embeddings) retriever = vector_store.as_retriever(search_kwargs={"k" : 5 }) llm = Ollama(model="deepseek-r1:7b" ) template = """ 你是一个有用的AI助手,使用提供的上下文来回答问题。 如果你不知道答案,就说你不知道,不要试图编造答案。 上下文信息: {context} 问题: {question} 回答: """ prompt = PromptTemplate(template=template, input_variables=["context" , "question" ]) qa_chain = RetrievalQA.from_chain_type( llm=llm, chain_type="stuff" , retriever=retriever, chain_type_kwargs={"prompt" : prompt} ) def ask (question ): return qa_chain.run(question) if __name__ == "__main__" : while True : question = input ("\n请输入您的问题 (输入'退出'结束): " ) if question.lower() in ['退出' , 'exit' , 'quit' ]: break answer = ask(question) print (f"\n回答: {answer} " )
3.4 创建Web界面(可选) 使用Streamlit创建一个简单的Web界面:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 import streamlit as stimport sysimport ossys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) from app.rag_app import askst.title("DeepSeek知识库问答系统" ) if "messages" not in st.session_state: st.session_state.messages = [] for message in st.session_state.messages: with st.chat_message(message["role" ]): st.markdown(message["content" ]) if prompt := st.chat_input("请输入您的问题" ): st.session_state.messages.append({"role" : "user" , "content" : prompt}) with st.chat_message("user" ): st.markdown(prompt) with st.chat_message("assistant" ): with st.spinner("思考中..." ): response = ask(prompt) st.markdown(response) st.session_state.messages.append({"role" : "assistant" , "content" : response})
4. 实现知识库动态更新机制 创建一个脚本来检测知识库变化并更新向量存储:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 import osimport timeimport hashlibimport subprocessfrom watchdog.observers import Observerfrom watchdog.events import FileSystemEventHandlerclass KnowledgeUpdateHandler (FileSystemEventHandler ): def __init__ (self, knowledge_dir, update_script ): self.knowledge_dir = knowledge_dir self.update_script = update_script self.last_hash = self.get_directory_hash() self.cooldown = False self.cooldown_time = 60 def get_directory_hash (self ): """计算目录中所有txt文件的哈希值""" hash_md5 = hashlib.md5() for root, _, files in os.walk(self.knowledge_dir): for file in sorted (files): if file.endswith('.txt' ): file_path = os.path.join(root, file) with open (file_path, 'rb' ) as f: for chunk in iter (lambda : f.read(4096 ), b"" ): hash_md5.update(chunk) return hash_md5.hexdigest() def on_modified (self, event ): if self.cooldown: return current_hash = self.get_directory_hash() if current_hash != self.last_hash: print (f"检测到知识库变化,正在更新向量存储..." ) subprocess.run(['python' , self.update_script]) self.last_hash = current_hash self.cooldown = True time.sleep(self.cooldown_time) self.cooldown = False if __name__ == "__main__" : knowledge_dir = os.path.expanduser("~/ai_knowledge_base/knowledge" ) update_script = os.path.expanduser("~/ai_knowledge_base/scripts/process_knowledge.py" ) event_handler = KnowledgeUpdateHandler(knowledge_dir, update_script) observer = Observer() observer.schedule(event_handler, knowledge_dir, recursive=True ) observer.start() try : print (f"开始监控知识库目录: {knowledge_dir} " ) while True : time.sleep(1 ) except KeyboardInterrupt: observer.stop() observer.join()
5. 创建启动脚本 创建一个便捷的启动脚本:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 #!/bin/bash source ~/ai_knowledge_base_env/bin/activateif ! pgrep -x "ollama" > /dev/null; then echo "正在启动Ollama服务..." open -a Ollama sleep 5 fi KNOWLEDGE_DIR=~/ai_knowledge_base/knowledge if [ ! -d "$KNOWLEDGE_DIR " ]; then echo "创建知识库目录..." mkdir -p "$KNOWLEDGE_DIR " echo "请将您的txt文件放入 $KNOWLEDGE_DIR 目录" fi VECTOR_STORE=~/ai_knowledge_base/vector_store_latest if [ ! -d "$VECTOR_STORE " ]; then echo "初始化向量存储..." python ~/ai_knowledge_base/scripts/process_knowledge.py fi echo "启动知识库监控..." python ~/ai_knowledge_base/scripts/update_knowledge.py & MONITOR_PID=$! echo "启动Web界面..." streamlit run ~/ai_knowledge_base/app/web_app.py kill $MONITOR_PID
使用说明
将您的txt文件放入~/ai_knowledge_base/knowledge
目录
运行启动脚本:bash ~/ai_knowledge_base/start.sh
在Web界面中提问,系统会自动从您的知识库中检索相关信息并生成回答
当您向知识库添加新的txt文件或修改现有文件时,系统会自动检测变化并更新向量存储
注意事项
首次处理大量文档可能需要一些时间
模型推理速度取决于您的硬件性能
为获得最佳性能,请确保您的MacBook连接电源并处于良好的散热条件下
如果遇到内存不足的问题,可以尝试使用更小的模型(如DeepSeek-R1:1.5b)
故障排除
如果Ollama服务无法启动,请检查安装是否正确
如果向量存储创建失败,请检查知识库目录中是否有有效的txt文件
如果模型响应速度过慢,可以考虑使用更小的模型或减少检索的文档数量
如果遇到Python依赖问题,请确保已安装所有必要的包