Skip to content

Repository files navigation

LangChain 文档 AI 问答系统

基于混合检索(语义 + BM25 + RRF)的生产级 RAG 系统,完全本地运行,含完整评估框架。

Python LangChain ChromaDB Ollama FastAPI Streamlit Docker


系统架构

flowchart LR
    subgraph Ingestion["文档导入(一次性)"]
        A["MD / HTML / TXT\n文档"] --> B[DocumentLoader]
        B --> C["DocumentChunker\n两阶段:标题→字符"]
        C --> D["SentenceTransformer\nall-MiniLM-L6-v2"]
        D --> E[(ChromaDB)]
    end

    subgraph Query["查询(实时)"]
        F[用户问题] --> G["VectorStore\n语义检索"]
        F --> H["BM25\n关键词检索"]
        G --> I["RRF 融合\nscore = Σ 1/(60+rank)"]
        H --> I
        I --> J[Top-K Chunks]
        J --> K["RAGGenerator\nOllama qwen2.5:7b"]
        K --> L[回答 + 来源]
    end

    subgraph Interfaces["接口"]
        L --> M["FastAPI\nREST / SSE"]
        L --> N["Streamlit\n聊天界面"]
    end

    E --> G
    E --> H
Loading

模块职责

模块 职责
src/config.py 所有可调参数的统一入口
src/document_pipeline/ 加载 → 两阶段分块(标题切分 + 字符切分)
src/embeddings/store.py SentenceTransformer 向量化 + ChromaDB 读写
src/retrieval/hybrid.py 语义检索 + BM25 并行,RRF 融合(0.7 / 0.3)
src/generation/generator.py Prompt 拼装 + Ollama 调用 + SSE 流式输出
src/api/app.py FastAPI 应用(lifespan 生命周期管理)
src/evaluation/evaluator.py 4 项规则评估,无需额外 API 调用
src/frontend/streamlit_app.py Streamlit 聊天界面,直接调用 Python 模块

