RAG & LLm大模型构建本地知识库

💡 本文会带给你

  1. RAG与LLM的优势
  2. 如何构建垂域(向量)知识库
  3. 文本分块的技巧
  4. 构建一套完整 RAG 系统的步骤

一、RAG的优势

使用 RAG(Retrieval-Augmented Generation)检索增强生成, 核心目的是 结合检索(Retrieval)和生成(Generation)的优势,解决传统大语言模型(LLM)能力不足的问题。

1. 为什么需要 RAG?

传统大语言模型(LLM)存在着知识固化、幻觉、无妨访问企业内部数据等问题,RAG本地部署,可解决这些问题。

(1)解决 LLM 的“知识固化”问题
传统 LLM(如 GPT):依赖训练时的静态知识,无法实时更新。

缺点:如果训练数据中不包含最新信息(如 2024 年的新闻),模型无法正确回答。

RAG:通过检索外部知识库(如本地文档、网页),动态补充最新信息。

示例:
❌ 纯 LLM:问 “2025 年诺贝尔奖得主是谁?” → 可能胡编乱造(幻觉)。
✅ RAG:先检索权威新闻 → 返回真实结果。

(2)减少“幻觉”(Hallucination)
LLM 在缺乏相关知识时容易生成看似合理但错误的答案。

RAG 通过 检索真实文档 提供依据,让生成结果更可信。

示例:
❌ 纯 LLM:“爱因斯坦是如何发明量子计算机的?” → 可能编造故事。
✅ RAG:检索物理学史 → 回答 “爱因斯坦并未直接发明量子计算机”。

(3)支持私有/领域知识
纯 LLM:无法访问企业内部文档、个人笔记等私有数据。

RAG:可直接从本地知识库(如公司 Wiki、PDF 报告)中检索信息。

应用场景:

法律顾问:检索最新法律法规 → 生成合规建议。

医疗诊断:结合医学论文 → 提供参考资料。

(4)低成本 & 可解释性
微调(Fine-tuning):需要大量标注数据和算力,成本高。

RAG:仅需构建检索库,无需重新训练模型,且能提供检索来源(如引用文档)。

对比:

方法比较表
方法成本可解释性更新知识
纯 LLM
微调需重新训练
RAG即时

2. RAG 的核心优势

(1)动态知识更新
无需重新训练模型,只需更新检索库(如新增 PDF 文件)。

(2)精准答案
生成结果基于检索到的真实文本,而非模型记忆。

(3)模块化设计
可灵活替换组件:

检索器:从 FAISS 切换到 Elasticsearch。

生成模型:从 GPT-4 切换到 Llama3。

(4)适合长尾问题
对冷门、专业问题,RAG 能通过检索补充 LLM 未覆盖的知识。

3. 适用场景

(1)企业知识库
内部文档问答(如员工手册、技术文档)。

客户支持:快速检索产品说明书生成回答。

(2)学术研究
从海量论文中检索相关研究,生成综述。

(3)法律 & 医疗
法律条文、病例报告的精准查询和摘要。

(4)个人知识管理
对个人笔记、收藏文章进行智能搜索和总结。

4. RAG 的局限性

(1)依赖检索质量 如果检索库不完整或噪声多,生成结果也会受影响。

(2)延迟较高 检索 + 生成两步流程,比纯生成慢(可通过缓存优化)。

(3)上下文长度限制 LLM 的输入长度有限(如 GPT-4 Turbo 的 128K),可能截断长文档。

5. 对比其他方案

构建知识库方案对比表
方案优点缺点
纯 LLM响应快,通用性强知识静态,易幻觉
微调适应特定任务成本高,难更新
RAG动态知识,可解释依赖检索质量
Agent多工具联动复杂度高

总之
使用 RAG 的核心原因:
✅ 动态知识:突破 LLM 的训练数据时间限制。
✅ 减少幻觉:答案基于真实文档,而非模型想象。
✅ 私有数据:支持本地/领域知识库。
✅ 低成本:无需微调,快速部署。

适合对 准确性、实时性 要求高的场景(如企业知识管理、专业领域问答)。

二、应用RAG构建知识库

以下是构建本地知识库的完整步骤和基本流程,涵盖从数据准备到部署应用的各个环节:

1. 整体架构

