Skip to content

Latest commit

 

History

2 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 

Repository files navigation

📚 本地知识库问答系统(RAG)

基于 LangChain + Chroma + DeepSeek 的检索增强生成(RAG)应用 —— 纯 Python 实现,开箱即用。

Python Streamlit LangChain Chroma DeepSeek License


📖 目录


🎯 项目背景

痛点

痛点 说明
LLM 幻觉 大模型会"编造"不存在的事实,在专业场景下尤其危险
知识时效性 通用大模型的知识截止日期固定,无法获取最新信息
领域知识不足 通用模型在垂直领域(如企业内部文档、产品手册)表现不佳
回答不可溯源 传统 LLM 回答没有引用来源,无法验证真伪

RAG 如何解决

RAG(Retrieval-Augmented Generation) 在 LLM 生成回答之前,先从本地知识库中检索相关文档片段,再将文档片段作为"参考材料"一起发给 LLM,确保回答有据可依、可溯源

传统 LLM:  用户问题 ──→ LLM ──→ 回答(可能幻觉)
RAG:       用户问题 ──→ 检索相关文档 ──→ LLM + 文档 ──→ 回答 + 引用来源 ✅

本项目的定位是 RAG 的最小可行产品(MVP):用最简洁的代码(单文件 ~900 行)展示 RAG 的完整链路,同时具备生产可用的 UI 交互体验。


✨ 核心特性

  • 🔍 语义检索 — 基于 BAAI/bge-small-zh-v1.5 中文嵌入模型,精准匹配文档语义
  • 🤖 DeepSeek 驱动 — 接入 DeepSeek API,中文理解力强、性价比极高
  • 💾 本地向量库 — Chroma 本地持久化,无需外部数据库,数据完全私有
  • 🎨 类 DeepSeek 风格 UI — 初始居中搜索框,对话式交互,侧边栏管理
  • 💬 多对话管理 — 支持多轮对话、对话持久化、重命名、删除
  • 📎 可溯源回答 — 每次回答附带参考来源,可对照验证
  • 📂 文档管理 — 侧边栏支持上传、预览、删除文件,支持 .txt / .md / .csv / .json / .docx / .pdf
  • 离线嵌入 — 嵌入模型完全本地运行(预下载后),零 API 费用、零延迟
  • 🔑 API Key 热配置 — 侧边栏底部直接修改 API Key,无需重启

🏗 架构设计

系统架构总览

┌──────────────────────────────────────────────────────────────────────┐
│                         Streamlit 前端层                              │
│                                                                      │
│   ┌──────────────────────┐          ┌──────────────────────────┐     │
│   │      侧边栏 (管理)    │          │      主区域 (对话)         │     │
│   │                      │          │                          │     │
│   │  • API Key 配置       │          │  • 居中搜索框(初始态)     │     │
│   │  • 知识库构建/加载    │          │  • 聊天气泡(对话态)       │     │
│   │  • 文档上传/管理      │          │  • 参考来源折叠面板        │     │
│   │  • 多对话切换        │          │  • 流式思考提示           │     │
│   │  • 文件预览          │          │                          │     │
│   └──────────────────────┘          └──────────────────────────┘     │
│                                                                      │
└────────────────────────────────┬─────────────────────────────────────┘
                                 │
                                 ▼
┌──────────────────────────────────────────────────────────────────────┐
│                       LangChain 编排层                                │
│                                                                      │
│   ┌──────────────┐   ┌───────────────┐   ┌──────────────────────┐   │
│   │  Document    │   │  Text         │   │  RAG Chain (LCEL)    │   │
│   │  Loader      │──▶│  Splitter     │   │                      │   │
│   │  文档加载器   │   │  文本分割器    │   │  retriever           │   │
│   └──────────────┘   └───────────────┘   │    │ format_docs     │   │
│                                          │    ▼                 │   │
│   ┌──────────────────────────────┐      │  prompt_template     │   │
│   │  LangChain Hub / Callbacks   │      │    │                 │   │
│   │  (可扩展监控、缓存、日志)      │      │    ▼                 │   │
│   └──────────────────────────────┘      │  LLM (ChatOpenAI)    │   │
│                                          │    │                 │   │
│                                          │    ▼                 │   │
│                                          │  StrOutputParser()  │   │
│                                          └──────────────────────┘   │
│                                                                      │
└───────┬──────────────────────────────────┬───────────────────────────┘
        │                                  │
        ▼                                  ▼
