节点与边 #

概述 #

节点(Nodes)和边(Edges)是 LangGraph 图结构的两个基本构建块。节点执行工作,边决定下一步做什么。通过组合节点和边,你可以创建复杂的、有状态的、可循环的工作流。

text
┌─────────────────────────────────────────────────────────────┐
│                    节点与边的关系                            │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│   节点(Nodes):                                            │
│   - 执行实际工作                                            │
│   - 接收状态,返回更新                                       │
│   - 可以是函数或 Runnable                                   │
│                                                             │
│   边(Edges):                                              │
│   - 定义控制流                                              │
│   - 决定下一个执行的节点                                     │
│   - 可以是固定或条件的                                       │
│                                                             │
│   简而言之:                                                 │
│   节点做工作,边告诉下一步做什么                             │
│                                                             │
└─────────────────────────────────────────────────────────────┘

节点(Nodes) #

节点函数签名 #

节点是 Python 函数,可以接收以下参数:

python
from langchain_core.runnables import RunnableConfig
from langgraph.runtime import Runtime
from typing_extensions import TypedDict

class State(TypedDict):
    messages: list
    count: int

def my_node(
    state: State,                    # 必需:当前状态
    config: RunnableConfig,          # 可选:配置信息
    runtime: Runtime,                # 可选:运行时上下文
):
    return {"count": state["count"] + 1}

基本节点 #

python
def agent_node(state: State):
    response = llm.invoke(state["messages"])
    return {"messages": [response]}

graph.add_node("agent", agent_node)

带配置的节点 #

python
def node_with_config(state: State, config: RunnableConfig):
    thread_id = config["configurable"]["thread_id"]
    user_id = config["configurable"].get("user_id")
    
    return {"thread_id": thread_id, "user_id": user_id}

graph.add_node("configured", node_with_config)

带运行时上下文的节点 #

python
from dataclasses import dataclass
from langgraph.runtime import Runtime

@dataclass
class Context:
    db_connection: any
    user_id: str

def node_with_runtime(state: State, runtime: Runtime[Context]):
    db = runtime.context.db_connection
    user_id = runtime.context.user_id
    
    data = db.query(user_id)
    return {"data": data}

graph = StateGraph(State, context_schema=Context)
graph.add_node("db_node", node_with_runtime)

异步节点 #

python
async def async_node(state: State):
    response = await llm.ainvoke(state["messages"])
    return {"messages": [response]}

graph.add_node("async_agent", async_node)

节点返回值 #

节点可以返回以下类型:

python
def node_returns_dict(state: State):
    return {"count": state["count"] + 1}

def node_returns_command(state: State):
    from langgraph.types import Command
    return Command(update={"count": 1}, goto="next")

def node_returns_none(state: State):
    return None

添加节点的方式 #

python
def my_node(state: State):
    return {"count": 1}

graph.add_node("named_node", my_node)

graph.add_node(my_node)

graph.add_node("lambda_node", lambda state: {"count": 2})

特殊节点 #

START 节点 #

python
from langgraph.graph import START

graph.add_edge(START, "first_node")

graph.set_entry_point("first_node")

END 节点 #

python
from langgraph.graph import END

graph.add_edge("last_node", END)

graph.set_finish_point("last_node")

ToolNode #

LangGraph 提供了预构建的 ToolNode:

python
from langgraph.prebuilt import ToolNode
from langchain_core.tools import tool

@tool
def search(query: str) -> str:
    return f"Results for: {query}"

tools = [search]
tool_node = ToolNode(tools)

graph.add_node("tools", tool_node)

节点缓存 #

python
from langgraph.types import CachePolicy
from langgraph.cache.memory import InMemoryCache
import time

def expensive_node(state: State):
    time.sleep(2)
    return {"result": state["input"] * 2}

graph.add_node("expensive", expensive_node, cache_policy=CachePolicy(ttl=60))

app = graph.compile(cache=InMemoryCache())

边(Edges) #

边的类型 #

text
┌─────────────────────────────────────────────────────────────┐
│                    边的类型                                  │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  1. 普通边(Normal Edge)                                    │
│     - 固定转换                                              │
│     - A 总是到 B                                            │
│                                                             │
│  2. 条件边(Conditional Edge)                               │
│     - 动态路由                                              │
│     - 根据状态决定下一个节点                                 │
│                                                             │
│  3. 入口点(Entry Point)                                    │
│     - 定义图的起始节点                                       │
│                                                             │
│  4. 条件入口点(Conditional Entry Point)                    │
│     - 动态决定起始节点                                       │
│                                                             │
└─────────────────────────────────────────────────────────────┘

