Skip to content

什么是 RAG?在 LangChain 中如何实现 RAG 应用? ​

  • 标签:LangChain、RAG 检索增强

🏛 业务架构师 ​

RAG 在 LangChain 中的实现,我按组件到链路讲(0.2/0.3 版写法):

核心组件装配:

python
from langchain_community.vectorstores import FAISS
from langchain_huggingface import HuggingFaceEmbeddings
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import RunnablePassthrough
from langchain_openai import ChatOpenAI

# 1. 知识入库
docs = WebBaseLoader(url).load_and_split(
    RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=50))
embeddings = HuggingFaceEmbeddings(model_name="BAAI/bge-large-zh-v1.5")
vs = FAISS.from_documents(docs, embeddings)

# 2. 检索+生成链
retriever = vs.as_retriever(search_kwargs={"k": 4})
prompt = ChatPromptTemplate.from_messages([
    ("system", "仅依据 <ctx>{context}</ctx> 回答,标注引用,资料不足则说明。"),
    ("human", "{question}"),
])
rag_chain = (
    {"context": retriever, "question": RunnablePassthrough()}
    | prompt | ChatOpenAI(model="qwen-plus") | StrOutputParser()
)
rag_chain.invoke("退货政策是什么")

生产化补齐清单(业务重点):

  • 检索增强:多路召回(EnsembleRetriever 向量+BM25)+ Rerank 精排 + 元数据过滤(权限、时效);
  • 引用溯源:分块带编号与来源元数据,要求模型标注引用;
  • 会话支持:加历史管理(RunnableWithMessageHistory)+ 问题改写(结合历史消解指代);
  • 质量评测:RAGAS 评命中率/忠实度,badcase 回流迭代。

落地节奏:先用最小链路(上面 20 行)两周验证业务价值,再按 badcase 逐项增强——RAG 的护城河在数据治理和评测迭代,不在代码复杂度。

🏗 系统架构师 ​

RAG(检索增强生成)= 生成前检索相关知识注入上下文;LangChain 实现(LCEL 现代姿势):

python
from langchain_openai import ChatOpenAI, OpenAIEmbeddings
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthrough
from langchain_community.vectorstores import FAISS

# 1. 建库(离线)
vectorstore = FAISS.from_documents(docs, OpenAIEmbeddings())
retriever = vectorstore.as_retriever(search_kwargs={"k": 4})

# 2. RAG 链(在线)
prompt = ChatPromptTemplate.from_messages([
    ("system", "仅根据以下资料回答,无依据时回答'资料不足',标注来源[1][2]。\n资料:\n{context}"),
    ("human", "{question}"),
])

def format_docs(docs):
    return "\n\n".join(f"[{i+1}] {d.page_content}" for i, d in enumerate(docs))

rag_chain = (
    {"context": retriever | format_docs, "question": RunnablePassthrough()}
    | prompt | ChatOpenAI(model="gpt-4o") | StrOutputParser()
)
rag_chain.invoke("退货政策是什么?")

架构师视角的要点(比代码重要):

  • 生产化增强:混合检索(EnsembleRetriever:向量+BM25)+ Rerank 提精度;元数据过滤(权限 pre-filter)保安全;引用校验(citation ID ⊆ 检索集合)防假引用;
  • 评估配套:recall@K(检索)与 faithfulness(生成)分开设测——没有评测的 RAG 无法迭代;
  • 进阶路径:查询改写/扩展(MultiQueryRetriever)→ 父子块(ParentDocumentRetriever)→ 图状态机(LangGraph)处理多跳与循环;
  • 流式输出 .stream()、缓存友好布局(system 稳定前缀)是上线标配。

💻 全栈开发专家 ​

RAG 在 LangChain 里的最小完整实现(Python 版,逐环节可运行):

python
from langchain_community.document_loaders import WebBaseLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_openai import OpenAIEmbeddings, ChatOpenAI
from langchain_community.vectorstores import Chroma
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthrough

# 1. 离线索引:加载→切块→向量化→入库
docs = WebBaseLoader("https://docs.example.com/guide").load()
chunks = RecursiveCharacterTextSplitter(
    chunk_size=512, chunk_overlap=64).split_documents(docs)
vs = Chroma.from_documents(
    chunks, OpenAIEmbeddings(),
    persist_directory="./chroma_db")      # 持久化

# 2. 在线链:检索→格式化→生成
retriever = vs.as_retriever(search_kwargs={"k": 4})
prompt = ChatPromptTemplate.from_template(
    "仅根据以下资料回答,标注引用,资料不足则回答不知道:\n"
    "{context}\n\n问题:{question}")
def fmt(ds): return "\n\n".join(d.page_content for d in ds)

rag = ({"context": retriever | fmt, "question": RunnablePassthrough()}
       | prompt | ChatOpenAI(temperature=0) | StrOutputParser())

print(rag.invoke("退货流程是什么"))

生产化要补的四处(demo 到上线的距离):

  1. 元数据:chunk 带 source/时间/权限字段,检索加 filter(search_kwargs={"filter": {...}});
  2. rerank: retriever 包一层 ContextualCompressionRetriever(bge-reranker),top50 精排取 5;
  3. 拒答阈值:检索分数低于阈值直接回「未找到」,防硬答;
  4. 评测:50 条 golden set 跑 recall@5 和 faithfulness,每次调优回归。

Java 提示:Spring AI 里等价实现是 QuestionAnswerAdvisor(vectorStore) + ChatClient,二十行内搞定,思路一一对应。