标题:同一套 RAG 助手,三种构建方式:LangChain vs LangGraph vs Strands Agents

Generative AI - Medium 2026-08-13T18:49:19.621284

原文:

Building the Same RAG Assistant 3 Ways: LangChain vs LangGraph vs Strands Agents

标题:用三种方式构建同一个 RAG 助手:LangChain vs LangGraph vs Strands Agents

同一个文档助手,分别用链式结构、受控图流程和工具调用型智能体来实现,到底有什么不同?这是我做这个项目的初衷。

RAG(Retrieval-Augmented Generation,检索增强生成)是当代 AI 工程中最实用的模式之一。普通语言模型只能根据训练时学到的知识作答,而 RAG 系统在生成答案之前多加了一步:先从可信的知识源中检索,再把相关上下文带入提示词里。这一步看似微小,效果却天差地别。它不再让模型凭记忆猜答案,而是让它依据 PDF、Word 文档、政策手册、Excel 表格、培训笔记、产品 FAQ、创意简报或内部文档等文件来回答。正因如此,RAG 如今在企业助手、HR 聊天机器人、客服系统、法律研究辅助工具、医疗知识平台和教育类应用中随处可见。

做这个项目时,我想搞清楚一个更大的问题:如果目标一致——都是构建一个基于文档的 AI 助手——那么分别用 LangChain、LangGraph 和 Strands Agents 来实现,代码和实现方式会发生怎样的变化?于是我用三种方式构建了同一个 Streamlit RAG 助手:

  1. LangChain——基于链式结构的 RAG 应用。
  2. LangGraph——基于状态图的工作流,用于受控 RAG。
  3. Strands Agents——智能体风格实现,把文档检索当作一个工具来用。

三个版本的用户体验完全一样:用户上传文档,点击处理按钮,在聊天框里提问,收到答案并附上检索到的来源片段。区别不在用户能看到什么,而在系统背后是怎么"思考"的。

共同的基石:每个 RAG 系统都需要什么

在对比框架之前,先理解 RAG 的共同基础会更有帮助。这个项目的每个版本都遵循同一条核心流水线:

文档 → 通用加载器 → 文本分块 → 向量化 → 向量数据库 → 检索器 → 大模型回答 → 会话记忆

文档加载

第一步是数据摄入(ingestion)。真实用户上传的不一定都是干净的纯文本文件,可能是 PDF、Word 文档、Excel 表格、CSV 文件、Markdown 笔记或 HTML 页面。

因此,这个项目包含一个通用的文档加载器。加载器会检测文件扩展名,并使用对应的加载策略。LangChain 的加载器生态在这里很有用,因为它能把多种外部格式统一成一种通用的文档表示形式。这意味着,即使来源格式不同,下游代码也能一致地处理文档。

分块(Chunking)

文档加载完成后,下一步是分块。分块就是把长文本拆成更小的片段。这一步很重要,因为语言模型和嵌入模型都有 token 数量限制。更重要的是,当每个分块只包含一个集中的主题时,检索效果会更好。分块太大可能混杂过多主题,太小又会丢失有用的上下文。常见的起步参数是:chunk_size ≈ 500 到 1000 个 token,chunk_overlap ≈ 50 到 150 个 token。

重叠也很关键,因为一个意思常常会跨越分块边界。例如,某条政策规则可能从一段的末尾开始,延续到下一段。重叠有助于保留这段上下文。

嵌入(Embeddings)

嵌入把文本转换为向量。向量是一串数字,用来捕捉语义。比如,下面两个短语在关键词层面完全不同:remote work policy 和 working from home rules。但在语义上,它们很接近。嵌入帮助系统理解这种接近性。

向量数据库(Vector Store)

向量数据库保存嵌入,并支持相似度搜索。用户提问时,系统先嵌入问题,再与已存储的文档向量比较,把最相似的分块作为上下文返回。本项目在设计上让本地演示无需昂贵的基础设施也能运行。对真正的生产系统,团队可能会用 FAISS、ChromaDB、Pinecone、Weaviate、Qdrant、Milvus、Elasticsearch 向量检索,或云原生数据库。