┌───────────────────────┐    ┌──────────────────────────────────┐
│    检索层 (Retrieval)  │    │       生成层 (Generation)         │
│                       │    │                                  │
│  ┌─────────────────┐  │    │  ┌────────────────────────────┐  │
│  │  Chroma          │  │    │  │  DeepSeek API              │  │
│  │  向量数据库       │  │    │  │  model: deepseek-chat      │  │
│  │  (本地持久化)     │  │    │  │  base_url: api.deepseek.com│  │
│  └────────┬────────┘  │    │  │  temperature: 0.3           │  │
│           │           │    │  │  max_tokens: 1024           │  │
│  ┌────────▼────────┐  │    │  └────────────────────────────┘  │
│  │  HuggingFace     │  │    │                                  │
│  │  Embeddings      │  │    └──────────────────────────────────┘
│  │  bge-small-zh    │  │
│  │  512 维向量      │  │
│  │  (本地 CPU 推理) │  │
│  └─────────────────┘  │
│                       │
└───────────────────────┘

数据流:构建知识库

data/*.txt (原始文档)
    │
    ▼
┌──────────────────────────────┐
│ ① TextLoader                │  读取 UTF-8 文本文件
│    生成 Document 对象        │  (page_content + metadata)
└──────────────┬───────────────┘
               ▼
┌──────────────────────────────┐
│ ② RecursiveCharacterTextSplitter │
│    chunk_size=500            │  按中文标点递归切分
│    chunk_overlap=50          │  保留上下文连贯性
└──────────────┬───────────────┘
               ▼
┌──────────────────────────────┐
│ ③ HuggingFaceEmbeddings     │
│    BAAI/bge-small-zh-v1.5   │  文本 → 512 维归一化向量
│    device=cpu, local only   │
└──────────────┬───────────────┘
               ▼
┌──────────────────────────────┐
│ ④ Chroma.from_documents()   │
│    persist_directory=       │  向量 + 原文持久化到磁盘
│    chroma_db/               │
└──────────────────────────────┘

数据流:问答检索

用户问题 "什么是RAG?"
    │
    ▼
┌──────────────────────────────┐
│ ① 问题向量化                │  同一嵌入模型 → 512 维向量
└──────────────┬───────────────┘
               ▼
┌──────────────────────────────┐
│ ② ANN 相似度检索 (Chroma)   │  余弦相似度 Top-4 文档块
│    retriever.invoke(q)      │
└──────────────┬───────────────┘
               ▼
┌──────────────────────────────┐
│ ③ 格式化上下文              │  [来源 1]\n内容\n---\n[来源 2]...
│    format_docs(docs)        │
└──────────────┬───────────────┘
               ▼
┌──────────────────────────────┐
│ ④ 组装 Prompt              │  System Prompt (角色+规则+上下文)
│    ChatPromptTemplate       │  + Human Prompt (用户问题)
└──────────────┬───────────────┘
               ▼
┌──────────────────────────────┐
│ ⑤ LLM 生成 (DeepSeek)      │  temperature=0.3 确定性生成
│    ChatOpenAI.invoke()      │
└──────────────┬───────────────┘
               ▼
┌──────────────────────────────┐
│ ⑥ 结果展示 + 来源追溯       │  回答气泡 + 📎 参考来源面板
└──────────────────────────────┘

🔬 技术选型

技术栈一览

层级 技术 版本 选型理由
UI 框架 Streamlit 1.x 纯 Python 写 Web UI,零前端代码,适合快速原型
编排框架 LangChain latest 标准化 RAG 工作流,LCEL 链式语法简洁可读
文档加载 LangChain Community TextLoader 开箱即用
文本分割 LangChain Text Splitters RecursiveCharacterTextSplitter 按中文标点递归切分
向量数据库 Chroma latest 开源、轻量、本地文件持久化,零配置
嵌入模型 BAAI/bge-small-zh-v1.5 v1.5 中文优化、512维轻量、本地运行、免费
LLM DeepSeek (deepseek-chat) 中文能力强、API 性价比极高、OpenAI 兼容协议
LLM 适配 LangChain OpenAI ChatOpenAI 直接适配 DeepSeek(OpenAI 兼容)
模型下载 ModelScope 国内 CDN,解决 HuggingFace 下载慢的问题
环境管理 python-dotenv .env 管理 API Key,不入版本控制

选型对比

嵌入模型:BAAI/bge-small-zh-v1.5 vs 其他

模型 维度 大小 中文效果 运行方式 费用
bge-small-zh-v1.5 512 ~100MB ⭐⭐⭐⭐ 本地 CPU 免费
bge-large-zh-v1.5 1024 ~400MB ⭐⭐⭐⭐⭐ 本地 CPU/GPU 免费
text-embedding-ada-002 (OpenAI) 1536 ⭐⭐⭐ API 调用 付费
m3e-base 768 ~400MB ⭐⭐⭐⭐ 本地 CPU 免费

选择 small 而非 large:MVP 阶段追求快速启动,100MB 模型 10 秒内下载完成,CPU 推理速度足够。

向量数据库:Chroma vs 其他

数据库 部署方式 持久化 适用场景 选型理由
Chroma 嵌入式 本地文件 原型/小规模 零配置,pip install 即用
FAISS 嵌入式 手动 原型/大规模 纯向量检索,无元数据管理
Milvus Docker/K8s 服务端 生产/海量 功能强大但部署复杂
Pinecone SaaS 云端 生产/无运维 需注册、付费、网络依赖

选择 Chroma:项目定位是本地 MVP,Chroma 的 persist_directory 一行代码搞定持久化,完全满足需求。

LLM:DeepSeek vs 其他

模型 中文能力 价格 (1M tokens) 协议兼容 选型理由
DeepSeek (deepseek-chat) ⭐⭐⭐⭐⭐ ¥1 (输入) / ¥2 (输出) OpenAI 兼容 性价比之王
GPT-4o ⭐⭐⭐⭐ $2.5 / $10 OpenAI 原生 最强但贵
Qwen-Max ⭐⭐⭐⭐⭐ ¥2 / ¥6 OpenAI 兼容 中文最强,成本可控
GLM-4 ⭐⭐⭐⭐ ¥0.1 / ¥0.1 自有协议 超低价但协议不兼容

选择 DeepSeek:中文理解力一流,API 价格是 GPT-4o 的 1/50,协议完全兼容 OpenAI SDK,一行代码切换。


📈 优化思路与效果对比

优化 v2 → v3(当前版本)

本项目的演进经历了多个版本迭代,以下是核心优化点:

1. 嵌入模型:从联网下载 → 本地离线加载

版本 方式 首启时间 问题
v1 HuggingFace 在线下载 15+ 分钟(国内超时) 严重卡点,新手劝退
v2 ModelScope 预下载 + 环境变量 local_files_only=True < 2 秒 ✅ 一次下载,永久使用

优化效果:首启时间从 900+ 秒 → 2 秒,快了 450 倍

# v1: 每次启动尝试从 HuggingFace 下载(国内极慢)
embeddings = HuggingFaceEmbeddings(model_name="BAAI/bge-small-zh-v1.5")

# v2: 设置国内镜像 + 仅用本地文件
os.environ["HF_ENDPOINT"] = "https://hf-mirror.com"
# modelscope 预下载到 ./models/
embeddings = HuggingFaceEmbeddings(
    model_name="./models/.../BAAI--bge-small-zh-v1.5/.../master",
    model_kwargs={"device": "cpu", "local_files_only": True},
)

2. 界面体验:从基础 Streamlit → DeepSeek 风格 UI

版本 UI 特点 问题
v1 Streamlit 默认布局,标题+侧边栏 平淡,不像对话应用
v2 自定义 CSS ~300 行 DeepSeek 风格居中搜索框、现代侧边栏

优化效果:初始态仿 DeepSeek 居中搜索,有对话后自动切换为标准对话布局,用户体验显著提升。

/* 核心技巧:用 body:has(.initial-mode) 隐藏默认 chat_input */
body:has(.initial-mode) .stChatInput {
    display: none !important;
}

