使用 Ollama + LangChain 构建本地 RAG 知识库系统

背景介绍

大语言模型现在很火,但直接用云端 API 有不少问题。数据要传出去、公司机密可能泄露,每个月调用费用也是笔开销。最麻烦的是网络一不稳定,整个服务就挂掉。

Ollama 这东西让在本地跑 LLM 变成了现实。它支持 Llama 2、Mistral、Gemma 这些开源模型,macOS 和 Linux 都能装,一行命令就把模型拉下来用。

光有模型不够用。有时候我们需要让 AI 学特定领域的知识,比如公司内部文档、产品手册,这时候 RAG(检索增强生成)就派上用场了。它把外部知识库和 LLM 结合起来,能用我们自己的数据来回答问题。

这篇文章就说清楚:怎么在普通电脑上搭建一个完整的 RAG 系统,用 Ollama 提供模型,LangChain 处理文档、抽文本、存向量、最后问答。

问题描述

几个常见的真实需求:

  • 企业内部有大量内部文档、技术手册、产品资料,想做个 AI 助手来帮员工查东西。
  • 文档散落在各处,想把它们整成一个统一的问答系统,用自然语言来查。
  • 数据敏感或者网络受限,必须在本地跑,不能用任何第三方云服务。

以前要实现这些,得买 GPU 服务器,或者花大钱买商业 API。现在消费级电脑也能跑了,Ollama + LangChain 这套组合就是解决方案。

核心问题就一个:怎么把本地的 PDF、Markdown、TXT 这些文档变成可检索的知识库,再基于知识库做出问答系统。

详细步骤

环境准备

先装好需要的软件。

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

# 拉取模型
ollama pull llama2

# 创建项目目录
mkdir -p ~/rag-demo && cd ~/rag-demo

# 创建 Python 虚拟环境
python -m venv venv
source venv/bin/activate  # Linux/macOS 用这句
# Windows 上用: venv\Scripts\activate

# 安装依赖包
pip install langchain langchain-community langchain-openai \
    chromadb beautifulsoup4 pypdf pydantic \
    sentence-transformers

核心流程

RAG 系统实际就绕着这几个步骤转:

  1. 文档加载:从 PDF、Markdown、TXT 这些文件里把文字内容抽出来
  2. 文本分块:把长文章切成小段
  3. 向量化:把小段文字转成向量
  4. 向量存库:把向量存到数据库里,方便做相似性搜索
  5. 问答检索:用户问了问题,去库里找相关的文档片段
  6. 生成回答:把找到的文档和用户问题一起发给 LLM,让它生成答案

完整代码示例

1. 配置和初始化

import os
from langchain_community.chat_models import ChatOllama
from langchain_community.embeddings import HuggingFaceEmbeddings
from langchain_community.vectorstores import Chroma
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_community.document_loaders import (
    PyPDFLoader, 
    TextLoader, 
    MarkdownLoader
)
from langchain.chains import RetrievalQA

# 初始化 Ollama 模型
llm = ChatOllama(
    model="llama2",
    temperature=0.7,
    base_url="http://localhost:11434"
)

# 向量化模型配置
embeddings = HuggingFaceEmbeddings(
    model_name="sentence-transformers/all-MiniLM-L6-v2",
    model_kwargs={device: cpu}
)

2. 文档加载

from pathlib import Path

def load_documents(directory: str):
    """遍历目录,把所有能识别的文档都加载进来"""
    documents = []
    path = Path(directory)
    
    for file_path in path.rglob("*"):
        if file_path.is_file():
            try:
                if file_path.suffix == .pdf:
                    loader = PyPDFLoader(str(file_path))
                    documents.extend(loader.load())
                elif file_path.suffix == .txt:
                    loader = TextLoader(str(file_path), encoding=utf-8)
                    documents.extend(loader.load())
                elif file_path.suffix == .md:
                    loader = MarkdownLoader(str(file_path))
                    documents.extend(loader.load())
            except Exception as e:
                print(f"加载失败 {file_path}: {e}")
    
    return documents

# 调用示例
docs = load_documents("./knowledge_base")
print(f"共加载 {len(docs)} 个文档")

3. 文本分块

def split_documents(documents, chunk_size=1000, chunk_overlap=200):
    """把大段文字切成小块,方便后面做检索"""
    text_splitter = RecursiveCharacterTextSplitter(
        chunk_size=chunk_size,
        chunk_overlap=chunk_overlap,
        length_function=len,
        separators=["\n\n", "\n", "。", " ", ""]
    )
    return text_splitter.split_documents(documents)

