LangChain 能力详解!:输出解析、RAG、向量数据库与 Retriever 检索器

到目前为止,我们已经学习了如何向模型发送消息、如何使用提示词模板,以及如何通过 Few-Shot 约束模型输出。但如果希望模型真正回答企业文档、项目资料、PDF、数据库中的内容,还需要掌握 RAG 相关组件。

一、输出解析器 Output Parser

模型原始返回的数据通常是 AIMessage

python 复制代码
result = model.invoke("介绍一下 LangChain")

print(result.content)

如果只需要获取文本内容,可以使用 StrOutputParser

python 复制代码
from langchain_core.output_parsers import StrOutputParser

parser = StrOutputParser()

chain = model | parser

result = chain.invoke("介绍一下 LangChain")

print(result)

输出解析器的作用可以理解为:

python 复制代码
模型原始输出
   ↓
解析器处理
   ↓
得到字符串、JSON、Pydantic 对象等结构化结果

1. StrOutputParser

最简单的解析器,用于提取模型文本内容。

python 复制代码
from langchain_core.output_parsers import StrOutputParser

chain = model | StrOutputParser()

result = chain.invoke("什么是向量数据库?")

print(result)

2. JsonOutputParser

如果希望模型返回 JSON,可以使用 JsonOutputParser

python 复制代码
from langchain_core.output_parsers import JsonOutputParser
from langchain_core.prompts import PromptTemplate

parser = JsonOutputParser()

prompt = PromptTemplate(
    template="""
请从下面文本中提取姓名、年龄和职业。

文本:
{text}

{format_instructions}
""",
    input_variables=["text"],
    partial_variables={
        "format_instructions": parser.get_format_instructions()
    }
)

chain = prompt | model | parser

result = chain.invoke({
    "text": "张三今年 25 岁,是一名 Python 后端开发工程师。"
})

print(result)

输出示例:

python 复制代码
{
  "name": "张三",
  "age": 25,
  "job": "Python 后端开发工程师"
}

3. Pydantic 输出解析

Pydantic 可以让输出类型更明确。

python 复制代码
from pydantic import BaseModel, Field
from langchain_core.output_parsers import PydanticOutputParser

class Person(BaseModel):
    name: str = Field(description="姓名")
    age: int = Field(description="年龄")
    job: str = Field(description="职业")

parser = PydanticOutputParser(pydantic_object=Person)

之后将 parser.get_format_instructions() 加入 Prompt,即可要求模型按指定结构返回。

二、什么是 RAG

RAG 的全称是 Retrieval-Augmented Generation,即检索增强生成。

RAG 主要解决三个问题:

  1. 模型知识可能已经过时;
  2. 模型不知道企业私有资料;
  3. 模型可能产生幻觉。

例如,企业有一份员工手册、产品说明书、项目文档。如果不使用 RAG,模型并不知道文档内容。如果使用 RAG,流程会变成:

复制代码
用户问题
   ↓
在知识库中检索相关文档
   ↓
把相关文档放进 Prompt
   ↓
模型依据文档回答

RAG 的完整流程通常分为两部分。

1. 索引阶段

复制代码
原始文档
   ↓
文档加载
   ↓
文本切分
   ↓
文本向量化
   ↓
写入向量数据库

2. 检索与生成阶段

复制代码
用户问题
   ↓
问题向量化
   ↓
相似度搜索
   ↓
获取相关文本块
   ↓
将文本块与问题交给模型
   ↓
模型生成最终答案

三、Document:LangChain 中的文档对象

LangChain 中,文档通常使用 Document 对象表示。

python 复制代码
from langchain_core.documents import Document

document = Document(
    page_content="LangChain 是一个用于构建 LLM 应用的开发框架。",
    metadata={
        "source": "langchain_intro.md",
        "category": "技术文档"
    }
)

print(document.page_content)
print(document.metadata)

一个 Document 包含两部分:

属性 作用
page_content 文档正文内容
metadata 文档来源、页码、标题、分类等辅助信息

元数据非常重要。例如,最终回答时,我们可能需要告诉用户:

复制代码
该回答来自《员工手册》第 12 页。

或者只检索某类资料:

复制代码
只查询"产品文档"分类下的内容。

这些都依赖元数据。

四、文档加载器 Document Loader

文档加载器负责将外部资料读取为 Document 对象。

常见文档来源包括:

  • TXT;
  • Markdown;
  • PDF;
  • Word;
  • Excel;
  • HTML 网页;
  • 数据库;
  • Notion;
  • 企业 Wiki。

1. 加载 TXT 文件

python 复制代码
from langchain_community.document_loaders import TextLoader

loader = TextLoader(
    "knowledge.txt",
    encoding="utf-8"
)

documents = loader.load()

print(documents[0].page_content)
print(documents[0].metadata)

