大模型项目:知识库构建工具与 LangChain 操作ChromaDB

知识库构建工具与 LangChain 操作ChromaDB

之前的课程学习了 ChromaDB 基础操作和 LangChain 集成。今天深入底层向量化方案对比各方案的完整代码流程,并掌握重排序(Reranker)的用法与作用。


一、回顾重排序(Reranker)--- 用法与作用

1. 为什么要重排序?

粗排(bi-encoder) 阶段------向量检索速度快但精度有限。因为它把问题和文档各自独立编码为向量,仅靠余弦相似度打分,丢失了 token 级别的交互信息。

精排(cross-encoder) 阶段------将问题与每个候选文档配对为 (question, doc),一起输入 Transformer,通过自注意力机制让问题与文档的 token 充分交互,输出精准的相关性分数。

RAG 标准流程中的位置 :用户问题 → 向量检索(粗排 top-100) → Reranker(精排 top-5) → 注入 LLM 上下文 → 生成回复

2. 粗排 vs 精排对比
阶段 模型类型 原理 速度 精度 处理量级
粗排 bi-encoder(双编码器) 各自独立编码→余弦相似度 快(毫秒级) 中等 百万级
精排 cross-encoder(交叉编码器) 拼接后 Transformer 自注意力 慢(秒级) 高 🔥 百级
3. 代码用法
python 复制代码
from FlagEmbedding import FlagReranker

# 加载重排序模型
reranker = FlagReranker(
    model_name_or_path=r'D:/TechWorkPy/models/bge-reranker-large',
    use_fp16=True,
)

# 将问题与粗排召回的文档配对
reranker_input = [(question, doc) for doc in retriever_result]

# 计算相关性得分(得分越高越相关 🔥)
scores = reranker.compute_score(reranker_input)
print(f"重排序得分:{scores}")

# 按得分降序排列,取精排后的 top-3
scored_docs = list(zip(retriever_result, scores))
scored_docs.sort(key=lambda x: x[1], reverse=True)
final_docs = [doc for doc, _ in scored_docs][:3]

关键点:

  • FlagReranker 加载 cross-encoder 模型(BGE-Reranker 系列)
  • compute_score() 接收 [(q, d1), (q, d2), ...] 配对列表,返回每个对的得分
  • 重排序发生在粗排之后、注入 LLM 之前,是 RAG 最强的提质手段
  • cross-encoder 速度快不了,所以只对粗排召回的少量候选(如 top-100)做精排

二、Sentence-Transformers 写代码流程

Sentence-Transformers 封装了微调后的 Transformer 模型,几行代码即可获得高质量的句子向量,是学术和工业的首选。

写代码流程:

复制代码
① 准备数据 → ② 加载模型 → ③ 向量化 → ④ 计算相似度 → ⑤ 排序取 top-k
python 复制代码
from sentence_transformers import SentenceTransformer

# ---------- ① 准备数据 ----------
documents = [
    "RAG(检索增强生成)是一种利用外部知识库来增强大模型回答能力的技术。",
    "FlagEmbedding 是一个由北京智源人工智能研究院开发的文本嵌入模型。",
    "BGE 模型在 Massive Text Embedding Benchmark (MTEB) 排行榜上取得了优异的成绩。",
    "苹果公司由史蒂夫·乔布斯、史蒂夫·沃兹尼亚克和罗恩·韦恩于 1976 年创立。",
    "深度学习是机器学习的一个分支,它基于深层神经网络。",
    "熊猫是中国的国宝,主要栖息地是四川、陕西和甘肃的山区。",
]

question = "什么是rag?"

# ---------- ② 加载模型 ----------
model = SentenceTransformer(
    model_name_or_path=r'D:\TechWorkPy\models\bge-base-zh-v1.5',
    device="cuda",
)

# ---------- ③ 向量化(normalize_embeddings 归一化)----------
embeddings_1 = model.encode(documents, normalize_embeddings=True)  # (6, 768)
embeddings_2 = model.encode([question], normalize_embeddings=True) # (1, 768)

# ---------- ④ 计算相似度(归一化后点积 = 余弦相似度)----------
similarity = embeddings_1 @ embeddings_2.T   # shape: (6, 1)

