使用 LangChain + Ollama 构建本地 RAG 应用

# 使用 LangChain + Ollama 构建本地 RAG 应用

## 背景介绍

大语言模型很聪明,但它不知道你公司内部的事情。你问它”我们上季度的技术方案是什么”,它只能道歉。这就是 RAG(检索增强生成)要解决的问题——先从你的知识库里找到相关文档,再把内容喂给模型让它回答。

搭建 RAG 通常得依赖云端 API,数据要上传到 OpenAI 或 Anthropic 的服务器。有些公司不乐意,个人项目也不想花这笔钱。Ollama 出来后,在本地跑大模型变得很方便。LangChain 配套的组件足够完善,两者一结合,一个完全本地运行的 RAG 系统就出来了。

## 问题描述

实际做的时候会遇到几个具体问题:

**接口版本**:LangChain 现在的版本是 0.2.x,但网上很多教程还是 0.1.x 的写法。`langchain_community` 和 `langchain_ollama` 的调用方式不一样,用错了就会报错。

**向量模型**:文本要转成向量才能做相似性搜索。开源的 embedding 模型不少,Ollama 自带的 `nomic-embed-text` 足够用了,不用再额外部署 sentence-transformers。

**文档格式**:PDF、Word、Markdown 都得处理。分块是技术活,块太大了检索不准,太小了上下文不完整。

**检索效果**:光靠语义相似有时不够干净,可能需要 reranking 或者混合关键词搜索。

## 详细步骤

### 1. 环境准备

Ollama 安装很简单。macOS 用 Homebrew:

“`bash
brew install ollama
ollama serve
“`

Linux 直接跑安装脚本:

“`bash
curl -fsSL https://ollama.com/install.sh | sh
“`

装好后拉模型:

“`bash
ollama pull mistral
ollama pull nomic-embed-text
“`

Python 环境:

“`bash
python -m venv venv
source venv/bin/activate
pip install langchain langchain-community langchain-ollama langchain-text-splitters langchain-huggingface faiss-cpu beautifulsoup4 requests
“`

注意 `langchain-ollama` 是官方包,0.1.x 和 0.2.x 的 import 路径可能不一样,用的时候注意一下版本。

### 2. 实现 RAG 流水线

新建 `rag_pipeline.py`,实现文档加载、分块、向量存储和检索。

#### 2.1 文档加载与处理

“`python
import os
from pathlib import Path
from typing import List
from langchain_community.document_loaders import (
TextLoader,
PyPDFLoader,
UnstructuredMarkdownLoader,
BSHTMLLoader
)
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_ollama import OllamaEmbeddings

class DocumentProcessor:
“””文档处理器:加载和分块文档”””

def __init__(self, chunk_size: int = 500, chunk_overlap: int = 50):
self.chunk_size = chunk_size
self.chunk_overlap = chunk_overlap
self.text_splitter = RecursiveCharacterTextSplitter(
chunk_size=chunk_size,
chunk_overlap=chunk_overlap,
separators=[“\n\n”, “\n”, “。”, “!”, “?”, ” “, “”]
)

def load_document(self, file_path: str):
“””根据文件类型选择合适的加载器”””
path = Path(file_path)
suffix = path.suffix.lower()

loaders = {
.txt: TextLoader,
.md: UnstructuredMarkdownLoader,
.pdf: PyPDFLoader,
.html: BSHTMLLoader,
.htm: BSHTMLLoader
}

loader_class = loaders.get(suffix, TextLoader)
loader = loader_class(file_path, encoding=”utf-8″)
return loader.load()

def process_file(self, file_path: str) -> List[str]:
“””加载并分块文档”””
documents = self.load_document(file_path)
chunks = self.text_splitter.split_documents(documents)
return chunks
“`

这段代码支持 txt、md、pdf、html 几种常见格式。`RecursiveCharacterTextSplitter` 按段落和句子递归切分,`chunk_size=500` 表示每块大约 500 字符,`chunk_overlap=50` 让相邻块有 50 字符重叠,防止关键信息被切断。

#### 2.2 向量存储与检索