2. 加载 Markdown 文件

python 复制代码
from langchain_community.document_loaders import UnstructuredMarkdownLoader

loader = UnstructuredMarkdownLoader(
    "knowledge.md",
    mode="single"
)

documents = loader.load()

print(documents[0].page_content)

mode="single" 通常表示将整个文件加载为一个 Document。有些加载器也支持按标题、段落等元素拆分文档。

3. 文档加载的注意点

加载文档后,不建议直接将整份文档传给模型。

原因包括:

  • 文档可能过长;
  • 超出上下文窗口;
  • Token 成本过高;
  • 无关内容影响回答质量。

因此,下一步通常是文本切分。

五、文本分割器 Text Splitter

文本切分器的职责是将长文档拆成多个小文本块。例如:

复制代码
一份 100 页的产品说明书
   ↓
切成数百个可检索文本块
   ↓
每次只检索其中最相关的几块

1. 使用递归文本切分器

python 复制代码
from langchain_text_splitters import RecursiveCharacterTextSplitter

text_splitter = RecursiveCharacterTextSplitter(
    chunk_size=500,
    chunk_overlap=80
)

chunks = text_splitter.split_documents(documents)

print(len(chunks))
print(chunks[0].page_content)

参数说明:

参数 含义
chunk_size 每个文本块的最大长度
chunk_overlap 相邻文本块的重叠长度

2. 为什么需要 chunk_overlap

假设原始内容是:

复制代码
Redis 是一个高性能内存数据库。
它支持字符串、列表、集合、有序集合等多种数据结构。

如果恰好在"数据库"和"它支持"之间切分,可能导致前后语义断裂。因此保留一部分重叠内容,可以减少切分造成的上下文丢失。

3. 如何选择切分大小

没有一个绝对通用的参数,需要根据资料类型调整。

文档类型 切分建议
FAQ 问答 按问题和答案切分
Markdown 文档 按标题层级切分
普通文章 chunk_size=300~800
代码文件 按函数、类、模块切分
PDF 资料 结合页码、标题、段落切分

一个好的文本块通常应该:

  • 包含相对完整的语义;
  • 不要过长;
  • 不要只剩半句话;
  • 能够独立回答一类小问题;
  • 保留来源信息。

六、Embedding:文本向量化

计算机并不真正理解文字含义。Embedding 的作用是将文字转换为向量。

复制代码
"Python 是一门编程语言"
   ↓
[0.012, -0.463, 0.842, ...]

这些数字本身没有直接可读性,但它们能表达文本之间的语义关系。例如:

复制代码
Python 入门路线
Python 初学者如何学习

这两句话字面不同,但语义接近,因此它们的向量距离通常比较近。

1. 初始化 Embedding 模型

python 复制代码
from langchain_openai import OpenAIEmbeddings

embeddings = OpenAIEmbeddings(
    model="text-embedding-3-small"
)

2. 向量化查询文本

python 复制代码
vector = embeddings.embed_query(
    "什么是向量数据库?"
)

print(len(vector))

3. 批量向量化文档

python 复制代码
vectors = embeddings.embed_documents([
    "LangChain 可以帮助开发者构建 LLM 应用。",
    "RAG 可以让模型使用外部知识库。",
    "Redis 可以作为缓存和向量数据库使用。"
])

print(len(vectors))

一般来说:

  • embed_documents():用于知识库文档入库;
  • embed_query():用于用户问题检索。

七、Vector Store:向量数据库

向量数据库负责保存:

复制代码
文本块
+ 文本向量
+ 元数据

并支持相似度搜索。

常见向量存储包括:

  • Chroma;
  • FAISS;
  • Redis;
  • Milvus;
  • Pinecone;
  • Qdrant。

1. 使用 Chroma 创建向量库

python 复制代码
from langchain_chroma import Chroma
from langchain_openai import OpenAIEmbeddings

embeddings = OpenAIEmbeddings(
    model="text-embedding-3-small"
)

vector_store = Chroma.from_documents(
    documents=chunks,
    embedding=embeddings,
    persist_directory="./chroma_db"
)

2. 相似度检索

python 复制代码
results = vector_store.similarity_search(
    query="LangChain 的作用是什么?",
    k=3
)

for doc in results:
    print(doc.page_content)
    print(doc.metadata)

k=3 表示返回最相近的三段文档。

3. MMR 检索

普通相似度检索有时会返回内容高度重复的文档。MMR,即最大边际相关性,可以在"相关性"和"多样性"之间取得平衡。

python 复制代码
results = vector_store.max_marginal_relevance_search(
    query="如何使用 LangChain 构建知识库问答系统?",
    k=3,
    fetch_k=10
)

可以理解为:

复制代码
先找出 10 条候选结果
   ↓
从中挑选 3 条既相关、又不重复的内容

