1. State 是什么
State 可以理解为:
LangGraph 中各个节点之间共享和传递的数据。
节点执行时:
从 State 读取数据
↓
执行节点逻辑
↓
return 部分字段
↓
更新 State
所以 State 本质上就是整个 Graph 的共享数据空间。
2. TypedDict 在 State 中的作用
LangGraph 中经常使用 TypedDict 定义 State:
from typing import TypedDict
class ChatState(TypedDict):
user_message: str
intent: str
response: str
TypedDict 的作用是:
定义一种固定字段和字段类型的字典结构。
它虽然使用 class 语法:
class ChatState(TypedDict):
但实际使用的仍然是字典:
state["intent"]
而不是:
state.intent
可以记成:
TypedDict 本质上是给字典定义类型结构,只是语法上使用了
class。
3. 全局 State 是什么
例如:
class ChatState(TypedDict):
user_message: str
intent: str
response: str
然后:
builder = StateGraph(ChatState)
这里的 ChatState 就定义了这个 Graph 的核心共享状态结构。
可以理解为:
Graph 主要维护:
user_message
intent
response
所以全局 State 的作用是:
定义整个 Graph 主要有哪些共享状态字段,以及这些字段是什么类型、如何更新。
需要注意:
"定义了字段"不代表每个字段在运行一开始就一定已经有值。
所以经常写:
state.get("intent", "")
而不是:
state["intent"]
这样可以防止字段尚未出现时报错。
4. 节点输入 State
节点函数一般会接收一个 State:
def node(state: ChatState):
...
这里的:
state: ChatState
表示这个节点需要读取哪些状态字段。
例如:
def recognize(state: ChatState):
message = state["user_message"]
相当于:
Graph 当前状态
↓
按照节点输入 State 的定义
↓
把节点需要的字段传进去
所以可以理解为:
节点函数的输入 State 决定这个节点从 Graph 状态空间中读取哪些数据。
5. 节点返回值
节点执行完成以后,一般返回一个字典:
return {
"intent": "submit_leave"
}
这个返回值不是一个新的完整 State。
它表示:
对当前 Graph State 做一次局部更新。
例如原来的状态:
{
"user_message": "我要请假",
"intent": "",
"response": ""
}
节点返回:
{
"intent": "submit_leave"
}
更新以后:
{
"user_message": "我要请假",
"intent": "submit_leave",
"response": ""
}
所以节点通常只返回自己需要修改的字段。
6. 节点输入和节点返回的关系
可以统一理解成:
Graph State
↓
节点输入 state
↓
读取需要的数据
↓
执行逻辑
↓
return {...}
↓
更新 Graph State
一句话:
输入 State 是读,return 是写。
7. 私有 State 是什么
私有 State 是:
只在 Graph 内部节点之间传递,不作为主要输入或最终输出暴露的数据。
例如:
class PrivateState(TypedDict):
intent: str
某个节点写入:
def node_a(state):
return {
"intent": "submit_leave"
}
后续节点读取:
def node_b(state: PrivateState):
print(state["intent"])
这里的 intent 可以作为内部中间状态使用。
"私有"不是 Python 中:
__intent
这种私有属性。
它真正的意思是:
这个字段主要服务于 Graph 内部节点通信,而不是暴露给 Graph 外部。
8. 私有 State 不是另一套独立状态
私有 State 并不是重新创建一份完全独立的数据。
可以把 LangGraph 理解成内部维护一个更大的共享状态空间:
Graph 内部状态空间
user_message
intent
response
temp_result
search_result
...
其中:
user_message
response
可能属于公开状态。
而:
temp_result
intent
也可能只是内部节点使用的状态。
所以私有状态仍然属于 Graph 的共享状态空间。
9. 为什么全局 State 没有某字段,私有 State 还能使用
因为 Graph 内部实际维护的状态空间可以比对外暴露的 State 更大。
例如:
class OverallState(TypedDict):
user_message: str
response: str
再定义:
class PrivateState(TypedDict):
intent: str
那么 Graph 内部可以理解为:
{
user_message,
response,
intent
}
其中:
user_message
response
属于主要状态。
而:
intent
属于内部状态。
所以节点可以:
return {"intent": "submit_leave"}
后续节点也可以:
state["intent"]
10. 私有状态会保存多久
通常可以理解为:
私有状态会在这一次 Graph 执行期间持续存在。
例如:
节点 A 写入 temp_result
↓
节点 B 可以读取
↓
节点 C 也可以继续读取
↓
直到 Graph 本次运行结束
如果后续节点更新同名字段:
return {
"temp_result": "new value"
}
旧值就会被更新。
至于 Graph 执行结束以后是否继续保存,则取决于是否使用了:
checkpointer
thread
持久化机制
普通一次性运行结束以后,运行时状态通常也就结束了。
11. 同名字段会怎样
如果两个节点顺序写同一个普通字段:
return {"intent": "A"}
然后:
return {"intent": "B"}
最终一般是:
intent = "B"
也就是:
后写覆盖前写。
如果两个节点并行写同一个普通字段,则可能发生冲突。
12. Reducer 的作用
有些字段不是覆盖,而是合并。
例如:
from typing import Annotated
from operator import add
class ChatState(TypedDict):
logs: Annotated[list[str], add]
这里:
add
就是 reducer。
如果一个节点返回:
{"logs": ["A"]}
另一个节点返回:
{"logs": ["B"]}
则可以合并成:
["A", "B"]
而不是简单覆盖。
所以:
普通字段
→ 默认更新/覆盖
Annotated[..., reducer]
→ 按 reducer 规则合并
13. Node 和 State 的关系
Node 是 Graph 中的执行单元。
例如:
builder.add_node("recognize", recognize_node)
其中:
"recognize"
是节点。
recognize_node
是节点执行的函数逻辑。
节点执行时:
读取 State
↓
执行函数
↓
返回 State 更新
所以 State 是节点之间通信的桥梁。
14. 最终心智模型
可以把整个 LangGraph 想象成:
Graph 共享状态空间
┌────────────────────────┐
│ user_message │
│ intent │
│ response │
│ task_result │
│ temp_result │
│ logs │
└────────────────────────┘
↑ ↓
节点写入 节点读取
↑ ↓
Node A Node B
其中:
全局 State
定义 Graph 的主要共享状态结构。
节点输入 State
决定节点读取哪些状态。
节点 return
决定这次更新哪些状态。
私有 State
表示只在 Graph 内部节点之间使用的状态字段。
Reducer
决定同一个字段多次更新时如何合并。
15. 最后一句总结
LangGraph 的 State 可以记成:
Graph 有一个共享状态空间,节点从中读取数据,并通过 return 对它进行局部更新;全局 State 定义主要状态结构,私有 State 用于内部节点通信,Reducer 决定字段更新时是覆盖还是合并。