LangGraph == 图的状态(State)管理 (下)

3.5. 预定义状态

3.5.1. MessagesState

LangGraph 构建的计算图通常会和 LLM 结合使用,而 LLM 在运行过程中通常需要维护一组消息列表。为了提升开发效率,LangGraph 官方提供了一个预定义状态类型:langgraph.graph.message.MessagesState。

开发者可以直接继承该状态类型,并在其基础上扩展自定义状态字段。

源码如下:

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

由此可知,MessagesState 只有一个字段:messages。

该字段的类型是列表,元素类型为 AnyMessage;同时,它通过 Annotated 绑定了内置 Reducer 函数 add_messages。

add_messages 的完全限定名(英文全称 fully qualified name)是:

langgraph.graph.message.add_messages,正是上文 3.2.2.3.2 节介绍的内置 Reducer 函数。

示例如下:

复制代码
from typing import NotRequired
from langgraph.graph import StateGraph, START, END
from langgraph.graph.message import MessagesState
from langchain_core.messages import HumanMessage
from langchain_deepseek import ChatDeepSeek
from dotenv import load_dotenv

load_dotenv(verbose=True)

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

class OverAllState(MessagesState):
    username: str
    output: NotRequired[str]   # 关键:改为可选

# 不返回 output,让它保持未设置状态(或者返回空字符串)
def node_a(state: OverAllState) -> OverAllState:
    return {
        "messages": [HumanMessage(content="你好,我是" + state["username"])],
        "username": state["username"],
    }


def llm_node(state: OverAllState) -> OverAllState:
    res = model.invoke(state["messages"])
    return {
        "messages": [res],
        "username": state["username"],
        "output": res.content,
    }

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

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

输出如下

复制代码
{
    "messages": [
        HumanMessage(
            content="你好,我是 小黄",
            additional_kwargs={},
            response_metadata={},
            id="155e1ef4-5bbc-4250-b978-62a7a5918cef",
        ),
        AIMessage(
            content="你好呀,小黄!😊 我是DeepSeek,很高兴认识你!有什么我可以帮你的吗?无论是聊天、解答问题、帮你写作、编程,还是其他任何需要,尽管告诉我吧!你名字里的"黄"是哪个黄呀?😄",
            additional_kwargs={
                "refusal": "None",
            },
            response_metadata={
                "token_usage": {
                    "completion_tokens": 57,
                    "prompt_tokens": 10,
                    "total_tokens": 67,
                    "completion_tokens_details": "None",
                    "prompt_tokens_details": {
                        "audio_tokens": "None",
                        "cached_tokens": 0,
                    },
                    "prompt_cache_hit_tokens": 0,
                    "prompt_cache_miss_tokens": 10,
                },
                "model_provider": "deepseek",
                "model_name": "deepseek-v4-flash",
                "system_fingerprint": "fp_8b330d02d0_prod0820_fp8_kvcache_20260402",
                "id": "17d57633-2ffb-4af3-be87-b3d58f9acd2b",
                "finish_reason": "stop",
                "logprobs": "None",
            },
            id="lc_run--019e6890-4544-7212-b4f2-aa20fc079911-0",
            tool_calls=[],
            invalid_tool_calls=[],
            usage_metadata={
                "input_tokens": 10,
                "output_tokens": 57,
                "total_tokens": 67,
                "input_token_details": {
                    "cache_read": 0,
                },
                "output_token_details": {},
            },
        ),
    ],
    "username": "小黄",
    "output": "你好呀,小黄!😊 我是DeepSeek,很高兴认识你!有什么我可以帮你的吗?无论是聊天、解答问题、帮你写作、编程,还是其他任何需要,尽管告诉我吧!你名字里的"黄"是哪个黄呀?😄",
}

上述案例中,OverAllState 继承了 MessagesState,因此可用的状态字段为

复制代码
messages
username
output

只关注 messages 状态,执行流程如下:

  1. node_a 返回一条 HumanMessage;
  2. LangGraph 使用 add_messages 将该消息合并到 messages 状态字段中;
  3. llm_node 从 state["messages"] 中读取完整消息列表,并调用模型;
  4. llm_node 将模型生成的 AIMessage 作为状态更新返回;
  5. LangGraph 再次通过 add_messages 将 AIMessage 合并到 messages 中;
  6. 最终状态中包含完整消息列表。

MessagesState 帮开发者预先定义好了 messages 字段及其合并规则。在构建聊天机器人、Agent、工具调用流程、多轮对话流程时,它可以提升开发效率。

3.5.2. AgentState

AgentState 是 LangChain Agent 内部使用的状态类型。由于 LangChain Agent 底层也是基于 LangGraph 运行图构建的,所以从技术上讲,开发者也可以将 AgentState 或其子类作为自定义 LangGraph 的状态类型。

AgentState 的全类名,也可以称为类的完全限定名(英文全称: fully qualified class name)是:langchain.agents.middleware.types.AgentState

源码如下

复制代码
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]]

该状态中主要包含三个字段。

messages

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

messages 用于存储 Agent 运行过程中的消息列表。

该字段和 MessagesState 中的 messages 字段类似,也使用 add_messages 作为 Reducer。

jump_to

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

jump_to 是 LangChain Agent 内部使用的控制字段,主要服务于 Agent 中间件体系。

它通常用于表示运行流程的跳转意图,例如某些中间件希望影响 Agent 后续应该进入哪个节点。

需要注意的是,jump_to 并不是普通 LangGraph 状态图中的通用跳转机制。

在自定义 StateGraph 中,即使状态中定义了 jump_to 字段,LangGraph 也不会因为该字段的值自动跳转到某个节点。普通 LangGraph 运行图如果需要控制后续流向,通常应使用:

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

见下文。

structured_response

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

structured_response 用于存储 Agent 最终生成的结构化输出。

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

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

总体来看,AgentState 是专门为 LangChain Agent 运行时设计的状态类型。

因此,在普通自定义 LangGraph 项目中,一般不建议直接基于 AgentState 扩展图状态。

相关推荐
星恒随风1 小时前
Linux进程间通信(二):深入理解System V共享内存、消息队列、信号量与内核IPC资源管理
linux·运维·服务器·笔记·学习·状态模式
zlwool1 小时前
机械行业ERP与进销存的合规选型边界
数据库·erp
yangmu32031 小时前
PLC 设备介绍:工业自动化的“控制大脑”
运维·自动化
梦想的颜色1 小时前
【AI速览】RAG 向量数据库选型:Milvus、Chroma、Qdrant、PGVector 全维度横向对比
数据库·milvus·chroma·rag·pgvector·向量库·qdrant
IMPYLH1 小时前
Linux 文件检索:find、locate、grep 与 which 实战指南
linux·运维·服务器
腾讯蓝鲸智云1 小时前
【运维自动化-监控平台】如何配置进程监控之基于进程名规则匹配
运维·自动化·云计算·sass·paas
优化Henry1 小时前
LTE基站传输割接后S1连接失败排查实录
运维·网络·笔记·学习·信息与通信
西索斯coding1 小时前
Claude Code 工具调用返回空怎么办?claude-opus-5.5 的 input_schema 校验踩坑记录与修复方案
服务器·前端·javascript·ai
小溪学编程1 小时前
向量数据库 Milvus 基础实践:从入门到上手
数据库·milvus