LangGraph 拓展核心知识点
2026/5/14 7:43:28 网站建设 项目流程

LangGraph 的其他特性

在把前面三个基础案例给大家介绍完成之后,我们再来讲解LangGraph里一些可以单独拎出来重点学习的拓展知识点。


使用Overwrite绕过 reducer

我们之前讲解案例的时候,给大家提到过状态追加更新的机制,大家应该还有印象。我们自定义的消息状态,默认可以通过add这种聚合方式,把每一次更新的内容追加拼接到列表当中。那大家有没有思考过一个问题:我们能不能打破这种默认的追加规则?

举个实际场景,我们和 AI 持续对话,message列表里会慢慢累积十几条历史消息。如果这时我们想清空所有历史对话,不再沿用追加逻辑,有没有办法无视add的合并规则,直接用一条新消息覆盖替换掉整个历史消息列表?

答案是有的。想要绕过默认的追加更新机制,我们可以使用 LangGraph 提供的专属关键字Overwrite来实现状态覆盖,这也是我们要讲的第一个核心知识点。

在 LangGraph 中,reducers 用于控制状态更新的处理方式。默认情况下,每个状态键都有其独立的 reducer 函数,用于决定如何合并节点返回的更新。但有时我们需要完全覆盖状态值而不是合并,这时就需要使用Overwrite

为什么需要Overwrite?想象一下开发一个聊天应用时:

  • 正常情况下,新消息会追加到消息列表中。
  • 但有时需要清空聊天记录并重新开始,这时候就需要绕过追加逻辑,直接覆盖整个消息列表。

理解原理之后,我们直接通过代码案例实操演示,逻辑非常简单。首先自定义状态,状态中定义messages字段,类型为字符串列表,搭配operator.add实现默认追加规则。

接着编写两个节点:第一个节点专门用来追加消息,按照默认规则往列表里新增内容;第二个节点我们不使用追加逻辑,而是引入Overwrite关键字,用全新列表直接覆盖原有历史列表。

随后构建StateGraph流程图,依次添加两个节点、配置节点执行边,最后调用invoke方法执行流程图并打印最终结果。

我们可以分两次测试:不使用Overwrite时,新消息会正常追加在历史消息后方;使用Overwrite后,会直接清空旧的历史消息,只保留最新覆盖的内容,完美实现状态覆盖效果。

Overwrite在实际开发中有非常多实用场景:比如对话系统中历史消息过多时,一键清空对话记录;程序出现异常报错后,重置整体应用状态;清理系统中过时、损坏的中间数据等。核心作用就是绕过 LangGraph 默认的状态追加更新机制,直接整体覆盖状态值

from langgraph.graph import StateGraph, START, END from langgraph.types import Overwrite from typing_extensions import Annotated, TypedDict import operator class State(TypedDict): messages: Annotated[list, operator.add] def add_message(state: State): return {"messages": ["first message"]} def replace_messages(state: State): # 绕过reducer并替换整个消息列表 return {"messages": Overwrite(["replacement message"])} builder = StateGraph(State) builder.add_node("add_message", add_message) builder.add_node("replace_messages", replace_messages) builder.add_edge(START, "add_message") builder.add_edge("add_message", "replace_messages") builder.add_edge("replace_messages", END) graph = builder.compile() result = graph.invoke({"messages": ["initial"]}) print(result["messages"])

输出:['replacement message'],而不是['initial message', 'first message', 'replacement message']


使用Overwrite的应用场景如下:

  • 重置对话:清空聊天历史,开始新对话
  • 状态重置:在错误恢复后重置应用状态
  • 数据清理:替换损坏或过时的数据

讲完Overwrite,我们接着学习第二个知识点:定义独立的输入输出模式

定义输入输出模式

回顾我们之前写的所有案例,LangGraph 默认只使用单一全局状态,流程图的输入、输出、节点间通信,全部共用同一个状态结构。但在实际企业开发中,我们可以把输入状态、输出状态、内部流转状态拆分开独立定义,这就是输入输出模式的核心意义。