RAG 核心流程 检索(Retrieval):从本地知识库中检索相关文档片段。

生成(Generation):LLM 结合检索到的内容生成回答

2. 详细步骤

2.1 数据准备阶段

(1) 文档收集及预处理

支持格式:PDF/Word/TXT/Markdown/Excel/PPT/Json等,这里也可以自定义文档处理类

存储方式:建议按类别分目录存放(如/data/legal, /data/tech)

文档加载成功后,对文档的文本进行清洗和规范化,包括: 编码标准化、空白字符处理、移除多余空格、理HTML/XML标签(如
,

等)、制表符、换行符、规范化段落分隔(统一为1-2个换行符)、特殊字符清理等操作。

文档加载及预处理示例代码:

In [ ]:

from langchain_community.document_loaders import (
    PyPDFLoader,
    Docx2txtLoader,
    UnstructuredFileLoader,
    UnstructuredExcelLoader,
    UnstructuredPowerPointLoader,
    CSVLoader,
    JSONLoader,
    UnstructuredWordDocumentLoader
)
LOADER_MAPPING = {
            ".json": QAPairLoader,  # 动态JSON处理器
            ".pdf": PyPDFLoader,
            ".txt": UnstructuredFileLoader,
            ".xlsx": UnstructuredExcelLoader,
            ".pptx": UnstructuredPowerPointLoader,
            ".csv": CSVLoader,
            ".docx": UnstructuredWordDocumentLoader,
            ".doc": UnstructuredWordDocumentLoader
        }
#加载问目录下所有文件
input_dir="./data/"
documents = []
    for root, _, files in os.walk(input_dir):
        for file in files:
            ext = os.path.splitext(file)[1].lower()
            if ext in LOADER_MAPPING:
                try:
                    file_path = os.path.join(root, file)
                    loader = LOADER_MAPPING[ext](file_path)
                    docs = loader.load()

                    # 添加文件来源元数据
                    for doc in docs:
                        doc.metadata["source"] = file_path
                        doc.metadata["file_type"] = ext

                    documents.extend(docs)
                    print(f"成功加载: {file_path}")
                except Exception as e:
                    print(f"加载失败 {file_path}: {str(e)}")
#对数据文本进行清洗
for doc in documents:
    text = doc.page_content
    # 1. 去除HTML标签(如果是HTML/XML文件)
    text = BeautifulSoup(text, "html.parser").get_text()
    # 2. 标准化空白字符
    text = re.sub(r"\s+", " ", text).strip()
    # 3. 处理特殊字符(保留中文、英文、常见标点)
    text = re.sub(r"[^\u4e00-\u9fa5a-zA-Z0-9\s,.!?;:,。!?;:、]", "", text)
    # 4. 处理换行符和段落分隔
    text = text.replace("\r\n", "\n").replace("\r", "\n")
    doc.page_content = text
2.2 文本处理阶段

(1) 文本分割

推荐参数:Chunk大小:300-500字符,重叠窗口:50-100字符(避免信息割裂)

在文档处理或文本分块(Text Chunking)中,chunk_size 和 chunk_overlap 是两个关键参数,用于控制如何将长文本分割成较小的片段。

详细解释如下:

  1. chunk_size = 500 含义:每个文本块的最大长度(按字符或token计算)

作用:

确保每个分块不超过500个字符(或token)

避免生成过长的文本片段,影响后续处理(如向量化、模型输入等)

实际表现:

In [ ]:

#python
# 输入文本(假设共800字符)
text = "这是一个很长的文档内容...[共800字符]..."
# 分割后:
chunk1 = "这是一个很长的文档内容...[500字符]"  # 第1个块
chunk2 = "...[剩余300字符]"                   # 第2个块
  1. chunk_overlap = 100 含义:相邻文本块之间的重叠字符数(或token数)

作用:

防止关键信息被割裂在不同块中

提升上下文连贯性(尤其在处理句子或段落边界时)

实际表现:

In [ ]:

# 分割效果(接上例):
chunk1 = "这是一个很长的文档内容...[500字符]"  
chunk2 = "文档内容...[最后100字符重叠] + [新400字符]"  # 前100字符与chunk1重叠
  1. 联合工作原理

  1. 参数选择建议
