乐于分享
好东西不私藏

AI大模型开发 从 0 到企业级:RAG检索增强全流程实现 + 本地私有知识库搭建实战

AI大模型开发 从 0 到企业级:RAG检索增强全流程实现 + 本地私有知识库搭建实战

文章目录

    • 引言
    • 1 核心架构与流程对比
      • 1.1 原生RAG vs 企业级RAG横向对比
    • 2 环境准备与依赖安装
      • 2.1 软硬件要求
      • 2.2 依赖安装
    • 3 全流程核心代码实现
      • 3.1 多源数据接入与预处理
      • 3.2 嵌入模型选型与量化性能对比
      • 3.3 向量库搭建(入门+企业级双版本)
        • 入门版:Chroma本地向量库,适合快速测试
        • 企业级版:PGVector基于PostgreSQL,支持事务和权限过滤
      • 3.4 多路召回+重排序实现
      • 3.5 大模型生成问答结果
    • 4 企业级生产部署方案(含安全审计)
      • 4.1 核心安全设计
      • 4.2 FastAPI接口核心代码
    • 5 技术前瞻性分析
    • 6 附录:完整技术图谱
    • 总结

引言

大模型落地企业私有业务场景时,始终绕不开两大核心痛点:一是大模型训练数据截止日期固定,无法获取企业内部最新的私有数据;二是大模型天生存在幻觉问题,回答私有领域问题容易编造内容,无法直接落地使用。RAG(检索增强生成)因为其改造成本低、效果稳定、可解释性强、数据更新方便,已经成为当前企业私有大模型落地最成熟主流的方案。

网上大部分教程仅能跑通玩具级Demo,并没有覆盖生产环境需要的权限控制、安全审计、性能优化等核心需求,本文从零开始,一步步带你搭建一个可以直接在生产环境运行的本地私有知识库RAG系统,新手小白也可以跟着步骤直接落地。

1 核心架构与流程对比

本文设计的企业级RAG架构和原生Demo级RAG有本质区别,我们先通过两个流程图看整体差异。

1.1 原生RAG vs 企业级RAG横向对比

2 环境准备与依赖安装

2.1 软硬件要求

  • 本地开发测试:CPU 4核以上,内存16G以上,GPU显存8G以上即可流畅运行,CPU也可运行仅速度较慢
  • 企业生产部署:服务器CPU 8核以上,内存32G以上,GPU 16G显存以上,支持百级并发请求

2.2 依赖安装

创建requirements.txt文件,内容如下:

langchain==0.1.20langchain-community==0.0.38langchain-chroma==0.1.2sentence-transformers==2.7.0pypdf==4.2.0python-docx==1.1.2bitsandbytes==0.43.1transformers==4.41.2torch==2.3.0psycopg2-binary==2.9.9pgvector==0.2.5fastapi==0.110.2uvicorn==0.29.0python-jose[cryptography]==3.3.0passlib[bcrypt]==1.7.4

执行安装命令:

pip install -r requirements.txt

3 全流程核心代码实现

3.1 多源数据接入与预处理

支持PDF、Word、TXT、MD四种常见格式,自带去重清洗逻辑:

