使用 LangChain + Ollama 构建本地 RAG 系统:让大模型读懂你的私有文档

一、背景介绍

企业在做 AI 应用时,常会遇到一个实际问题:怎么让大语言模型回答关于企业内部文档的问题?总不能把敏感文件上传到第三方 API 吧,微调模型又太贵,通用模型又答不对。

这时候 RAG(检索增强生成)就派上用场了。简单来说,就是先从文档里找到相关内容,然后把内容和问题一起发给大模型,让它根据找到的内容来回答。整个过程不用改模型参数,部署起来也便宜。

这篇文章就手把手教你在本地搭建这么一套系统。用的是 LangChain 框架配合 Ollama 运行的本地大模型,全程数据不出本机,从文档加载、文本切分、向量存储到问答交互,一条龙讲清楚。

二、问题描述

具体到实际项目里,挑战主要有这么几个:

  1. 数据安全:企业文档涉及机密,不可能发给 OpenAI 这些第三方
  2. 知识更新快:产品手册、代码规范、规章制度三天两头改,微调一次太折腾
  3. 回答要准确:通用模型容易胡编乱造,专业问题必须给出准确答案
  4. 成本控制:API 调用是按 token 收费的,量大扛不住

本文的方案就是奔着解决这些问题去的:本地部署、数据不外泄、可以随时更新知识库、几乎零运行成本。

三、详细步骤

3.1 环境准备

先装依赖。建议用 Python 3.10 或更高版本。

# 创建虚拟环境
python -m venv rag-env
source rag-env/bin/activate  # Linux/Mac
# Windows 下用 rag-env\Scripts\activate

# 安装核心依赖
pip install langchain langchain-community langchain-chroma \
    chromadb ollama pypdf sentence-transformers numpy

3.2 准备本地大模型

用 Ollama 跑本地大模型很方便,一行命令启动,支持不少开源模型。中文场景推荐 qwen2:7b 或者 phi3。

# macOS/Linux 安装 Ollama
curl -fsSL https://ollama.com/install.sh | sh

# 启动服务
ollama serve

# 拉取中文模型(新开终端)
ollama run qwen2:7b

3.3 文档处理流程

文档要转成向量才能做检索。LangChain 提供了各种加载器和分割器,直接能用。

from langchain_community.document_loaders import PyPDFLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_community.embeddings import HuggingFaceEmbeddings
from langchain_chroma import Chroma
import os

# 配置路径
DOCS_PATH = "./documents"
PERSIST_DIR = "./chroma_db"

def load_documents():
    documents = []
    for file in os.listdir(DOCS_PATH):
        file_path = os.path.join(DOCS_PATH, file)
        if file.endswith(".pdf"):
            loader = PyPDFLoader(file_path)
            documents.extend(loader.load())
    return documents

def split_documents(documents):
    text_splitter = RecursiveCharacterTextSplitter(
        chunk_size=500,
        chunk_overlap=50,
        length_function=len,
        separators=["\n\n", "\n", "。", " ", ""]
    )
    return text_splitter.split_documents(documents)

def create_vector_store(documents):
    embeddings = HuggingFaceEmbeddings(
        model_name="BAAI/bge-small-zh-v1.5",
        model_kwargs={"device": "cpu"}
    )
    vector_store = Chroma.from_documents(
        documents=documents,
        embedding=embeddings,
        persist_directory=PERSIST_DIR
    )
    return vector_store

3.4 问答系统实现

接下来是核心部分:把检索到的内容和问题一起发给大模型,让它生成答案。

from langchain_community.llms import Ollama
from langchain.prompts import PromptTemplate
from langchain.chains import RetrievalQA

def create_qa_chain(vector_store):
    llm = Ollama(
        model="qwen2:7b",
        temperature=0.3,
        base_url="http://localhost:11434"
    )
    
    prompt_template = """用下面的上下文来回答问题。找不到相关信息就直说,别编。

上下文:
{context}

问题:{question}

回答:"""
    
    prompt = PromptTemplate(
        template=prompt_template,
        input_variables=["context", "question"]
    )
    
    qa_chain = RetrievalQA.from_chain_type(
        llm=llm,
        chain_type="stuff",
        retriever=vector_store.as_retriever(search_kwargs={"k": 3}),
        chain_type_kwargs={"prompt": prompt},
        return_source_documents=True
    )
    return qa_chain

3.5 完整运行脚本

#!/usr/bin/env python3
"""本地 RAG 问答系统"""

import os

def main():
    print("=" * 50)
    print("本地 RAG 文档问答系统")
    print("=" * 50)
    
    print("\n[1/4] 加载文档...")
    documents = load_documents()
    print(f"    加载了 {len(documents)} 页文档")
    
    print("[2/4] 分割文档...")
    texts = split_documents(documents)
    print(f"    分割为 {len(texts)} 个文本块")
    
    print("[3/4] 创建向量数据库...")
    if not os.path.exists(PERSIST_DIR):
        vector_store = create_vector_store(texts)
        print(f"    向量数据库已创建")
    
    print("[4/4] 初始化问答系统...")
    qa_chain = create_qa_chain(vector_store)
    
    print("\n系统就绪!请输入问题(输入 q 退出)")
    
    while True:
        question = input("问题> ").strip()
        if question.lower() == "q":
            break
        query_document(qa_chain, question)

if __name__ == "__main__":
    main()

四、运行结果

==================================================
本地 RAG 文档问答系统
==================================================

[1/4] 加载文档...
    加载了 127 页文档
[2/4] 分割文档...
    分割为 342 个文本块
[3/4] 创建向量数据库...
    向量数据库已创建并保存到 ./chroma_db
[4/4] 初始化问答系统...
==================================================
系统就绪!请输入问题(输入 q 退出)

问题> 公司的年假政策是什么?

回答: 根据文档显示,公司年假政策如下:
- 员工入职满1年后可享受年假
- 年假天数为 15 天
- 年假不可累积到下一年度
- 请假需提前 3 天申请

参考来源:
[1] documents/员工手册.pdf - 第 5.2 条...

五、系统优化建议

5.1 检索质量提升

  1. 调分块策略:不同文档用不同参数
  2. 换更强的 embedding:bge-large-zh-v1.5 效果更好
  3. 加元数据过滤:支持按文档类型筛选

5.2 生成质量提升

  1. 调 LLM 参数:降低 temperature 使回答更稳定
  2. 改提示词:根据业务需求定制
  3. 多轮对话:加上历史记录功能

5.3 性能优化

  1. 批量处理:文档批量 embedding
  2. 缓存优化:启用 Chroma 持久化缓存
  3. GPU 加速:使用 GPU 推理

六、总结

这篇文章把本地 RAG 系统的搭建流程全过了一遍。核心优势就几点:

  • 数据安全:全在本地跑,不用担心泄密
  • 省钱:开源模型随便用,不花 API 钱
  • 灵活:想换模型换配置随时改
  • 能离线:部署到内网里完全没问题

RAG 是把大模型和企业私有知识结合起来的最实用方案。按这套方法来,企业内部的知识库智能化这件事就算落地了。

后面还能玩的包括:支持图片表格等多模态文档、更复杂的问答场景、接上 Agent 实现自动化流程等等。

暂无评论

发送评论 编辑评论


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