本文基于 LangChain-Chatchat 项目,写一份“从 0 到 1 搭建 RAG 系统”的实战教程。即使你没做过 RAG,也能按步骤跑起来,并理解为什么要这么做、每一步影响什么。

RAG 是什么,为什么要用

RAG(Retrieval-Augmented Generation,检索增强生成)本质上是“先找资料、再回答问题”。它能把模型的回答限制在你自己的知识库里,减少幻觉、提升可追溯性,尤其适合企业文档、内部规范、产品手册等场景。

一句话概括:RAG = 检索 + 生成。检索负责召回相关内容,生成负责组织答案。

RAG 的全流程

可以理解成一条流水线:

  1. 文档准备:把 PDF、Word、Markdown、网页等资料整理出来。
  2. 文本抽取:从文件里提取纯文本。
  3. 分块切片:把长文本按规则切成“可检索的小段”。
  4. 向量化(Embedding):把每个文本块转换成向量。
  5. 索引入库:把向量和原文一起存进向量库/搜索引擎。
  6. 用户提问:用户输入问题。
  7. 检索召回:按问题召回 top_k 个最相关片段(向量/全文/混合)。
  8. (可选)重排序:用更强的模型对候选结果再排序,提高命中率。
  9. Prompt 组装:把检索片段 + 用户问题拼成提示词。
  10. LLM 生成回答:模型输出答案,并可附上引用来源。

LangChain-Chatchat 的 README 里对流程也有一句很清晰的总结:
“加载文件 -> 读取文本 -> 文本分割 -> 文本向量化 -> 问句向量化 -> 匹配 top k -> 拼到 prompt -> 交给 LLM 生成”。

基于 LangChain-Chatchat 搭建一个可用的 RAG

下面以 LangChain-Chatchat 项目为例,给出完整落地步骤。你可以只在本机跑,也可以把模型和服务拆开部署。

1. 安装与初始化

如果你在项目源码中运行(推荐),按 Quickstart 的步骤:

1
2
3
4
5
6
7
8
# 安装依赖(任选其一)
uv sync
# 或
pip install -e libs/chatchat-server

# 初始化配置(在 libs/chatchat-server 目录下)
cd libs/chatchat-server
python -m chatchat.cli init

初始化后会生成配置文件(data/ 目录):

  • basic_settings.yaml:基础配置
  • model_settings.yaml:模型配置
  • kb_settings.yaml:知识库配置
  • prompt_settings.yaml:提示词配置

2. 配置模型(LLM + Embedding)

编辑 data/model_settings.yaml,至少要配置一个 LLM 和一个 Embedding 模型。
项目支持 Xinference、Ollama、OneAPI、OpenAI 兼容接口等。

示例结构(只保留关键字段,API key 记得替换):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
DEFAULT_LLM_MODEL: your-llm-model
DEFAULT_EMBEDDING_MODEL: your-embedding-model

MODEL_PLATFORMS:
  - platform_name: your-platform
    platform_type: openai
    api_base_url: http://localhost:9997/v1
    api_key: YOUR_API_KEY
    llm_models:
      - your-llm-model
    embed_models:
      - your-embedding-model

注意:模型服务必须提前启动,否则初始化知识库时会报错。

3. 配置知识库与切分参数

重点看 data/kb_settings.yaml 里的这几项:

1
2
3
4
5
DEFAULT_VS_TYPE: faiss
CHUNK_SIZE: 750
OVERLAP_SIZE: 150
VECTOR_SEARCH_TOP_K: 3
SCORE_THRESHOLD: 2.0

含义简要解释:

  • DEFAULT_VS_TYPE:向量库类型,支持 faiss/milvus/zilliz/pg/es/relyt/chromadb
  • CHUNK_SIZE:单段文本长度
  • OVERLAP_SIZE:分块重叠长度,避免上下文断裂
  • VECTOR_SEARCH_TOP_K:召回的候选数量
  • SCORE_THRESHOLD:相似度阈值(越小越严格)

这些参数直接影响检索准确率和响应速度,后面会给调整建议。

4. 初始化知识库

1
python -m chatchat.cli kb --recreate-vs

如果想更精细地管理知识库:

