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 扩展图状态。

相关推荐
镜子AI9 小时前
镜子AI 技术复盘:三条自动化链路上的“停止条件“设计
运维·人工智能·自动化
w-w0w-w9 小时前
SQL注入
网络·数据库·sql
tianyuanwo9 小时前
NetBox 数据库详解:当“关系型数据库”被装进一个网络运维的领域模型里
运维·数据库·netbox
一条泥憨鱼9 小时前
【计算机网络】短连接、长连接与连接池:从 TCP 握手到池化复用的完整链路
网络·数据库·网络协议·tcp/ip·计算机网络
IvorySQL9 小时前
深入 PostgreSQL WAL:从崩溃恢复到逻辑复制
数据库·postgresql
幽灵诶11 小时前
Mysql(基于测试的学习)
数据库·mysql
砚底藏山河11 小时前
python量化自定义周期 K 线合成
java·开发语言·数据库·python·金融
知已难寻11 小时前
03 · OpenStack的计算管理 Nova
运维·服务器·开发语言·云原生·云计算·openstack·开源软件
宇宙无敌暴龙战士66611 小时前
MySQL 8 库表操作学习笔记:SQL 基础、库表管理、约束与用户授权全流程
数据库·sql·mysql·学习笔记·后端开发
猿究院_夏燊11 小时前
MySQL 的四种隔离级别
java·数据库·mysql