跳至内容

🦜🕸️LangGraph

Version Downloads Open Issues Docs

⚡ 将语言代理构建为图 ⚡

注意

正在寻找 JS 版本?点击 此处 (JS 文档)。

概述

LangGraph 是一个用于构建具有 LLM 的有状态、多参与者应用程序的库,用于创建代理和多代理工作流。与其他 LLM 框架相比,它提供了以下核心优势:循环、可控性和持久性。LangGraph 允许您定义包含循环的流程,这对于大多数代理架构至关重要,使其有别于基于 DAG 的解决方案。作为一个非常底层的框架,它提供了对应用程序流程和状态的细粒度控制,这对创建可靠的代理至关重要。此外,LangGraph 包含内置的持久性,能够实现高级的人机交互和内存功能。

LangGraph 受到 PregelApache Beam 的启发。公共接口借鉴了 NetworkX。LangGraph 由 LangChain Inc 构建,LangChain 的创建者,但可以在没有 LangChain 的情况下使用。

要了解有关 LangGraph 的更多信息,请查看我们第一个 LangChain Academy 课程,LangGraph 入门,可免费获得 此处

主要功能

  • 循环和分支:在您的应用程序中实现循环和条件。
  • 持久性:在图中的每个步骤后自动保存状态。随时暂停和恢复图执行以支持错误恢复、人机交互工作流、时间旅行等。
  • 人机交互:中断图执行以批准或编辑代理计划的下一个操作。
  • 流支持:在每个节点生成输出时流式传输输出(包括令牌流)。
  • 与 LangChain 集成:LangGraph 与 LangChainLangSmith 无缝集成(但不需要它们)。

安装

pip install -U langgraph

示例

LangGraph 的核心概念之一是状态。每个图执行都会创建一个状态,该状态在图中的节点执行时在节点之间传递,并且每个节点在其执行后使用其返回值更新此内部状态。图更新其内部状态的方式由所选图的类型或自定义函数定义。

让我们看一个可以使用搜索工具的简单代理示例。

pip install langchain-anthropic
export ANTHROPIC_API_KEY=sk-...

或者,我们可以设置 LangSmith 以获得一流的可观察性。

export LANGSMITH_TRACING=true
export LANGSMITH_API_KEY=lsv2_sk_...
from typing import Annotated, Literal, TypedDict

from langchain_core.messages import HumanMessage
from langchain_anthropic import ChatAnthropic
from langchain_core.tools import tool
from langgraph.checkpoint.memory import MemorySaver
from langgraph.graph import END, START, StateGraph, MessagesState
from langgraph.prebuilt import ToolNode


# Define the tools for the agent to use
@tool
def search(query: str):
    """Call to surf the web."""
    # This is a placeholder, but don't tell the LLM that...
    if "sf" in query.lower() or "san francisco" in query.lower():
        return "It's 60 degrees and foggy."
    return "It's 90 degrees and sunny."


tools = [search]

tool_node = ToolNode(tools)

model = ChatAnthropic(model="claude-3-5-sonnet-20240620", temperature=0).bind_tools(tools)

# Define the function that determines whether to continue or not
def should_continue(state: MessagesState) -> Literal["tools", END]:
    messages = state['messages']
    last_message = messages[-1]
    # If the LLM makes a tool call, then we route to the "tools" node
    if last_message.tool_calls:
        return "tools"
    # Otherwise, we stop (reply to the user)
    return END


# Define the function that calls the model
def call_model(state: MessagesState):
    messages = state['messages']
    response = model.invoke(messages)
    # We return a list, because this will get added to the existing list
    return {"messages": [response]}


# Define a new graph
workflow = StateGraph(MessagesState)

# Define the two nodes we will cycle between
workflow.add_node("agent", call_model)
workflow.add_node("tools", tool_node)

# Set the entrypoint as `agent`
# This means that this node is the first one called
workflow.add_edge(START, "agent")

# We now add a conditional edge
workflow.add_conditional_edges(
    # First, we define the start node. We use `agent`.
    # This means these are the edges taken after the `agent` node is called.
    "agent",
    # Next, we pass in the function that will determine which node is called next.
    should_continue,
)

# We now add a normal edge from `tools` to `agent`.
# This means that after `tools` is called, `agent` node is called next.
workflow.add_edge("tools", 'agent')

# Initialize memory to persist state between graph runs
checkpointer = MemorySaver()

# Finally, we compile it!
# This compiles it into a LangChain Runnable,
# meaning you can use it as you would any other runnable.
# Note that we're (optionally) passing the memory when compiling the graph
app = workflow.compile(checkpointer=checkpointer)

