【实战】LangChain|Ollama本地模型快速集成与RAG应用构建指南(附完整代码)

【实战】LangChain|Ollama本地模型快速集成与RAG应用构建指南(附完整代码) 1. 环境准备与工具安装在开始构建RAG应用之前我们需要先准备好开发环境和必要的工具。Ollama和LangChain的组合为本地大语言模型应用开发提供了极大的便利但正确安装和配置是成功的第一步。1.1 安装OllamaOllama是一个开源的本地大语言模型运行平台它简化了模型的管理和部署过程。安装Ollama非常简单根据不同的操作系统安装方式略有不同对于Linux/macOS用户可以直接使用以下命令安装curl -fsSL https://ollama.com/install.sh | shWindows用户可以从Ollama官网下载安装程序。安装完成后可以通过运行ollama --version来验证安装是否成功。安装完成后我们需要下载所需的语言模型。Ollama支持多种开源模型比如Llama 3.1、Mistral等。以下命令可以下载Llama 3.1模型ollama pull llama3.11.2 安装LangChain及相关依赖LangChain是一个用于构建大语言模型应用的框架它提供了丰富的组件和工具来简化开发流程。我们需要安装LangChain的核心库以及与Ollama集成的扩展pip install langchain langchain-ollama此外为了构建完整的RAG应用我们还需要安装一些额外的依赖pip install faiss-cpu sentence-transformers pypdf这些库将帮助我们处理文档加载、文本分割和向量存储等任务。如果你有GPU设备可以考虑安装faiss-gpu以获得更好的性能。1.3 验证安装安装完成后我们可以通过一个简单的测试脚本来验证所有组件是否正常工作from langchain_ollama import ChatOllama llm ChatOllama(modelllama3.1) response llm.invoke(Tell me a joke about AI) print(response)如果一切正常你应该能看到模型生成的幽默回复。这个简单的测试确认了Ollama模型能够通过LangChain正常调用。2. 加载与处理文档构建RAG应用的核心之一是能够有效地处理和准备文档数据。这一部分我们将详细介绍如何从不同来源加载文档并进行适当的预处理。2.1 文档加载LangChain提供了多种文档加载器支持从不同来源加载文档。以下是一些常见的文档加载方式从PDF文件加载from langchain_community.document_loaders import PyPDFLoader loader PyPDFLoader(example.pdf) documents loader.load()从网页加载from langchain_community.document_loaders import WebBaseLoader loader WebBaseLoader(https://example.com) documents loader.load()从纯文本文件加载from langchain_community.document_loaders import TextLoader loader TextLoader(example.txt) documents loader.load()每种加载器都会返回一个Document对象的列表每个Document对象包含页面内容和元数据。在实际应用中你可能需要根据文档类型选择合适的加载器。2.2 文本分割加载后的文档通常需要进行分割以适应模型的上下文窗口限制。LangChain提供了多种文本分割器最常用的是递归字符文本分割器from langchain_text_splitters import RecursiveCharacterTextSplitter text_splitter RecursiveCharacterTextSplitter( chunk_size1000, chunk_overlap200, length_functionlen, is_separator_regexFalse, ) split_documents text_splitter.split_documents(documents)这里有几个关键参数需要注意chunk_size每个文本块的最大长度chunk_overlap块之间的重叠字符数有助于保持上下文连贯length_function用于计算文本长度的函数选择合适的分割策略对后续的检索效果有很大影响。一般来说技术文档适合较小的块大小500-1000字符而叙述性内容可以使用较大的块。2.3 文档预处理在将文档存入向量数据库之前可能还需要进行一些预处理def preprocess_text(text): # 移除多余的空格和换行符 text .join(text.split()) # 这里可以添加其他预处理步骤 return text for doc in split_documents: doc.page_content preprocess_text(doc.page_content)预处理步骤可以根据具体需求定制比如移除特殊字符、标准化术语等。保持文档的整洁有助于提高后续检索的准确性。3. 向量存储与检索向量存储是RAG系统的核心组件它使我们能够高效地检索与用户查询相关的文档片段。这一部分我们将详细介绍如何使用Ollama的嵌入模型和FAISS向量数据库。3.1 初始化嵌入模型Ollama提供了高质量的嵌入模型我们可以通过LangChain轻松使用from langchain_community.embeddings import OllamaEmbeddings embeddings OllamaEmbeddings(modelllama3.1)这个嵌入模型将把文本转换为高维向量表示。我们可以测试一下嵌入功能text LangChain是一个强大的AI应用框架 vector embeddings.embed_query(text) print(f向量维度{len(vector)})3.2 创建向量存储FAISS是Meta开发的高效向量相似性搜索库非常适合本地RAG应用from langchain_community.vectorstores import FAISS vectorstore FAISS.from_documents( documentssplit_documents, embeddingembeddings )创建完成后我们可以将向量存储保存到本地以便后续使用vectorstore.save_local(faiss_index)需要重新加载时可以使用vectorstore FAISS.load_local(faiss_index, embeddings)3.3 文档检索有了向量存储后我们可以实现文档检索功能retriever vectorstore.as_retriever(search_kwargs{k: 3}) query 如何在LangChain中使用Ollama? relevant_docs retriever.get_relevant_documents(query) for i, doc in enumerate(relevant_docs): print(f文档 {i1}:) print(doc.page_content[:200] ...) # 打印前200个字符 print(- * 50)检索器会根据查询与文档的语义相似度返回最相关的文档片段。search_kwargs中的k参数控制返回的文档数量。为了提高检索质量我们可以尝试不同的检索策略# 最大边际相关性搜索平衡相似性和多样性 retriever vectorstore.as_retriever( search_typemmr, search_kwargs{k: 3, lambda_mult: 0.25} )4. 构建完整的RAG链现在我们已经准备好了所有组件可以将它们组合成一个完整的RAG系统。这一部分将展示如何构建一个能够基于私有知识库回答问题的智能应用。4.1 创建基础问答链首先我们需要定义一个提示模板指导模型如何利用检索到的上下文回答问题from langchain_core.prompts import ChatPromptTemplate template 基于以下上下文回答问题 context {context} /context 问题{input} prompt ChatPromptTemplate.from_template(template)接下来我们创建问答链from langchain_ollama.chat_models import ChatOllama from langchain.chains.combine_documents import create_stuff_documents_chain llm ChatOllama(modelllama3.1, temperature0.2) document_chain create_stuff_documents_chain(llm, prompt)4.2 集成检索器将检索器与问答链结合创建完整的RAG链from langchain.chains import create_retrieval_chain retrieval_chain create_retrieval_chain(retriever, document_chain)现在我们可以测试这个RAG系统response retrieval_chain.invoke({input: 如何在LangChain中使用Ollama?}) print(response[answer])4.3 添加对话历史为了使系统支持多轮对话我们需要引入对话历史管理from langchain_core.prompts import MessagesPlaceholder from langchain_core.messages import HumanMessage, AIMessage chat_template 基于以下上下文和对话历史回答问题 context {context} /context chat_history {chat_history} /chat_history 问题{input} chat_prompt ChatPromptTemplate.from_messages([ (system, 你是一个乐于助人的AI助手。), MessagesPlaceholder(variable_namechat_history), (human, {input}), ]) document_chain create_stuff_documents_chain(llm, chat_prompt)然后创建一个支持对话历史的检索链from langchain.chains import create_history_aware_retriever history_aware_retriever create_history_aware_retriever( llm, retriever, chat_prompt ) conversational_rag_chain create_retrieval_chain( history_aware_retriever, document_chain )使用示例chat_history [] question 如何在LangChain中使用Ollama? response conversational_rag_chain.invoke({ input: question, chat_history: chat_history }) chat_history.extend([ HumanMessage(contentquestion), AIMessage(contentresponse[answer]) ]) follow_up 能给我一个代码示例吗? response conversational_rag_chain.invoke({ input: follow_up, chat_history: chat_history }) print(response[answer])5. 高级功能与优化构建基础RAG系统后我们可以进一步优化和扩展其功能。这一部分将介绍一些高级技巧和优化策略。5.1 模型切换与比较Ollama支持多种模型我们可以轻松切换不同的模型进行比较models [llama3.1, mistral, neural-chat] for model_name in models: print(f\n使用模型: {model_name}) llm ChatOllama(modelmodel_name, temperature0.2) retrieval_chain create_retrieval_chain(retriever, document_chain) response retrieval_chain.invoke({input: 解释一下RAG架构}) print(response[answer][:500] ...)不同模型在理解能力、生成质量和速度上可能有所不同。在实际应用中可以根据需求选择合适的模型。5.2 检索优化为了提高检索质量我们可以尝试以下优化策略调整分块大小和重叠text_splitter RecursiveCharacterTextSplitter( chunk_size800, chunk_overlap150, separators[\n\n, \n, 。, , ] )使用混合检索策略from langchain.retrievers import BM25Retriever, EnsembleRetriever bm25_retriever BM25Retriever.from_documents(split_documents) bm25_retriever.k 2 ensemble_retriever EnsembleRetriever( retrievers[retriever, bm25_retriever], weights[0.7, 0.3] )添加元数据过滤retriever vectorstore.as_retriever( search_kwargs{ k: 3, filter: {source: official_docs.pdf} } )5.3 性能监控与评估为了确保系统性能我们可以添加监控和评估机制from langchain.callbacks import StdOutCallbackHandler handler StdOutCallbackHandler() response retrieval_chain.invoke( {input: 如何在LangChain中使用Ollama?}, config{callbacks: [handler]} )对于更详细的评估可以计算检索准确率和生成质量questions [问题1, 问题2, 问题3] expected_answers [答案1, 答案2, 答案3] correct 0 for q, expected in zip(questions, expected_answers): response retrieval_chain.invoke({input: q}) if expected in response[answer]: correct 1 print(f准确率: {correct/len(questions):.2f})6. 部署与应用完成开发和优化后我们需要考虑如何部署和使用这个RAG系统。这一部分将介绍几种常见的部署方式。6.1 本地API服务使用FastAPI创建一个简单的API服务from fastapi import FastAPI from pydantic import BaseModel import uvicorn app FastAPI() class Query(BaseModel): text: str chat_history: list [] app.post(/ask) async def ask_question(query: Query): response conversational_rag_chain.invoke({ input: query.text, chat_history: query.chat_history }) return {answer: response[answer]} if __name__ __main__: uvicorn.run(app, host0.0.0.0, port8000)启动服务后可以通过HTTP请求与RAG系统交互curl -X POST http://localhost:8000/ask \ -H Content-Type: application/json \ -d {text:如何在LangChain中使用Ollama?}6.2 命令行界面创建一个简单的命令行交互界面def chat_interface(): chat_history [] print(RAG系统已启动输入exit退出) while True: user_input input(你: ) if user_input.lower() exit: break response conversational_rag_chain.invoke({ input: user_input, chat_history: chat_history }) print(fAI: {response[answer]}) chat_history.extend([ HumanMessage(contentuser_input), AIMessage(contentresponse[answer]) ]) chat_interface()6.3 集成到现有系统将RAG系统集成到现有Python应用中class RAGAssistant: def __init__(self): self.llm ChatOllama(modelllama3.1) self.embeddings OllamaEmbeddings(modelllama3.1) self.vectorstore FAISS.load_local(faiss_index, self.embeddings) self.retriever self.vectorstore.as_retriever() self.chat_history [] def ask(self, question): response conversational_rag_chain.invoke({ input: question, chat_history: self.chat_history }) self.chat_history.extend([ HumanMessage(contentquestion), AIMessage(contentresponse[answer]) ]) return response[answer] assistant RAGAssistant() print(assistant.ask(如何在LangChain中使用Ollama?))7. 故障排除与常见问题在实际使用中可能会遇到各种问题。这一部分将介绍一些常见问题及其解决方法。7.1 模型加载问题如果遇到模型加载失败的问题可以尝试以下步骤检查Ollama服务是否运行ollama list确保模型已正确下载ollama pull llama3.1检查模型名称是否正确查看所有可用模型ollama list7.2 性能问题如果系统响应缓慢可以考虑以下优化减少检索文档数量retriever vectorstore.as_retriever(search_kwargs{k: 2})使用更小的模型llm ChatOllama(modelllama3.1:7b)启用GPU加速如果有llm ChatOllama(modelllama3.1, devicecuda)7.3 检索质量不佳如果检索结果不相关可以尝试调整文本分割策略text_splitter RecursiveCharacterTextSplitter( chunk_size500, chunk_overlap100 )尝试不同的嵌入模型embeddings OllamaEmbeddings(modelnomic-embed-text)添加查询扩展from langchain.retrievers import ContextualCompressionRetriever from langchain.retrievers.document_compressors import LLMChainExtractor compressor LLMChainExtractor.from_llm(llm) compression_retriever ContextualCompressionRetriever( base_compressorcompressor, base_retrieverretriever )7.4 内存问题处理大量文档时可能会遇到内存不足的问题使用更高效的向量数据库from langchain_community.vectorstores import Chroma vectorstore Chroma.from_documents( documentssplit_documents, embeddingembeddings, persist_directory./chroma_db )分批处理文档for i in range(0, len(documents), 100): batch documents[i:i100] vectorstore.add_documents(batch)使用更小的嵌入维度embeddings OllamaEmbeddings(modelllama3.1, embedding_dim384)8. 实际应用案例为了更好地理解如何应用这些技术让我们看几个实际的RAG应用场景。8.1 技术文档问答系统假设我们有一系列技术文档可以构建一个专门的问答系统# 加载技术文档 loader PyPDFLoader(technical_docs.pdf) docs loader.load() # 专门的技术文档处理 technical_splitter RecursiveCharacterTextSplitter( chunk_size800, chunk_overlap150, separators[\n\n, \n, 。, ;, , ] ) technical_docs technical_splitter.split_documents(docs) # 创建专门的向量存储 technical_store FAISS.from_documents(technical_docs, embeddings) technical_store.save_local(tech_faiss_index) # 创建技术问答链 tech_template 你是一个技术专家助手基于以下技术文档回答问题 context {context} /context 问题{input} 请用专业但易懂的语言回答必要时提供代码示例。 tech_prompt ChatPromptTemplate.from_template(tech_template) tech_chain create_stuff_documents_chain(llm, tech_prompt) tech_retriever technical_store.as_retriever() tech_rag_chain create_retrieval_chain(tech_retriever, tech_chain) # 使用示例 response tech_rag_chain.invoke({ input: 如何在LangChain中实现自定义记忆机制? }) print(response[answer])8.2 法律文件分析对于法律文件我们可以构建一个专门的分析工具# 法律文档处理 legal_splitter RecursiveCharacterTextSplitter( chunk_size600, chunk_overlap100, separators[\n\nArticle, \nSection, \n\n, \n, 。, ] ) legal_loader PyPDFLoader(contract.pdf) legal_docs legal_splitter.split_documents(legal_loader.load()) # 法律专用提示模板 legal_template 你是一个法律专家基于以下合同条款回答问题 context {context} /context 问题{input} 请准确引用相关条款用严谨的法律语言回答。 legal_prompt ChatPromptTemplate.from_template(legal_template) legal_llm ChatOllama(modelllama3.1, temperature0.1) # 使用更低温度以获得更确定性回答 legal_chain create_stuff_documents_chain(legal_llm, legal_prompt) # 创建法律文档向量存储 legal_store FAISS.from_documents(legal_docs, embeddings) legal_retriever legal_store.as_retriever(search_kwargs{k: 2}) legal_rag_chain create_retrieval_chain(legal_retriever, legal_chain) # 使用示例 response legal_rag_chain.invoke({ input: 合同中对违约金的约定是什么? }) print(response[answer])8.3 多语言支持RAG系统可以扩展支持多语言# 多语言文档处理 multi_lingual_loader WebBaseLoader([https://example.com/en, https://example.com/es]) multi_lingual_docs multi_lingual_loader.load() # 多语言提示模板 multi_lingual_template 请根据以下上下文回答问题使用与问题相同的语言 context {context} /context 问题{input} multi_lingual_prompt ChatPromptTemplate.from_template(multi_lingual_template) multi_lingual_chain create_stuff_documents_chain(llm, multi_lingual_prompt) # 创建多语言向量存储 multi_lingual_store FAISS.from_documents(multi_lingual_docs, embeddings) multi_lingual_retriever multi_lingual_store.as_retriever() multi_lingual_rag_chain create_retrieval_chain(multi_lingual_retriever, multi_lingual_chain) # 使用示例 - 英文 response_en multi_lingual_rag_chain.invoke({ input: What are the main features of this product? }) print(response_en[answer]) # 使用示例 - 西班牙文 response_es multi_lingual_rag_chain.invoke({ input: ¿Cuáles son las principales características de este producto? }) print(response_es[answer])