# ---------- ⑤ 排序取 top-k ----------
scores = [{index: round(item[0], 2)} for index, item in enumerate(similarity.tolist())]
scores.sort(key=lambda x: list(x.values())[0], reverse=True)

print("检索结果(top-2):")
for item in scores[:2]:
    for idx in item.keys():
        print(f"  {documents[idx]}")

关键点:

  • normalize_embeddings=True 将向量归一化到单位长度,归一化后点积 = 余弦相似度
  • 矩阵乘法 @ 一次性计算所有相似度(比逐条循环快得多)
  • Sentence-Transformers 还封装了 util.semantic_search() 便捷方法

模型选择:

场景 推荐模型 说明
通用语义相似度 paraphrase-multilingual-MiniLM-L12-v2 多语言,平衡速度和性能
中文语义搜索/RAG BAAI/bge-base-zh-v1.5 🔥 中文首选,检索优化
英语语义搜索 msmarco-distilbert-base-v4 专为检索微调

三、Transformers 库加载 --- 手动池化

HuggingFace Transformers 是通用模型加载库,需要手动实现池化策略来获取句子向量。灵活但代码量更多。

写代码流程:

复制代码
① 准备数据 → ② 加载分词器+模型 → ③ 定义向量化函数(CLS池化) → ④ 计算相似度 → ⑤ 排序取 top-k
python 复制代码
from transformers import AutoTokenizer, AutoModel
import torch

# ---------- ① 准备数据 ----------
documents = [
    "RAG(检索增强生成)是一种利用外部知识库来增强大模型回答能力的技术。",
    "FlagEmbedding 是一个由北京智源人工智能研究院开发的文本嵌入模型。",
    "BGE 模型在 MTEB 排行榜上取得了优异的成绩。",
    "深度学习是机器学习的一个分支,它基于深层神经网络。",
]

question = "什么是rag?"

# ---------- ② 加载分词器和模型 ----------
tokenizer = AutoTokenizer.from_pretrained(
    pretrained_model_name_or_path=r'D:\TechWorkPy\models\bge-base-zh-v1.5',
)
model = AutoModel.from_pretrained(
    pretrained_model_name_or_path=r'D:\TechWorkPy\models\bge-base-zh-v1.5',
)
model.eval()  # 切换到评估模式

# ---------- ③ 定义文本向量化函数(CLS 池化)----------
def get_embeddings(texts):
    encoded_input = tokenizer(texts, padding=True, truncation=True, return_tensors='pt')
    with torch.no_grad():
        model_output = model(**encoded_input)
        sentence_embeddings = model_output[0][:, 0]    # 取 [CLS] token
        return torch.nn.functional.normalize(sentence_embeddings, p=2, dim=1)

# ---------- ④ 计算相似度 ----------
docs_embeddings = get_embeddings(documents)
question_embeddings = get_embeddings([question])
similarity = torch.matmul(docs_embeddings, question_embeddings.T)

# ---------- ⑤ 排序取 top-k ----------
scores = [{index: round(item[0], 2)} for index, item in enumerate(similarity.tolist())]
scores.sort(key=lambda x: list(x.values())[0], reverse=True)

print("检索结果:")
for item in scores:
    for idx in item.keys():
        print(f"  {documents[idx]}")

关键点:

  • 获取句子向量需要池化策略 (Pooling):
    • CLS 池化 :取 [CLS] 位置的输出向量(如上例)
    • Mean 池化:对所有 token 输出取平均
    • Max 池化:对所有 token 输出取最大值
  • BGE 模型推荐 CLS 池化 + L2 归一化
  • model.eval() 关闭训练相关操作,确保推理结果稳定
  • Transformers 方案灵活但繁琐,生产环境推荐 Sentence-Transformers

四、LangChain RAG 完整写代码流程

这是今天的核心实战------手动搭建 LCEL 管道实现 RAG 流程,并集成 Reranker 重排序。

写代码流程:

复制代码
① 声明 LLM → ② 准备数据/分割 → ③ 创建向量化模型 → ④ 存入向量数据库 → ⑤ 创建检索器 → ⑥ 定义提示词模板 → ⑦ 定义重排序函数 → ⑧ 构建 Chain → ⑨ 执行问答
① 声明 LLM
python 复制代码
import os
from langchain_openai import ChatOpenAI

LLM = ChatOpenAI(
    api_key=os.getenv("DASHSCOPE_API_KEY"),
    base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
    model="qwen3.7-max-preview",
    streaming=True,
)
② 准备数据与分割
python 复制代码
from langchain_community.document_loaders import TextLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_core.documents import Document

# 方式一:从文件加载并分割
loader = TextLoader(
    r"D:\TechWorkPy\workspace\stu_rag\datasets\华清远见.txt",
    encoding='utf-8'
)
data = loader.load()

text_splitter = RecursiveCharacterTextSplitter(
    chunk_size=100,          # 每块最大字符数
    chunk_overlap=20,        # 重叠字符数(保留上下文连贯性)
    length_function=len,
    separators=["\n\n",",", "?", "!", "\n"],  # 分割优先级
)
chunks = text_splitter.split_documents(data)

# 方式二:直接准备文本 → 转为 Document 对象
# documents = [Document(page_content=chunk) for chunk in text_chunks]
参数 说明 建议值
chunk_size 每块最大字符数 100~500
chunk_overlap 块之间重叠字符数 chunk_size 的 10%~20%
separators 分割优先级列表 中文优先 ["\n\n", "。", ",", " "]
③-④ 创建向量化模型 + 存入向量数据库
python 复制代码
from langchain_chroma import Chroma
from langchain_huggingface import HuggingFaceEmbeddings

EmbeddingModel = HuggingFaceEmbeddings(
    model_name=r"D:\TechWorkPy\models\paraphrase-multilingual-MiniLM-L12-v2",
    model_kwargs={
        "device": "cuda",
        "local_files_only": True,
    },
)

# 存入向量数据库(一步到位:分割 → 向量化 → 存储)
vector = Chroma.from_documents(
    documents=chunks,
    embedding=EmbeddingModel,
    persist_directory=r"D:\TechWorkPy\workspace\stu_rag\06langchain操作chromaDB\chroma",
    collection_name="hqyj",
    collection_metadata={"hnsw:space": "cosine"},
)
⑤ 创建检索器
python 复制代码
# 若已存在持久化数据,直接加载
# vector = Chroma(
#     persist_directory=r"....chroma",
#     collection_name="hqyj",
#     embedding_function=EmbeddingModel,
# )

retriever = vector.as_retriever(search_kwargs={"k": 6})  # 粗排召回 6 条
⑥ 定义提示词模板和问题
python 复制代码
from langchain_core.prompts import PromptTemplate

# 问答内容
question = "华清远见有多少个部门"

template = """
你是一个基于知识库的AI助手。请根据RAG检索内容回答用户问题。
规则:
    - 仅基于提供的知识回答,不使用外部知识补充。
    - 检索内容不足时,说明信息不足,不要猜测。
    - 优先提炼关键答案,避免冗长解释。
    - 输出结果时,不允许输出"根据提供的参考资料"这样的内容。
参考资料:
    {context}
问题:
    {question}
答案:
"""

prompt = PromptTemplate(
    template=template,
    input_variables=["context", "question"],
)
⑦ 定义重排序函数 输出重排序内容
python 复制代码
from FlagEmbedding import FlagReranker

reranker = FlagReranker(
    model_name_or_path=r'D:/TechWorkPy/models/bge-reranker-large',
    use_fp16=True,
)

def reranker_func(data):
    print("开始进行重排序...")
    print(data)
    
    question = data["question"]
    docs = data["context"]

    # 没有检索到文档,无需重排序
    if not docs:
        return data

    # 将问题与每个召回文档配对,计算重排序得分
    reranker_input = [(question, doc.page_content) for doc in docs]
    scores = reranker.compute_score(reranker_input)

    # 将文档和得分配对,按得分降序排序
    scored_docs = list(zip(docs, scores))
    scored_docs.sort(key=lambda x: x[1], reverse=True)

    # 返回排序后的文档
    data["context"] = [doc for doc, _ in scored_docs][:3]
    
    print("重排序后的结果:")
    for doc in data["context"]:
        print(doc.page_content)
        
    return data
