从您想要自动化的过程开始
假设您需要构建一个处理客户支持邮件的 AI 代理。您的产品团队给出了以下要求:步骤 1:将您的工作流映射为离散的步骤
首先,确定您过程中的各个独立步骤。每个步骤将成为一个节点(一个执行特定功能的函数)。然后,勾勒出这些步骤是如何相互连接的。 图中的箭头显示了可能的路径,但实际选择哪条路径的决策发生在每个节点内部。 现在我们已经确定了工作流中的组件,让我们了解每个节点需要做什么:读取邮件:提取并解析邮件内容分类意图:使用 LLM 对紧急程度和主题进行分类,然后路由到相应的操作文档搜索:在您的知识库中查询相关信息缺陷跟踪:在跟踪系统中创建或更新问题起草回复:生成合适的响应人工审核:上报给人工客服进行批准或处理发送回复:发送邮件回复
步骤 2:确定每个步骤需要做什么
对于图中的每个节点,确定它代表的操作类型以及它正常工作所需的上下文。LLM 步骤
当您需要理解、分析、生成文本或进行推理决策时使用
数据步骤
当您需要从外部源检索信息时使用
操作步骤
当您需要执行外部操作时使用
用户输入步骤
当您需要人工干预时使用
LLM 步骤
当一个步骤需要理解、分析、生成文本或进行推理决策时:分类意图
分类意图
- 静态上下文(提示词):分类类别、紧急程度定义、响应格式
- 动态上下文(来自状态):邮件内容、发件人信息
- 期望结果:决定路由的结构化分类
起草回复
起草回复
- 静态上下文(提示词):语气指南、公司政策、响应模板
- 动态上下文(来自状态):分类结果、搜索结果、客户历史
- 期望结果:可供审核的专业邮件回复
数据步骤
当一个步骤需要从外部源检索信息时:文档搜索
文档搜索
- 参数:根据意图和主题构建的查询
- 重试策略:是,针对瞬时故障采用指数退避
- 缓存:可以缓存常见查询以减少 API 调用
客户历史查找
客户历史查找
- 参数:来自状态的客户邮箱或 ID
- 重试策略:是,但如果不可用则回退到基本信息
- 缓存:是,带有存活时间以平衡新鲜度和性能
操作步骤
当一个步骤需要执行外部操作时:发送回复
发送回复
- 何时执行节点:批准后(人工或自动)
- 重试策略:是,针对网络问题采用指数退避
- 不应缓存:每次发送都是独特的操作
缺陷跟踪
缺陷跟踪
- 何时执行节点:当意图为“缺陷”时总是执行
- 重试策略:是,关键是不丢失缺陷报告
- 返回:工单 ID,用于包含在回复中
用户输入步骤
当一个步骤需要人工干预时:人工审核节点
人工审核节点
- 决策上下文:原始邮件、起草的回复、紧急程度、分类
- 预期输入格式:批准布尔值加上可选的编辑后回复
- 触发条件:高紧急程度、复杂问题或质量疑虑
步骤 3:设计您的状态
状态是代理中所有节点都可以访问的共享内存。可以将其视为代理在处理过程中用来记录所学到和决定的所有内容的笔记本。什么属于状态?
对每条数据问自己以下问题:包含在状态中
它需要跨步骤持续存在吗?如果是,它就应该进入状态。
不存储
您可以从其他数据推导出它吗?如果是,请在需要时计算它,而不是将其存储在状态中。
- 原始邮件和发件人信息(稍后无法重建)
- 分类结果(被多个下游节点需要)
- 搜索结果和客户数据(重新获取成本高)
- 起草的回复(需要在审核过程中保持)
- 执行元数据(用于调试和恢复)
保持状态原始,按需格式化提示
这种分离意味着:- 不同的节点可以根据其需求以不同方式格式化相同的数据
- 您可以更改提示模板而无需修改状态架构
- 调试更清晰——您可以看到每个节点接收到的确切数据
- 您的代理可以发展而不会破坏现有状态
步骤 4:构建您的节点
现在我们将每个步骤实现为一个函数。LangGraph 中的一个节点就是一个接受当前状态并返回状态更新的 Python 函数。适当地处理错误
不同的错误需要不同的处理策略:- 瞬时错误
- LLM 可恢复
- 用户可修复
- 意外
- Saga / 补偿
实现我们的邮件代理节点
我们将把每个节点实现为一个简单的函数。记住:节点接收状态,执行工作,并返回更新。读取和分类节点
读取和分类节点
搜索和跟踪节点
搜索和跟踪节点
响应节点
响应节点
步骤 5:将它们连接起来
现在我们将节点连接成一个工作图。由于我们的节点自己处理路由决策,我们只需要几条基本边。 要启用 human-in-the-loop 与interrupt(),我们需要使用检查点器进行编译,以便在运行之间保存状态:
图编译代码
图编译代码
Command 对象实现。每个节点使用类型提示(如 Command[Literal["node1", "node2"]])声明它可以去的地方,使流程明确且可追溯。
试用您的代理
让我们用一个需要人工审核的紧急计费问题来运行我们的代理:测试代理
测试代理
interrupt() 时,图会暂停,将所有内容保存到检查点器,并等待。它可以在几天后恢复,从上次中断的地方精确继续。thread_id 确保此对话的所有状态都保存在一起。
总结与后续步骤
关键见解
构建这个邮件代理向我们展示了 LangGraph 的思考方式:分解为离散步骤
每个节点做好一件事。这种分解使得流式进度更新、可暂停和恢复的持久化执行成为可能,并且由于您可以在步骤之间检查状态,调试也变得清晰。
状态是共享内存
存储原始数据,而不是格式化后的文本。这让不同的节点可以以不同方式使用相同的信息。
节点是函数
它们接收状态,执行工作,并返回更新。当它们需要做出路由决策时,它们会同时指定状态更新和下一个目的地。
错误是流程的一部分
瞬时故障会重试,LLM 可恢复的错误会带回上下文循环回来,用户可修复的问题会暂停等待输入,意外错误会冒泡以供调试。
人工输入是一等公民
interrupt() 函数会无限期暂停执行,保存所有状态,并在您提供输入时从上次中断的地方精确恢复。当与节点中的其他操作结合使用时,它必须放在最前面。图结构自然涌现
您定义基本连接,您的节点处理自己的路由逻辑。这使得控制流明确且可追溯——通过查看当前节点,您总是可以理解您的代理下一步会做什么。
高级考虑因素
节点粒度权衡
节点粒度权衡
本节探讨节点粒度设计的权衡。大多数应用程序可以跳过此部分,并使用上面展示的模式。
读取邮件 和 分类意图 合并到一个节点中?或者为什么将文档搜索从起草回复中分离出来?答案涉及到复原性和可观测性之间的权衡。复原性考虑: LangGraph 的持久层在节点边界处创建检查点。当工作流在中断或故障后恢复时,它会从执行停止的那个节点的开头开始。更小的节点意味着更频繁的检查点,这意味着如果出现问题,需要重复的工作更少。如果将多个操作组合成一个大节点,靠近结尾处的故障意味着从该节点的开头重新执行所有内容。我们为邮件代理选择这种分解方式的原因:- 外部服务的隔离: 文档搜索和缺陷跟踪是单独的节点,因为它们调用外部 API。如果搜索服务缓慢或失败,我们希望将其与 LLM 调用隔离开来。我们可以为这些特定节点添加重试策略,而不会影响其他节点。
- 中间可见性: 将
分类意图作为自己的节点,让我们可以在采取行动之前检查 LLM 的决定。这对于调试和监控非常有价值——您可以确切地看到代理何时以及为何路由到人工审核。 - 不同的故障模式: LLM 调用、数据库查询和电子邮件发送有不同的重试策略。单独的节点允许您独立配置这些策略。
- 可重用性和测试: 更小的节点更容易单独测试并在其他工作流中重用。
读取邮件 和 分类意图 合并到一个节点中。您将失去在分类之前检查原始邮件的能力,并且在该节点中的任何故障都会重新执行这两个操作。对于大多数应用程序来说,单独节点的可观测性和调试好处值得权衡。应用级关注点:步骤 2 中的缓存讨论(是否缓存搜索结果)是应用级的决策,而不是 LangGraph 框架特性。您根据具体需求在节点函数内实现缓存——LangGraph 并不规定这一点。性能考虑:更多的节点并不意味着更慢的执行。LangGraph 默认在后台写入检查点(异步持久性模式),因此您的图可以继续运行,而无需等待检查点完成。这意味着您可以获得频繁的检查点,而性能影响最小。如果需要,您可以调整此行为——使用 "exit" 模式仅在完成时检查点,或使用 "sync" 模式阻塞执行直到每个检查点写入完成。下一步去哪里
这是关于使用 LangGraph 构建代理的思考介绍。您可以通过以下方式扩展这个基础:Human-in-the-loop 模式
学习如何在执行前添加工具批准、批量批准和其他模式
子图
为复杂的多步骤操作创建子图
流式处理
添加流式处理以向用户显示实时进度
可观测性
使用 LangSmith 添加可观测性以进行调试和监控
工具集成
集成更多工具用于网络搜索、数据库查询和 API 调用
重试逻辑
为失败的操作实现具有指数退避的重试逻辑
Connect these docs to Claude, VSCode, and more via MCP for real-time answers.

