LangGraph 中的 MessagesState
LangGraph 中的MessagesState注意官方名称是复数MessagesState不是MessageState是整个框架专为对话和 Agent 场景预制的内置状态类型。它的核心价值就一句话用一个带add_messagesreducer 的messages字段把多轮对话历史如何累积这件最烦的事接管掉。文章目录一、MessagesState 到底是什么二、add_messages 的三种合并语义1. 追加AppendID 不同 → 加到末尾2. 更新ReplaceID 相同 → 原地替换3. 删除Delete使用 RemoveMessage附加能力反序列化三、在图中使用 MessagesState基础用法直接使用进阶用法继承 MessagesState 扩展字段四、多轮对话 持久化五、生产级必考虑的上下文裁剪方案 1自定义 Reducer 做滑动窗口方案 2用 RemoveMessage 主动删除方案 3配合 LangGraph 官方的消息裁剪工具六、多智能体场景消息通道隔离七、常见坑与排查坑 1以为 invoke 自动记忆坑 2用了 operator.add 而非 add_messages坑 3消息 ID 重复坑 4忘记消息是累积的八、完整生产级模板 核心要点回顾下面从原理到实战完整讲透。一、MessagesState 到底是什么它本质上是一个TypedDict只有一个字段messages类型注解为Annotated[list[AnyMessage], add_messages]。# 等价于以下定义来自官方源码 class MessagesState(TypedDict): messages: Annotated[list[AnyMessage], add_messages]这里有两个关键点list[AnyMessage]消息列表可以装HumanMessage、AIMessage、SystemMessage、ToolMessage等所有 LangChain 消息对象add_messagesreducer告诉 LangGraph 当节点返回新的messages时如何合并到现有状态而不是简单替换 如果你不写 reducer框架默认是覆盖语义——节点返回{messages: [msg3]}历史里的 msg1、msg2 就全丢了。这正是多轮对话的致命坑。add_messages就是为解决这个而生。二、add_messages 的三种合并语义这是MessagesState的灵魂。它不只是追加而是按消息 ID 智能合并1. 追加AppendID 不同 → 加到末尾from langchain_core.messages import HumanMessage, AIMessage from langgraph.graph.message import add_messages old [HumanMessage(contentHello, id1)] new [AIMessage(contentHi there!, id2)] result add_messages(old, new) # [HumanMessage(id1), AIMessage(id2)] ← 两条都在2. 更新ReplaceID 相同 → 原地替换old [AIMessage(content你好, idmsg_001)] new [AIMessage(content你好有什么可以帮你, idmsg_001)] result add_messages(old, new) # 长度仍是 1内容被新消息替换 # [AIMessage(content你好有什么可以帮你, idmsg_001)]⚠️ 这个特性在流式输出场景中至关重要AI 先返回一个占位消息如idstream_1token 逐步到达时不断用相同 ID回写add_messages从而更新而非堆叠消息。3. 删除Delete使用RemoveMessagefrom langchain_core.messages import RemoveMessage # 从状态中移除指定 ID 的消息 result add_messages(old, [RemoveMessage(idmsg_001)])这在对历史做裁剪、或者 human-in-the-loop 修正对话时非常有用。附加能力反序列化add_messages还能把字典自动反序列化成 LangChain 消息对象。所以以下两种写法都合法{messages: [HumanMessage(contenthi)]} # ✅ 对象形式 {messages: [{type: human, content: hi}]} # ✅ 字典形式自动转成 HumanMessage三、在图中使用 MessagesState基础用法直接使用from langgraph.graph import StateGraph, MessagesState, START, END from langchain_core.messages import HumanMessage, AIMessage def chat_node(state: MessagesState): # state[messages] 是整个历史 last_user_msg state[messages][-1].content # 节点只需要返回增量 return {messages: [AIMessage(contentf你说了{last_user_msg})]} graph StateGraph(MessagesState) graph.add_node(chat, chat_node) graph.add_edge(START, chat) graph.add_edge(chat, END) app graph.compile() result app.invoke({messages: [(user, 今天天气怎么样)]}) print(result[messages][-1].content) # 你说了今天天气怎么样核心规律节点函数只需要返回变化的部分这里是新增的 AI 消息LangGraph 会通过add_messages自动把它追加到state[messages]历史末尾。进阶用法继承 MessagesState 扩展字段实际项目中你往往需要更多状态字段用户信息、迭代次数、检索到的文档等from langgraph.graph import MessagesState from typing import Optional class AgentState(MessagesState): 继承 MessagesState自动拥有 messages 字段 user_id: Optional[str] None current_tool: str iteration: int 0 documents: list[str] []继承后messages字段原样继承reducer 仍是add_messages新字段如果没有标注 reducer默认是覆盖语义新字段也可以自己加 reducer比如iteration: Annotated[int, operator.add]实现累加四、多轮对话 持久化MessagesState本身只是内存里的记事本要实现跨invoke调用的多轮记忆需要配合Checkpointerfrom langgraph.checkpoint.memory import MemorySaver # 1. 定义状态 class ChatState(MessagesState): pass # 2. 构建图时挂载检查点 graph StateGraph(ChatState) graph.add_node(chat, chat_node) graph.add_edge(START, chat) graph.add_edge(chat, END) app graph.compile(checkpointerMemorySaver()) # 3. 用 thread_id 区分不同会话 config {configurable: {thread_id: user_123_session_abc}} # 第一轮 app.invoke({messages: [(user, 我叫小明)]}, config) # 第二轮同一个 thread_id历史自动从 Checkpointer 恢复 app.invoke({messages: [(user, 我刚才说我叫什么)]}, config) # AI 能正确回答你叫小明State 是单次运行的临时记事本Checkpointer 是跨调用的存档柜靠thread_id隔离不同会话。五、生产级必考虑的上下文裁剪MessagesState的add_messages永远追加意味着消息列表会无限增长。生产环境中必须主动裁剪方案 1自定义 Reducer 做滑动窗口from typing import Annotated, TypedDict def keep_last_n(current: list, new: list, n: int 10) - list: 只保留最近 N 条消息 combined current new return combined[-n:] class BoundedState(MessagesState): # 重写 messages 字段的 reducer messages: Annotated[list, lambda c, n: keep_last_n(c, n, n10)]方案 2用 RemoveMessage 主动删除from langchain_core.messages import RemoveMessage def trim_node(state): # 删除最早的消息 oldest_id state[messages][0].id return {messages: [RemoveMessage(idoldest_id)]}方案 3配合 LangGraph 官方的消息裁剪工具对于超长对话可以定期把旧消息摘要化只保留最近的 K 条原文 一个历史摘要消息。六、多智能体场景消息通道隔离在多 Agent 系统中默认所有 Agent 共享同一个messages列表。如果你不希望子 Agent 的内部历史污染主对话可以用不同的消息键from typing_extensions import TypedDict, Annotated from langchain.messages import AnyMessage from langgraph.graph import StateGraph, add_messages class AliceState(TypedDict): alice_messages: Annotated[list[AnyMessage], add_messages] # Alice 用自己的 alice_messages 通道 alice ( StateGraph(AliceState) .add_node(model, ...) .compile() ) # 父图调用时用 wrapper 转换 def call_alice(state: SwarmState): response alice.invoke({alice_messages: state[messages]}) return {messages: response[alice_messages]}这是 LangGraph 官方 Multi-Agent Swarm 推荐的做法每个 Agent 使用独立消息通道通过 wrapper 函数做父子图状态转换。七、常见坑与排查坑 1以为invoke自动记忆# ❌ 错误每次 invoke 都是全新状态 app.invoke({messages: [(user, 你好)]}) app.invoke({messages: [(user, 还记得我吗)]}) # AI 不记得上一句 # ✅ 正确必须配合 Checkpointer thread_id app.invoke({messages: [...]}, config)坑 2用了operator.add而非add_messages# ❌ 错误operator.add 只会盲目追加无法更新相同 ID 的消息 messages: Annotated[list, operator.add] # ✅ 正确add_messages 能正确处理更新语义 messages: Annotated[list[AnyMessage], add_messages]流式输出场景下operator.add会导致同一个流式消息被叠加成多条。坑 3消息 ID 重复如果手动构造消息时复用 ID会导致意外覆盖。让 LangChain 自动生成 ID 通常是更安全的选择。坑 4忘记消息是累积的新手常写出这样的代码def node(state): # ❌ 错误把历史和新消息拼在一起返回 return {messages: state[messages] [new_msg]} # 这样 add_messages 会把整个历史再追加一次 → 消息翻倍 # ✅ 正确只返回新增的消息 def node(state): return {messages: [new_msg]}八、完整生产级模板from typing import Optional, Annotated from langgraph.graph import StateGraph, MessagesState, START, END from langgraph.checkpoint.memory import MemorySaver from langchain_core.messages import HumanMessage, AIMessage, RemoveMessage from operator import add class ProductionChatState(MessagesState): user_id: Optional[str] None iteration: Annotated[int, add] 0 # 累加器 tool_calls: list[str] [] def chatbot(state: ProductionChatState): # 1. 读取历史 messages state[messages] # 2. 调用 LLM伪代码 response llm.invoke(messages) # 3. 返回增量 return { messages: [response], iteration: 1, # 通过 reducer 累加 tool_calls: extract_tool_names(response) } def trim_history(state: ProductionChatState): 保持消息列表不超过 20 条 if len(state[messages]) 20: to_remove state[messages][:-20] return { messages: [RemoveMessage(idm.id) for m in to_remove] } return {} # 构建图 graph StateGraph(ProductionChatState) graph.add_node(chat, chatbot) graph.add_node(trim, trim_history) graph.add_edge(START, chat) graph.add_edge(chat, trim) graph.add_edge(trim, END) app graph.compile(checkpointerMemorySaver()) # 使用 config {configurable: {thread_id: prod_session_001}} result app.invoke({ messages: [(user, 帮我分析 Q3 销售数据)], user_id: u_12345 }, config) 核心要点回顾MessagesState TypedDictmessages: Annotated[list[AnyMessage], add_messages]add_messages 是智能 reducerID 不同→追加ID 相同→更新RemoveMessage→删除节点只返回增量框架负责合并多轮记忆必须配 Checkpointer thread_id生产环境必须考虑消息裁剪否则上下文无限膨胀多 Agent 协作可通过不同消息键实现通道隔离

相关新闻

Mac本地AI性能监控:Llamatop工具详解与llama.cpp优化实战

Mac本地AI性能监控:Llamatop工具详解与llama.cpp优化实战

如果你在 MacBook 上跑过本地大模型,一定遇到过这样的困惑:风扇狂转、机器发烫,但你真的知道每个 CPU/GPU 核心在忙什么吗?是模型加载、推理计算,还是数据预处理在消耗资源?传统的活动监视器只能告诉你整体…

2026/7/26 2:27:52阅读更多 →
Alexa Plus更新解析:MCP协议如何简化智能家居设备连接

Alexa Plus更新解析:MCP协议如何简化智能家居设备连接

1. 先搞清楚 Alexa Plus 这次更新到底解决了什么问题如果你正在用或者考虑用 Alexa 控制家里的智能设备,这次更新最值得关注的就两点:连接更多设备和支持 MCP 开放标准。先说连接设备这部分。很多人在用智能家居时最头疼的就是“这个设备 Alexa 支不支持…

2026/7/26 2:27:52阅读更多 →
OpenAI API免费与付费模型差异分析及优化策略

OpenAI API免费与付费模型差异分析及优化策略

最近在技术圈里,一个现象引发了广泛讨论:当开发者使用 OpenAI 的 API 时,免费用户和付费用户获得的模型能力差异正在拉大。特别是标题中提到的 GPT-5.5 Instant 和 GPT-5.6 Sol 这两个模型版本,虽然命名带有未来色彩,但…

2026/7/26 2:27:52阅读更多 →
AI Agent 面试题 589:如何设计RAG系统的查询理解和改写模块?

AI Agent 面试题 589:如何设计RAG系统的查询理解和改写模块?

🔥 AI Agent 面试题 589:如何设计RAG系统的查询理解和改写模块?摘要:本文深入解析了「如何设计RAG系统的查询理解和改写模块?」这一 AI Agent 领域的核心面试题。文章从 检索增强生成原理 的基本概念出发,系…

2026/7/26 9:51:11阅读更多 →
YOLO标注无人机遥感图像在茶叶病害检测中的应用

YOLO标注无人机遥感图像在茶叶病害检测中的应用

1. 数据集背景与应用价值 这个基于YOLO标注格式的无人机遥感图像茶叶病害检测数据集,是农业智能化领域的重要基础设施。我在参与某省智慧农业项目时,深刻体会到优质标注数据对于茶叶病害识别模型性能的决定性影响。传统人工巡检方式需要农技人员步行数公…

2026/7/26 9:51:11阅读更多 →
VC++开发中LNK1168错误:文件被占用的原理与解决方案

VC++开发中LNK1168错误:文件被占用的原理与解决方案

1. 项目概述:当链接器告诉你“文件被占用”在Windows平台上用Visual C(VC)进行开发,尤其是调试阶段,LINK : fatal error LNK1168: cannot open Debug/Menu.exe for writing这个报错,几乎可以算作是每个C开发…

2026/7/26 9:51:11阅读更多 →
双架构容器镜像构建指南:BuildKit实战与优化

双架构容器镜像构建指南:BuildKit实战与优化

1. 为什么需要双架构容器镜像在容器化部署的实际场景中,我们经常会遇到一个棘手的问题:开发环境和生产环境的CPU架构不一致。比如开发团队普遍使用x86架构的MacBook或Windows笔记本,而生产服务器可能是基于ARM架构的AWS Graviton实例。传统做…

2026/7/26 9:51:11阅读更多 →
Docker Compose部署GitLab全攻略:从入门到生产级实践

Docker Compose部署GitLab全攻略:从入门到生产级实践

1. 为什么选择Docker Compose部署GitLab 十年前我第一次搭建GitLab时,光环境配置就折腾了两天。如今用Docker Compose方案,从零到可用只需要15分钟。这种部署方式之所以成为主流,核心在于它完美解决了传统部署的三大痛点: 依赖地…

2026/7/26 9:51:11阅读更多 →
深入理解Cortex-M NVIC寄存器:从原理到实战避坑指南

深入理解Cortex-M NVIC寄存器:从原理到实战避坑指南

1. 从手册到实战:为什么我们需要深入理解NVIC寄存器 搞嵌入式开发,尤其是基于ARM Cortex-M内核的,中断绝对是绕不开的核心话题。无论是处理一个按键、接收一帧串口数据,还是响应一个定时器溢出,背后都是中断在驱动。很…

2026/7/26 9:49:10阅读更多 →
覆盖国产 + 海外 + 开源模型,OpenClaw 2.7.9 Windows/Mac 双端部署详解

覆盖国产 + 海外 + 开源模型,OpenClaw 2.7.9 Windows/Mac 双端部署详解

🔹 工具基础介绍 OpenClaw 是开源生态中一款实用性较强的本地智能工具,凭借本地离线运行、可视化图形操作和任务自动化三大核心特性,赢得了众多用户的青睐。与普通在线对话AI工具不同,它属于能够直接操控本机软硬件的智能数字员工…

2026/7/26 0:01:28阅读更多 →
伺服阀焊完微漏毁整机?精密激光焊接三关锁住高压

伺服阀焊完微漏毁整机?精密激光焊接三关锁住高压

所谓液压伺服阀体的精密激光焊接,是用激光束对阀座壳体(通常为不锈钢或铝合金)进行密封焊接,使阀体在21-35MPa的高压液压油或压缩气体中长期运行而不发生介质泄漏。液压伺服阀是高端液压系统的"大脑"。从航空航天飞行控…

2026/7/26 0:01:28阅读更多 →
D2DX:三步实现《暗黑破坏神2》高清宽屏体验的终极指南

D2DX:三步实现《暗黑破坏神2》高清宽屏体验的终极指南

D2DX:三步实现《暗黑破坏神2》高清宽屏体验的终极指南 【免费下载链接】d2dx D2DX is a complete solution to make Diablo II run well on modern PCs, with high fps and better resolutions. 项目地址: https://gitcode.com/gh_mirrors/d2/d2dx 你是否还在…

2026/7/26 0:01:28阅读更多 →
覆盖国产 + 海外 + 开源模型,OpenClaw 2.7.9 Windows/Mac 双端部署详解

覆盖国产 + 海外 + 开源模型,OpenClaw 2.7.9 Windows/Mac 双端部署详解

🔹 工具基础介绍 OpenClaw 是开源生态中一款实用性较强的本地智能工具,凭借本地离线运行、可视化图形操作和任务自动化三大核心特性,赢得了众多用户的青睐。与普通在线对话AI工具不同,它属于能够直接操控本机软硬件的智能数字员工…

2026/7/26 0:01:28阅读更多 →
伺服阀焊完微漏毁整机?精密激光焊接三关锁住高压

伺服阀焊完微漏毁整机?精密激光焊接三关锁住高压

所谓液压伺服阀体的精密激光焊接,是用激光束对阀座壳体(通常为不锈钢或铝合金)进行密封焊接,使阀体在21-35MPa的高压液压油或压缩气体中长期运行而不发生介质泄漏。液压伺服阀是高端液压系统的"大脑"。从航空航天飞行控…

2026/7/26 0:01:28阅读更多 →
D2DX:三步实现《暗黑破坏神2》高清宽屏体验的终极指南

D2DX:三步实现《暗黑破坏神2》高清宽屏体验的终极指南

D2DX:三步实现《暗黑破坏神2》高清宽屏体验的终极指南 【免费下载链接】d2dx D2DX is a complete solution to make Diablo II run well on modern PCs, with high fps and better resolutions. 项目地址: https://gitcode.com/gh_mirrors/d2/d2dx 你是否还在…

2026/7/26 0:01:28阅读更多 →
YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

如果你在部署 YOLOv8 时,发现推理速度只有可怜的 1-2 FPS,而别人的演示视频却能跑到 30 FPS 以上,那么问题很可能不在模型本身,而在于你的整个处理链路。很多开发者拿到一个训练好的 YOLOv8 模型后,会直接使用官方示例…

2026/7/25 23:03:25阅读更多 →
Coze与Dify对比指南:低代码AI应用开发从入门到实战

Coze与Dify对比指南:低代码AI应用开发从入门到实战

1. 从零到一:为什么你需要了解 Coze 和 Dify?如果你对 AI 应用开发感兴趣,但一看到“大模型”、“智能体”、“工作流”这些词就头疼,觉得门槛太高,那这篇文章就是为你准备的。很多开发者,包括我自己&#…

2026/7/25 19:03:04阅读更多 →
AI生图工具怎么选?2026年6月版实测对比

AI生图工具怎么选?2026年6月版实测对比

做自媒体的朋友应该都有体会:配图一直是个让人头疼的问题。2026年,AI生图工具已经非常成熟了,但工具太多反而不知道怎么选。以下是截至2026年6月我对主流AI生图工具的实测对比。Midjourney V8.1:速度之王2026年6月11日&#xff0c…

2026/7/25 19:03:04阅读更多 →