“`python
from langchain.vectorstores import FAISS
from langchain_ollama import OllamaLLM

class RAGSystem:
“””RAG 系统核心类”””

def __init__(self, embed_model: str = “nomic-embed-text”,
llm_model: str = “mistral”):
# 初始化本地嵌入模型
self.embeddings = OllamaEmbeddings(
model=embed_model,
base_url=”http://localhost:11434″
)

# 初始化本地 LLM
self.llm = OllamaLLM(
model=llm_model,
base_url=”http://localhost:11434″
)

self.vectorstore = None
self.processor = DocumentProcessor()

def build_index(self, documents: List[str]):
“””构建向量索引”””
self.vectorstore = FAISS.from_texts(
documents,
embedding=self.embeddings
)
print(f”索引构建完成,共 {len(documents)} 个文档块”)

def build_index_from_files(self, file_paths: List[str]):
“””从多个文件构建索引”””
all_chunks = []
for file_path in file_paths:
chunks = self.processor.process_file(file_path)
all_chunks.extend([chunk.page_content for chunk in chunks])

self.build_index(all_chunks)
return self

def retrieve(self, query: str, k: int = 3) -> List[str]:
“””检索相关文档”””
if self.vectorstore is None:
raise ValueError(“请先构建索引”)

docs = self.vectorstore.similarity_search(query, k=k)
return [doc.page_content for doc in docs]

def answer(self, query: str, k: int = 3) -> str:
“””检索并生成答案”””
# 检索相关文档
relevant_docs = self.retrieve(query, k)

# 构建提示词
context = “\n\n”.join(relevant_docs)
prompt = f”””基于以下参考文档回答问题。如果文档中没有相关信息,请如实说明。

参考文档:
{context}

问题:{query}

回答:”””

# 调用本地 LLM
response = self.llm.invoke(prompt)
return response
“`

FAISS 是 Facebook 开发的向量检索库,速度快且支持多种索引类型。`OllamaEmbeddings` 调用本地的 nomic-embed-text 生成向量,`OllamaLLM` 调用本地的 mistral 生成回答。检索结果通过 prompt 注入上下文。

#### 2.3 运行示例

`main.py` 脚本演示完整流程:

“`python
from rag_pipeline import RAGSystem

def main():
# 创建 RAG 系统
rag = RAGSystem(
embed_model=”nomic-embed-text”,
llm_model=”mistral”
)

# 准备示例文档
docs = [
“LangChain 是一个用于构建 LLM 应用的框架。”,
“它提供了丰富的组件,包括模型调用、提示词管理和链式调用。”,
“Ollama 是一个在本地运行大语言模型的工具。”,
“它支持多种模型,如 Llama、Mistral 和 CodeLlama。”,
“RAG 是检索增强生成的缩写,是一种让 LLM 访问私有知识库的技术。”,
“向量数据库用于存储文本的向量表示,支持高效相似性搜索。”
]

# 构建索引
print(“正在构建向量索引…”)
rag.build_index(docs)

# 测试问答
test_queries = [
“LangChain 是什么?”,
“Ollama 支持哪些模型?”,
“RAG 的全称是什么?”
]

print(“\n” + “=”*60)
print(“开始问答测试”)
print(“=”*60)

for query in test_queries:
print(f”\n问题: {query}”)
print(“-” * 40)
answer = rag.answer(query)
print(f”回答: {answer}”)

if __name__ == “__main__”:
main()
“`

## 运行结果

执行 `python main.py`:

“`
正在构建向量索引…
索引构建完成,共 6 个文档块

============================================================
开始问答测试
============================================================

问题: LangChain 是什么?
—————————————-
回答: LangChain 是一个用于构建 LLM 应用的框架,它提供了丰富的组件,包括模型调用、提示词管理和链式调用。

问题: Ollama 支持哪些模型?
—————————————-
回答: Ollama 支持多种模型,如 Llama、Mistral 和 CodeLlama。

问题: RAG 的全称是什么?
—————————————-
回答: RAG 是检索增强生成的缩写(Retrieval-Augmented Generation),是一种让 LLM 访问私有知识库的技术。
“`

系统成功检索了相关文档并生成了正确答案。检索到的内容与问题匹配,LLM 基于这些上下文给出了准确的回答。

如果遇到 “connection refused” 错误,说明 Ollama 服务没启动。确保先运行 `ollama serve`,然后用 `ollama list` 确认模型已下载。

## 总结

这篇文章写了用 LangChain + Ollama 搭建本地 RAG 的全过程。核心就两个东西:Ollama 提供本地模型能力,LangChain 负责把检索和生成串起来。数据全程不用上网,存在自己机器上。

几个关键点:

**轻量**:不需要额外的向量数据库服务。FAISS 索引就是几个文件,复制粘贴就能迁移。

**模型选择**:7B 参数的量化模型在普通 GPU 上就能跑,内存要求不算高。mistral 和 llama3 都行,看具体需求。

**可扩展**:现在是最基础的向量检索,后面可以加混合搜索、加 reranking、加对话历史管理。

后续可以优化的方向:用 bge-m3 这种更强embedding模型、加入关键词索引做混合搜索、对检索结果做 reranking、支持更多文档格式、做多轮对话。

完整代码扔在 GitHub 上了,有需要自取。希望这篇文章对你有帮助。

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