我们可以单独定义InputState作为工作流专属输入结构、OutputState作为专属输出结构,再额外定义一个完整的内部状态,用来承载节点流转过程中需要用到的上下文、中间数据等不对外暴露的信息。

在构建StateGraph的时候,通过input_schemaoutput_schema两个参数,分别指定图的输入规范和输出规范。schema本身具备数据验证作用,input_schema会校验传入的输入数据结构,output_schema则会过滤最终输出结果。即便节点内部流转时产生了多个状态字段,最终也只会返回我们指定的输出字段,自动隐藏内部冗余字段。

这种独立输入输出模式,落地场景十分广泛:做 API 接口开发时,可以定义清晰的请求、响应数据格式,隐藏后端内部处理逻辑;微服务架构中,能统一服务之间的数据交互契约;数据管道开发里,也能明确整个流程的输入输出标准,让接口规范更清晰、更安全。

简单来说:

默认情况下,LangGraph 使用单一的状态模式。但我们可以定义独立的输入和输出模式,解释如下:

  • 独立的输入模式:验证输入数据的结构
  • 独立的输出模式:过滤输出数据,只返回需要的信息
  • 内部模式:节点间通信使用的完整状态

为什么需要独立的内部模式?考虑一个问答系统:

  • 输入:用户的问题(字符串)
  • 输出:AI 的答案(字符串)
  • 内部:可能需要存储中间结果、上下文等信息

我们不想把内部状态都暴露给用户!

在 LangGraph 中,如果想要定义独立的输入和输出模式,可以使用StateGraph的初始化参数:

参数名类型描述
input_schematype[InputT]None定义StateGraph输入的 State 类
output_schematype[OutputT]None定义StateGraph输出的 State 类
state_schematype[StateT]定义StateGraph的 State 类
from langgraph.graph import StateGraph, START, END from typing_extensions import TypedDict # 1. 定义输入模式 - 只包含用户问题 class InputState(TypedDict): question: str # 2. 定义输出模式 - 只包含AI答案 class OutputState(TypedDict): answer: str # 3. 定义完整状态模式(内部使用) class OverallState(InputState, OutputState): pass def answer_node(state: InputState): """处理输入并生成答案""" # 这里可以访问 question, 生成 answer return { "answer": f"Answer to: {state['question']}", "question": state["question"] } # 构建图时指定输入输出模式 builder = StateGraph( OverallState, input_schema=InputState, # 输入验证 output_schema=OutputState # 输出过滤 ) builder.add_node("answer_node", answer_node) builder.add_edge(START, "answer_node") builder.add_edge("answer_node", END) graph = builder.compile() # 测试 result = graph.invoke({"question": "What is LangGraph?"}) print(result) # 输出: {'answer': 'Answer to: What is LangGraph?'} # 注意: question 字段被过滤掉了, 不在输出中

独立的输入和输出实际应用场景如下:

  • API 开发:定义清晰的请求 / 响应格式
  • 微服务:服务间明确的数据契约
  • 数据管道:明确的输入输出规范

接下来讲解第三个核心特性:节点间传递私有状态

在节点间传递私有状态

我们都知道,LangGraph 节点的核心逻辑是:接收状态、处理逻辑、返回状态更新,节点之间依靠状态完成通信。基于这个特性,我们可以实现私有状态传递:有些敏感数据、临时中间数据,只需要在指定节点之间共享,不需要暴露给后续节点,也不用对外输出。

举个典型场景:我们设计三个执行节点,节点 1 负责从数据源获取敏感隐私数据,节点 2 需要接收这份隐私数据做业务处理,而节点 3 只需要接收处理后的公开结果,完全不需要感知原始敏感数据。

