概述
LLM 最强大的应用之一是复杂的问答(Q&A)聊天机器人。这类应用能够针对特定来源的信息回答问题,使用的技术称为检索增强生成(Retrieval Augmented Generation),即 RAG。 本教程将展示如何在非结构化文本数据源上构建一个简单的问答应用。我们将演示:概念
我们将涵盖以下概念:- 索引:将数据从来源摄取并建立索引的流水线。这通常在独立进程中进行。
- 检索与生成:实际的 RAG 流程,在运行时获取用户查询,从索引中检索相关数据,然后将其传递给模型。
预览
在本指南中,我们将构建一个回答网站内容相关问题的应用。我们使用的具体网站是 Lilian Weng 的博客文章 LLM Powered Autonomous Agents,以便提问关于该文章内容的问题。 我们可以用约 40 行代码创建一个简单的索引流水线和 RAG 链。完整代码片段如下:展开查看完整代码片段
展开查看完整代码片段
安装配置
安装
本教程需要以下 langchain 依赖:LangSmith
使用 LangChain 构建的许多应用包含多个步骤和多次 LLM 调用。随着应用变得更加复杂,能够检查链或代理内部发生的事情变得至关重要。最好的方式是使用 LangSmith。 在上方链接注册后,确保设置环境变量以开始记录追踪:组件
我们需要从 LangChain 的集成套件中选择三个组件。 选择聊天模型:- OpenAI
- Anthropic
- Azure
- Google Gemini
- AWS Bedrock
- HuggingFace
- OpenAI
- Azure
- Google Gemini
- Google Vertex
- AWS
- HuggingFace
- Ollama
- Cohere
- MistralAI
- Nomic
- NVIDIA
- Voyage AI
- IBM watsonx
- Fake
- Isaacus
- In-memory
- Amazon OpenSearch
- AstraDB
- Chroma
- FAISS
- Milvus
- MongoDB
- PGVector
- PGVectorStore
- Pinecone
- Qdrant
1. 索引
索引通常按如下流程进行:- 加载:首先需要加载数据,使用文档加载器完成。
- 分割:文本分割器将大型
Document拆分成较小的块。这对于索引数据和将其传递给模型都很有用,因为大块内容更难检索,且可能超出模型有限的上下文窗口。 - 存储:我们需要一个地方来存储和索引分割后的内容,以便之后进行检索。通常使用向量存储和嵌入模型来完成。

加载文档
我们首先需要加载博客文章的内容。可以使用文档加载器来完成,它们是从来源加载数据并返回 Document 对象列表的对象。 本例中我们使用WebBaseLoader,它使用 urllib 从网页 URL 加载 HTML,并使用 BeautifulSoup 将其解析为文本。我们可以通过 bs_kwargs 向 BeautifulSoup 解析器传入参数来自定义 HTML → 文本的解析过程(参见 BeautifulSoup 文档)。本例中只保留 class 为 “post-content”、“post-title” 或 “post-header” 的 HTML 标签。
DocumentLoader:从来源加载数据并返回 Document 列表的对象。
- 集成:160+ 种集成可供选择。
BaseLoader:基础接口的 API 参考。
分割文档
我们加载的文档超过 42,000 个字符,对于许多模型的上下文窗口来说过长。即便是能够放入完整文章的模型,在非常长的输入中查找信息也会遇到困难。 为了解决这个问题,我们将Document 分割成块以便嵌入和向量存储。这有助于我们在运行时只检索博客文章中最相关的部分。
与语义搜索教程一样,我们使用 RecursiveCharacterTextSplitter,它会使用换行符等常见分隔符递归地分割文档,直到每个块达到合适的大小。这是通用文本场景下推荐的文本分割器。
TextSplitter:将 Document 对象列表分割成较小块以供存储和检索的对象。
存储文档
现在我们需要对 66 个文本块建立索引,以便在运行时进行检索。遵循语义搜索教程,我们的做法是嵌入每个文档块的内容,并将这些嵌入插入向量存储。对于给定的输入查询,我们可以使用向量搜索检索相关文档。 我们可以使用教程开头选择的向量存储和嵌入模型,通过单条命令完成所有文档块的嵌入和存储。Embeddings:文本嵌入模型的封装,用于将文本转换为嵌入向量。
VectorStore:向量数据库的封装,用于存储和查询嵌入向量。
至此,索引部分的流水线已完成。现在我们拥有一个可查询的向量存储,其中包含博客文章分块后的内容。对于用户的问题,我们应该能够返回博客文章中能够回答该问题的片段。
2. 检索与生成
RAG 应用通常按如下流程工作:
RAG 代理
RAG 应用的一种实现方式是构建一个带有检索信息工具的简单代理。我们可以通过实现一个封装向量存储的工具来组建一个最简 RAG 代理:- 生成查询以搜索任务分解的标准方法;
- 收到答案后,生成第二个查询以搜索该方法的常见扩展;
- 获得所有必要上下文后,回答问题。
RAG 链
在上述代理式 RAG 实现中,我们允许 LLM 自主决定是否发起工具调用来帮助回答用户查询。这是一种通用解决方案,但也存在一些权衡:| ✅ 优点 | ⚠️ 缺点 |
|---|---|
| 按需检索 – LLM 可以处理问候、追问和简单查询,无需触发不必要的检索。 | 两次推理调用 – 当执行检索时,需要一次调用生成查询,另一次生成最终响应。 |
上下文感知搜索查询 – 将检索作为带有 query 输入的工具,LLM 可以结合对话上下文构造自己的查询。 | 控制力降低 – LLM 可能在实际需要检索时跳过,或在不必要时发起额外检索。 |
| 支持多次检索 – LLM 可以为单个用户查询执行多次检索。 |
返回来源文档
返回来源文档
后续步骤
现在我们已经通过create_agent 实现了一个简单的 RAG 应用,可以轻松地融入新功能并深入探索:
- 流式传输 token 和其他信息,提升用户响应体验
- 添加对话记忆以支持多轮交互
- 添加长期记忆以支持跨对话线程的记忆
- 添加结构化响应
- 使用 LangSmith 部署部署你的应用
将这些文档连接到 Claude、VSCode 等工具,通过 MCP 获取实时答案。