3. 对话管理:从单会话 → 多对话持久化

版本 方式 问题
v1 st.session_state.chat_history 列表 内存存储,刷新即丢
v2 conversations.json 文件持久化 + 多对话切换 ✅ 刷新不丢,支持重命名/删除

优化效果:从 刷新丢失永久保存,支持多主题对话隔离。

4. Prompt 工程优化

版本 System Prompt 效果
v1 简单指令:"请基于上下文回答" AI 可能因上下文不足而乱答
v2 分级指令:足够→准确回答 / 不足→诚实告知并标注来源 ✅ 区分知识库内容 vs AI 补充
# v2 System Prompt 核心设计
1. 上下文足够 → 基于上下文准确完整回答
2. 上下文不足 → 诚实告知 + 标注哪些来自知识库、哪些来自你的知识
3. 使用中文,条理清晰
4. 适当引用上下文具体内容

5. 文档支持扩展

版本 支持格式 限制
v1 .txt 需手动转换格式
v2 .txt .md .csv .json .doc .docx .pdf ✅ 6 种格式覆盖常见文档

6. 嵌入模型缓存优化

版本 方式 问题
v1 每次构建/加载都 HuggingFaceEmbeddings() 新建 重复加载模型,浪费内存和时间
v2 st.session_state.embeddings 单例缓存 ✅ 模型只加载一次
# v2: 复用嵌入模型实例
if st.session_state.embeddings is None:
    st.session_state.embeddings = HuggingFaceEmbeddings(...)
