mcpField
L
langgraph-implementation

langgraph-implementation

github.com/existential-birdsAgent 工作流85160 次安装11 周前更新
代码仓库

关于 langgraph-implementation

langgraph-implementation 是面向 LangGraph 开发者的实现指南,用于构建有状态的图式 Agent 应用,涵盖图构建、持久化、人机协作、流式执行和常见模式。

主要功能

  1. 01StateGraph 节点、边与状态
  2. 02检查点和状态检查
  3. 03中断与恢复工作流
  4. 04条件路由与扇出
  5. 05图执行流式输出
  6. 06重试、缓存与子图

适用场景

  1. 01构建有状态的 Agent 图
  2. 02持久化并恢复对话
  3. 03暂停图以供人工审核
  4. 04并行运行图分支

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/existential-birds/beagle --skill langgraph-implementation

安装后在 Claude Code 中输入/langgraph-implementation确认已加载。

附带文件PATTERNS.md

详细介绍

LangGraph 实现

核心概念

LangGraph 使用基于图的架构构建有状态、多参与者的 Agent 应用:

  • StateGraph:用于定义共享状态图的构建器类
  • 节点:读取状态并返回部分更新的函数
  • 边:定义执行流程(静态或条件式)
  • 通道:内部状态管理(LastValue、BinaryOperatorAggregate)
  • 检查点保存器:为暂停和恢复功能提供持久化支持

实现检查点

对于持久化和人在回路中(human-in-the-loop)流程,请按顺序执行以下检查(不要在没有证据的情况下认为“应该可以运行”):

  1. 带检查点的运行

    • 在调用 invoke / ainvoke 之前,使用 {"configurable": {"thread_id": "<stable-id>"}} 构建 config。
    • **通过标准:**同一对话的每一轮都复用同一个 thread_id;新对话使用新的 id。
  2. 步骤后的状态

    • 通过标准:graph.get_state(config).values(或等效内容)包含下一节点或客户端所需的键和 reducer 输出;如果不包含,请先修复路由、reducer 或节点顺序,再继续。
  3. 中断和恢复(HITL)

    • 通过标准:暂停后,已检查待处理的工作(get_state,以及你所用 LangGraph 版本的中断列表,如果你依赖该列表),因此知道哪个节点正在等待,以及应发送何种形状的恢复负载。
    • 通过标准:Command(resume=...)(或等效内容)包含 interrupt() 之后代码路径会读取的所有字段。
  4. 检查点保存器与环境

    • **通过标准:**测试或本地开发使用 InMemorySaver 或可丢弃的 SQLite;生产环境使用为该部署配置的持久检查点保存器(而非内存存储)。

必要导入

from langgraph.graph import StateGraph, START, END
from langgraph.graph.message import MessagesState, add_messages
from langgraph.checkpoint.memory import InMemorySaver
from langgraph.types import Command, Send, interrupt, RetryPolicy
from typing import Annotated
from typing_extensions import TypedDict

状态架构模式

使用 TypedDict 定义基本状态
import operator

class State(TypedDict):
    counter: int                                    # LastValue - stores last value
    messages: Annotated[list, operator.add]         # Reducer - appends lists
    items: Annotated[list, lambda a, b: a + [b] if b else a]  # Custom reducer
用于聊天应用的 MessagesState
from langgraph.graph.message import MessagesState

class State(MessagesState):
    # Inherits: messages: Annotated[list[AnyMessage], add_messages]
    user_id: str
    context: dict
Pydantic 状态(用于验证)
from pydantic import BaseModel

class State(BaseModel):
    messages: Annotated[list, add_messages]
    validated_field: str  # Pydantic validates on assignment

构建图

基本模式
builder = StateGraph(State)

# Add nodes - functions that take state, return partial updates
builder.add_node("process", process_fn)
builder.add_node("decide", decide_fn)

# Add edges
builder.add_edge(START, "process")
builder.add_edge("process", "decide")
builder.add_edge("decide", END)

# Compile
graph = builder.compile()
节点函数签名
def my_node(state: State) -> dict:
    """Node receives full state, returns partial update."""
    return {"counter": state["counter"] + 1}

# With config access
def my_node(state: State, config: RunnableConfig) -> dict:
    thread_id = config["configurable"]["thread_id"]
    return {"result": process(state, thread_id)}

# With Runtime context (v0.6+)
def my_node(state: State, runtime: Runtime[Context]) -> dict:
    user_id = runtime.context.get("user_id")
    return {"result": user_id}
条件边
from typing import Literal

def router(state: State) -> Literal["agent", "tools", "__end__"]:
    last_msg = state["messages"][-1]
    if hasattr(last_msg, "tool_calls") and last_msg.tool_calls:
        return "tools"
    return END  # or "__end__"

builder.add_conditional_edges("agent", router)

# With path_map for visualization
builder.add_conditional_edges(
    "agent",
    router,
    path_map={"agent": "agent", "tools": "tools", "__end__": END}
)
Command 模式(动态路由 + 状态更新)
from langgraph.types import Command

