LangGraph实战教程:预定义状态MessagesState与AgentState

  1. LangGraph 入门基础全解析
  2. LangGraph中的Reducer是什么
  3. LangGraph 核心概念详解:从编译到可视化
  4. LangGraph实战教程:一文搞懂图的状态(State)管理
  5. LangGraph实战教程:状态管理与graph.invoke入参深度解析
  6. LangGraph高级教程:Multi Schema多状态管理详解

在构建与LLM交互的LangGraph应用时,消息列表的管理是一个非常常见的需求。每次调用模型都需要传入历史消息,而模型返回的新消息又需要追加到列表中。如果每次都手动处理消息的合并逻辑,不仅繁琐,而且容易出错。

LangGraph官方为此提供了预定义状态类型,帮助开发者快速搭建基于消息的对话流程。

一、MessagesState:消息管理的开箱即用方案

1.1 什么是MessagesState?

MessagesStatelanggraph.graph.message模块中预定义的一个状态类型,专门用于管理LLM交互过程中的消息列表。

源码定义:

py 复制代码
class MessagesState(TypedDict):
    messages: Annotated[list[AnyMessage], add_messages]

可以看到,MessagesState只有一个字段 messages

  • 类型list[AnyMessage],可以存储任意类型的消息(HumanMessage、AIMessage、SystemMessage等)
  • Reducer :绑定了 add_messages,这是一个内置的归约器,负责将新消息智能地合并到已有消息列表中

1.2 add_messages的工作机制

add_messages并不是简单的列表拼接,它具备以下智能行为:

  1. 追加新消息:默认将新消息追加到列表末尾
  2. 去重 :如果新消息的 id与列表中某条消息的 id相同,则会覆盖旧消息,而不是重复添加
  3. 删除消息 :如果新消息的内容为 None,则会从列表中移除该 id对应的消息

这种机制非常适合多轮对话场景,既能保证消息顺序,又能避免重复。

1.3 继承MessagesState扩展自定义字段

开发者可以直接继承 MessagesState,在其基础上添加业务所需的字段。

py 复制代码
from langgraph.graph.message import MessagesState
from typing import TypedDict

class OverAllState(MessagesState):
    username: str
    output: str

此时 OverAllState拥有三个字段:

  • messages:来自父类,管理对话消息
  • username:自定义字段,存储用户名
  • output:自定义字段,存储模型输出的文本内容

1.4 完整案例:集成ChatDeepSeek

py 复制代码
from langchain_deepseek import ChatDeepSeek
from langgraph.graph import StateGraph, START, END
from langgraph.graph.message import MessagesState
from langchain.messages import HumanMessage

from dotenv import load_dotenv
load_dotenv(override=True)

model = ChatDeepSeek(
    model='deepseek-v4-flash',
    extra_body={"thinking": {"type": "disabled"}}
)

class OverAllState(MessagesState):
    username: str
    output: str

def node_a(state: OverAllState) -> OverAllState:
    """构造用户消息"""
    return {
        "messages": [HumanMessage("你好,我是 " + state["username"])]
    }

def llm_node(state: OverAllState) -> OverAllState:
    """调用LLM并返回结果"""
    res = model.invoke(state["messages"])
    return {
        "messages": [res],
        "output": res.content
    }

builder = StateGraph(state_schema=OverAllState)
builder.add_node("node_a", node_a)
builder.add_node("llm_node", llm_node)
builder.add_edge(START, "node_a")
builder.add_edge("node_a", "llm_node")
builder.add_edge("llm_node", END)

graph = builder.compile()
response = graph.invoke({"username": "小黄"})
print(response)

输出结果:

css 复制代码
{
    "messages": [
        HumanMessage(content="你好,我是 小黄", ...),
        AIMessage(content="你好呀,小黄!😊 我是DeepSeek,很高兴认识你!...", ...)
    ],
    "username": "小黄",
    "output": "你好呀,小黄!😊 我是DeepSeek,很高兴认识你!..."
}

1.5 执行流程分析

  1. node_a执行 :返回一条 HumanMessage,内容为"你好,我是 小黄"
  2. add_messages合并 :将该消息追加到 messages列表中
  3. llm_node执行 :从 state["messages"]读取完整消息列表,调用模型
  4. 模型返回 :生成 AIMessage
  5. add_messages再次合并 :将 AIMessage追加到 messages列表中
  6. 最终状态messages包含完整的对话历史

整个过程无需手动处理消息的追加逻辑,MessagesStateadd_messages自动完成了所有管理工作。

二、AgentState:LangChain Agent的内部状态

2.1 什么是AgentState?

AgentState是 LangChain Agent 内部使用的状态类型,位于 langchain.agents.middleware.types.AgentState。由于 LangChain Agent 底层也是基于 LangGraph 构建的,所以 AgentState本质上也是一个 LangGraph 状态类型。

源码定义:

py 复制代码
class AgentState(TypedDict, Generic[ResponseT]):
    """State schema for the agent."""
    
    messages: Required[Annotated[list[AnyMessage], add_messages]]
    jump_to: NotRequired[Annotated[JumpTo | None, EphemeralValue, PrivateStateAttr]]
    structured_response: NotRequired[Annotated[ResponseT, OmitFromInput]]

2.2 三个核心字段

字段一:messages

ini 复制代码
messages: Required[Annotated[list[AnyMessage], add_messages]]

MessagesState中的 messages字段类似,用于存储 Agent 运行过程中的消息列表,同样使用 add_messages作为 Reducer。

字段二:jump_to

ini 复制代码
jump_to: NotRequired[Annotated[JumpTo | None, EphemeralValue, PrivateStateAttr]]

jump_to是 Agent 内部使用的控制字段,主要用于 Agent 中间件体系,表示运行流程的跳转意图。

重要提示 :在自定义的普通 StateGraph中,即使状态中定义了 jump_to字段,LangGraph 也不会自动根据该字段的值跳转到某个节点。如果需要控制图的分支流向,应使用:

ini 复制代码
Command(goto="node_name")

字段三:structured_response

ini 复制代码
structured_response: NotRequired[Annotated[ResponseT, OmitFromInput]]

structured_response用于存储 Agent 最终生成的结构化输出。当使用 LangChain Agent 的结构化输出能力时(例如指定 response_format),结果会被写入该字段。

其中 OmitFromInput表示该字段不应作为外部输入暴露给调用方,而是由 Agent 运行过程中内部生成。

2.3 使用建议

AgentState是专门为 LangChain Agent 运行时设计的状态类型。在普通自定义 LangGraph 项目中,一般不建议直接基于 AgentState扩展图状态

如果你只是构建一个与 LLM 交互的图,推荐使用 MessagesState作为基类;如果你在使用 LangChain Agent 的高级功能,Agent 内部自然会使用 AgentState

三、总结

特性 MessagesState AgentState
所属模块 langgraph.graph.message langchain.agents.middleware.types
核心字段 messages messagesjump_tostructured_response
消息Reducer add_messages add_messages
适用场景 通用的LLM对话图 LangChain Agent内部运行
推荐使用 ✅ 推荐作为自定义状态的基类 ❌ 不推荐在普通项目中使用

一句话总结:MessagesState是构建LLM对话应用的利器,它帮你自动管理消息列表,让你专注于业务逻辑;而 AgentState是 LangChain Agent 的内部实现细节,了解即可,普通项目无需直接使用。


四、相关面试题

面试题1:MessagesState中的 add_messagesReducer 和普通的 addReducer 有什么区别?

参考答案:

特性 add add_messages
合并方式 简单的列表拼接 基于消息ID的智能合并
去重能力 有(相同ID的消息会覆盖)
删除能力 有(消息内容为None时删除)
适用类型 任意列表 AnyMessage消息列表

add_messagesadd更适合消息管理场景,因为它能正确处理消息的去重和更新,避免在多轮对话中出现重复消息。

面试题2:如果在一个节点中返回了两条 HumanMessageadd_messages会如何处理?

参考答案:

两条消息都会追加到 messages列表末尾。add_messages会遍历节点返回的消息列表,逐条处理每条消息:

  • 如果消息的 id在列表中不存在,则追加
  • 如果消息的 id已存在,则覆盖

因此,返回多条不同 id的消息等同于连续多次追加。

面试题3:如何删除 MessagesState中的某条历史消息?

参考答案:

可以利用 add_messages的删除机制:返回一条与被删除消息具有相同 id、但内容为 None的消息。

python 复制代码
from langchain.messages import AIMessage, RemoveMessage

def delete_node(state: OverAllState) -> OverAllState:
    # 删除最后一条消息
    last_message = state["messages"][-1]
    return {
        "messages": [RemoveMessage(id=last_message.id)]
    }

LangGraph 提供了 RemoveMessage工具类,专门用于标记要删除的消息。

面试题4:AgentState中的 jump_to字段在普通 StateGraph中能用吗?

参考答案:

不能直接使用jump_to是 LangChain Agent 中间件体系内部使用的控制字段,普通 StateGraph并不会识别或处理该字段。

在自定义的 StateGraph中,如果需要控制图的分支跳转,应使用以下方式:

python 复制代码
# 方式一:条件边
builder.add_conditional_edges("node_a", routing_function)

# 方式二:Command方式
from langgraph.types import Command

def node_with_routing(state) -> Command:
    # 处理逻辑
    return Command(goto="next_node")

面试题5:为什么不推荐在普通项目中使用 AgentState

参考答案:

主要原因有三点:

  1. 设计目的不同AgentState是为 LangChain Agent 的中间件体系设计的,包含了许多 Agent 内部专用的字段(如 jump_tostructured_response),普通项目用不到这些字段。
  2. 依赖关系 :引入 AgentState意味着引入了 langchain.agents模块的依赖,增加了项目的复杂度。
  3. 语义混淆 :使用 AgentState会让阅读代码的人误以为这个图是一个 Agent,但实际上它可能只是一个简单的 LLM 调用链。

推荐做法 :如果是构建与 LLM 交互的普通图,使用 MessagesState作为基类就足够了。

面试题6:MessagesState中的 messages字段为什么是 list[AnyMessage]而不是 list[BaseMessage]

参考答案:

AnyMessage是 LangChain 中所有消息类型的联合类型(Union Type),包括 HumanMessageAIMessageSystemMessageToolMessage等。使用 AnyMessageBaseMessage更精确地表达了"这个列表可以包含任意类型的消息"这一语义。

从功能上看,两者都能正常工作,但 AnyMessage在类型提示和静态检查方面更为严谨。

面试题7:如果我想在 MessagesState的基础上增加一个 message_count字段,自动统计消息数量,应该如何实现?

参考答案:

可以使用自定义 Reducer 来实现自动计数:

py 复制代码
from typing import TypedDict, Annotated
from langgraph.graph.message import MessagesState, add_messages
from langchain.messages import AnyMessage

def count_messages(current_count: int, new_messages: list[AnyMessage]) -> int:
    """自定义Reducer:统计消息总数"""
    return current_count + len(new_messages)

class OverAllState(MessagesState):
    message_count: Annotated[int, count_messages]
    username: str

或者在节点中手动更新:

py 复制代码
def log_node(state: OverAllState) -> OverAllState:
    return {
        "message_count": len(state["messages"])
    }

第一种方式更自动化,第二种方式更直观。根据实际需求选择即可。


希望这篇教程能帮助你掌握 LangGraph 的预定义状态机制,让你的 LLM 应用开发更加高效!

相关推荐
网络工程小王1 小时前
【HCIE-AI】11.模型 昇腾迁移适配-精度调试-性能调优
人工智能·学习·华为·迁移学习·昇腾
我叫黑大帅1 小时前
add()和 __add__() 写法哪个更好呢?
后端·python·面试
不如语冰1 小时前
AI大模型入门-Python进阶-上下文管理与with语句
开发语言·数据结构·数据库·人工智能·pytorch·redis·python
科技新芯1 小时前
WAIC首个AI影视专场落地 万兴科技发布多重出海布局
人工智能·科技
日月新著1 小时前
2026年最新!五款高评Agent开源模型介绍:能自主干活的AI来了
人工智能·语言模型
柠檬味的Cat1 小时前
GEO优化系统哪个渠道商好
大数据·人工智能·python
GuWenyue2 小时前
等AI回复卡顿到劝退?Vue3+DeepSeek流式输出实战,70行代码实现打字机效果
前端·人工智能·客户端
Gopher_HBo2 小时前
moby-daemon-config配置
后端
拉斯特当思2 小时前
设备总掉线却复现不了?把弱网“造“出来:tcconfig 五种网络故障模拟实战
后端