embeddings = st.session_state.embeddings  # 复用

7. 启动自动恢复知识库

版本 方式 问题
v1 每次启动需手动点"加载" 多余操作
v2 启动时检测 chroma_db/ 存在则自动加载 ✅ 如已有向量库,启动即就绪

效果对比总结

维度 优化前 (v1) 优化后 (v2/v3) 提升
首次启动时间 15+ 分钟 < 3 秒 300x+
嵌入模型加载 每次请求 HuggingFace 本地离线 + session cache
对话持久化 ❌ 刷新丢失 ✅ JSON 文件持久化
多对话支持 ❌ 单会话 ✅ 多对话 + 重命名 + 删除
UI 体验 默认 Streamlit DeepSeek 风格居中搜索
文档格式 仅 .txt 6 种格式 (.txt .md .csv .json .doc .docx .pdf) 6x
文档管理 手动放文件 侧边栏上传/预览/删除
Prompt 鲁棒性 可能乱答 分级指令,诚实标注
错误提示 技术报错 用户友好提示
启动恢复 手动加载 自动检测恢复

📁 项目结构

RAG Demo(LangChain + Chroma +API)/
├── README.md                     # 本文档
└── my_local_rag/
    ├── app.py                    # 主程序(单文件 ~900 行)
    ├── requirements.txt          # Python 依赖清单
    ├── .env                      # 环境变量(API Key + HF 镜像)
    ├── conversations.json        # 对话持久化文件
    ├── .streamlit/
    │   └── config.toml           # Streamlit 配置(无统计、无头模式)
    ├── data/
    │   ├── .gitkeep              # 保持目录存在
    │   └── my_knowledge.txt      # 示例知识库文档
    ├── chroma_db/                # 向量数据库(构建后自动生成)
    │   ├── chroma.sqlite3        # 元数据/索引
    │   └── <collection-id>/      # 向量二进制数据
    ├── models/
    │   └── models/
    │       └── BAAI--bge-small-zh-v1.5/  # 嵌入模型缓存
    │           └── snapshots/master/     # 模型权重文件
    ├── 启动指南.md                # 环境搭建与启动方法
    ├── 使用文档.md                # 操作步骤说明
    └── 说明文档.md                # 技术架构详解

🚀 快速开始

前置要求

工具 要求
Python ≥ 3.10
DeepSeek API Key platform.deepseek.com 注册获取

第一步:克隆项目

git clone https://github.com/LoongzF/RAG-Demo-LangChain-Chroma-API-.git
cd RAG-Demo-LangChain-Chroma-API-/my_local_rag

第二步:安装依赖

pip install -r requirements.txt

第三步:配置 API Key

编辑 .env 文件:

DEEPSEEK_API_KEY=sk-your-api-key-here
HF_ENDPOINT=https://hf-mirror.com

第四步:预下载嵌入模型(重要!)

嵌入模型约 100MB,从 HuggingFace 直接下载在国内会极慢。通过 ModelScope 国内 CDN 10 秒内完成。

pip install modelscope

python -c "
from modelscope import snapshot_download
snapshot_download('BAAI/bge-small-zh-v1.5', cache_dir='./models')
"

第五步:启动应用

streamlit run app.py

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

第六步:构建知识库

  1. 左侧边栏确认 API Key 已自动填充
  2. 点击 🔨 构建 按钮
  3. 等待 2 秒,看到 "✅ 构建完成"
  4. 在居中搜索框输入问题,如 "什么是 RAG?"

💡 之后启动会自动检测 chroma_db/ 并恢复知识库,无需重复构建。


📘 使用指南

界面布局

┌──────────────────────┬──────────────────────────────────┐
│    侧边栏 (280px)     │         主区域                     │
│                      │                                  │
│  📚 品牌标识          │  初始态:居中标题 + 居中搜索框       │
│  ➕ 新对话            │  对话态:聊天气泡 + 底部输入框       │
│  历史对话列表         │                                  │
│  ─────────────       │  ┌────────────────────────────┐  │
│  知识库状态 / 构建    │  │ 回答内容                    │  │
│  文档导入/删除        │  │ 📎 参考来源 (可折叠)         │  │
│  文档列表/预览       │  └────────────────────────────┘  │
│  ─────────────       │                                  │
│  ⚙️ API 设置         │                                  │
└──────────────────────┴──────────────────────────────────┘