普通边 #

普通边表示固定的转换:

python
from langgraph.graph import START, END

graph.add_edge(START, "agent")
graph.add_edge("agent", "tools")
graph.add_edge("tools", "agent")
graph.add_edge("agent", END)

条件边 #

条件边根据状态动态路由:

python
def should_continue(state: State):
    if state["messages"][-1].tool_calls:
        return "tools"
    return END

graph.add_conditional_edges(
    "agent",
    should_continue,
    ["tools", END]
)

条件边映射 #

使用字典映射返回值到节点名称:

python
def route_by_type(state: State):
    return state["type"]

graph.add_conditional_edges(
    "router",
    route_by_type,
    {
        "math": "math_agent",
        "code": "code_agent",
        "general": "general_agent"
    }
)

条件边返回多个节点 #

python
def parallel_route(state: State):
    return ["worker_1", "worker_2", "worker_3"]

graph.add_conditional_edges("splitter", parallel_route)

入口点 #

python
from langgraph.graph import START

graph.add_edge(START, "first_node")

graph.set_entry_point("first_node")

条件入口点 #

python
from langgraph.graph import START

def route_entry(state: State):
    if state.get("priority") == "high":
        return "priority_handler"
    return "normal_handler"

graph.add_conditional_edges(START, route_entry)

控制流模式 #

顺序执行 #

text
START ───> Node A ───> Node B ───> Node C ───> END
python
graph.add_edge(START, "a")
graph.add_edge("a", "b")
graph.add_edge("b", "c")
graph.add_edge("c", END)

循环执行 #

text
         ┌──────────────────────┐
         │                      │
         ▼                      │
START ───> Agent ───> Tools ────┘
         │
         │ (完成)
         ▼
        END
python
def should_continue(state: State):
    if state["messages"][-1].tool_calls:
        return "tools"
    return END

graph.add_edge(START, "agent")
graph.add_conditional_edges("agent", should_continue)
graph.add_edge("tools", "agent")

分支执行 #

text
                    ┌───> Node B ───┐
                    │               │
START ───> Router ──┼───> Node C ───┼───> Merge ───> END
                    │               │
                    └───> Node D ───┘
python
def route(state: State):
    return state["route_to"]

graph.add_edge(START, "router")
graph.add_conditional_edges("router", route, ["b", "c", "d"])
graph.add_edge("b", "merge")
graph.add_edge("c", "merge")
graph.add_edge("d", "merge")
graph.add_edge("merge", END)

并行执行 #

text
                    ┌───> Worker 1 ───┐
                    │                 │
START ───> Split ───┼───> Worker 2 ───┼───> Combine ───> END
                    │                 │
                    └───> Worker 3 ───┘
python
from langgraph.types import Send

def split_work(state: State):
    items = state["items"]
    return [Send("worker", {"item": item}) for item in items]

graph.add_edge(START, "splitter")
graph.add_conditional_edges("splitter", split_work)
graph.add_edge("worker", "combiner")
graph.add_edge("combiner", END)

Send 对象 #

Send 用于动态创建边,实现 Map-Reduce 等模式。

基本用法 #

python
from langgraph.types import Send

def generate_tasks(state: State):
    topics = state["topics"]
    return [
        Send("process", {"topic": topic})
        for topic in topics
    ]

graph.add_conditional_edges("generator", generate_tasks)

Map-Reduce 示例 #

python
from typing import TypedDict, Annotated
from operator import add
from langgraph.types import Send
from langgraph.graph import StateGraph, START, END

class State(TypedDict):
    topics: list[str]
    jokes: Annotated[list[str], add]

def generate_topics(state: State):
    return {"topics": ["cats", "dogs", "birds"]}

def map_jokes(state: State):
    return [Send("generate_joke", {"topic": t}) for t in state["topics"]]

def generate_joke(state: State):
    topic = state["topic"]
    joke = llm.invoke(f"Tell a joke about {topic}")
    return {"jokes": [joke]}

def combine(state: State):
    return {"result": "\n".join(state["jokes"])}

graph = StateGraph(State)
graph.add_node("generate_topics", generate_topics)
graph.add_node("generate_joke", generate_joke)
graph.add_node("combine", combine)

graph.add_edge(START, "generate_topics")
graph.add_conditional_edges("generate_topics", map_jokes)
graph.add_edge("generate_joke", "combine")
graph.add_edge("combine", END)

