尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

从零搭建企业级RAG问答系统:基于LangChain、Ollama与Chroma的实战指南

从零搭建企业级RAG问答系统:基于LangChain、Ollama与Chroma的实战指南 在构建智能应用时我们常常面临一个核心矛盾大模型虽然知识渊博但其训练数据是静态的无法获取最新的、私有的或特定领域的精确信息。你是否遇到过向大模型提问公司内部制度它却答非所问或者询问最新的技术文档它只能给出过时的答案这正是RAG技术要解决的痛点。RAG检索增强生成已成为连接大模型与私有知识库的“标准答案”。然而网上教程要么过于理论化要么代码片段零散环境依赖复杂让开发者从入门到放弃。本文将带你从零开始手把手搭建一个企业级可用的RAG问答系统。我们将使用主流的LangChain框架结合本地部署的Ollama大模型和Chroma向量数据库实现一个功能完整、代码清晰、可一键运行的项目。无论你是想快速入门RAG还是需要在业务中落地智能问答这篇文章都将为你提供一条清晰的路径避开99%的配置与代码坑点。1. RAG核心概念与为什么选择它在深入代码之前我们必须理解RAG是什么以及为什么它比单纯微调或直接提问大模型更适合企业场景。1.1 什么是RAGRAG全称Retrieval-Augmented Generation即检索增强生成。你可以把它理解为一个“开卷考试”系统检索Retrieval当用户提出一个问题Query系统不会让大模型凭空想象而是先去一个庞大的“资料库”通常是向量数据库中快速查找与问题最相关的几段资料Chunks。增强Augmented将找到的相关资料和用户原始问题一起组合成一个更丰富的“提示词”Prompt提交给大模型。生成Generation大模型基于这个包含了标准答案“参考资料”的提示词生成最终的回答。这个过程确保了回答的准确性有据可查和时效性资料库可随时更新。1.2 RAG vs. 微调 vs. 直接提问很多初学者会混淆RAG和大模型微调Fine-tuning这里做一个清晰的对比特性RAG (检索增强生成)大模型微调直接提问大模型核心原理检索外部知识 生成答案调整模型内部权重学习新知识依赖模型预训练的记忆知识更新非常便捷只需更新向量数据库成本高需重新训练或增量训练无法更新知识截止于训练数据数据敏感性外部知识库相对安全可控知识被编码进模型存在泄露风险同微调回答事实性高答案来源于检索到的文档中等依赖训练数据的质量和覆盖度低容易产生“幻觉”适用场景问答、客服、知识库查询、最新信息查询改变模型风格、学习特定任务格式、深入理解领域数据通用对话、创意写作、代码生成成本与速度部署成本低响应速度较快依赖检索训练成本极高部署后推理速度快成本低仅API调用但可能产生不准确回答对于大多数企业来说内部知识库、产品手册、规章制度等文档经常变动RAG以其低成本、易更新、高准确性的优势成为构建智能问答系统的首选架构。1.3 企业级RAG项目技术栈选型一个完整的企业级RAG系统通常包含以下组件我们的实战项目也将基于此搭建文档加载与处理LangChain- 提供了统一的接口来处理PDF、Word、TXT、HTML等多种格式文档。文本分割LangChain文本分割器 - 将长文档切分成适合检索的小片段。向量化模型Sentence Transformers- 将文本片段转换为计算机可以理解的数值向量嵌入。向量数据库Chroma- 轻量级、易用的开源向量数据库用于存储和检索向量。大语言模型OllamaQwen- 使用Ollama在本地部署免费的千问大模型保证数据隐私。应用框架LangChainGradio- LangChain编排整个流程Gradio快速构建Web交互界面。2. 环境准备与项目初始化“工欲善其事必先利其器”。为了避免环境冲突强烈建议使用Conda或Venv创建独立的Python环境。2.1 创建并激活Python虚拟环境打开你的终端Windows CMD/PowerShell, macOS/Linux Terminal执行以下命令# 使用 conda推荐 conda create -n rag_tutorial python3.10 conda activate rag_tutorial # 或者使用 venv python -m venv rag_tutorial # Windows rag_tutorial\Scripts\activate # macOS/Linux source rag_tutorial/bin/activate2.2 安装Ollama并拉取大模型Ollama允许我们在本地运行各种开源大模型无需GPU也能体验速度会慢些但适合学习。安装Ollama 访问 Ollama官网 下载对应操作系统的安装包安装过程非常简单一路下一步即可。拉取千问模型 安装完成后打开新的终端窗口运行以下命令。这里我们使用参数量较小的qwen2.5:0.5b模型对硬件要求极低。ollama pull qwen2.5:0.5b等待下载完成。你可以根据需要拉取其他模型如llama3.2、qwen2.5:7b等模型越大所需内存和显存越多。验证Ollama运行 在新终端运行ollama run qwen2.5:0.5b如果能进入对话界面说明模型加载成功。按CtrlD退出。2.3 安装Python依赖库在我们的rag_tutorial虚拟环境激活状态下安装项目所需的Python包。创建一个requirements.txt文件内容如下langchain0.1.0 langchain-community0.0.10 chromadb0.4.22 sentence-transformers2.2.2 gradio4.19.1 pypdf3.17.4 python-dotenv1.0.0 langchain-chroma0.1.0 unstructured0.10.30然后使用pip安装pip install -r requirements.txt如果下载速度慢可以使用清华镜像源pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple重要版本说明LangChain版本迭代较快API可能有变动。本文代码基于langchain0.1.x版本编写这是目前相对稳定的主流版本。若你使用其他版本遇到问题请优先检查导入语句和API调用方式。2.4 创建项目目录结构一个清晰的项目结构是良好工程的开始。创建如下目录和文件rag_enterprise_project/ │ ├── data/ # 存放原始知识文档PDF、TXT等 │ └── (放入你的知识文档) │ ├── vector_db/ # Chroma向量数据库持久化存储目录 │ ├── src/ │ ├── __init__.py │ ├── document_processor.py # 文档加载与处理模块 │ ├── vector_store.py # 向量数据库构建与检索模块 │ └── rag_chain.py # RAG链构建模块 │ ├── app.py # Gradio Web应用入口 ├── requirements.txt ├── .env.example # 环境变量示例文件 └── README.md现在基础环境已经就绪我们可以开始编写核心代码了。3. 核心模块拆解与实现我们将系统拆解为三个核心模块逐个击破。3.1 文档处理模块 (document_processor.py)这个模块负责读取各种格式的文档并将其切分成适合检索的文本块Chunks。# file: src/document_processor.py import os from typing import List from langchain_community.document_loaders import PyPDFLoader, TextLoader, UnstructuredFileLoader from langchain.text_splitter import RecursiveCharacterTextSplitter from langchain.schema import Document class DocumentProcessor: 文档处理器支持多种格式执行文本分割 def __init__(self, chunk_size: int 500, chunk_overlap: int 50): 初始化文本分割器。 Args: chunk_size: 每个文本块的最大字符数。 chunk_overlap: 块与块之间重叠的字符数用于保持上下文连贯。 self.text_splitter RecursiveCharacterTextSplitter( chunk_sizechunk_size, chunk_overlapchunk_overlap, separators[\n\n, \n, 。, , , , , , ] # 中文友好的分隔符 ) def load_and_split(self, file_path: str) - List[Document]: 根据文件后缀名加载并分割文档。 Args: file_path: 文档的绝对或相对路径。 Returns: 分割后的Document对象列表。 Raises: ValueError: 文件格式不支持或文件不存在。 if not os.path.exists(file_path): raise ValueError(f文件不存在: {file_path}) _, ext os.path.splitext(file_path) ext ext.lower() # 根据后缀选择加载器 if ext .pdf: loader PyPDFLoader(file_path) elif ext .txt: loader TextLoader(file_path, encodingutf-8) else: # 使用Unstructured作为兜底支持.docx, .html等 loader UnstructuredFileLoader(file_path) # 加载文档 documents loader.load() print(f已加载文档: {file_path}, 共 {len(documents)} 页/部分) # 分割文档 split_docs self.text_splitter.split_documents(documents) print(f分割为 {len(split_docs)} 个文本块) return split_docs def process_directory(self, dir_path: str) - List[Document]: 处理一个目录下的所有支持格式的文档。 Args: dir_path: 目录路径。 Returns: 所有文档分割后的Document列表。 all_split_docs [] supported_extensions [.pdf, .txt, .md, .docx, .html] for root, _, files in os.walk(dir_path): for file in files: if any(file.endswith(ext) for ext in supported_extensions): file_path os.path.join(root, file) try: split_docs self.load_and_split(file_path) all_split_docs.extend(split_docs) except Exception as e: print(f处理文件 {file_path} 时出错: {e}) print(f目录处理完成总共生成 {len(all_split_docs)} 个文本块。) return all_split_docs # 示例用法 if __name__ __main__: processor DocumentProcessor(chunk_size300, chunk_overlap30) # 处理单个文件 # docs processor.load_and_split(./data/产品手册.pdf) # 处理整个目录 # docs processor.process_directory(./data)关键参数解析chunk_size不宜过大或过小。过大可能导致检索不精准携带无关信息过小则可能丢失关键上下文。一般设置在300-1000之间根据文档类型调整。chunk_overlap重叠部分可以防止一个完整的句子或概念被割裂到两个块中是保证语义连贯性的重要手段。RecursiveCharacterTextSplitter是LangChain推荐的分割器它会递归地尝试用不同的分隔符进行分割直到块的大小符合要求对中文支持较好。3.2 向量数据库模块 (vector_store.py)本模块负责将文本块转换为向量嵌入并存储到Chroma向量数据库中同时提供检索功能。# file: src/vector_store.py import os from typing import List from langchain.schema import Document from langchain_chroma import Chroma from langchain_community.embeddings import HuggingFaceEmbeddings from langchain_community.embeddings import OllamaEmbeddings class VectorStoreManager: 向量数据库管理类封装嵌入模型和Chroma操作 def __init__(self, persist_directory: str ./vector_db, use_ollama: bool False): 初始化向量存储管理器。 Args: persist_directory: Chroma数据库持久化目录。 use_ollama: 是否使用Ollama的嵌入模型。False则使用SentenceTransformer。 self.persist_directory persist_directory self.use_ollama use_ollama # 初始化嵌入模型 if use_ollama: # 使用Ollama的嵌入模型需要本地运行Ollama并拉取相关模型如nomic-embed-text self.embeddings OllamaEmbeddings(modelnomic-embed-text) else: # 使用HuggingFace的SentenceTransformer模型本地运行无需网络 # 首次运行会自动下载模型推荐使用中文优化模型 model_name BAAI/bge-small-zh-v1.5 # 中文小模型效果不错 # model_name sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2 # 多语言模型 self.embeddings HuggingFaceEmbeddings( model_namemodel_name, model_kwargs{device: cpu}, # 使用CPU有GPU可改为cuda encode_kwargs{normalize_embeddings: True} # 标准化向量提升检索效果 ) # 初始化或加载向量数据库 self.vector_store self._get_or_create_vector_store() def _get_or_create_vector_store(self): 获取或创建Chroma向量存储实例 # 检查是否已存在持久化的数据库 if os.path.exists(self.persist_directory) and os.listdir(self.persist_directory): print(f从目录 {self.persist_directory} 加载已有向量数据库...) return Chroma( persist_directoryself.persist_directory, embedding_functionself.embeddings ) else: print(f在目录 {self.persist_directory} 创建新的向量数据库...) return Chroma( persist_directoryself.persist_directory, embedding_functionself.embeddings ) def add_documents(self, documents: List[Document]): 将文档列表添加到向量数据库。 Args: documents: 由Document对象组成的列表。 if not documents: print(警告文档列表为空未添加任何内容。) return # 直接使用LangChain Chroma的add_documents方法 # 它会自动将文档转换为向量并存储 self.vector_store.add_documents(documents) # 持久化到磁盘 self.vector_store.persist() print(f成功添加 {len(documents)} 个文档块到向量数据库。) def similarity_search(self, query: str, k: int 4) - List[Document]: 在向量数据库中进行相似性检索。 Args: query: 查询文本。 k: 返回最相似的k个结果。 Returns: 与查询最相似的Document列表。 return self.vector_store.similarity_search(query, kk) def as_retriever(self, search_kwargs: dict None): 将向量数据库转换为一个检索器(Retriever)对象便于集成到LangChain链中。 Args: search_kwargs: 传递给检索器的参数如{k: 4}。 Returns: 一个检索器实例。 if search_kwargs is None: search_kwargs {k: 4} return self.vector_store.as_retriever(search_kwargssearch_kwargs) # 示例用法构建知识库 if __name__ __main__: from document_processor import DocumentProcessor # 1. 处理文档 processor DocumentProcessor() docs processor.process_directory(./data) # 假设你的文档放在./data目录下 # 2. 初始化向量存储并添加文档 vs_manager VectorStoreManager(persist_directory./vector_db, use_ollamaFalse) vs_manager.add_documents(docs) # 3. 测试检索 test_query 公司的年假制度是怎样的 results vs_manager.similarity_search(test_query, k2) print(f\n针对查询 {test_query} 的检索结果) for i, doc in enumerate(results): print(f\n--- 结果 {i1} ---) print(f内容片段: {doc.page_content[:200]}...) # 打印前200字符 print(f来源: {doc.metadata.get(source, 未知)})嵌入模型选择建议BAAI/bge-small-zh-v1.5智源研究院开源的优秀中文嵌入模型在中文语义相似度任务上表现突出强烈推荐用于中文项目。nomic-embed-text需要Ollama支持通用性强但需要额外下载。text-embedding-ada-002OpenAI的API效果最好但需要付费和网络。 对于企业级内部应用优先选择本地部署的BAAI/bge系列模型在效果、速度和隐私之间取得最佳平衡。3.3 RAG链构建模块 (rag_chain.py)这是RAG系统的“大脑”它将检索器和大语言模型组合成一个完整的问答流水线。# file: src/rag_chain.py from langchain.prompts import PromptTemplate from langchain.chains import RetrievalQA from langchain_community.llms import Ollama from langchain.callbacks.streaming_stdout import StreamingStdOutCallbackHandler from typing import Any, Dict class RAGChainBuilder: 构建RAG问答链 def __init__(self, vector_store_retriever, model_name: str qwen2.5:0.5b): 初始化RAG链构建器。 Args: vector_store_retriever: 向量数据库检索器。 model_name: Ollama中使用的模型名称。 self.retriever vector_store_retriever self.model_name model_name self.llm self._init_llm() self.qa_chain None def _init_llm(self): 初始化Ollama大语言模型 # 注意这里假设Ollama服务已在本地运行默认端口11434 # 如果需要流式输出可以添加回调 callbacks [StreamingStdOutCallbackHandler()] llm Ollama( modelself.model_name, base_urlhttp://localhost:11434, # Ollama默认地址 temperature0.1, # 较低的温度使输出更确定、更少随机性 callbackscallbacks, # 其他可选参数top_p, top_k, num_predict等 ) return llm def build_chain(self, custom_prompt: str None): 构建RetrievalQA链。 Args: custom_prompt: 自定义的提示词模板。如果为None则使用默认模板。 Returns: 构建好的RetrievalQA链。 # 定义提示词模板 if custom_prompt is None: # 这是一个经过优化的中文RAG提示词模板 prompt_template 请根据以下上下文信息回答问题。如果上下文信息中没有相关答案请直接说“根据提供的资料我无法回答这个问题”不要编造信息。 上下文信息 {context} 问题{question} 请基于上下文信息给出准确、有用的回答 else: prompt_template custom_prompt PROMPT PromptTemplate( templateprompt_template, input_variables[context, question] ) # 构建链 chain RetrievalQA.from_chain_type( llmself.llm, chain_typestuff, # stuff是最简单的方式将所有检索到的上下文放入提示词 retrieverself.retriever, chain_type_kwargs{prompt: PROMPT}, return_source_documentsTrue, # 非常重要返回检索到的源文档用于验证和展示 ) self.qa_chain chain return chain def query(self, question: str) - Dict[str, Any]: 向RAG链提问。 Args: question: 用户问题。 Returns: 包含答案和源文档的字典。 if self.qa_chain is None: self.build_chain() # 调用链 result self.qa_chain.invoke({query: question}) return result # 示例用法 if __name__ __main__: from vector_store import VectorStoreManager # 1. 初始化向量存储检索器 vs_manager VectorStoreManager(persist_directory./vector_db) retriever vs_manager.as_retriever(search_kwargs{k: 3}) # 2. 构建RAG链 rag_builder RAGChainBuilder(retriever, model_nameqwen2.5:0.5b) chain rag_builder.build_chain() # 3. 提问 questions [ 我们公司今年的发展目标是什么, 请假流程需要哪些步骤, ] for q in questions: print(f\n{*50}) print(f问题: {q}) print(f{*50}) response rag_builder.query(q) print(f答案: {response[result]}) print(\n参考来源) for i, doc in enumerate(response[source_documents]): print(f [{i1}] {doc.metadata.get(source, 未知)} (页码: {doc.metadata.get(page, N/A)}))核心要点解析RetrievalQA链这是LangChain提供的“一站式”RAG链内部封装了“检索-组合提示词-调用LLM-返回结果”的完整流程。chain_typestuff这是最简单直接的方法将所有检索到的文档内容拼接起来塞进提示词。对于检索结果不多如4的情况很有效。如果文档块很大或很多可以考虑map_reduce或refine等更复杂的方法。return_source_documentsTrue这个参数至关重要它让我们能够看到答案是根据哪些文档片段生成的极大地增强了系统的可解释性和可信度是企业级应用的必要特性。提示词工程我们设计的提示词明确要求模型“基于上下文”回答并指示其在没有相关信息时拒绝回答。这是减少大模型“幻觉”的关键一步。4. 构建完整的Web应用 (app.py)现在我们将所有模块集成起来并用Gradio构建一个用户友好的Web界面。# file: app.py import gradio as gr import os import sys from src.vector_store import VectorStoreManager from src.rag_chain import RAGChainBuilder from src.document_processor import DocumentProcessor # 全局变量用于缓存已初始化的组件避免重复加载 vector_store_manager None rag_chain None def init_system(data_dir: str, persist_dir: str): 初始化系统处理文档、构建向量库、创建RAG链。 这是一个耗时的过程适合在启动时执行一次。 global vector_store_manager, rag_chain print(开始初始化RAG系统...) # 1. 处理文档 if not os.path.exists(data_dir): os.makedirs(data_dir) return f数据目录 {data_dir} 已创建但其中没有文档。请放入文档后重新初始化。, None, None processor DocumentProcessor(chunk_size500, chunk_overlap50) try: all_docs processor.process_directory(data_dir) if len(all_docs) 0: return f在 {data_dir} 中未找到支持的文档支持.pdf, .txt, .md等。, None, None except Exception as e: return f文档处理失败: {e}, None, None # 2. 初始化向量存储并添加文档 try: vector_store_manager VectorStoreManager(persist_directorypersist_dir, use_ollamaFalse) vector_store_manager.add_documents(all_docs) except Exception as e: return f向量数据库构建失败: {e}, None, None # 3. 构建检索器和RAG链 try: retriever vector_store_manager.as_retriever(search_kwargs{k: 4}) rag_chain RAGChainBuilder(retriever, model_nameqwen2.5:0.5b) rag_chain.build_chain() except Exception as e: return fRAG链构建失败: {e}. 请确保Ollama服务正在运行 (ollama serve)。, None, None doc_count len(all_docs) return f系统初始化成功已加载并处理 {doc_count} 个文本块。现在可以开始提问了。, data_dir, persist_dir def ask_question(question: str, history): 处理用户提问。 Args: question: 用户问题。 history: Gradio的聊天历史。 Returns: 模型回答和更新后的历史。 global rag_chain if rag_chain is None: answer 系统未初始化或初始化失败。请先点击‘初始化系统’按钮。 history.append((question, answer)) return history, history, if not question.strip(): answer 问题不能为空。 history.append((question, answer)) return history, history, print(f用户提问: {question}) try: # 调用RAG链获取答案 response rag_chain.query(question) answer response[result] # 构建详细的来源信息 source_info **参考来源**\n for i, doc in enumerate(response[source_documents]): source os.path.basename(doc.metadata.get(source, 未知文档)) page doc.metadata.get(page, ) page_info f 第{page}页 if page else # 预览片段 preview doc.page_content[:150].replace(\n, ) ... source_info f{i1}. {source}{page_info}\n {preview}\n full_response f{answer}\n\n---\n{source_info} except Exception as e: full_response f查询过程中出现错误: {str(e)}。请检查Ollama服务是否运行正常。 # 更新聊天历史 history.append((question, full_response)) return history, history, def clear_chat(): 清空聊天历史 return [], [] # 构建Gradio界面 with gr.Blocks(title企业级RAG知识库问答系统, themegr.themes.Soft()) as demo: gr.Markdown( # 企业级RAG知识库问答系统 基于本地大模型与私有文档构建的智能问答助手。 ) with gr.Row(): with gr.Column(scale1): gr.Markdown(### 系统配置) data_dir_input gr.Textbox(label知识文档目录, value./data, interactiveTrue) persist_dir_input gr.Textbox(label向量数据库存储目录, value./vector_db, interactiveTrue) init_btn gr.Button( 初始化/重建系统, variantprimary) init_status gr.Textbox(label初始化状态, interactiveFalse) gr.Markdown(---) gr.Markdown(**使用说明**) gr.Markdown( 1. 将你的知识文档PDF/TXT等放入 ./data 目录。 2. 点击 **初始化/重建系统** 按钮构建知识库。 3. 在右侧聊天框提问。 ) clear_btn gr.Button(️ 清空对话历史, variantsecondary) with gr.Column(scale2): chatbot gr.Chatbot(label智能助手, height500) msg gr.Textbox(label请输入你的问题, placeholder例如公司的年假政策是什么, lines2) submit_btn gr.Button(发送, variantprimary) # 绑定事件 init_btn.click( fninit_system, inputs[data_dir_input, persist_dir_input], outputs[init_status, data_dir_input, persist_dir_input] ) submit_btn.click( fnask_question, inputs[msg, chatbot], outputs[chatbot, chatbot, msg] ).then( lambda: , # 清空输入框 outputsmsg ) msg.submit( fnask_question, inputs[msg, chatbot], outputs[chatbot, chatbot, msg] ).then( lambda: , outputsmsg ) clear_btn.click( fnclear_chat, outputschatbot ) if __name__ __main__: # 检查默认数据目录 if not os.path.exists(./data): os.makedirs(./data) print(提示已创建 ./data 目录请将你的知识文档放入此目录后运行应用。) # 启动Gradio应用 demo.launch(server_name0.0.0.0, server_port7860, shareFalse) # 参数说明 # server_name0.0.0.0 允许局域网访问 # server_port7860 Gradio默认端口 # shareFalse 不创建公开链接若需要可设为True5. 完整实战从零搭建并运行现在让我们把所有的部分串联起来完成一次端到端的实战。5.1 准备知识文档在项目根目录下创建data文件夹。向data文件夹中放入你的知识文档。例如员工手册.pdf产品功能说明.txt项目计划.docx公司制度.md为了测试你可以创建一个简单的test_qa.txt文件内容如下问公司的年假制度是怎样的 答员工累计工作已满1年不满10年的年休假5天已满10年不满20年的年休假10天已满20年的年休假15天。国家法定休假日、休息日不计入年休假的假期。 问报销流程需要哪些步骤 答报销流程分为四步1. 员工在OA系统填写报销单并附上发票。2. 部门经理审批。3. 财务部审核票据。4. 出纳付款。整个流程通常在5个工作日内完成。 问公司的核心技术是什么 答公司的核心技术是基于人工智能的智能决策引擎专注于自然语言处理和知识图谱构建。5.2 一键启动系统确保Ollama服务运行打开一个终端运行ollama serve。保持此终端运行。启动Web应用在项目根目录下激活虚拟环境运行python app.py访问应用打开浏览器访问http://localhost:7860。5.3 操作流程演示初始化系统在Web界面确认“知识文档目录”为./data“向量数据库存储目录”为./vector_db点击“初始化/重建系统”按钮。稍等片刻看到“系统初始化成功”的提示。开始提问在右侧聊天框输入问题例如“报销流程需要哪些步骤”点击发送。查看结果系统会返回基于知识库的答案并在下方列出答案所参考的文档片段及其来源。这正是RAG可解释性的体现。6. 常见问题与排查思路在实际部署和运行中你可能会遇到以下问题。这里提供详细的排查指南。问题现象可能原因排查步骤与解决方案启动app.py时报错ImportError1. 虚拟环境未激活。2. 依赖包未安装或版本冲突。1. 确认终端前有(rag_tutorial)环境标识。2. 运行pip install -r requirements.txt重新安装。3. 检查Python版本是否为3.8。初始化时提示“在./data中未找到支持的文档”1.data目录不存在。2. 目录内无文件或格式不支持。1. 检查项目根目录下是否有data文件夹。2. 检查data文件夹内是否有.pdf,.txt,.md等文件。3. 查看document_processor.py中supported_extensions变量。提问时报错Connection error或Ollama is not runningOllama服务未启动或模型未加载。1. 新开一个终端运行ollama serve。2. 在另一个终端运行ollama list确认模型存在。3. 尝试运行ollama run qwen2.5:0.5b测试模型是否正常。回答速度非常慢1. 模型太大硬件资源不足。2. 检索的文档块(k)过多。3. 嵌入模型在CPU上运行。1. 换用更小模型如qwen2.5:0.5b或llama3.2:3b。2. 在RAGChainBuilder中减少search_kwargs中的k值如从4改为2。3. 如果有NVIDIA GPU确保安装了torch的CUDA版本并在HuggingFaceEmbeddings中设置model_kwargs{device: cuda}。答案与文档内容无关幻觉1. 检索到的文档不相关。2. 提示词指令不够强。3. 模型温度(temperature)过高。1. 检查vector_store.py中的嵌入模型对于中文强烈建议使用BAAI/bge-small-zh-v1.5。2. 优化rag_chain.py中的提示词模板明确要求“基于上下文”。3. 在Ollama初始化时降低temperature如0.1。4. 在Web界面查看“参考来源”确认检索到的文档是否与问题相关。若不相关可能需要调整chunk_size或优化文档内容。回答“根据提供的资料我无法回答这个问题”1. 知识库中确实没有相关信息。2. 检索相似度阈值问题默认无阈值。1. 这是正常行为说明系统在忠实于资料避免了幻觉。2. 如果想扩大检索范围可以增加similarity_search的k值或尝试使用similarity_search_with_score并调整分数阈值。Gradio界面无法访问1. 端口被占用。2. 防火墙阻止。1. 修改app.py中demo.launch(server_port7861)换一个端口。2. 检查本地防火墙设置或尝试在局域网内用server_name0.0.0.0访问。7. 企业级最佳实践与进阶优化一个可用的Demo和一個健壮的企业级应用之间还有很长的路要走。以下是提升系统可靠性、性能和可维护性的关键实践。7.1 工程化与部署配置化管理将模型路径、Chroma目录、端口号等配置项抽离到config.yaml或.env文件中便于不同环境开发、测试、生产切换。# config.yaml rag: data_dir: ./data persist_dir: ./vector_db embedding_model: BAAI/bge-small-zh-v1.5 llm_model: qwen2.5:7b # 生产环境可用更大模型 chunk_size: 500 chunk_overlap: 50 retrieval_top_k: 4 server: host: 0.0.0.0 port: 7860日志记录使用Python的logging模块为系统添加详细的日志记录记录文档处理、检索、问答的全过程便于监控和调试。异常处理与降级在关键步骤如文档加载、模型调用添加健壮的异常处理。例如当Ollama服务不可用时可以降级为返回检索到的原始文本片段。容器化部署使用Docker将整个应用Python环境、Ollama服务打包确保环境一致性。可以编写Dockerfile和docker-compose.yml。7.2 性能与效果优化嵌入模型选择中文场景无脑选BAAI/bge-*系列。bge-large-zh-v1.5效果更好但更慢bge-small-zh-v1.5是性价比之选。多语言/英文场景可考虑sentence-transformers/all-MiniLM-L6-v2或intfloat/multilingual-e5-large。使用FAISS或Qdrant等向量数据库时可以启用HNSW等索引加速检索。文本分割策略优化递归分割对于结构规整的文档如Markdown可以按标题(#)进行分割能更好地保持语义完整性。语义分割使用SemanticChunker等更高级的分割器尝试根据语义边界而非固定长度分割但这需要额外的模型。重叠率调整对于技术文档适当增加chunk_overlap如100-150有助于复杂概念的连贯性。检索策略增强重排序初步检索出k10个结果后使用一个更精细的交叉编码器模型如BGE-reranker对结果进行重排序只取前3个最相关的送入LLM能显著提升答案相关性。混合检索结合关键词检索如BM25和向量检索兼顾精确匹配和语义相似度。LangChain的EnsembleRetriever可以轻松实现。元数据过滤在存储文档时为其添加元数据如文档类型、部门、更新时间。检索时可以添加过滤器例如“只检索部门人力资源且年份2024的文档”实现更精准的查询。提示词工程在提示词中明确角色“你是一个专业的XX领域助手”。要求答案格式“请用分点列表的形式回答”。处理无答案情况“如果上下文不包含相关信息请明确说明‘该信息未在提供的资料中找到’”。7.3 安全与权限数据隐私本项目所有组件嵌入模型、LLM、向量数据库均在本地运行确保了企业数据的绝对安全。这是选择Ollama本地嵌入模型的核心优势。访问控制在生产环境Gradio前端必须配置身份验证或置于内网网关之后。可以考虑集成公司现有的SSO系统。输入输出过滤对用户的提问和模型的回答进行基本的敏感词过滤防止恶意输入或不当输出。7.4 知识库维护增量更新Chroma支持增量添加文档。可以编写一个定时任务或监听文件夹变动的脚本自动将新文档添加到向量库而无需全量重建。版本管理当文档大规模更新时建议为向量数据库建立版本例如vector_db_v2并与应用版本绑定便于回滚。效果评估定期准备一批“问题-标准答案”对对系统进行自动化测试监控其回答准确率的变化。通过以上步骤你已经成功搭建并理解了一个从文档处理到交互式问答的完整企业级RAG系统。这个项目骨架为你提供了坚实的基础你可以根据具体的业务需求在检索精度、回答质量、系统性能和安全合规等方面进行深度定制和扩展。记住RAG项目的成功三分在技术七分在知识库文档的质量与治理。从整理一份清晰、准确、结构化的内部文档开始你的智能助手就已经成功了一半。
返回列表