1
2
3
python -m chatchat.cli kb --create <知识库名称>
python -m chatchat.cli kb --add-docs <知识库名称> --files <文件路径>
python -m chatchat.cli kb --recreate-vs <知识库名称>

5. 启动服务

1
python -m chatchat.cli start -a

访问:

  • WebUI: http://localhost:8501
  • API 文档: http://localhost:7861/docs

至此,一个可用的 RAG 系统就跑起来了。

向量检索 vs 全文检索 vs 混合检索

下面是三种检索思路的核心对比:

1. 向量检索

特点:语义匹配能力强,能理解同义表达与上下文。

优势

  • 适合问法多样、语义模糊的自然语言问题
  • 对跨语言/同义改写更友好

劣势

  • 对精确字段(ID、版本号、代码片段)不稳定
  • 依赖 Embedding 模型质量
  • 索引构建成本高

常见技术选型

  • 本地:FAISS、ChromaDB
  • 集群:Milvus、Zilliz
  • 数据库扩展:PGVector、Timescale Vector
  • 搜索引擎向量:Elasticsearch/OpenSearch

LangChain-Chatchat 里 DEFAULT_VS_TYPE 就是向量库类型的入口。

特点:基于关键词匹配,擅长精准命中。

优势

  • 对人名、产品名、版本号等“精确词”命中率高
  • 构建成本低,检索速度快

劣势

  • 无法理解语义改写
  • 需要分词与同义词体系

常见技术选型

  • Elasticsearch/OpenSearch
  • PostgreSQL Full Text Search
  • SQLite FTS5
  • Meilisearch / Tantivy

LangChain-Chatchat 在 File RAG 中集成了 BM25 检索器(通过 BM25Retriever)。

3. 混合检索

特点:向量召回 + 关键词召回,结合两者优势。

优势

  • 语义相关 + 精确匹配同时兼顾
  • 对“语义问题 + 关键字段”类问题更稳

劣势

  • 需要调权重与融合策略
  • 系统复杂度与延迟上升

典型做法

  • 并行召回(BM25 + 向量)→ 合并去重 → rerank
  • 单引擎混合(ES/OpenSearch 同时支持 BM25 + dense vector)

LangChain-Chatchat 的 FAISS 默认就是混合检索:
BM25 + 向量,权重约 0.3 : 0.7,空知识库时会自动退回纯向量检索。

如何兼顾精度和响应延迟

给你一个实践中比较稳妥的策略:

推荐组合(大多数场景适用)

  1. 召回阶段:BM25 + 向量混合检索
  2. 候选裁剪top_k 控制在 5~10
  3. 重排序(可选):用轻量 rerank 模型对 top_k 重新排序
  4. 生成阶段:只把最终 top_n 拼进 prompt(一般 3~5 段)

低延迟方案

  • 只用向量检索或只用 BM25
  • 关闭 rerank
  • 降低 top_k
  • 控制分块数量(更大的 chunk 更少的条目)

高精度方案

  • 混合检索 + rerank
  • 增大 top_k
  • 细粒度分块(更小的 chunk + overlap)
  • 选择更强的 embedding 模型

参数调优清单

下面这些参数能直接改善检索质量:

  • CHUNK_SIZE / OVERLAP_SIZE
    文档越长,chunk 就越大;结构化内容建议更小 chunk。

  • VECTOR_SEARCH_TOP_K
    太小容易漏召回,太大增加 prompt 成本。

  • SCORE_THRESHOLD
    越小越严格,建议先放宽到 1.0~2.0 再逐步收紧。

  • Embedding 模型
    通用场景用 bge/Qwen 系列就够;专业领域可自定义微调。

  • 混合权重
    关键词强的业务场景(SKU/版本号)适当提高 BM25 权重。

常见误区

  1. 只关注 LLM,不关注检索
    RAG 的核心是检索,检索质量不行,LLM 再强也会胡说。

  2. 分块太大或太小
    太大影响召回,太小导致上下文碎片化。

  3. top_k 过大
    召回太多内容会让模型“迷失”,回答更不稳。

  4. 忽略 rerank
    如果问答质量不稳定,rerank 往往是低成本提升。