⑧-⑨ 构建 Chain + 执行问答
python 复制代码
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnableParallel, RunnablePassthrough, RunnableLambda

# 调试:打印召回结果
def print_recall(docs):
    print("检索到的文档内容:")
    for doc in docs:
        print(f"  {doc.page_content}")
    return docs  # ⚠️ 必须有返回值,否则 Chain 断流

# 🏗️ 构建 LCEL 管道(搭积木)
qa_chain = (
    RunnableParallel({
        "context": retriever | RunnableLambda(print_recall),  
        # 路径1:检索 + 打印
        "question": RunnablePassthrough(),                    
        # 路径2:原样传递问题
    })
    | RunnableLambda(reranker_func)   # 🔥 重排序精排
    | prompt                          # 拼接提示词
    | LLM                             # 大模型生成
    | StrOutputParser()               # AIMessage → 纯文本
)

result = qa_chain.invoke(question)
print("\n")
print(f"LLM回复: {result}")

LCEL 管道核心组件:

组件 作用 说明
RunnableParallel 并行执行多个路径 同时获取检索上下文 + 传递问题
RunnablePassthrough() 透明传递输入 不做任何修改,直接传给下一环节
RunnableLambda(func) 将普通函数包装为 Chain 组件 ⚠️ 函数必须有返回值,否则断流
PromptTemplate 模板化提示词 {context}{question} 占位
StrOutputParser() 提取纯文本 AIMessage 转为字符串
` ` (pipe) 管道运算符串联组件

五、三种方案对比总结

方案 代码量 精度 灵活性 适用场景
Sentence-Transformers 学术/工业快速实验
Transformers 需精细控制模型行为
LangChain + ChromaDB 高(+Reranker 更高) 生产级 RAG 系统 🔥

核心要点:

  • 重排序(Reranker) 是 RAG 最强提质手段,cross-encoder 精度远高于 bi-encoder
  • 粗排(向量检索) 负责快------从百万级文档中快速筛出候选
  • 精排(Reranker) 负责准------对少量候选做精细化排序,注入 LLM 之前把关
  • LangChain LCEL 管道的核心是"搭积木":每个组件实现 Runnable 接口,通过 | 串联
  • ⚠️ 最重要的原则 :建库和检索用的 Embedding 模型必须是同一个
  • RunnableLambda 包装的函数必须有返回值,否则管道断流

最终思考:最近流程都越来越进入 RAG 生产环境的标准模板了。无论搭建什么领域的知识库问答系统,核心步骤都一样:数据准备 → 向量化存储 → 粗排检索 → 精排重排序 → Prompt 拼接 → LLM 生成。理解这个流程,就能搭建任意领域的 RAG 应用

相关推荐
爱研究的小梁15 小时前
多链路聚合通信:时延控制与网络波动对抗逻辑梳理
网络·人工智能·信息与通信
IT_陈寒15 小时前
Vite静态资源路径这个坑差点让我加班到凌晨
前端·人工智能·后端
颜酱15 小时前
15 | 安全执行 SQL 并返回查询结果
人工智能
神经蛙199615 小时前
🌍 别再硬编码中文了!Python Web 项目国际化(i18n)完全指南
后端·python
新芒16 小时前
海尔洗衣机智慧洗护:AI赋能洗烘护全面进化
人工智能
掘金酱16 小时前
「TRAE Work 实战帮」征文启动!你沉淀的经验,值得被看见!
前端·人工智能·后端
颜酱16 小时前
14 | 验证并修正 LLM 生成的 SQL
人工智能·python
AI创界者16 小时前
AIGC进阶】Sulphur-2 视频生成大模型离线实战:文生视频/图生视频本地一键部署整合包解压即用与调优指南
人工智能·aigc·音视频
颜酱16 小时前
13 | 使用 LangChain 生成 SQL
人工智能·python·langchain
LDZKKJ16 小时前
OpenAI暂停GPT-6训练:AI行业从“竞速“到“刹车“的分水岭
人工智能·gpt·语言模型·chatgpt·transformer