Command 对象 #

Command 是一个多功能原语,用于控制图执行。

基本用法 #

python
from langgraph.types import Command
from typing import Literal

def my_node(state: State) -> Command[Literal["next_node"]]:
    return Command(
        update={"count": state["count"] + 1},
        goto="next_node"
    )

条件跳转 #

python
def router(state: State) -> Command:
    if state["type"] == "math":
        return Command(goto="math_agent")
    elif state["type"] == "code":
        return Command(goto="code_agent")
    return Command(goto="general_agent")

子图导航 #

python
def subgraph_node(state: State) -> Command:
    return Command(
        update={"result": "done"},
        goto="parent_node",
        graph=Command.PARENT
    )

中断恢复 #

python
from langgraph.types import interrupt

def human_review(state: State):
    answer = interrupt("Please review:")
    return {"approved": answer}

result = graph.invoke({"messages": [...]}, config)
result = graph.invoke(Command(resume="approved"), config)

边的最佳实践 #

1. 清晰的路由函数命名 #

python
def should_use_tools(state: State):
    """判断是否需要使用工具"""
    return "tools" if state["messages"][-1].tool_calls else END

def route_by_intent(state: State):
    """根据意图路由到不同的处理器"""
    intent = state["intent"]
    return INTENT_TO_NODE.get(intent, "general")

2. 使用类型注解 #

python
from typing import Literal

def route(state: State) -> Literal["tools", END]:
    if state["messages"][-1].tool_calls:
        return "tools"
    return END

3. 避免复杂的条件逻辑 #

python
def bad_route(state: State):
    if state["a"] and state["b"] or state["c"]:
        if state["d"]:
            return "x"
        else:
            return "y"
    return "z"

def good_route(state: State):
    if should_do_x(state):
        return "x"
    if should_do_y(state):
        return "y"
    return "z"

4. 使用常量定义节点名 #

python
NODE_AGENT = "agent"
NODE_TOOLS = "tools"
NODE_REVIEW = "review"

graph.add_node(NODE_AGENT, agent_node)
graph.add_node(NODE_TOOLS, tool_node)
graph.add_edge(NODE_AGENT, NODE_TOOLS)

调试节点和边 #

打印执行路径 #

python
for step in app.stream({"messages": [...]}):
    for node_name, output in step.items():
        print(f"Executed: {node_name}")
        print(f"Output: {output}")

可视化图结构 #

python
from IPython.display import Image, display

display(Image(app.get_graph().draw_mermaid_png()))

获取 Mermaid 图 #

python
print(app.get_graph().draw_mermaid())

常见模式 #

Agent 循环模式 #

python
def agent_node(state: State):
    return {"messages": [llm.invoke(state["messages"])]}

def should_continue(state: State):
    if state["messages"][-1].tool_calls:
        return "tools"
    return END

graph.add_node("agent", agent_node)
graph.add_node("tools", ToolNode(tools))
graph.add_edge(START, "agent")
graph.add_conditional_edges("agent", should_continue)
graph.add_edge("tools", "agent")

多 Agent 路由模式 #

python
def supervisor(state: State):
    response = llm.invoke(state["messages"])
    return {"next_agent": response.next_agent}

def route_to_agent(state: State):
    return state["next_agent"]

graph.add_node("supervisor", supervisor)
graph.add_node("researcher", researcher_node)
graph.add_node("coder", coder_node)
graph.add_node("writer", writer_node)

graph.add_edge(START, "supervisor")
graph.add_conditional_edges(
    "supervisor",
    route_to_agent,
    {"researcher": "researcher", "coder": "coder", "writer": "writer"}
)
graph.add_edge("researcher", "supervisor")
graph.add_edge("coder", "supervisor")
graph.add_edge("writer", END)

错误处理模式 #

python
def risky_operation(state: State):
    try:
        result = do_something_risky(state)
        return {"result": result, "error": None}
    except Exception as e:
        return {"error": str(e)}

def handle_error(state: State):
    if state.get("error"):
        return "error_handler"
    return "continue"

graph.add_node("operation", risky_operation)
graph.add_node("error_handler", error_handler_node)
graph.add_node("continue", continue_node)

graph.add_conditional_edges("operation", handle_error)
graph.add_edge("error_handler", END)
graph.add_edge("continue", END)

下一步 #

现在你已经掌握了节点和边,接下来学习 工具使用,了解如何让 Agent 使用外部工具!

最后更新:2026-03-30