核心功能

  • 混合检索 — 余弦相似度 + BM25,通过 Reciprocal Rank Fusion 融合(score = Σ 1/(60 + rank)
  • 100 篇 LangChain 官方文档,最优配置下生成 1,596 个 chunks
  • 完全离线 — 通过 Ollama 本地运行 qwen2.5:7b,无需云端 API
  • 双接口 — RESTful API(FastAPI + Swagger UI)+ 交互式聊天前端(Streamlit)
  • 评估框架 — 4 项指标(忠实度 / 答案相关性 / 上下文精度 / 上下文召回),零额外 API 消耗
  • 实验驱动配置 — 对 256 / 512 / 1024 三种 chunk_size 做了对照实验并公开结果

评估结果

Chunk Size 对比实验

语料:100 篇文档 | 测试集:10 条问题 | LLM:qwen2.5:7b | Embedding:all-MiniLM-L6-v2

Chunk Size Overlap Chunks Faithfulness Answer Relevancy Context Precision Context Recall Avg Latency Composite ↑
256 51 2,189 0.294 0.610 0.227 0.497 3,846 ms 0.407
512 102 1,596 0.305 0.645 0.237 0.507 3,998 ms 0.424
1024 205 1,440 0.306 0.628 0.234 0.500 3,931 ms 0.417

Composite = 四项指标等权平均。完整原始数据:eval/chunk_experiment_report.json

分析

答案相关性(Answer Relevancy)对 chunk_size 最敏感(变化幅度:0.610 → 0.645,+5.7%)。chunk_size=512 约等于一个完整段落,既能提供足够上下文,又不会稀释 embedding 信号。

  • 256 — 2,189 个细粒度 chunks。检索返回大量小片段,上下文不完整导致幻觉风险最高(Faithfulness 最低:0.294),Recall 也偏低。
  • 512 — 1,596 个中等 chunks。Answer Relevancy(+5.7%)、Context Precision(+4.4%)、Context Recall(+2.0%)均领先,综合分最高:0.424
  • 1024 — 1,440 个粗粒度 chunks。Faithfulness 略高(0.306),因为完整上下文减少了幻觉;但 chunk 越大 embedding 被稀释,Precision 低于 512。

延迟基本无差异(各配置均约 3.9 s),瓶颈在 LLM 推理,chunk_size 不影响响应时间。


快速开始

前置条件

  • Python 3.11+
  • 安装并运行 Ollama,拉取模型:
ollama pull qwen2.5:7b

安装

git clone https://github.com/GuddXzy/rag-from-scratch.git
cd rag-from-scratch

python -m venv .venv
.venv\Scripts\activate        # Windows
# source .venv/bin/activate   # macOS / Linux

pip install -e ".[dev]"

导入文档(一次性)

python -m scripts.ingest --docs-dir ./data/langchain_docs

# 清空后重新导入:
python -m scripts.ingest --docs-dir ./data/langchain_docs --reset

三种使用方式

方式一:Streamlit 聊天界面(推荐)

图形化交互界面,支持中英文切换,含系统状态面板和评估指标展示。

python -m streamlit run src/frontend/streamlit_app.py

浏览器访问 http://localhost:8501

功能亮点:

  • 🌐 侧栏语言切换(中文 / English)
  • 💬 多轮对话 + 引用来源折叠展示
  • 📊 实时显示 ChromaDB chunk 数量、评估指标
  • 🔖 一键填入示例问题

方式二:FastAPI REST 接口

标准 HTTP 接口,适合集成到其他应用或用 Apifox / Postman 调试。

python -m scripts.serve
# 开发模式(热重载):
python -m scripts.serve --reload

浏览器访问 http://localhost:8000/docs 查看 Swagger UI

方法 路径 说明
GET /health 健康检查
GET /stats chunk 数量、模型信息
POST /query RAG 问答,返回 answer + sources
POST /query/stream 同上,逐 token SSE 流式输出
POST /ingest 从本地目录导入文档
# 示例请求
curl -X POST http://localhost:8000/query \
  -H "Content-Type: application/json" \
  -d '{"question": "What is LCEL?", "top_k": 5}'

方式三:命令行直接查询

无需启动任何服务,适合快速测试或脚本集成。

# 完整问答(检索 + LLM 生成)
python -m scripts.query "What is LCEL?"

# 仅检索,不调用 LLM(速度更快,不消耗计算资源)
python -m scripts.query "How does hybrid retrieval work?" --no-generate

# 指定返回条数
python -m scripts.query "What are agents?" --top-k 3

Docker(一键启动 API)

docker compose up --build
# API 地址:http://localhost:8000

项目结构

rag-from-scratch/
├── src/
│   ├── config.py                      # 所有可调参数
│   ├── document_pipeline/
│   │   ├── loader.py                  # MD / HTML / TXT 文档加载
│   │   ├── chunker.py                 # 两阶段分块(标题 → 字符)
│   │   └── processor.py              # 串联 loader → chunker
│   ├── embeddings/
│   │   └── store.py                   # SentenceTransformer + ChromaDB
│   ├── retrieval/
│   │   └── hybrid.py                  # 语义 + BM25 + RRF 融合
│   ├── generation/
│   │   └── generator.py               # Prompt 拼装 + Ollama 调用 + SSE 流式
│   ├── api/
│   │   └── app.py                     # FastAPI 应用
│   ├── evaluation/
│   │   └── evaluator.py               # 4 项规则评估器
│   └── frontend/
│       └── streamlit_app.py           # Streamlit 聊天界面
├── scripts/
│   ├── ingest.py                      # CLI:导入文档
│   ├── query.py                       # CLI:单次查询
│   ├── serve.py                       # CLI:启动 API 服务
│   ├── evaluate.py                    # CLI:运行评估
│   ├── run_frontend.py                # CLI:启动 Streamlit
│   └── experiment_chunk_size.py      # chunk_size 对比实验
├── eval/
│   ├── test_set.json                  # 10 条评估问题
│   ├── chunk_experiment_report.json   # 实验原始数据(3 配置 × 10 问题)
│   └── chunk_experiment_summary.md   # 可读对比报告
├── data/
│   ├── langchain_docs/                # 源文档(MD / HTML,gitignored)
│   └── chroma_db/                     # ChromaDB 持久化(gitignored)
├── tests/
│   ├── test_pipeline.py               # 单元测试,无需下载模型
│   └── test_retrieval.py              # 集成测试,首次运行下载 ~80 MB 模型
├── Dockerfile
├── docker-compose.yml
└── pyproject.toml

运行测试

pytest tests/test_pipeline.py -v    # 快速,无需下载模型
pytest tests/test_retrieval.py -v   # 首次运行下载 ~80 MB 模型
pytest                               # 全部测试

技术选型说明

ChromaDB 而非 Pinecone / Weaviate

ChromaDB 完全在进程内运行,无需独立服务、无 API Key、无流量费用。对于本地优先的项目,这消除了所有基础设施开销。切换到 Pinecone 只需替换 src/embeddings/store.py 中的一个适配器。

混合检索而非纯语义检索

纯余弦相似度在精确关键词查询(API 方法名、类名、参数标志)上表现差,BM25 恰好擅长这类场景。RRF 融合无需对不同检索器的分数做归一化,天然兼容异构分数。权重(0.7 语义 / 0.3 BM25)可在 src/config.py 中调整。

本地 LLM(Ollama)而非云端 API

支持完全离线运行,无 API 费用,无数据外传,评估循环(3 种配置 × 10 条问题)无速率限制。生成器与后端无关,切换到 claude-sonnet-4-6 只需改 config.py 一行。

chunk_size=512 — 实验结论,非经验猜测

通过对照实验选定。512 字符 ≈ 一个完整段落 ≈ 一个完整概念。Answer Relevancy 领先 256 配置 +5.7%,Context Precision +4.4%,Context Recall +2.0%,延迟差异可忽略(+152 ms)。完整数据:eval/chunk_experiment_report.json


后续改进方向

  1. Cross-encoder 重排序 — 引入 ms-marco-MiniLM 对 Top-K chunks 做二次排序,提升 Context Precision
  2. 多轮记忆 — 通过 ConversationBufferMemory 持久化对话历史,支持连贯的追问
  3. 更大本地模型 — qwen2.5:7b 换为 qwen2.5:14b 或 llama3.1:8b;生成器与模型无关,直接换配置
  4. 评估 CI — 在 GitHub Actions 中每次 PR 自动运行 scripts/evaluate.py,综合分低于阈值则构建失败
  5. 增量文档更新 — 定时爬取 + 增量导入,保持知识库与 LangChain 官方文档同步

运行测试

pytest tests/test_pipeline.py -v    # 快速,无需下载模型
pytest tests/test_retrieval.py -v   # 首次运行下载 ~80 MB 模型
pytest                               # 全部测试

License

MIT


English Version

Click to expand

Quick Start

Prerequisites: Python 3.11+, Ollama running locally.

git clone https://github.com/GuddXzy/rag-from-scratch.git
cd rag-from-scratch
python -m venv .venv && .venv\Scripts\activate   # Windows
pip install -e ".[dev]"
ollama pull qwen2.5:7b
python -m scripts.ingest --docs-dir ./data/langchain_docs

Method 1 — Streamlit Chat UI (Recommended)

Bilingual interface (Chinese / English toggle in sidebar), chat history, source citations.

python -m streamlit run src/frontend/streamlit_app.py
# Open: http://localhost:8501

Method 2 — FastAPI REST API

Standard HTTP interface with auto-generated Swagger docs.

python -m scripts.serve
# Open: http://localhost:8000/docs
curl -X POST http://localhost:8000/query \
  -H "Content-Type: application/json" \
  -d '{"question": "What is LCEL?", "top_k": 5}'
Method Path Description
GET /health Health check
GET /stats Chunk count, model info
POST /query RAG Q&A → answer + sources
POST /query/stream Streaming SSE output
POST /ingest Import documents

Method 3 — Command Line

No server needed, great for scripting or quick tests.

python -m scripts.query "What is LCEL?"
python -m scripts.query "What is LCEL?" --no-generate   # retrieve only, skip LLM

Features

  • Hybrid Retrieval — cosine similarity + BM25 fused via RRF (score = Σ 1/(60 + rank))
  • 100 pages of LangChain official docs, 1,596 chunks at optimal chunk_size=512
  • Fully offline — Ollama qwen2.5:7b, no cloud API
  • Bilingual UI — Streamlit frontend with Chinese / English toggle
  • Evaluation — 4 metrics (Faithfulness / Answer Relevancy / Context Precision / Recall)

Tech Decisions

  • ChromaDB — zero-infrastructure, in-process; swap to Pinecone with one adapter change
  • Hybrid retrieval — BM25 handles exact keyword matches that pure cosine similarity misses
  • Ollama — fully offline, no API cost, model-agnostic generator
  • chunk_size=512 — empirically selected; leads in 3 of 4 metrics vs. 256 and 1024

About

Production-grade RAG system for LangChain documentation Q&A

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages