从 messages 到结构化字段:LangGraph 状态设计指南

发布时间:2026/6/10 7:12:15
从 messages 到结构化字段:LangGraph 状态设计指南
真正难的不是“LangGraph 里有没有 State”而是你的数据到底该怎么放才能既方便模型理解又方便节点协作和流程控制。一、 这篇解决什么问题很多人在第一次写 LangGraph 时虽然已经知道 State 是全局状态也知道节点返回的是状态补丁但一到真正设计字段时很快就会陷入混乱用户问题、工具结果、总结文本、路由标记、审批状态到底是不是都能塞进messages为什么有些数据放进messages很顺但有些数据一放进去整个图就越来越乱为什么有些字段必须单独拆出来比如question_type、need_search、approval_statusmessages和结构化字段之间到底该怎么分工这篇文章要回答的核心问题就是在 LangGraph 里状态不仅要“能存”更要“存得合理”。二、 先给一句最短结论如果你在设计状态时感到犹豫请直接用这句话作为判断中轴messages更适合存放“要持续喂给模型看的对话与工具交互历史”独立字段更适合存放“要给代码逻辑、路由判断和节点协作使用的结构化状态”。进一步解释如果一段数据的主要用途是让模型继续理解上下文优先考虑放进messages。如果一段数据的主要用途是让程序判断流程怎么走优先考虑拆成独立字段。三、 为什么“什么都塞进 messages”会出问题在最开始做小 Demo 时把所有中间信息都塞进messages看起来很省事。因为模型本来就会读messages工具结果也常常会回填成ToolMessage最后总结、判断、路由理由好像也能当作文本塞进去。但问题是一旦流程稍微复杂一点这种做法会迅速失控。常见问题路由判断变脆如果你要判断“是否需要搜索”、“是否需要审批”、“当前是否已完成某步检查”而这些信息只存在某段自然语言消息里你就不得不在代码里从一堆文本里“猜”。结构化数据难取像search_results、approval_status、retry_count、question_type这类状态如果混进消息文本后续节点读取和复用会很痛苦。Prompt 污染所有信息都塞进消息后模型上下文会越来越长、越来越杂真正重要的信息反而容易被淹没。业务状态和对话状态混在一起这会让图的可维护性快速下降。一句话总结messages很重要但它不是万能垃圾桶。四、 messages 到底适合放什么最适合放进messages的内容用户输入比如用户提问、用户补充说明、用户修改需求。模型输出比如 AI 回复、模型提出的工具调用请求、模型基于工具结果给出的阶段性总结。工具回执比如ToolMessage、搜索结果的文本摘要、计算结果、API 返回的可读说明。需要持续暴露给模型的上下文如果某段内容后面还需要被模型多次参考放进messages通常是合理的。一句话概括messages适合承载“对模型可见的交互历史”。五、 什么时候必须拆成独立字段这是状态设计的重头戏。适合拆字段的典型情况用于流程路由比如need_search、need_approval、is_completed、next_step。这类字段本质上是流程控制信号应该让代码直接读取而不是从消息文本里猜。用于跨节点共享的结构化结果比如search_results、parsed_query、approval_decision、customer_profile。这些数据往往要被多个节点复用拆成独立字段会更稳定。用于统计、计数、状态机控制比如retry_count、attempts、status、error_type。这类信息本来就是程序状态放进messages反而别扭。不适合直接暴露给模型的大体量数据比如原始搜索结果 JSON、数据库记录列表、大段中间结构化结果。这些通常更适合存进独立字段或者存摘要给模型、原始数据给程序。一句话总结结构化字段不是为了“多建几个变量”而是为了把状态从自然语言里解耦出来。六、 一个最小反例把所有东西都塞进 messages用户问“今天有哪些值得关注的 AI 新闻”如果你把下面这些全塞进messages问题分类结果是否需要搜索搜索原始返回搜索结果筛选理由是否继续下一轮搜索最终摘要那么很快就会出现这些问题路由节点要从文本里判断need_search总结节点要从一堆消息里硬扒搜索结果模型每轮都被喂很多本来不必重复看的中间文本。状态既难维护也难调试。一句话点评能跑不等于设计合理。七、 一个正例把 messages 和业务字段分开真正的工程级设计职责必须清晰分明。我们可以这样设计class State(TypedDict): messages: Annotated[list[AnyMessage], add_messages] question_type: str | None need_search: bool search_query: str | None search_results: list[str] | None summary: str | None各字段分工messages保存用户问题、模型回复、工具消息。供模型继续理解上下文。question_type给路由和节点逻辑判断问题类型。need_search给条件边直接判断下一步是否进入搜索节点。search_query给搜索节点用不必每次都从消息文本里重解析。search_results给总结节点读的结构化中间产物。summary给最终回答节点用的中间结论。八、 一个实用判断口诀在实战中你可以直接用这套口诀来拍板状态设计要给模型反复看-优先考虑放进messages要给代码判断流程-优先拆成独立字段要跨节点稳定传递结构化结果-优先拆成独立字段内容太大、太原始、太脏-不要直接塞进messages既要给模型看又要给程序判断- 考虑“双层表示”给模型一份摘要给程序一份结构化字段九、 常见误区误区一messages就是 State 的全部。不是。messages很常见但绝不是全部状态。误区二字段越少越高级。不是。乱塞进messages反而会让设计更脆弱。误区三只要模型能读懂就不用拆结构化字段。这是典型误区。程序逻辑和模型理解是两套需求。误区四所有结构化字段都要暴露给模型。也不一定。很多字段本来就只该给路由逻辑和节点代码使用。十、 总结在 LangGraph 里State 的难点从来不只是“怎么存数据”而是“怎么让数据在模型、节点和流程控制之间分工合理”。messages适合承载对模型可见的交互历史独立字段适合承载程序要稳定读取和复用的结构化状态。一旦你把所有东西都塞进messages图虽然也许还能跑但很快就会变得脆弱、难调、难扩展而一旦你学会把“对话上下文”和“业务状态”拆开整个图的可维护性会明显上一个台阶。所以State 设计的关键不在于“字段越少越好”而在于每一份数据都应该出现在最适合它的位置。