具体实现方式很简单:定义一个全局公共状态,用来存放最终对外展示的结果;再单独自定义节点 1 的输出状态、节点 2 的输入状态,专门用来承载私有敏感字段。节点 1 执行后,不返回公共状态,而是返回自定义的私有状态;节点 2 接收这份私有状态完成数据处理,处理完成后只把脱敏后的公开结果更新到全局公共状态;节点 3 只依赖公共状态执行业务逻辑,完全无法访问前面的私有敏感数据。

同时如果多个节点是固定链式执行顺序,还可以使用add_sequence方法,一次性批量添加多个节点,自动帮我们配置好顺序执行的边,简化流程图配置代码。

现在我们需要根据数据库中的信息,生成相关数据报告,因此可以设置三个节点:

  • 节点 1:从数据库获取原始数据(包含敏感信息)
  • 节点 2:处理数据,过滤掉敏感信息
  • 节点 3:生成最终报告

此时,节点 1 和 节点 2 需要共享原始数据,但 节点 3 不应该看到敏感信息。代码如下:

from langgraph.graph import StateGraph, START, END from typing_extensions import TypedDict # 公共状态(最终输出中可见) class OverallState(TypedDict): final_result: str # 节点1的私有输出 class Node1Output(TypedDict): sensitive_data: str # 这个字段不会出现在最终状态中 # 节点2需要的输入(包含私有数据) class Node2Input(TypedDict): sensitive_data: str def node_1(state: OverallState) -> Node1Output: """第一步:获取包含敏感信息的原始数据""" private_data = "这是敏感信息" print(f"Node1: 获取到敏感数据,但不会暴露给最终输出") return {"sensitive_data": private_data} def node_2(state: Node2Input) -> OverallState: """第二步:处理数据,移除敏感信息""" print(f"Node2: 处理敏感数据: {state['sensitive_data']}") # 处理数据,返回清理后的结果 return {"final_result": "清理后的处理结果"} def node_3(state: OverallState) -> OverallState: """第三步:只看到清理后的数据""" print(f"Node3: 只能看到最终结果: {state['final_result']}") return {"final_result": state["final_result"] + " - 完成"} # 构建图 builder = StateGraph(OverallState) # add_sequence: 支持添加一系列节点,按所给的顺序执行。 # 注意:我们使用 add_sequence 但类型系统会处理私有状态 builder.add_sequence([node_1, node_2, node_3]) builder.add_edge(START, "node_1") graph = builder.compile() # 测试 response = graph.invoke({"final_result": "initial"}) print(f"\n最终输出: {response}")

运行上述代码,会看到:

Node1: 获取到敏感数据,但不会暴露给最终输出 Node2: 处理敏感数据: 这是敏感信息 Node3: 只能看到最终结果: 清理后的处理结果 最终输出: {'final_result': '清理后的处理结果 - 完成'}

节点间传递私有状态实际应用场景如下:

  • 数据处理:中间处理步骤的临时数据
  • 认证流程:令牌等敏感信息的传递
  • 复杂计算:中间计算结果
  • 错误处理:错误详情在内部传递,但对外提供友好消息

以上三个特性让 LangGraph 能够处理复杂的企业级应用场景,同时保持代码的清晰和安全性。

总的来说,Overwrite状态覆盖、独立输入输出模式、节点私有状态传递,这三大拓展特性,底层都是围绕节点可自定义接收和返回不同状态这个核心原理延伸出来的。掌握这三个特性,我们就可以开发更规范、更安全、更适配企业级复杂场景的 LangGraph 应用。

到这里,LangGraph 快速入门以及三大拓展核心知识点就全部讲解完毕了。入门阶段我们已经掌握了固定开发流程:定义状态 → 定义业务节点 → 构建流程图 → 配置节点边 → 编译并运行图。后续更高级的功能,也都是在这套基础流程上,新增参数、拓展字段、叠加能力而已。后续我们再继续学习 LangGraph 工作流常见模式和更多高阶独有能力,帮大家全面吃透 LangGraph 框架。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询