运行第一个两节点图

第一个状态图

本章目标

掌握状态、节点、边、编译和调用五个基本动作。

完整代码:examples/hello_graph.py

from typing_extensions import TypedDict
from langgraph.graph import END, START, StateGraph


class HelloState(TypedDict):
    name: str
    greeting: str


def normalize_name(state: HelloState) -> dict[str, str]:
    return {"name": state["name"].strip().title()}


def build_greeting(state: HelloState) -> dict[str, str]:
    return {"greeting": f"Hello, {state['name']}!"}

节点收到完整 State,但只返回需要更新的字段。不要原地修改 state;局部更新更容易追踪,也能让并行写入和 Reducer 保持确定性。

节点只返回更新

构图与运行:

先编译再运行

builder = StateGraph(HelloState)
builder.add_node("normalize_name", normalize_name)
builder.add_node("build_greeting", build_greeting)
builder.add_edge(START, "normalize_name")
builder.add_edge("normalize_name", "build_greeting")
builder.add_edge("build_greeting", END)
graph = builder.compile()

result = graph.invoke({"name": "  langgraph learner  ", "greeting": ""})

验证:

python examples/hello_graph.py

预期:

{'name': 'Langgraph Learner', 'greeting': 'Hello, Langgraph Learner!'}

一次调用内部发生了什么

compile() 先验证图结构并生成可执行对象;invoke() 再创建一次运行,向 normalize_name 传入完整状态,将其局部返回值合并回状态,然后把新状态交给 build_greeting。节点之间没有共享的可变局部变量,所有需要跨节点存在的数据都必须出现在 State 中。

这带来两个直接收益:节点可以单独测试,运行过程也可以通过更新事件或 Checkpoint 重放。代价是 State 会成为长期数据契约,随意改名或改变字段类型可能让历史 Checkpoint 无法被新版本正确读取。

TypedDict 不是运行时输入校验

TypedDict 帮助静态检查器理解字段,但不会拒绝 HTTP 请求里的空字符串或超长输入。生产系统应在边界使用 Pydantic,再把已经验证的数据转换成图输入:

from pydantic import BaseModel, Field


class HelloRequest(BaseModel):
    name: str = Field(min_length=1, max_length=100)

节点内部仍应处理业务上可能出现的缺失状态,尤其是 total=False 的 State。不要把来自客户端的任意字典直接交给持久图。

先测节点,再测整张图

纯节点可以像普通函数一样测试:

def test_normalize_name():
    update = normalize_name({"name": "  learner  ", "greeting": ""})
    assert update == {"name": "Learner"}

需要确认拓扑时可以输出 Mermaid 源码:

print(graph.get_graph().draw_mermaid())

失败实验:故意删除 normalize_name → build_greeting 的边,重新编译并运行,观察结果为什么缺少 greeting。再让一个节点原地修改 State 且返回空字典,对比局部更新方式的可测试性。

本章验收

  • 能解释 buildercompile() 和已编译 graph 的职责差异。
  • 能为两个节点分别编写单元测试。
  • 能说明 API 输入模型与持久化 State 为什么不是同一层对象。
  • 能输出并检查图结构,而不是只看最终字符串。

两节点链用普通函数更短。本章的意义是建立统一语法,收益会在条件边、Checkpointer 和审批出现后体现。