毛球

毛球

技术爱好者 · 探索者

← 返回项目列表

本地 AI 知识库助手

🤖 AI 工具 ⭐ 热门项目 📅 2026-03-21
Python Ollama ChromaDB LangChain Streamlit

项目简介

一个基于本地 Ollama 大模型的 RAG(检索增强生成)知识库问答系统。可以上传文档,在本地进行智能问答,完全离线,保护隐私。特别适合处理敏感文档的研究人员和开发者~

💡 核心特性:完全本地运行 · 支持多种文档格式 · 无需 API 费用 · 隐私安全

主要功能

  • PDF、TXT、Markdown 文档解析和向量化
  • 基于向量数据库的语义检索
  • 结合检索结果生成准确回答
  • 友好的 Web 界面(Streamlit)
  • 支持自定义知识库和对话历史
  • 可连接 Ollama 或 OpenAI API

环境准备

确保你已安装以下环境:

  • Python 3.10+
  • Ollama(官网
  • 16GB+ RAM(推荐)

详细搭建步骤

1 安装依赖

创建项目目录并安装 Python 依赖:

mkdir local-rag-assistant
cd local-rag-assistant
pip install langchain langchain-community chromadb streamlit
pip install pypdf python-docx tiktoken sentence-transformers

或者一次性安装所有依赖:

pip install -r requirements.txt
2 下载 Ollama 模型

确保 Ollama 服务正在运行,然后下载需要的模型:

# 下载嵌入模型(用于文档向量化)
ollama pull nomic-embed-text

# 下载对话模型
ollama pull llama3

# 验证模型已下载
ollama list
预期输出

NAME ID SIZE MODIFIED

llama3 365c0c14b0e3 4.7GB 2026-03-21

nomic-embed-text 0a109f422b47 274MB 2026-03-21

⚠️ 注意:首次运行会自动下载模型,请确保网络畅通。模型文件较大(数GB),请耐心等待。

3 创建核心代码文件

创建项目主文件 app.py

"""
本地 AI 知识库助手
基于 Ollama + ChromaDB + Streamlit
"""
import streamlit as st
from langchain_community.document_loaders import PyPDFLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_community.embeddings import OllamaEmbeddings
from langchain_community.vectorstores import Chroma
from langchain_community.chat_models import ChatOllama
from langchain.chains import RetrievalQA
import tempfile
import os

# 配置页面
st.set_page_config(page_title="本地知识库助手", page_icon="🤖")
st.title("🤖 本地 AI 知识库助手")

# 初始化向量数据库
@st.cache_resource
def get_vectorstore():
    embeddings = OllamaEmbeddings(model="nomic-embed-text")
    vectorstore = Chroma(
        persist_directory="./chroma_db",
        embedding_function=embeddings
    )
    return vectorstore

# 初始化聊天模型
@st.cache_resource
def get_llm():
    return ChatOllama(model="llama3", temperature=0.7)

# 上传文档
def process_document(uploaded_file):
    # 保存上传文件到临时目录
    with tempfile.NamedTemporaryFile(delete=False, suffix=".pdf") as tmp_file:
        tmp_file.write(uploaded_file.getvalue())
        tmp_path = tmp_file.name
    
    # 加载文档
    if uploaded_file.name.endswith(".pdf"):
        loader = PyPDFLoader(tmp_path)
    else:
        from langchain_community.document_loaders import TextLoader
        loader = TextLoader(tmp_path)
    
    documents = loader.load()
    
    # 文本分块
    text_splitter = RecursiveCharacterTextSplitter(
        chunk_size=500,
        chunk_overlap=50
    )
    chunks = text_splitter.split_documents(documents)
    
    # 向量化并存入数据库
    embeddings = OllamaEmbeddings(model="nomic-embed-text")
    vectorstore = Chroma.from_documents(
        documents=chunks,
        embedding=embeddings,
        persist_directory="./chroma_db"
    )
    
    os.unlink(tmp_path)
    return len(chunks)

# 问答
def answer_question(question, vectorstore, llm):
    retriever = vectorstore.as_retriever(search_kwargs={"k": 3})
    qa_chain = RetrievalQA.from_chain_type(
        llm=llm,
        chain_type="stuff",
        retriever=retriever
    )
    return qa_chain.run(question)

# 侧边栏 - 上传文档
with st.sidebar:
    st.header("📁 上传文档")
    uploaded_file = st.file_uploader(
        "选择 PDF 或 TXT 文件",
        type=["pdf", "txt"],
        help="支持 PDF 和纯文本文件"
    )
    
    if uploaded_file and st.button("处理文档", type="primary"):
        with st.spinner("正在处理文档..."):
            chunk_count = process_document(uploaded_file)
            st.success(f"✅ 文档处理完成!共 {chunk_count} 个文本块")
    
    st.divider()
    st.markdown("### 使用说明")
    st.markdown("""
    1. 上传 PDF 或 TXT 文档
    2. 点击「处理文档」按钮
    3. 在下方输入问题并提问
    """)

# 主界面 - 问答
if "messages" not in st.session_state:
    st.session_state.messages = []

for message in st.session_state.messages:
    with st.chat_message(message["role"]):
        st.markdown(message["content"])

question = st.chat_input("请输入你的问题...")

if question:
    with st.chat_message("user"):
        st.markdown(question)
    st.session_state.messages.append({"role": "user", "content": question})
    
    with st.spinner("思考中..."):
        try:
            vectorstore = get_vectorstore()
            llm = get_llm()
            answer = answer_question(question, vectorstore, llm)
            
            with st.chat_message("assistant"):
                st.markdown(answer)
            st.session_state.messages.append({"role": "assistant", "content": answer})
        except Exception as e:
            st.error(f"出错了: {str(e)}")
4 创建依赖文件

创建 requirements.txt 方便管理依赖:

langchain>=0.1.0
langchain-community>=0.0.10
chromadb>=0.4.0
streamlit>=1.28.0
pypdf>=3.0.0
python-docx>=1.0.0
tiktoken>=0.5.0
sentence-transformers>=2.2.0
5 启动服务

确保 Ollama 服务正在运行(如果没有,自动启动):

# 在另一个终端启动 Ollama(如果未运行)
ollama serve

# 启动 Streamlit 应用
streamlit run app.py --server.port 8501
预期效果

浏览器自动打开 http://localhost:8501

可以看到带有侧边栏的上传区域和主聊天界面

6 测试使用

完整使用流程:

  1. 上传一个 PDF 文档(如论文、技术文档)
  2. 点击「处理文档」按钮,等待处理完成
  3. 在底部输入框输入问题,如:「这篇文章的主要观点是什么?」
  4. 查看 AI 返回的基于文档内容的回答
预期效果截图描述

左侧边栏:文件上传区域 + 使用说明

右侧主区域:聊天历史 + 输入框

深色主题,紫色强调色,现代简洁界面

代码说明

核心组件

  • PyPDFLoader:加载 PDF 文档
  • RecursiveCharacterTextSplitter:将长文本分割成小块
  • OllamaEmbeddings:使用本地 Ollama 生成文本向量
  • Chroma:向量数据库,存储和检索文档向量
  • RetrievalQA:RAG 问答链,结合检索和生成

向量检索原理

系统会:

  1. 将上传的文档分割成 500 字符的小块
  2. 使用 nomic-embed-text 模型将每块文本转换为向量
  3. 将向量存储在 ChromaDB 中
  4. 用户提问时,将问题也转为向量
  5. 在向量数据库中找到最相似的文档块
  6. 将相关文档块作为上下文发送给大模型生成回答

常见问题

Q: 报错 "Connection refused"?

确保 Ollama 服务正在运行:

ollama serve

Q: 模型下载太慢?

可以尝试使用国内镜像源,或选择更小的模型如 phiqwen

Q: 内存不足?

减少同时加载的模型,或使用更小的模型:

ollama run phi  # 较小但速度更快

项目结构

local-rag-assistant/
├── app.py              # 主应用
├── requirements.txt    # 依赖列表
├── chroma_db/          # 向量数据库(自动生成)
└── README.md           # 项目说明

扩展方向

  • 添加更多文档格式支持(Word、Excel)
  • 支持多文档同时检索
  • 添加对话历史管理
  • 集成 Web 搜索功能
  • 添加文档摘要功能

🎉 完成! 现在你拥有了一个完全本地运行的知识库问答系统,可以安全地处理敏感文档了!