使用langchain构建简单RAG

使用langchain构建简单RAG

环境准备

pip install chromadb #安装 
chroma run	#运行


#conda创建 python=3.10版本的虚环境 
conda create -n python_10 python=3.10 

#torch安装
conda install pytorch==2.3.1 torchvision==0.18.1 torchaudio==2.3.1 pytorch-cuda=12.1 -c pytorch -c nvidia

#如果是本地部署ollama,langchain 对ollama的支持
pip install -U langchain-ollama

#支持chroma
pip install langchain_chroma
pip install -U langchain-community

#在控制台把ollama跑起来
ollama run deepseek-r1:1.5b

代码

import os
import requests
# 设置用户代理
os.environ["USER_AGENT"] = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
os.environ["HF_ENDPOINT"] = "https://hf-mirror.com"
from langchain_community.document_loaders import WebBaseLoader  # 修改后的导入路径
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.prompts import ChatPromptTemplate
from langchain.schema.runnable import RunnablePassthrough

from langchain.schema.output_parser import StrOutputParser
from langchain_ollama.llms import OllamaLLM
from langchain_chroma import Chroma
from langchain_community.embeddings import HuggingFaceBgeEmbeddings



def prepare_data():
    loader = WebBaseLoader(
        web_paths=["https://zhuanlan.zhihu.com/p/648063002"],
        requests_kwargs={
            "headers": {
                "User-Agent": os.environ["USER_AGENT"]
            }
        }
    )
    documents = loader.load()
    text_splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=50)
    chunks = text_splitter.split_documents(documents)
    print(chunks[0].page_content)
    return chunks


#embedding 知识库,保存到向量数据库
def embedding_data(chunks):
    #创建BAAI的embedding
    rag_embeddings = HuggingFaceBgeEmbeddings(model_name="BAAI/bge-small-zh-v1.5")
    #embed保存知识到向量数据库
    vector_store = Chroma.from_documents(documents=chunks, embedding=rag_embeddings,persist_directory="./chroma_langchain_db")
    retriever = vector_store.as_retriever() 
    return vector_store,retriever

#使用ollama服务
llm = OllamaLLM(model="deepseek-r1:1.5b") 
template = """您是问答任务的助理。
使用以下检索到的上下文来回答问题。
如果你不知道答案,就说你不知道。
最多使用三句话,不超过100字,保持答案简洁。
Question: {question} Context: {context} Answer:
"""
prompt = ChatPromptTemplate.from_template(template)
chunks = prepare_data()
vector_store,retriever = embedding_data(chunks)


#生成答案
def generate_answer(question):
    rag_chain = (
        {"context": retriever,  "question": RunnablePassthrough()}
        | prompt
        | llm
        | StrOutputParser()
    )
    print("问题:"+question)
    resp=rag_chain.invoke(question)
    print(resp)

query = "Midjourney是什么"
generate_answer(query)

代码解释

retriever = vector_store.as_retriever()

retriever = vector_store.as_retriever() :这行代码的含义是将 Chroma 向量数据库(vector_store)转换为一个检索器对象(retriever),这是 LangChain 框架中一个非常重要的概念。以下是具体解析:

核心作用

  1. 封装检索逻辑
    将向量数据库的底层操作(如相似性搜索、最大边际相关性搜索等)包装成一个标准化的检索接口,后续可以直接通过这个 retriever 对象进行语义检索。
  2. 统一交互方式
    在 LangChain 的生态中,不同的向量数据库(Chroma/FAISS/Pinecone 等)都有自己的底层 API,.as_retriever() 方法将它们统一成一致的检索接口,方便后续与其他组件(如 Chain、Agent)无缝集成。

关键特性

通过 retriever 可以:

  1. 直接执行语义搜索
results = retriever.get_relevant_documents("你的查询问题")

无需手动调用 similarity_search 等底层方法。

配置检索参数
可以通过参数控制搜索行为:

retriever = vector_store.as_retriever(
    search_type="similarity",       # 相似性搜索(默认)
    search_kwargs={"k": 5}           # 返回前5个结果
    # 其他参数如 score_threshold(分数阈值)等
)

支持链式调用
retriever 可以直接作为 RetrievalQA 链的输入:

qa_chain = RetrievalQA.from_chain_type(
    llm=your_llm,
    chain_type="stuff",
    retriever=retriever  # 直接传入检索器
)

rag_chain = ( {"context": retriever, "question": RunnablePassthrough()} | prompt | llm | StrOutputParser() )

这是用 LangChain 构建的 RAG(检索增强生成)流水线,其核心逻辑是通过管道操作符 | 将多个组件串联起来。它的工作流程如下:

1. 输入处理阶段

{"context": retriever, "question": RunnablePassthrough()}
  • 作用:定义输入数据的来源和传递方式
  • 关键组件
    • retriever:向量数据库的检索器(负责获取相关上下文)
    • RunnablePassthrough():直接透传用户的原始输入问题
  • 数据流向
    • context:自动调用 retriever.get_relevant_documents(question) 获取与问题相关的文档
    • question:直接传递用户输入的问题(例如用户提问 "如何申请年假?"

2. 提示模板阶段

| prompt
  • 作用:将检索到的上下文(context)和用户问题(question)组装成 LLM 能理解的提示词
  • 典型提示模板示例
from langchain.prompts import ChatPromptTemplate

prompt = ChatPromptTemplate.from_template("""
    根据以下上下文回答问题:
    {context}
    
    问题:{question}
    答案:
""")

实际生成的提示词

  • 根据以下上下文回答问题:
    [文档1内容]...  
    [文档2内容]...
    
    问题:如何申请年假?
    答案:
    

3. 语言模型推理阶段

| llm
  • 作用:将组装好的提示词输入给大语言模型(如 GPT-3.5、Llama2 等)

  • 典型模型调用

  • from langchain.chat_models import ChatOpenAI
    llm = ChatOpenAI(model="gpt-3.5-turbo")
    
  • 输出结果:模型生成的原始响应(通常为包含元数据的复杂对象)

4. 输出解析阶段

| StrOutputParser()
  • 作用:提取模型响应中的纯文本内容
  • 必要性:LLM 返回的原始响应可能包含冗余信息(如日志、置信度分数等),此步骤将其简化为字符串
  • 示例转换
# 输入(复杂对象):
AIMessage(content="申请年假需提交OA审批...", additional_kwargs={...})

# 输出(纯文本):
"申请年假需提交OA审批..."

运行结果

1

posted @ 2025-04-15 09:45  狐狸胡兔  阅读(184)  评论(0)    收藏  举报