操作速览

操作 位置 说明
新建对话 侧边栏 ➕ 新对话 创建独立对话,互不干扰
切换/重命名/删除对话 侧边栏历史对话列表 点击切换,r 重命名,x 删除
构建知识库 侧边栏 🔨 构建 读取 data/ 所有文件 → 向量化 → 存入 Chroma
加载知识库 侧边栏 📂 加载 从 chroma_db/ 直接加载已有向量库
导入文件 侧边栏 📥 导入 上传文件到 data/ 目录
删除文件 侧边栏 🗑️ 删除 从 data/ 删除文件
预览文件 侧边栏展开 📁 文档列表 点击文件名预览前 100 字符
配置 API Key 侧边栏底部 ⚙️ API 设置 实时生效,无需重启
提问 主区域输入框 Enter 发送
查看来源 回答下方 📎 参考来源 显示检索到的文档碎片
清空对话 底部 🗑️ 清空对话 仅清空消息,不影响知识库

添加自定义文档

将文档(.txt / .md / .csv / .json 等)放入 data/ 目录或通过侧边栏上传,然后点击 🔨 构建 重建知识库即可。


🔄 核心流程

知识库构建

data/*.txt → TextLoader → RecursiveCharacterTextSplitter(chunk=500,overlap=50)
→ HuggingFaceEmbeddings(bge-small-zh, cpu) → 512维向量
→ Chroma.from_documents(persist=chroma_db/) → 持久化到磁盘

RAG 问答链 (LCEL)

rag_chain = (
    {
        "context": retriever | format_docs,
        "question": RunnablePassthrough(),
    }
    | prompt_template
    | llm
    | StrOutputParser()
)
response = rag_chain.invoke(user_question)

RAG 关键参数

参数 默认值 说明 调优建议
chunk_size 500 文本块大小 增大 → 上下文更完整;减小 → 检索更精准
chunk_overlap 50 块间重叠 防止关键信息落在切分边界
k (检索数量) 4 返回文档块数 增多 → 信息更全但 Prompt 更长
temperature 0.3 LLM 随机性 RAG 建议 0~0.3,追求准确性
max_tokens 1024 回答长度上限 根据场景调整
separators \n\n \n 切分优先级 中文场景按标点切分效果更好

❓ 常见问题

问题 原因 解决
构建知识库报错 API Key 未配置 侧边栏 ⚙️ API 设置 填入有效 Key
构建极慢 (>10分钟) 嵌入模型从 HuggingFace 下载 按上文第四步通过 ModelScope 预下载
问答返回错误 API Key 无效/余额不足/网络问题 检查 Key 有效性,确认能访问 api.deepseek.com
AI 回答与文档不一致 System Prompt 允许 AI 补充 查看 📎 参考来源 对照验证
知识库状态显示"未构建" chroma_db/ 不存在或为空 点击 🔨 构建
添加文档后问答不生效 未重建向量库 点击 🔨 构建(不是加载)

🧭 扩展方向

方向 说明 涉及技术
📄 更多文档格式 PDF 直接解析 PyPDFLoader
💬 多轮对话记忆 结合历史上下文追问 ConversationBufferMemory / RunnableWithMessageHistory
🔀 混合检索 关键词 BM25 + 向量融合 EnsembleRetriever
🌊 流式输出 逐字符实时显示 llm.stream() + Streamlit write_stream()
📊 更大嵌入模型 提升检索精度 bge-large-zh-v1.5 (1024 维)
🗂️ 多知识库 不同场景独立知识库 多个 Chroma Collection
📝 对话导出 导出问答为 Markdown/CSV Python 文件 IO
🔐 用户认证 多用户隔离 Streamlit Authenticator
🧠 重排序 对检索结果二次精排 CohereRerank / bge-reranker
📈 监控追踪 调试 RAG 链各环节 LangSmith / LangFuse

📄 License

MIT License


🎉 祝你使用愉快! 如有问题或建议,欢迎提 Issue 或 PR。

About

本项目的定位是 **RAG 的最小可行产品(MVP)**:用最简洁的代码(单文件 ~900 行)展示 RAG 的完整链路,同时具备生产可用的 UI 交互体验。

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages