欢迎光临
我们一直在努力

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

真正难的不是“LangGraph 里有没有 State”,而是:你的数据到底该怎么放,才能既方便模型理解,又方便节点协作和流程控制。

一、 这篇解决什么问题

很多人在第一次写 LangGraph 时,虽然已经知道 State 是全局状态,也知道节点返回的是状态补丁,但一到真正设计字段时,很快就会陷入混乱:

  • 用户问题、工具结果、总结文本、路由标记、审批状态,到底是不是都能塞进 messages?
  • 为什么有些数据放进 messages 很顺,但有些数据一放进去,整个图就越来越乱?
  • 为什么有些字段必须单独拆出来,比如 question_type、need_search、approval_status?
  • messages 和结构化字段之间,到底该怎么分工?

这篇文章要回答的核心问题就是:在 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 设计的关键不在于“字段越少越好”,而在于:每一份数据都应该出现在最适合它的位置。

    赞(0)
    未经允许不得转载:171主机测评 » 从 messages 到结构化字段:LangGraph 状态设计指南
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址