文本分块参数推荐表
场景推荐 chunk_size推荐 chunk_overlap原因
通用文档检索300-50050-100平衡信息完整性和效率
代码处理800-1200100-200代码块通常较长
技术论文解析500-800150-200保持数学公式完整性
对话记录分析200-30030-50短文本无需大重叠
  1. 代码示例验证

In [ ]:

from langchain.text_splitter import RecursiveCharacterTextSplitter

text = "..."  # 假设这里是一个长文本

splitter = RecursiveCharacterTextSplitter(
    chunk_size=500,
    chunk_overlap=100,
    separators=["\n\n", "\n", "。", "!", "?"]
)

chunks = splitter.split_text(text)
for i, chunk in enumerate(chunks):
    print(f"Chunk {i+1} (长度: {len(chunk)}):\n{chunk[:100]}...\n")
  1. ✅注意事项 长度计算方式:

默认按字符计数(中文1字=1字符)

如需按token计算(如LLM输入),需指定tokenizer:

In [ ]:

from transformers import AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained("bert-base-chinese")
chunk_size = 500  # 500个token ≈ 375个汉字

重叠部分优化:

避免在单词/术语中间分割:

In [ ]:

splitter = RecursiveCharacterTextSplitter(
    chunk_size=500,
    chunk_overlap=100,
    keep_separator=True  # 保留分隔符完整性
)

性能影响:

重叠部分会增加存储和计算量(向量库需存储更多数据)

但对召回率(Recall)有显著提升

通过合理设置这两个参数,可以确保: ✅ 关键信息不被割裂 ✅ 保持语义完整性 ✅ 适配下游任务(如检索、生成等)的需求

In [ ]:

#智能分块与结构化处理
from langchain.text_splitter import (
    RecursiveCharacterTextSplitter,
    MarkdownHeaderTextSplitter
)
# 通用文本分块器
text_splitter = RecursiveCharacterTextSplitter(
    chunk_size=400,
    chunk_overlap=80,
    separators=["\n\n", "\n", "。", "!", "?", ";", "……", " ", ""]
)

for doc in documents:
    # Markdown文件按标题分块
    if doc.metadata["file_type"] == ".md":
        headers_to_split_on = [
            ("#", "Header 1"),
            ("##", "Header 2"),
            ("###", "Header 3"),
        ]
        markdown_splitter = MarkdownHeaderTextSplitter(
            headers_to_split_on=headers_to_split_on
        )
        split_docs = markdown_splitter.split_text(doc.page_content)
        final_docs.extend(split_docs)
    # 表格特殊处理
    elif doc.metadata["file_type"] == ".csv":
        table_text = "表格内容:\n"
        for row in doc.page_content.split("\n"):
            table_text += "| " + " | ".join(row.split(",")) + " |\n"
        doc.page_content = table_text
    #json问答对
    elif doc.metadata["file_type"] == ".json":
        split_docs = [doc]
        final_docs.extend(split_docs)
    # 其他文件类型使用通用分块
    else:
        split_docs = text_splitter.split_documents([doc])
        final_docs.extend(split_docs)
2.3 向量化阶段

(1) Embedding模型选择

使用text2vec-large-chinese,其是一个专注于中文文本嵌入(Embedding)的预训练模型,从魔塔社区下载到本地使用

(2) 向量化实现

In [ ]:

# 使用HuggingFace本地模型
from sentence_transformers import SentenceTransformer
model_name = "./models/text2vec-large-chinese"
embedding_model = SentenceTransformer(model_name)
# 批量生成向量(全部生成,避免重复计算)
texts = [doc.page_content for doc in documents]
embeddings = self.embedding_model.encode(
    texts,
    normalize_embeddings=True,
    batch_size=32,  # 根据GPU内存调整
    show_progress_bar=True
)
2.4 向量数据库构建

(1) 数据库选型对比

向量数据库性能比较
数据库写入速度查询性能适合场景
FAISS极快实验/小规模
Chroma快速原型开发
Milvus大规模生产环境

由于Chroma操作方便,无需安装,直接建立目录即使用,所以选择了Chroma,同时可把向量入库和查询的代码都规划好模块,等以后用方便替换为其他数据库

(2) 向量入库

In [ ]:

#向量入库代码示例
import chromadb
persist_dir = "./dbs/chromadb"
client = chromadb.PersistentClient(path=persist_dir)
# 准备元数据
metadatas = [doc.metadata for doc in documents]
ids = [f"doc_{i}" for i in range(len(documents))]  # 统一生成ID

# 分批次提交(每批100条)
batch_size = 100
for i in range(0, len(texts), batch_size):
    batch_texts = texts[i:i + batch_size]
    batch_embeddings = embeddings[i:i + batch_size].tolist()  # 当前批的embeddings
    batch_metadatas = metadatas[i:i + batch_size]
    batch_ids = ids[i:i + batch_size]
    # 存入当前批次
    self.collection.add(
        documents=batch_texts,
        embeddings=batch_embeddings,
        metadatas=batch_metadatas,
        ids=batch_ids
    )
2.5 检索增强生成(RAG)

用户提问 → 检索最相关的文本片段 → LLM 生成回答。 推荐 LLM:
云端:GPT-4、DeepSeek-V3

本地:Llama3、ChatGLM3、DeepSeek-R1-Distill-Qwen-1.5B

以DeepSeek-R1-Distill-Qwen-1.5B为例

In [ ]:

#检索最相关的文本片段
from embedding import VectorDatabase
query = "上游泳课,泳裤没干"#查询内容
vector_db = VectorDatabase(persist_dir="./dbs/chromadb",model_name=model_name)
query_embedding = vector_db.embedding_model.encode(
            query,
            normalize_embeddings=True
        )

# ChromaDB查询
results = vector_db.collection.query(
    query_embeddings=[query_embedding.tolist()],
    n_results=5
)
# 格式化输出
formatted_results = []
for i in range(len(results["ids"][0])):
    doc_id = results["ids"][0][i]
    doc_text = results["documents"][0][i]
    metadata = results["metadatas"][0][i]
    score = 1 - results["distances"][0][i]  # 余弦相似度转换

    if score >= 0.6:
        formatted_results.append({
            "id": doc_id,
            "text": doc_text,
            "metadata": metadata,
            "score": round(score, 4)
        })

In [ ]:

#LLM 生成回答,以本地模型DeepSeek-R1-Distill-Qwen-1.5B为例
#生成提示词
content = formatted_results
prompt = "
[
        {
            "role": "system",
            "content": "你是一个专业问答助手,根据提供的上下文回答问题。回答时请直接给出最终答案,不要包含思考过程或中间推理。如果不知道答案,请回答'根据已有信息无法确定'。"
        },
        {
            "role": "user",
            "content": f"上下文:{context}\n\n问题:{query}\n请用中文回答:"
        }
    ]
"
#调用大模型,使用LMDeploy加载的,下面的代码稍加修改也可以调用云上的模型
from openai import OpenAI
# 默认值
DEFAULT_BASE_URL = "http://localhost:23333/v1"
DEFAULT_API_KEY = "your-default-api-key"
DEFAULT_MODEL_NAME = "DeepSeek-R1-Distill-Qwen-1.5B"#"Qwen2.5-0.5B-Instruct"
# 初始化 OpenAI 客户端
client = OpenAI(base_url=DEFAULT_BASE_URL, api_key=DEFAULT_API_KEY if DEFAULT_API_KEY else None)
response = client.chat.completions.create(
            model=DEFAULT_MODEL_NAME,  # 使用用户指定的模型名称
            messages=[
                {"role": "user", "content": prompt}
            ],
            max_tokens=100,  # 设置生成的最大 token 数
            temperature=0.7,  # 设置生成文本的随机性
        )
print(response.choices[0].message.content)

三. 应用部署

选择使用FastAPI+Nginx 反向代理+html

FastAPI的优点

框架: FastAPI (高性能,自动生成文档)

认证: JWT (适合企业级应用) 或 API Key (快速实现)

文档: Swagger UI (内置于FastAPI)

并发: Uvicorn + Gunicorn (生产环境),Gunicorn 管理多进程,Uvicorn 处理异步请求

四、关键优化点

检索质量优化

多路召回(关键词+向量)

动态chunk大小(重要文档分小块)

性能优化

批量处理文档

建立增量更新机制

安全措施

文档访问权限控制

问答记录审计

五、典型技术栈组合

建议从小规模原型(FAISS+ChatGLM3)开始验证,再逐步扩展到生产级方案(Milvus+LLama3)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值