# 执行分块
chunks = split_documents(docs)
print(f"切成了 {len(chunks)} 个小块")

4. 向量存储

def create_vector_store(chunks, embeddings, persist_directory="./chroma_db"):
    """建向量数据库,把文本块转成的向量存进去"""
    vectorstore = Chroma.from_documents(
        documents=chunks,
        embedding=embeddings,
        persist_directory=persist_directory
    )
    return vectorstore

# 创建向量库
vectorstore = create_vector_store(chunks, embeddings)
print("向量数据库好了")

5. 问答链

def create_qa_system(llm, vectorstore):
    """把检索和 LLM 连起来,做成问答系统"""
    retriever = vectorstore.as_retriever(
        search_type="similarity",
        search_kwargs={"k": 3}
    )
    
    qa_chain = RetrievalQA.from_chain_type(
        llm=llm,
        chain_type="stuff",
        retriever=retriever,
        return_source_documents=True
    )
    
    return qa_chain

# 初始化问答系统
qa_system = create_qa_system(llm, vectorstore)

6. 主程序示例

def main():
    knowledge_dir = "./knowledge_base"
    
    # 第一次跑,建个测试用的知识库
    if not os.path.exists(knowledge_dir):
        print("生成测试文档...")
        os.makedirs(knowledge_dir)
        
        sample_content = """
        # 产品功能介绍
        
        智能助手有这些核心功能:
        
        1. 文本生成:能写各种类型的文章和内容
        2. 问答系统:根据知识库里的资料回答问题
        3. 翻译:多语言互译
        4. 编程辅助:帮忙写代码和调试
        
        联系渠道:
        邮箱:support@example.com
        电话:400-123-4567
        """
        
        with open(os.path.join(knowledge_dir, "产品介绍.md"), "w", encoding="utf-8") as f:
            f.write(sample_content)
    
    # 加载文档
    print("加载文档中...")
    documents = load_documents(knowledge_dir)
    print(f"加载了 {len(documents)} 个")
    
    # 切分
    print("切分文档...")
    text_chunks = split_documents(documents)
    print(f"切完 {len(text_chunks)} 个块")
    
    # 建向量库
    print("建向量库...")
    vector_store = create_vector_store(text_chunks, embeddings)
    
    # 初始化问答
    print("问答系统好了...")
    qa = create_qa_system(llm, vector_store)
    
    # 进入交互
    print("\n" + "="*50)
    print("RAG 系统跑起来了!")
    print("="*50)
    
    while True:
        question = input("\n问点啥(输入 q 退出): ")
        if question.lower() == q:
            break
            
        result = qa({"query": question})
        print(f"\n回答: {result[result]}")
        print(f"\n参考来源:")
        for i, doc in enumerate(result[source_documents], 1):
            print(f"  {i}. {doc.metadata.get(source, 未知来源)}")

if __name__ == "__main__":
    main()

运行结果

跑起来是这样的:

==================================================
RAG 系统跑起来了!
==================================================

问点啥(输入 q 退出): 产品有哪些功能?

回答: 根据资料,产品有这些功能:

1. 文本生成 - 能写各种类型的文章和内容
2. 问答系统 - 根据知识库里的资料回答问题
3. 翻译 - 多语言互译
4. 编程辅助 - 帮忙写代码和调试

参考来源:
  1. ./knowledge_base/产品介绍.md

答案不仅准确,还能看到从哪个文件来的。RAG 就是这个原理:先去库里找相关的,再把找到的内容喂给 LLM 生成答案。

问点啥(输入 q 退出): 联系方式是什么?

回答: 联系邮箱是 support@example.com,电话是 400-123-4567

即使是文档里的具体信息,也能准确定位和提取出来。

总结

这套方案能让你在本地跑一个完整的 RAG 系统。几个明显的好处:

  • 数据不用出去,全部在本地处理
  • 成本就一台电脑的事,跑起来不用网络,断网也能用
  • 可以自己选模型、改参数,满足各种奇怪的需求
  • 模块都拆开了,加新文档、加功能都很方便

代码结构不复杂,就是文档加载、切分、建向量库、问答这几步。个人用够了。Production 环境里可能要考虑的:多用户并发、知识库更新、更多文件格式支持,但那是后话。

私有化的 LLM + RAG,这组合现在真能打了。手里有资料想做个 AI 助手的,直接抄这套就行。

暂无评论

发送评论 编辑评论


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