# Use the Runnable
final_state = app.invoke(
    {"messages": [HumanMessage(content="what is the weather in sf")]},
    config={"configurable": {"thread_id": 42}}
)
final_state["messages"][-1].content
"Based on the search results, I can tell you that the current weather in San Francisco is:\n\nTemperature: 60 degrees Fahrenheit\nConditions: Foggy\n\nSan Francisco is known for its microclimates and frequent fog, especially during the summer months. The temperature of 60°F (about 15.5°C) is quite typical for the city, which tends to have mild temperatures year-round. The fog, often referred to as "Karl the Fog" by locals, is a characteristic feature of San Francisco\'s weather, particularly in the mornings and evenings.\n\nIs there anything else you\'d like to know about the weather in San Francisco or any other location?"

现在,当我们传递相同的 "thread_id" 时,对话上下文通过保存的状态(即存储的消息列表)保留。

final_state = app.invoke(
    {"messages": [HumanMessage(content="what about ny")]},
    config={"configurable": {"thread_id": 42}}
)
final_state["messages"][-1].content
"Based on the search results, I can tell you that the current weather in New York City is:\n\nTemperature: 90 degrees Fahrenheit (approximately 32.2 degrees Celsius)\nConditions: Sunny\n\nThis weather is quite different from what we just saw in San Francisco. New York is experiencing much warmer temperatures right now. Here are a few points to note:\n\n1. The temperature of 90°F is quite hot, typical of summer weather in New York City.\n2. The sunny conditions suggest clear skies, which is great for outdoor activities but also means it might feel even hotter due to direct sunlight.\n3. This kind of weather in New York often comes with high humidity, which can make it feel even warmer than the actual temperature suggests.\n\nIt's interesting to see the stark contrast between San Francisco's mild, foggy weather and New York's hot, sunny conditions. This difference illustrates how varied weather can be across different parts of the United States, even on the same day.\n\nIs there anything else you'd like to know about the weather in New York or any other location?"

分步分解

  1. 初始化模型和工具。

    • 我们使用 ChatAnthropic 作为我们的 LLM。注意:我们需要确保模型知道它可以使用这些工具进行调用。我们可以通过将 LangChain 工具转换为 OpenAI 工具调用的格式来实现这一点,使用 .bind_tools() 方法。
    • 我们定义了我们想要使用的工具——在本例中是一个搜索工具。创建自己的工具非常容易——请参阅此处有关如何操作的文档 此处
  2. 使用状态初始化图。

    • 我们通过传递状态模式(在本例中为 MessagesState)来初始化图 (StateGraph)
    • MessagesState 是一个预构建的状态模式,它有一个属性——一个 LangChain Message 对象列表,以及将每个节点的更新合并到状态中的逻辑
  3. 定义图节点。

    我们需要两个主要的节点

    • agent 节点:负责决定采取哪些(如果有)操作。
    • tools 节点调用工具:如果代理决定采取操作,则此节点将执行该操作。
  4. 定义入口点和图边。

    首先,我们需要设置图执行的入口点——agent 节点。

    然后我们定义一条普通边和一条条件边。条件边意味着目标取决于图的状态内容 (MessageState)。在我们的例子中,目标直到代理 (LLM) 决定后才为人所知。

    • 条件边:调用代理后,我们应该:
      • a. 如果代理说要采取行动,则运行工具,或者
      • b. 如果代理没有要求运行工具,则完成(回复用户)
    • 普通边:调用工具后,图应始终返回到代理以决定下一步该做什么
  5. 编译图。

    • 当我们编译图时,我们将其转换为 LangChain 可运行对象,这会自动启用使用输入调用 .invoke().stream().batch()
    • 我们还可以选择传递检查点对象以在图运行之间持久化状态,并启用内存、人机交互工作流、时间旅行等。在我们的例子中,我们使用 MemorySaver——一个简单的内存中检查点

  6. 执行图。

    1. LangGraph 将输入消息添加到内部状态,然后将状态传递给入口节点 "agent"
    2. "agent" 节点执行,调用聊天模型。
    3. 聊天模型返回 AIMessage。LangGraph 将其添加到状态中。
    4. 图循环执行以下步骤,直到 AIMessage 上没有更多 tool_calls

      • 如果 AIMessagetool_calls,则 "tools" 节点执行
      • "agent" 节点再次执行并返回 AIMessage
    5. 执行进展到特殊的 END 值并输出最终状态。结果,我们得到一个包含所有聊天消息的列表作为输出。

文档

  • 教程:通过指导示例学习使用 LangGraph 进行构建。
  • 操作指南:在 LangGraph 中完成特定操作,从流式传输到添加内存和持久性,再到常见的设计模式(分支、子图等),如果您想复制和运行特定的代码片段,这些是您需要去的地方。
  • 概念指南:深入解释 LangGraph 背后的关键概念和原则,例如节点、边、状态等。
  • API 参考:查看重要的类和方法、如何使用图和检查点 API 的简单示例、更高级别的预构建组件等。
  • 云 (beta):一键将 LangGraph 应用程序部署到 LangGraph Cloud。

贡献

有关如何贡献的更多信息,请参阅 此处