import osimport refrom typing import Listfrom langchain.schema import Documentfrom pypdf import PdfReaderfrom docx import Document as DocxDocumentdef load_single_pdf(file_path: str) -> List[Document]:    pdf_reader = PdfReader(file_path)    text = ""    for page in pdf_reader.pages:        text += page.extract_text() + "\n"    return [Document(page_content=text, metadata={"source": file_path, "file_type""pdf"})]def load_single_docx(file_path: str) -> List[Document]:    doc = DocxDocument(file_path)    text = "\n".join([para.text for para in doc.paragraphs])    return [Document(page_content=text, metadata={"source": file_path, "file_type""docx"})]def load_text_file(file_path: str) -> List[Document]:    with open(file_path, "r", encoding="utf-8"as f:        text = f.read()    ext = os.path.splitext(file_path)[1]    return [Document(page_content=text, metadata={"source": file_path, "file_type": ext})]def load_dir_documents(dir_path: str) -> List[Document]:    documents = []    for root, _, files in os.walk(dir_path):        for file in files:            file_path = os.path.join(root, file)            ext = file.lower().split(".")[-1]            if ext == "pdf":                documents.extend(load_single_pdf(file_path))            elif ext in ["docx""doc"]:                documents.extend(load_single_docx(file_path))            elif ext in ["txt""md"]:                documents.extend(load_text_file(file_path))    return documentsdef preprocess_documents(documents: List[Document]) -> List[Document]:    processed = []    seen_content = set()    for doc in documents:        content = doc.page_content.strip()        content = "\n".join([line.strip() for line in content.splitlines() if line.strip()])        content = re.sub(r'[\x00-\x08\x0b\x0c\x0e-\x1f\x7f-\xff]''', content)        if content and content not in seen_content:            seen_content.add(content)            doc.page_content = content            processed.append(doc)    return processed

文档分块采用企业级常用的递归分块,兼顾语义完整性:

from langchain.text_splitter import RecursiveCharacterTextSplitterdef split_documents(documents: List[Document], chunk_size: int = 512, chunk_overlap: int = 50) -> List[Document]:    text_splitter = RecursiveCharacterTextSplitter(        chunk_size=chunk_size,        chunk_overlap=chunk_overlap,        add_start_index=True,        separators=["\n\n""\n""。"","" """]    )    return text_splitter.split_documents(documents)

3.2 嵌入模型选型与量化性能对比

中文领域当前效果最好的开源嵌入模型是BAAI的BGE系列,下表是不同量化版本的性能对比,可根据自身需求选择:

模型名称
量化方式
参数量
显存占用
中文MRR@10
单条文本嵌入速度(CPU)
适合场景
bge-small-zh-v1.5
FP32
33M
130MB
59.3
12ms
测试/低并发场景
bge-small-zh-v1.5
INT4
33M
40MB
58.1
8ms
边缘/端侧部署
bge-base-zh-v1.5
FP32
109M
418MB
64.7
21ms
企业生产一般场景
bge-base-zh-v1.5
INT4
109M
130MB
63.2
13ms
生产高并发场景
bge-large-zh-v1.5
FP32
335M
1.3GB
67.8
45ms
对精度要求高的场景
bge-large-zh-v1.5
INT4
335M
420MB
66.1
22ms
生产高精度场景

本文选择兼顾速度和精度的bge-base-zh-v1.5 INT4版本,加载代码如下:

import torchfrom langchain_community.embeddings import HuggingFaceBgeEmbeddingsmodel_name = "BAAI/bge-base-zh-v1.5"model_kwargs = {"device""cuda" if torch.cuda.is_available() else "cpu""load_in_4bit"True}encode_kwargs = {"normalize_embeddings"True}embeddings = HuggingFaceBgeEmbeddings(    model_name=model_name,    model_kwargs=model_kwargs,    encode_kwargs=encode_kwargs)

3.3 向量库搭建(入门+企业级双版本)

入门版:Chroma本地向量库,适合快速测试

from langchain_chroma import Chroma# 初始化向量库persist_directory = "./chroma_db"vector_db = Chroma(    persist_directory=persist_directory,    embedding_function=embeddings,    collection_name="private_knowledge")# 插入分块后的文档split_docs = split_documents(preprocess_documents(load_dir_documents("./my_docs")))vector_db.add_documents(split_docs)# 持久化存储vector_db.persist()

企业级版:PGVector基于PostgreSQL,支持事务和权限过滤

首先用Docker启动PGVector服务,docker-compose.yml配置如下:

version: '3.8'services:  postgres:    image: pgvector/pgvector:pg16    container_name: pgvector-rag    environment:      POSTGRES_USER: rag_user      POSTGRES_PASSWORD: rag_2024_secure_private      POSTGRES_DB: rag_db    ports:      - "5432:5432"    volumes:      - ./pg_data:/var/lib/postgresql/data    restart: unless-stopped

启动命令:

docker-compose up -d

代码连接:

from langchain_community.vectorstores import PGVectorconnection_string = "postgresql+psycopg2://rag_user:rag_2024_secure_private@localhost:5432/rag_db"collection_name = "private_knowledge"vector_db = PGVector(    embedding=embeddings,    collection_name=collection_name,    connection_string=connection_string,    use_jsonb=True,)split_docs = split_documents(preprocess_documents(load_dir_documents("./my_docs")))vector_db.add_documents(split_docs)

3.4 多路召回+重排序实现

企业级RAG通过融合稀疏和稠密召回,再用重排序提升精度,代码如下:

from langchain.retrievers import BM25Retriever, EnsembleRetrieverfrom langchain_community.cross_encoders import HuggingFaceCrossEncoderfrom langchain.retrievers import ContextualCompressionRetrieverfrom langchain.retrievers.document_compressors import CrossEncoderRerank# 初始化BM25稀疏召回bm25_retriever = BM25Retriever.from_documents(split_docs)bm25_retriever.k = 10# 初始化稠密向量召回vector_retriever = vector_db.as_retriever(search_kwargs={"k"10})# 融合两路召回结果ensemble_retriever = EnsembleRetriever(    retrievers=[bm25_retriever, vector_retriever],    weights=[0.40.6])# BGE重排序模型,筛选最相关的Top5rerank_model = HuggingFaceCrossEncoder(model_name="BAAI/bge-reranker-base")compressor = CrossEncoderRerank(model=rerank_model, top_n=5)compression_retriever = ContextualCompressionRetriever(    base_retriever=ensemble_retriever,    base_compressor=compressor)

3.5 大模型生成问答结果

本文采用本地量化Llama3-8B,8G显存即可运行,代码如下:

from langchain_community.llms import HuggingFacePipelinefrom langchain.chains import RetrievalQAfrom transformers import AutoTokenizer, AutoModelForCausalLM, pipelinefrom langchain.prompts import PromptTemplate# 私有知识库专属Prompt模板template = """你是一个专业的企业内部助手,你需要根据提供的上下文信息回答用户的问题,如果你不知道答案,就直接说你不知道,不要编造答案。上下文信息如下:{context}用户问题: {question}回答:"""prompt = PromptTemplate.from_template(template)# 加载4bit量化Llama3-8B-Instructmodel_name = "NousResearch/Meta-Llama-3-8B-Instruct"tokenizer = AutoTokenizer.from_pretrained(model_name)model = AutoModelForCausalLM.from_pretrained(    model_name,    load_in_4bit=True,    device_map="auto")pipe = pipeline(    "text-generation",    model=model,    tokenizer=tokenizer,    max_new_tokens=512,    temperature=0.1,    top_p=0.9,    repetition_penalty=1.1)llm = HuggingFacePipeline(pipeline=pipe)# 构建RAG问答链qa_chain = RetrievalQA.from_chain_type(    llm=llm,    retriever=compression_retriever,    chain_type_kwargs={"prompt": prompt},    return_source_documents=True)# 测试问答question = "请问公司的年假申请规则是什么?"result = qa_chain.invoke({"query": question})print("回答:", result["result"])print("参考文档:", [doc.metadata["source"for doc in result["source_documents"]])

到这里,你已经跑通了完整的RAG流程,可以正常问答了。

4 企业级生产部署方案(含安全审计)

4.1 核心安全设计

企业生产部署必须满足合规要求,我们实现了五大安全能力:

  1. JWT身份认证:仅授权用户可访问接口
  2. 行级权限控制:用户只能查看自己有权限的文档
  3. 输入输出双重敏感内容检查:防止违规内容生成
  4. PII信息自动脱敏:对手机号、身份证等敏感信息自动打码
  5. 全链路日志审计:所有请求都记录留存,满足等保要求

4.2 FastAPI接口核心代码

import loggingimport refrom fastapi import FastAPI, Depends, HTTPException, statusfrom jose import JWTError, jwtfrom pydantic import BaseModelfrom typing import Optional# 初始化日志审计logging.basicConfig(filename='./rag_audit.log', level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')app = FastAPI(title="企业私有知识库RAG API", version="1.0")SECRET_KEY = "替换为你自己的生产级密钥"ALGORITHM = "HS256"class QueryRequest(BaseModel):    question: str    token: str# JWT认证依赖async def get_current_user(token: str) -> Optional[int]:    credentials_exception = HTTPException(        status_code=status.HTTP_401_UNAUTHORIZED,        detail="无效的访问凭证",    )    try:        payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])        user_id: int = payload.get("user_id")        if user_id is None:            raise credentials_exception    except JWTError:        raise credentials_exception    return user_id# 敏感内容检查SENSITIVE_WORDS = ["替换为你的敏感词列表"]def sensitive_check(text: str) -> bool:    for word in SENSITIVE_WORDS:        if word in text:            return False    return True# PII脱敏def pii_mask(text: str) -> str:    text = re.sub(r'(1[3-9]\d)\d{4}(\d{4})'r'\1****\2', text)    text = re.sub(r'(\d{6})\d{8}(\d{4})'r'\1********\2', text)    return text# 问答接口@app.post("/api/rag/query")async def query_rag(request: QueryRequest, user_id: int = Depends(get_current_user)):    logging.info(f"用户ID: {user_id}, 请求问题: {request.question}")    # 输入敏感检查    if not sensitive_check(request.question):        logging.warning(f"用户ID: {user_id}, 请求包含敏感内容: {request.question}")        raise HTTPException(status_code=400, detail="请求包含违规内容")    # 权限过滤:只返回当前用户有权限的文档    result = qa_chain.invoke({"query": request.question, "filter": {"accessible_user": [user_id, -1]}})    # 输出脱敏    answer = pii_mask(result["result"])    # 输出敏感检查    if not sensitive_check(answer):        logging.warning(f"用户ID: {user_id}, 生成回答包含敏感内容, 问题: {request.question}")        return {"answer""回答包含敏感内容,无法展示""sources": []}    sources = [doc.metadata["source"for doc in result["source_documents"]]    return {"answer": answer, "sources": sources}

生产环境可以用Nginx反向代理,容器化整体部署,支持水平扩展,这里不展开赘述。

5 技术前瞻性分析

RAG技术当前还在快速迭代,未来企业级落地的趋势主要有三个方向:

  1. 架构升级
    :从普通RAG走向Graph RAG和多模态RAG,Graph RAG解决了长文档全局信息丢失的问题,适合大型知识库梳理场景;多模态RAG支持图片、表格、视频等非文本数据,拓展了RAG的应用边界。
  2. 自动化优化
    :自适应RAG会成为主流,系统会自动根据问题类型选择分块大小、召回策略、模型大小,无需人工调参,进一步降低落地成本。
  3. 技术融合
    :RAG不会替代微调,未来企业级落地会是RAG+参数高效微调的结合,RAG解决知识更新和可解释性问题,微调解决大模型对齐和风格统一问题,两者结合效果最优。

6 附录:完整技术图谱

总结

本文从零开始完整实现了企业级RAG全流程,新手按照步骤即可搭建自己的本地私有知识库,企业可以直接基于这套方案改造落地生产环境。