def dynamic_node(state: State) -> Command[Literal["next", "__end__"]]:
    if state["should_continue"]:
        return Command(goto="next", update={"step": state["step"] + 1})
    return Command(goto=END)

# Must declare destinations for visualization
builder.add_node("dynamic", dynamic_node, destinations=["next", END])
Send 模式(扇出/Map-Reduce)
from langgraph.types import Send

def fan_out(state: State) -> list[Send]:
    """Route to multiple node instances with different inputs."""
    return [Send("worker", {"item": item}) for item in state["items"]]

builder.add_conditional_edges(START, fan_out)
builder.add_edge("worker", "aggregate")  # Workers converge

检查点

启用持久化
from langgraph.checkpoint.memory import InMemorySaver
from langgraph.checkpoint.sqlite import SqliteSaver  # Development
from langgraph.checkpoint.postgres import PostgresSaver  # Production

# In-memory (testing only)
graph = builder.compile(checkpointer=InMemorySaver())

# SQLite (development)
with SqliteSaver.from_conn_string("checkpoints.db") as checkpointer:
    graph = builder.compile(checkpointer=checkpointer)

# Thread-based invocation
config = {"configurable": {"thread_id": "user-123"}}
result = graph.invoke({"messages": [...]}, config)
状态管理
# Get current state
state = graph.get_state(config)

# Get state history
for state in graph.get_state_history(config):
    print(state.values, state.next)

# Update state manually
graph.update_state(config, {"key": "new_value"}, as_node="node_name")

人在回路中

使用 interrupt()
from langgraph.types import interrupt, Command

def review_node(state: State) -> dict:
    # Pause and surface value to client
    human_input = interrupt({"question": "Please review", "data": state["draft"]})
    return {"approved": human_input["approved"]}

# Resume with Command
graph.invoke(Command(resume={"approved": True}), config)
在节点之前/之后中断
graph = builder.compile(
    checkpointer=checkpointer,
    interrupt_before=["human_review"],  # Pause before node
    interrupt_after=["agent"],          # Pause after node
)

# Check pending interrupts
state = graph.get_state(config)
if state.next:  # Has pending nodes
    # Resume
    graph.invoke(None, config)

流式传输

# Stream modes: "values", "updates", "custom", "messages", "debug"

# Updates only (node outputs)
for chunk in graph.stream(input, stream_mode="updates"):
    print(chunk)  # {"node_name": {"key": "value"}}

# Full state after each step
for chunk in graph.stream(input, stream_mode="values"):
    print(chunk)

# Multiple modes
for mode, chunk in graph.stream(input, stream_mode=["updates", "messages"]):
    if mode == "messages":
        print("Token:", chunk)

# Custom streaming from within nodes
from langgraph.config import get_stream_writer

def my_node(state):
    writer = get_stream_writer()
    writer({"progress": 0.5})  # Custom event
    return {"result": "done"}

子图

# Define subgraph
sub_builder = StateGraph(SubState)
sub_builder.add_node("step", step_fn)
sub_builder.add_edge(START, "step")
subgraph = sub_builder.compile()

# Use as node in parent
parent_builder = StateGraph(ParentState)
parent_builder.add_node("subprocess", subgraph)
parent_builder.add_edge(START, "subprocess")

# Subgraph checkpointing
subgraph = sub_builder.compile(
    checkpointer=None,   # Inherit from parent (default)
    # checkpointer=True,   # Use persistent checkpointing
    # checkpointer=False,  # Disable checkpointing
)

重试与缓存

from langgraph.types import RetryPolicy, CachePolicy

retry = RetryPolicy(
    initial_interval=0.5,
    backoff_factor=2.0,
    max_attempts=3,
    retry_on=ValueError,  # Or callable: lambda e: isinstance(e, ValueError)
)

cache = CachePolicy(ttl=3600)  # Cache for 1 hour

builder.add_node("risky", risky_fn, retry_policy=retry, cache_policy=cache)

预构建组件

create_react_agent(在 v1.0 中移至 langchain.agents)
from langgraph.prebuilt import create_react_agent, ToolNode

# Simple agent
graph = create_react_agent(
    model="anthropic:claude-3-5-sonnet",
    tools=[my_tool],
    prompt="You are a helpful assistant",
    checkpointer=InMemorySaver(),
)

# Custom tool node
tool_node = ToolNode([tool1, tool2])
builder.add_node("tools", tool_node)

常见模式

Agent 循环
def should_continue(state) -> Literal["tools", "__end__"]:
    if state["messages"][-1].tool_calls:
        return "tools"
    return END

builder.add_node("agent", call_model)
builder.add_node("tools", ToolNode(tools))
builder.add_edge(START, "agent")
builder.add_conditional_edges("agent", should_continue)
builder.add_edge("tools", "agent")
并行执行
# Multiple nodes execute in parallel when they share the same trigger
builder.add_edge(START, "node_a")
builder.add_edge(START, "node_b")  # Runs parallel with node_a
builder.add_edge(["node_a", "node_b"], "join")  # Wait for both

有关多 Agent 系统、分层图和复杂工作流等高级模式,请参阅 PATTERNS.md。

评论

支持 Markdown