MMR 在 RAG 场景中很实用,可以减少上下文内容重复。

八、Retriever:统一检索接口

Retriever 是 LangChain 对检索能力的统一抽象。

它不关心底层是:

  • Chroma;
  • Redis;
  • Elasticsearch;
  • SQL;
  • Web 搜索;
  • 企业内部 API。

只要能"根据问题返回相关文档",就可以封装为 Retriever。

1. 从向量数据库创建 Retriever

python 复制代码
retriever = vector_store.as_retriever(
    search_type="similarity",
    search_kwargs={
        "k": 3
    }
)

调用检索器:

python 复制代码
docs = retriever.invoke(
    "LangChain 中 PromptTemplate 的作用是什么?"
)

for doc in docs:
    print(doc.page_content)

2. 使用 MMR Retriever

python 复制代码
retriever = vector_store.as_retriever(
    search_type="mmr",
    search_kwargs={
        "k": 3,
        "fetch_k": 10
    }
)

3. Retriever 与模型的区别

组件 主要职责
Chat Model 根据上下文生成回答
Embedding Model 将文本转换为向量
Vector Store 保存向量并进行相似度搜索
Retriever 对外提供统一的检索接口

Retriever 本身通常只负责检索,不负责生成最终答案。

九、构建一个基础 RAG 链

前面所有组件组合起来,就可以构建一个基础 RAG 应用。

python 复制代码
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser

model = ChatOpenAI(model="gpt-4o-mini")

prompt = ChatPromptTemplate.from_template("""
你是一名知识库问答助手。

请严格依据参考资料回答问题。

参考资料:
{context}

用户问题:
{question}

要求:
1. 回答简洁准确;
2. 如果资料中没有答案,请明确说明;
3. 不要编造资料中不存在的内容。
""")

定义文档格式化方法:

python 复制代码
def format_docs(docs):
    return "\n\n".join(
        doc.page_content for doc in docs
    )

构建链:

python 复制代码
from langchain_core.runnables import RunnablePassthrough

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

调用:

python 复制代码
result = rag_chain.invoke(
    "LangChain 中 Retriever 的作用是什么?"
)

print(result)

整体流程如下:

复制代码
用户问题
   ↓
Retriever 检索相关文档
   ↓
format_docs 拼接检索结果
   ↓
Prompt 注入参考资料与问题
   ↓
Chat Model 生成最终回答
   ↓
StrOutputParser 提取文本结果

这就是一个最基础的 RAG 知识库问答流程。

十、总结

本文学习了构建 RAG 应用所需的核心组件:

  1. Output Parser:解析模型输出;
  2. Document:统一表示文档内容和元数据;
  3. Document Loader:加载 TXT、Markdown、PDF 等资料;
  4. Text Splitter:将长文档切分为小块;
  5. Embedding:将文本转换为语义向量;
  6. Vector Store:保存向量并进行相似度搜索;
  7. Retriever:对外提供统一的检索接口;
  8. RAG Chain:将检索结果交给模型生成答案。

至此,LangChain 的基础知识已经可以支撑一个简单的企业知识库问答系统。

相关推荐
蓝速科技40 分钟前
便民服务大厅 AI 数字人一体机场景适配与落地指南丨蓝速科技
大数据·网络·数据结构·人工智能·自然语言处理·数据分析·运维开发
xxwxx__1 小时前
C++ AVL 树深度剖析:平衡二叉搜索树原理、源码与面试考点
数据结构·c++
Logic1011 小时前
C语言/数据结构数组题解:数字列表加一(Plus One)——进位处理与数组扩展
c语言·数据结构·模拟·数组·时间复杂度·算法题·进位
山哥ol1 小时前
【Geany 环境配置与中文乱码解决参考】
python
会飞锦鲤2 小时前
基于 Mask R-CNN 的药片缺陷检测系统
人工智能·pytorch·python·神经网络·resnet-50
默 语2 小时前
Java新手入门:从零开始安装JDK并配置环境变量
java·开发语言·python·mysql·group by·1024程序员节·数据去重
清水白石0082 小时前
Python 异步编程深度解析:Cancellation 到底是异常还是控制信号?从 asyncio 取消机制到企业级事务设计最佳实践
开发语言·python
Patrick在香港3 小时前
Python 审计香港开放数据目录:两个端点差 10 倍,只有 9.3% 的资源标了「最后修改时间」
开发语言·数据库·python·数据分析·api·数据治理·开放数据
IT毕设梦工厂3 小时前
计算机毕业设计选题推荐:基于大数据的草鱼价格分析与可视化|毕业设计选题|计算机毕设|选题推荐|毕设指导|项目定制|源码|高质量项目
大数据·hadoop·python·数据挖掘·数据分析·课程设计·大数据毕设项目