生成(Generation)

检索完成后,选中的分块会和用户问题一起发送给语言模型。模型被要求只能依据检索到的上下文回答。一个良好的 RAG 提示词通常是这样:“请使用提供的上下文来回答问题。”

如果答案不在上下文中,就明确说文档没有提供足够的信息。这样做可以降低幻觉,让回答更有依据。

记忆(Memory)

Memory 用来保存对话历史。没有记忆,每个问题都是孤立的;有了记忆,助理才能理解追问。

例子:
用户:远程办公政策是什么?
助理:远程办公需要经理批准。
用户:谁批准?

这里的“谁”指的就是批准远程办公的那个人。有记忆能力的助理能理解这层关联。

方法一:LangChain——通往 RAG 最快的路径

LangChain 是构建经典 RAG 应用最直接的方式。它提供了文档加载、切分、嵌入、向量库、检索器、提示词、聊天模型和记忆等组件。

在这个项目里,LangChain 版本遵循一个简单流程:

用户提问 ↓ 检索器找出相关片段 ↓ 提示词把问题、上下文和聊天历史组合在一起 ↓ LLM 生成回答 ↓ 更新对话历史

LangChain 为什么有用

因为它给开发者提供了现成的积木组件。如果你想快速搭建一个文档问答系统,LangChain 能帮你省下大量时间。它特别适合这些场景:

LangChain 的心智模型

我把 LangChain 看成一套组件框架。你只需要把组件连起来:加载器 + 切分器 + 嵌入 + 向量库 + 检索器 + LLM + 记忆。

这样设计,既容易解释,也容易教学。

优势

LangChain 的主要优势是快。你可以很快做出一个能用的 RAG 应用。它还有庞大的集成生态,在处理各种文件格式和外部服务时非常有用。

局限

一旦工作流变得复杂,挑战就来了。比如:

这些情况在 LangChain 里也能处理,但逻辑会变得越来越难组织。

这正是 LangGraph 派上用场的地方。

方法二:LangGraph —— 把 RAG 做成有状态的工作流

LangGraph 专为有状态的工作流和智能体而设计。它不要求你只从"链"的角度去思考,而是定义一个由节点和边构成的图。节点是工作流中的一步,边控制工作流下一步往哪走。就本项目来说,LangGraph 版本可以这样理解:

START
  ↓
retrieve_node
  ↓
generate_node
  ↓
critique_node
  ↓
回答是否有据可依?
  ├── 是 → final_node → END
  └── 否 → retry_node → retrieve_node

LangGraph 为什么有用:当你的 RAG 助手需要更多控制时,LangGraph 就变得重要了。例如,标准 RAG 系统可能检索到相关文档块就直接作答;LangGraph 系统则可以加入各种检查:检索结果是否提供了足够的证据?生成的答案有没有源文档块支撑?要不要用改写后的查询重试一次?要不要暂停等待人工审核?工作流是继续、走分支还是停下来?

这让 LangGraph 更适合更严肃的 AI 工作流。

LangGraph 心智模型:我把 LangGraph 看作 AI 应用的状态机。它不会把过程藏在一个链里,而是把工作流一步步暴露出来:

状态 = 问题 + 历史记录 + 检索到的文档块 + 答案草稿 + 评审意见 + 重试次数

每个节点都会读取并更新状态。这很强大,因为调试变得更简单了——你可以检查每一步发生了什么。

优势:LangGraph 在这些场景下很强:

局限:LangGraph 需要更多的设计思考。对于简单的文档问答助手,你可能会觉得这是额外的工作量。但当系统规模变大后,图结构就会变成优势。

方法三:Strands Agents —— 把 RAG 当作会使用工具的智能体

Strands Agents 走了另一条路。它不把每一步硬编码成链或图,而是创建一个带工具的智能体,由模型自行决定何时以及如何使用这些工具。在这个项目中,文档检索变成了一个工具:

Tool: search_uploaded_documents(query)

智能体可以自己判断:我该

查看原文