Skip to content

🚀 LangGraph 快速入门

在本教程中,我们将构建一个支持的聊天机器人,在LangGraph中可以:

通过搜索网络回答常见问题
在调用之间保持对话状态
将复杂查询 转发给人工进行审核
使用自定义状态 来控制其行为
回溯并探索 替代对话路径

我们将从一个 基本的聊天机器人 开始,并逐步添加更复杂的功能,在此过程中介绍关键的LangGraph概念。让我们开始吧!🌟

设置

首先,安装所需的包并配置您的环境:

%%capture --no-stderr
%pip install -U langgraph langsmith langchain_anthropic

import getpass
import os


def _set_env(var: str):
    if not os.environ.get(var):
        os.environ[var] = getpass.getpass(f"{var}: ")


_set_env("ANTHROPIC_API_KEY")
ANTHROPIC_API_KEY:  ········

为LangGraph开发设置LangSmith

注册使用LangSmith,快速发现问题并提高您的LangGraph项目的性能。LangSmith允许您使用跟踪数据来调试、测试和监控基于LangGraph构建的LLM应用程序 — 阅读更多关于如何启动的信息,请点击这里

第1部分:构建一个基本的聊天机器人

我们将首先使用LangGraph创建一个简单的聊天机器人。这个聊天机器人将直接对用户消息做出回应。虽然简单,但它将说明使用LangGraph构建的核心概念。在本节结束时,您将构建一个基本的聊天机器人。

首先创建一个StateGraphStateGraph对象定义了我们聊天机器人的结构,作为一个“状态机”。我们将添加nodes来表示聊天机器人可以调用的llm和函数,并添加edges来指定机器人如何在这些函数之间转换。

from typing import Annotated

from typing_extensions import TypedDict

from langgraph.graph import StateGraph, START, END
from langgraph.graph.message import add_messages


class State(TypedDict):
    # Messages have the type "list". The `add_messages` function
    # 在注释中定义了该状态键应如何更新。
    # (在这种情况下,它将消息附加到列表中,而不是覆盖它们)
    messages: Annotated[list, add_messages]


graph_builder = StateGraph(State)
API Reference: StateGraph | START | END | add_messages

我们的图现在可以处理两个关键任务:

  1. 每个 node 可以接收当前 State 作为输入,并输出对状态的更新。
  2. messages 的更新将附加到现有列表中,而不是覆盖它,这得益于与 Annotated 语法一起使用的预构建 add_messages 函数。

概念

定义图的第一步是定义它的 StateState 包括图的架构和处理状态更新的 reducer 函数。在我们的示例中,State 是一个 TypedDict,其中有一个键:messagesadd_messages reducer 函数用于将新消息附加到列表中,而不是覆盖它。没有 reducer 注释的键将覆盖先前的值。请在 此指南 中了解更多有关状态、reducer 及相关概念的信息。


接下来,添加一个 "chatbot" 节点。节点表示工作单元。它们通常是常规的 python 函数。

from langchain_anthropic import ChatAnthropic

llm = ChatAnthropic(model="claude-3-5-sonnet-20240620")


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


# 第一个参数是唯一的节点名称。
# 第二个参数是将在每次调用时使用的函数或对象。
# 节点正在被使用。
graph_builder.add_node("chatbot", chatbot)
API Reference: ChatAnthropic

注意 chatbot 节点函数如何将当前的 State 作为输入,并返回一个包含更新后的 messages 列表的字典,键为 "messages"。这是所有 LangGraph 节点函数的基本模式。

我们 State 中的 add_messages 函数将 LLM 的响应消息追加到状态中已有的消息中。

接下来,添加一个 entry 点。这告诉我们的图 每次运行时应该从哪里开始工作

graph_builder.add_edge(START, "chatbot")

同样,设置一个 finish 点。这指示图 “每当运行这个节点时,你可以退出。”

graph_builder.add_edge("chatbot", END)

最后,我们希望能够运行我们的图。为此,调用图构建器上的 "compile()"。这会创建一个 "CompiledGraph",我们可以在我们的状态上调用它。

graph = graph_builder.compile()

您可以使用 get_graph 方法和 draw 方法之一(如 draw_asciidraw_png)来可视化图形。每个 draw 方法都需要额外的依赖项。

from IPython.display import Image, display

try:
    display(Image(graph.get_graph().draw_mermaid_png()))
except Exception:
    # 这需要一些额外的依赖,并且是可选的。
    pass

现在让我们运行聊天机器人!

提示: 您可以随时通过输入 "quit"、"exit" 或 "q" 来退出聊天循环。

def stream_graph_updates(user_input: str):
    for event in graph.stream({"messages": [("user", user_input)]}):
        for value in event.values():
            print("Assistant:", value["messages"][-1].content)


while True:
    try:
        user_input = input("User: ")
        if user_input.lower() in ["quit", "exit", "q"]:
            print("Goodbye!")
            break

        stream_graph_updates(user_input)
    except:
        # 如果 input() 不可用,则备选方案。
        user_input = "What do you know about LangGraph?"
        print("User: " + user_input)
        stream_graph_updates(user_input)
        break
Assistant: LangGraph is a library designed to help build stateful multi-agent applications using language models. It provides tools for creating workflows and state machines to coordinate multiple AI agents or language model interactions. LangGraph is built on top of LangChain, leveraging its components while adding graph-based coordination capabilities. It's particularly useful for developing more complex, stateful AI applications that go beyond simple query-response interactions.
Goodbye!
恭喜你! 你已经使用LangGraph构建了你的第一个聊天机器人。这个机器人可以通过接受用户输入并使用LLM生成响应来进行基本的对话。你可以通过提供的链接查看上述调用的LangSmith Trace

然而,你可能注意到机器人的知识仅限于其训练数据。在接下来的部分,我们将添加一个网络搜索工具,以扩展机器人的知识,使其更加强大。

以下是本节的完整代码供你参考:

完整代码

from typing import Annotated

from langchain_anthropic import ChatAnthropic
from typing_extensions import TypedDict

from langgraph.graph import StateGraph
from langgraph.graph.message import add_messages


class State(TypedDict):
    messages: Annotated[list, add_messages]


graph_builder = StateGraph(State)


llm = ChatAnthropic(model="claude-3-5-sonnet-20240620")


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


# 第一个参数是唯一的节点名称
# 第二个参数是每当使用该节点时将被调用的函数或对象。
graph_builder.add_node("chatbot", chatbot)
graph_builder.set_entry_point("chatbot")
graph_builder.set_finish_point("chatbot")
graph = graph_builder.compile()

第二部分:🛠️ 用工具增强聊天机器人

为了处理我们的聊天机器人无法“凭记忆”回答的查询,我们将集成一个网络搜索工具。我们的机器人可以使用这个工具找到相关信息并提供更好的响应。

要求

在我们开始之前,请确保您已安装必要的软件包并设置了 API 密钥:

首先,安装使用 Tavily 搜索引擎所需的依赖,并设置您的 TAVILY_API_KEY

%%capture --no-stderr
%pip install -U tavily-python langchain_community

_set_env("TAVILY_API_KEY")
TAVILY_API_KEY:  ········
请提供ipynb文件中的markdown内容,我将为您翻译成中文。

from langchain_community.tools.tavily_search import TavilySearchResults

tool = TavilySearchResults(max_results=2)
tools = [tool]
tool.invoke("What's a 'node' in LangGraph?")
[{'url': 'https://medium.com/@cplog/introduction-to-langgraph-a-beginners-guide-14f9be027141',
  'content': 'Nodes: Nodes are the building blocks of your LangGraph. Each node represents a function or a computation step. You define nodes to perform specific tasks, such as processing input, making ...'},
 {'url': 'https://saksheepatil05.medium.com/demystifying-langgraph-a-beginner-friendly-dive-into-langgraph-concepts-5ffe890ddac0',
  'content': 'Nodes (Tasks): Nodes are like the workstations on the assembly line. Each node performs a specific task on the product. In LangGraph, nodes are Python functions that take the current state, do some work, and return an updated state. Next, we define the nodes, each representing a task in our sandwich-making process.'}]
API Reference: TavilySearchResults

结果是我们的聊天机器人可以用来回答问题的页面摘要。

接下来,我们将开始定义我们的图形。以下内容与第一部分**完全相同**,除了我们在我们的LLM上添加了bind_tools。这让LLM知道如果它想使用我们的搜索引擎,应该使用正确的JSON格式。

from typing import Annotated

from langchain_anthropic import ChatAnthropic
from typing_extensions import TypedDict

from langgraph.graph import StateGraph, START, END
from langgraph.graph.message import add_messages


class State(TypedDict):
    messages: Annotated[list, add_messages]


graph_builder = StateGraph(State)


llm = ChatAnthropic(model="claude-3-5-sonnet-20240620")
# 修改:告诉语言模型可以调用哪些工具。
llm_with_tools = llm.bind_tools(tools)


def chatbot(state: State):
    return {"messages": [llm_with_tools.invoke(state["messages"])]}


graph_builder.add_node("chatbot", chatbot)
API Reference: ChatAnthropic | StateGraph | START | END | add_messages

接下来,我们需要创建一个函数,以便在调用工具时实际运行这些工具。我们通过将工具添加到一个新的节点来实现这一点。

下面,我们实现了一个 BasicToolNode,它检查状态中最近的消息,并在消息包含 tool_calls 时调用工具。它依赖于LLM的 tool_calling 支持,该支持在Anthropic、OpenAI、Google Gemini以及其他多个LLM提供商中可用。

稍后我们将用LangGraph的预构建 ToolNode 来替代它,以加快进程,但首先自己构建它是很有启发性的。

import json

from langchain_core.messages import ToolMessage


class BasicToolNode:
    """一个运行上一个AI消息中请求的工具的节点。"""

    def __init__(self, tools: list) -> None:
        self.tools_by_name = {tool.name: tool for tool in tools}

    def __call__(self, inputs: dict):
        if messages := inputs.get("messages", []):
            message = messages[-1]
        else:
            raise ValueError("No message found in input")
        outputs = []
        for tool_call in message.tool_calls:
            tool_result = self.tools_by_name[tool_call["name"]].invoke(
                tool_call["args"]
            )
            outputs.append(
                ToolMessage(
                    content=json.dumps(tool_result),
                    name=tool_call["name"],
                    tool_call_id=tool_call["id"],
                )
            )
        return {"messages": outputs}


tool_node = BasicToolNode(tools=[tool])
graph_builder.add_node("tools", tool_node)
API Reference: ToolMessage

添加了工具节点后,我们可以定义 conditional_edges

回想一下,**边**负责将控制流从一个节点路由到下一个节点。**条件边**通常包含“if”语句,以根据当前图的状态路由到不同的节点。这些函数接收当前图的 state,并返回一个字符串或字符串列表,指示下一个要调用的节点。

下面定义一个名为 route_tools 的路由函数,该函数检查聊天机器人的输出中的工具调用。通过调用 add_conditional_edges 将此函数提供给图,以告知图在 chatbot 节点完成后检查此函数以查看下一步该去哪里。

如果存在工具调用,则条件将路由到 tools,否则路由到 END

稍后,我们将用预构建的 tools_condition 来替代这个函数,以使其更加简洁,但我们首先自己实现它可以使事情更加清晰。

from typing import Literal


def route_tools(
    state: State,
):
    """
    在conditional_edge中使用以便在最后一条消息有工具调用时路由到ToolNode。否则,路由到结束。
    """
    if isinstance(state, list):
        ai_message = state[-1]
    elif messages := state.get("messages", []):
        ai_message = messages[-1]
    else:
        raise ValueError(f"No messages found in input state to tool_edge: {state}")
    if hasattr(ai_message, "tool_calls") and len(ai_message.tool_calls) > 0:
        return "tools"
    return END


# The `tools_condition` function returns "tools" if the chatbot asks to use a tool, and "END" if
# 直接响应是可以的。这个条件路由定义了主要的代理循环。
graph_builder.add_conditional_edges(
    "chatbot",
    route_tools,
    # 以下字典允许你告诉图形将条件的输出解释为特定节点。
    # 它默认为恒等函数,但如果你
    # want to use a node named something else apart from "tools",
    # 你可以将字典的值更新为其他内容。
    # e.g., "tools": "my_tools"
    {"tools": "tools", END: END},
)
# 每当调用一个工具时,我们会返回到聊天机器人以决定下一步。
graph_builder.add_edge("tools", "chatbot")
graph_builder.add_edge(START, "chatbot")
graph = graph_builder.compile()

注意 条件边从单个节点开始。这告诉图形“每当 'chatbot' 节点运行时,如果它调用工具,则转到 'tools',或者如果它直接响应,则结束循环。”

像预构建的 tools_condition 一样,我们的函数在没有工具调用时返回 END 字符串。当图形过渡到 END 时,它没有更多的任务要完成,并停止执行。由于条件可以返回 END,这次我们不需要显式设置 finish_point。我们的图形已经有了一种完成方式!

让我们可视化我们构建的图形。以下函数有一些额外的依赖项需要运行,但对于本教程而言,这些并不重要。

from IPython.display import Image, display

try:
    display(Image(graph.get_graph().draw_mermaid_png()))
except Exception:
    # 这需要一些额外的依赖项,并且是可选的。
    pass

现在我们可以向机器人提出超出其训练数据的问题。

while True:
    try:
        user_input = input("User: ")
        if user_input.lower() in ["quit", "exit", "q"]:
            print("Goodbye!")
            break

        stream_graph_updates(user_input)
    except:
        # 如果 input() 不可用,则采用后备方案。
        user_input = "What do you know about LangGraph?"
        print("User: " + user_input)
        stream_graph_updates(user_input)
        break
Assistant: [{'text': "To provide you with accurate and up-to-date information about LangGraph, I'll need to search for the latest details. Let me do that for you.", 'type': 'text'}, {'id': 'toolu_01Q588CszHaSvvP2MxRq9zRD', 'input': {'query': 'LangGraph AI tool information'}, 'name': 'tavily_search_results_json', 'type': 'tool_use'}]
Assistant: [{"url": "https://www.langchain.com/langgraph", "content": "LangGraph sets the foundation for how we can build and scale AI workloads \u2014 from conversational agents, complex task automation, to custom LLM-backed experiences that 'just work'. The next chapter in building complex production-ready features with LLMs is agentic, and with LangGraph and LangSmith, LangChain delivers an out-of-the-box solution ..."}, {"url": "https://github.com/langchain-ai/langgraph", "content": "Overview. LangGraph is a library for building stateful, multi-actor applications with LLMs, used to create agent and multi-agent workflows. Compared to other LLM frameworks, it offers these core benefits: cycles, controllability, and persistence. LangGraph allows you to define flows that involve cycles, essential for most agentic architectures ..."}]
Assistant: Based on the search results, I can provide you with information about LangGraph:

1. Purpose:
   LangGraph is a library designed for building stateful, multi-actor applications with Large Language Models (LLMs). It's particularly useful for creating agent and multi-agent workflows.

2. Developer:
   LangGraph is developed by LangChain, a company known for its tools and frameworks in the AI and LLM space.

3. Key Features:
   - Cycles: LangGraph allows the definition of flows that involve cycles, which is essential for most agentic architectures.
   - Controllability: It offers enhanced control over the application flow.
   - Persistence: The library provides ways to maintain state and persistence in LLM-based applications.

4. Use Cases:
   LangGraph can be used for various applications, including:
   - Conversational agents
   - Complex task automation
   - Custom LLM-backed experiences

5. Integration:
   LangGraph works in conjunction with LangSmith, another tool by LangChain, to provide an out-of-the-box solution for building complex, production-ready features with LLMs.

6. Significance:
   LangGraph is described as setting the foundation for building and scaling AI workloads. It's positioned as a key tool in the next chapter of LLM-based application development, particularly in the realm of agentic AI.

7. Availability:
   LangGraph is open-source and available on GitHub, which suggests that developers can access and contribute to its codebase.

8. Comparison to Other Frameworks:
   LangGraph is noted to offer unique benefits compared to other LLM frameworks, particularly in its ability to handle cycles, provide controllability, and maintain persistence.

LangGraph appears to be a significant tool in the evolving landscape of LLM-based application development, offering developers new ways to create more complex, stateful, and interactive AI systems.
Goodbye!
恭喜! 你已经在 langgraph 中创建了一个可以使用搜索引擎检索更新信息的对话代理。现在它可以处理更广泛的用户查询。要查看你代理所采取的所有步骤,请查看这个 LangSmith 追踪

我们的聊天机器人仍然不能自我记忆过去的互动,这限制了它进行连贯的多轮对话的能力。在接下来的部分中,我们将添加 记忆 来解决这个问题。

我们在这一部分创建的图的完整代码如下,替换了我们之前的 BasicToolNode 为预构建的 ToolNode,并将我们的 route_tools 条件替换为预构建的 tools_condition

完整代码

from typing import Annotated

from langchain_anthropic import ChatAnthropic
from langchain_community.tools.tavily_search import TavilySearchResults
from langchain_core.messages import BaseMessage
from typing_extensions import TypedDict

from langgraph.graph import StateGraph
from langgraph.graph.message import add_messages
from langgraph.prebuilt import ToolNode, tools_condition


class State(TypedDict):
    messages: Annotated[list, add_messages]


graph_builder = StateGraph(State)


tool = TavilySearchResults(max_results=2)
tools = [tool]
llm = ChatAnthropic(model="claude-3-5-sonnet-20240620")
llm_with_tools = llm.bind_tools(tools)


def chatbot(state: State):
    return {"messages": [llm_with_tools.invoke(state["messages"])]}


graph_builder.add_node("chatbot", chatbot)

tool_node = ToolNode(tools=[tool])
graph_builder.add_node("tools", tool_node)

graph_builder.add_conditional_edges(
    "chatbot",
    tools_condition,
)
# 每当调用工具时,我们返回到聊天机器人以决定下一步
graph_builder.add_edge("tools", "chatbot")
graph_builder.set_entry_point("chatbot")
graph = graph_builder.compile()

第三部分:为聊天机器人添加记忆

我们的聊天机器人现在可以使用工具回答用户的问题,但它无法记住先前互动的上下文。这限制了它进行连贯的多轮对话的能力。

LangGraph通过**持久检查点**解决了这个问题。如果在编译图时提供checkpointer,并在调用图时提供thread_id,LangGraph将在每一步之后自动保存状态。当再次使用相同的thread_id调用图时,图会加载其保存的状态,使聊天机器人能够从中断的地方继续对话。

我们稍后会看到,**检查点**的功能比简单的聊天记忆强大得多——它可以让您在任何时候保存和恢复复杂状态以进行错误恢复、人机协作工作流、时间旅行交互等等。但在我们走得太远之前,先添加检查点以启用多轮对话。

要开始,请创建一个MemorySaver检查点。

from langgraph.checkpoint.memory import MemorySaver

memory = MemorySaver()
API Reference: MemorySaver

注意 我们使用的是内存检查点。这对于我们的教程很方便(它将所有内容保存在内存中)。在生产应用中,您可能会将其更改为使用 SqliteSaverPostgresSaver 并连接到您自己的数据库。

接下来定义图形。现在您已经构建了自己的 BasicToolNode,我们将用 LangGraph 的预构建 ToolNodetools_condition 替换它,因为这些可以执行一些不错的功能,比如并行 API 执行。除此之外,以下内容全部来自第2部分。

from typing import Annotated

from langchain_anthropic import ChatAnthropic
from langchain_community.tools.tavily_search import TavilySearchResults
from langchain_core.messages import BaseMessage
from typing_extensions import TypedDict

from langgraph.graph import StateGraph, START, END
from langgraph.graph.message import add_messages
from langgraph.prebuilt import ToolNode, tools_condition


class State(TypedDict):
    messages: Annotated[list, add_messages]


graph_builder = StateGraph(State)


tool = TavilySearchResults(max_results=2)
tools = [tool]
llm = ChatAnthropic(model="claude-3-5-sonnet-20240620")
llm_with_tools = llm.bind_tools(tools)


def chatbot(state: State):
    return {"messages": [llm_with_tools.invoke(state["messages"])]}


graph_builder.add_node("chatbot", chatbot)

tool_node = ToolNode(tools=[tool])
graph_builder.add_node("tools", tool_node)

graph_builder.add_conditional_edges(
    "chatbot",
    tools_condition,
)
# 每当调用工具时,我们会返回到聊天机器人,以决定下一步。
graph_builder.add_edge("tools", "chatbot")
graph_builder.add_edge(START, "chatbot")

最后,使用提供的检查点器编译图表。

graph = graph_builder.compile(checkpointer=memory)

注意到自第2部分以来,图的连接性没有改变。我们所做的只是在图形遍历每个节点时对State进行检查点保存。

from IPython.display import Image, display

try:
    display(Image(graph.get_graph().draw_mermaid_png()))
except Exception:
    # 这需要一些额外的依赖项,并且是可选的。
    pass

现在你可以与您的机器人进行交互!首先,选择一个线程作为此次对话的关键。

config = {"configurable": {"thread_id": "1"}}

Next, call your chat bot.

user_input = "Hi there! My name is Will."

# config 是 stream() 或 invoke() 的 **第二个位置参数**!
events = graph.stream(
    {"messages": [("user", user_input)]}, config, stream_mode="values"
)
for event in events:
    event["messages"][-1].pretty_print()
================================ Human Message =================================

Hi there! My name is Will.
================================== Ai Message ==================================

Hello Will! It's nice to meet you. How can I assist you today? Is there anything specific you'd like to know or discuss?
注意: 配置作为调用我们图形时的 第二个位置参数 提供。重要的是,它_不_嵌套在图形输入中({'messages': []})。

让我们来问一个后续问题:看看它是否记得你的名字。

user_input = "Remember my name?"

# config 是 stream() 或 invoke() 的 **第二个位置参数**!
events = graph.stream(
    {"messages": [("user", user_input)]}, config, stream_mode="values"
)
for event in events:
    event["messages"][-1].pretty_print()
================================ Human Message =================================

Remember my name?
================================== Ai Message ==================================

Of course, I remember your name, Will. I always try to pay attention to important details that users share with me. Is there anything else you'd like to talk about or any questions you have? I'm here to help with a wide range of topics or tasks.
注意,我们没有使用外部列表来存储内存:所有的操作都由检查点处理器完成!你可以在这个 LangSmith 跟踪 中查看完整的执行过程,了解发生了什么。

不相信我?试试使用不同的配置。

# The only difference is we change the `thread_id` here to "2" instead of "1"
events = graph.stream(
    {"messages": [("user", user_input)]},
    {"configurable": {"thread_id": "2"}},
    stream_mode="values",
)
for event in events:
    event["messages"][-1].pretty_print()
================================ Human Message =================================

Remember my name?
================================== Ai Message ==================================

I apologize, but I don't have any previous context or memory of your name. As an AI assistant, I don't retain information from past conversations. Each interaction starts fresh. Could you please tell me your name so I can address you properly in this conversation?
注意,我们所做的**唯一**更改是修改了配置中的 thread_id。请查看此调用的 LangSmith 跟踪 以作比较。

到目前为止,我们在两个不同的线程中创建了几个检查点。那么,检查点中包含了什么?要随时检查给定配置的图的 state,请调用 get_state(config)

snapshot = graph.get_state(config)
snapshot
StateSnapshot(values={'messages': [HumanMessage(content='Hi there! My name is Will.', additional_kwargs={}, response_metadata={}, id='8c1ca919-c553-4ebf-95d4-b59a2d61e078'), AIMessage(content="Hello Will! It's nice to meet you. How can I assist you today? Is there anything specific you'd like to know or discuss?", additional_kwargs={}, response_metadata={'id': 'msg_01WTQebPhNwmMrmmWojJ9KXJ', 'model': 'claude-3-5-sonnet-20240620', 'stop_reason': 'end_turn', 'stop_sequence': None, 'usage': {'input_tokens': 405, 'output_tokens': 32}}, id='run-58587b77-8c82-41e6-8a90-d62c444a261d-0', usage_metadata={'input_tokens': 405, 'output_tokens': 32, 'total_tokens': 437}), HumanMessage(content='Remember my name?', additional_kwargs={}, response_metadata={}, id='daba7df6-ad75-4d6b-8057-745881cea1ca'), AIMessage(content="Of course, I remember your name, Will. I always try to pay attention to important details that users share with me. Is there anything else you'd like to talk about or any questions you have? I'm here to help with a wide range of topics or tasks.", additional_kwargs={}, response_metadata={'id': 'msg_01E41KitY74HpENRgXx94vag', 'model': 'claude-3-5-sonnet-20240620', 'stop_reason': 'end_turn', 'stop_sequence': None, 'usage': {'input_tokens': 444, 'output_tokens': 58}}, id='run-ffeaae5c-4d2d-4ddb-bd59-5d5cbf2a5af8-0', usage_metadata={'input_tokens': 444, 'output_tokens': 58, 'total_tokens': 502})]}, next=(), config={'configurable': {'thread_id': '1', 'checkpoint_ns': '', 'checkpoint_id': '1ef7d06e-93e0-6acc-8004-f2ac846575d2'}}, metadata={'source': 'loop', 'writes': {'chatbot': {'messages': [AIMessage(content="Of course, I remember your name, Will. I always try to pay attention to important details that users share with me. Is there anything else you'd like to talk about or any questions you have? I'm here to help with a wide range of topics or tasks.", additional_kwargs={}, response_metadata={'id': 'msg_01E41KitY74HpENRgXx94vag', 'model': 'claude-3-5-sonnet-20240620', 'stop_reason': 'end_turn', 'stop_sequence': None, 'usage': {'input_tokens': 444, 'output_tokens': 58}}, id='run-ffeaae5c-4d2d-4ddb-bd59-5d5cbf2a5af8-0', usage_metadata={'input_tokens': 444, 'output_tokens': 58, 'total_tokens': 502})]}}, 'step': 4, 'parents': {}}, created_at='2024-09-27T19:30:10.820758+00:00', parent_config={'configurable': {'thread_id': '1', 'checkpoint_ns': '', 'checkpoint_id': '1ef7d06e-859f-6206-8003-e1bd3c264b8f'}}, tasks=())
snapshot.next  # (由于图在这一轮结束,`next` 是空的。如果您从图的调用中获取一个状态,next 会告诉下一个将要执行的节点。)
()

上面的快照包含了当前状态值、相应的配置以及要处理的 next 节点。在我们的例子中,图已经达到了 END 状态,因此 next 是空的。

恭喜! 由于 LangGraph 的检查点系统,您的聊天机器人现在可以在会话之间维持对话状态。这为更自然、上下文相关的交互开辟了令人兴奋的可能性。LangGraph 的检查点甚至可以处理 任意复杂的图状态,这比简单的聊天记忆更具表现力和强大。

在接下来的部分,我们将为我们的机器人引入人工监督,以处理其可能在继续之前需要指导或验证的情况。

请查看下面的代码片段以查看我们本节的图。

完整代码

from typing import Annotated

from langchain_anthropic import ChatAnthropic
from langchain_community.tools.tavily_search import TavilySearchResults
from langchain_core.messages import BaseMessage
from typing_extensions import TypedDict

from langgraph.checkpoint.memory import MemorySaver
from langgraph.graph import StateGraph
from langgraph.graph.message import add_messages
from langgraph.prebuilt import ToolNode


class State(TypedDict):
    messages: Annotated[list, add_messages]


graph_builder = StateGraph(State)


tool = TavilySearchResults(max_results=2)
tools = [tool]
llm = ChatAnthropic(model="claude-3-5-sonnet-20240620")
llm_with_tools = llm.bind_tools(tools)


def chatbot(state: State):
    return {"messages": [llm_with_tools.invoke(state["messages"])]}


graph_builder.add_node("chatbot", chatbot)

tool_node = ToolNode(tools=[tool])
graph_builder.add_node("tools", tool_node)

graph_builder.add_conditional_edges(
    "chatbot",
    tools_condition,
)
graph_builder.add_edge("tools", "chatbot")
graph_builder.set_entry_point("chatbot")
graph = graph_builder.compile(checkpointer=memory)

第4部分:人机协作

代理可能不可靠,可能需要人类的输入才能成功完成任务。同样,对于某些操作,在执行之前可能希望要求人类批准,以确保一切按预期进行。

LangGraph以多种方式支持“人机协作”工作流程。在这一部分中,我们将使用LangGraph的interrupt_before功能始终中断工具节点。

首先,从我们现有的代码开始。以下内容复制自第3部分。

from typing import Annotated

from langchain_anthropic import ChatAnthropic
from langchain_community.tools.tavily_search import TavilySearchResults
from typing_extensions import TypedDict

from langgraph.checkpoint.memory import MemorySaver
from langgraph.graph import StateGraph, START
from langgraph.graph.message import add_messages
from langgraph.prebuilt import ToolNode, tools_condition

memory = MemorySaver()


class State(TypedDict):
    messages: Annotated[list, add_messages]


graph_builder = StateGraph(State)


tool = TavilySearchResults(max_results=2)
tools = [tool]
llm = ChatAnthropic(model="claude-3-5-sonnet-20240620")
llm_with_tools = llm.bind_tools(tools)


def chatbot(state: State):
    return {"messages": [llm_with_tools.invoke(state["messages"])]}


graph_builder.add_node("chatbot", chatbot)

tool_node = ToolNode(tools=[tool])
graph_builder.add_node("tools", tool_node)

graph_builder.add_conditional_edges(
    "chatbot",
    tools_condition,
)
graph_builder.add_edge("tools", "chatbot")
graph_builder.add_edge(START, "chatbot")

现在编译图形,指定在 tools 节点之前 interrupt_before

graph = graph_builder.compile(
    checkpointer=memory,
    # 这很新颖!
    interrupt_before=["tools"],
    # 注意:如果需要,也可以在工具之后中断。
    # interrupt_after=["tools"]
)

user_input = "I'm learning LangGraph. Could you do some research on it for me?"
config = {"configurable": {"thread_id": "1"}}
# 配置是 stream() 或 invoke() 的 **第二个位置参数**!
events = graph.stream(
    {"messages": [("user", user_input)]}, config, stream_mode="values"
)
for event in events:
    if "messages" in event:
        event["messages"][-1].pretty_print()
================================ Human Message =================================

I'm learning LangGraph. Could you do some research on it for me?
================================== Ai Message ==================================

[{'text': "Certainly! I'd be happy to research LangGraph for you. To get the most up-to-date and comprehensive information, I'll use the Tavily search engine to look this up. Let me do that for you now.", 'type': 'text'}, {'id': 'toolu_01R4ZFcb5hohpiVZwr88Bxhc', 'input': {'query': 'LangGraph framework for building language model applications'}, 'name': 'tavily_search_results_json', 'type': 'tool_use'}]
Tool Calls:
  tavily_search_results_json (toolu_01R4ZFcb5hohpiVZwr88Bxhc)
 Call ID: toolu_01R4ZFcb5hohpiVZwr88Bxhc
  Args:
    query: LangGraph framework for building language model applications
让我们检查图状态以确认它成功了。

snapshot = graph.get_state(config)
snapshot.next
('tools',)

注意,与上次不同的是,“下一个”节点被设置为 'tools'。我们在这里进行了中断!让我们检查一下工具调用。

existing_message = snapshot.values["messages"][-1]
existing_message.tool_calls
[{'name': 'tavily_search_results_json',
  'args': {'query': 'LangGraph framework for building language model applications'},
  'id': 'toolu_01R4ZFcb5hohpiVZwr88Bxhc',
  'type': 'tool_call'}]

这个查询看起来很合理。这儿没有需要过滤的内容。人类能做的最简单的事情就是让图形继续执行。接下来,我们就这样做。

接下来,继续图形!传入 None 将只是让图形在原地继续,而不往状态中添加任何新内容。

# `None`将不会向当前状态添加任何新内容,使其继续运行,就像从未被中断过一样。
events = graph.stream(None, config, stream_mode="values")
for event in events:
    if "messages" in event:
        event["messages"][-1].pretty_print()
================================== Ai Message ==================================

[{'text': "Certainly! I'd be happy to research LangGraph for you. To get the most up-to-date and comprehensive information, I'll use the Tavily search engine to look this up. Let me do that for you now.", 'type': 'text'}, {'id': 'toolu_01R4ZFcb5hohpiVZwr88Bxhc', 'input': {'query': 'LangGraph framework for building language model applications'}, 'name': 'tavily_search_results_json', 'type': 'tool_use'}]
Tool Calls:
  tavily_search_results_json (toolu_01R4ZFcb5hohpiVZwr88Bxhc)
 Call ID: toolu_01R4ZFcb5hohpiVZwr88Bxhc
  Args:
    query: LangGraph framework for building language model applications
================================= Tool Message =================================
Name: tavily_search_results_json

[{"url": "https://towardsdatascience.com/from-basics-to-advanced-exploring-langgraph-e8c1cf4db787", "content": "LangChain is one of the leading frameworks for building applications powered by Lardge Language Models. With the LangChain Expression Language (LCEL), defining and executing step-by-step action sequences — also known as chains — becomes much simpler. In more technical terms, LangChain allows us to create DAGs (directed acyclic graphs). As LLM applications, particularly LLM agents, have ..."}, {"url": "https://github.com/langchain-ai/langgraph", "content": "Overview. LangGraph is a library for building stateful, multi-actor applications with LLMs, used to create agent and multi-agent workflows. Compared to other LLM frameworks, it offers these core benefits: cycles, controllability, and persistence. LangGraph allows you to define flows that involve cycles, essential for most agentic architectures ..."}]
================================== Ai Message ==================================

Thank you for your patience. I've found some valuable information about LangGraph for you. Let me summarize the key points:

1. LangGraph is a library for building stateful, multi-actor applications with Large Language Models (LLMs).

2. It is particularly useful for creating agent and multi-agent workflows.

3. LangGraph is built on top of LangChain, which is one of the leading frameworks for building LLM-powered applications.

4. Key benefits of LangGraph compared to other LLM frameworks include:
   a) Cycles: It allows you to define flows that involve cycles, which is essential for most agent architectures.
   b) Controllability: Offers more control over the application flow.
   c) Persistence: Provides ways to maintain state across interactions.

5. LangGraph works well with the LangChain Expression Language (LCEL), which simplifies the process of defining and executing step-by-step action sequences (chains).

6. In technical terms, LangGraph enables the creation of Directed Acyclic Graphs (DAGs) for LLM applications.

7. It's particularly useful for building more complex LLM agents and multi-agent systems.

LangGraph seems to be an advanced tool that builds upon LangChain to provide more sophisticated capabilities for creating stateful and multi-actor LLM applications. It's especially valuable if you're looking to create complex agent systems or applications that require maintaining state across interactions.

Is there any specific aspect of LangGraph you'd like to know more about? I'd be happy to dive deeper into any particular area of interest.
查看此调用的 LangSmith 跟踪 以了解上述调用中执行的具体工作。请注意,状态在第一步加载,以便您的聊天机器人可以从上次中断的地方继续。

恭喜! 您已使用 interrupt 为您的聊天机器人添加了人工参与的执行,这使得在需要时可以进行人工监督和干预。这为您可以与 AI 系统创建的潜在用户界面打开了可能性。由于我们已经添加了 checkpointer,图形可以 无限期 暂停,并在任何时候恢复,就好像什么都没发生过一样。

接下来,我们将探讨如何使用自定义状态更新进一步定制机器人的行为。

以下是您在本节中使用的代码的副本。此代码与之前部分的唯一区别是添加了 interrupt_before 参数。

完整代码

from typing import Annotated

from langchain_anthropic import ChatAnthropic
from langchain_community.tools.tavily_search import TavilySearchResults
from langchain_core.messages import BaseMessage
from typing_extensions import TypedDict

from langgraph.checkpoint.memory import MemorySaver
from langgraph.graph import StateGraph
from langgraph.graph.message import add_messages
from langgraph.prebuilt import ToolNode, tools_condition


class State(TypedDict):
    messages: Annotated[list, add_messages]


graph_builder = StateGraph(State)


tool = TavilySearchResults(max_results=2)
tools = [tool]
llm = ChatAnthropic(model="claude-3-5-sonnet-20240620")
llm_with_tools = llm.bind_tools(tools)


def chatbot(state: State):
    return {"messages": [llm_with_tools.invoke(state["messages"])]}


graph_builder.add_node("chatbot", chatbot)

tool_node = ToolNode(tools=[tool])
graph_builder.add_node("tools", tool_node)

graph_builder.add_conditional_edges(
    "chatbot",
    tools_condition,
)
graph_builder.add_edge("tools", "chatbot")
graph_builder.set_entry_point("chatbot")

memory = MemorySaver()
graph = graph_builder.compile(
    checkpointer=memory,
    # 这是新的!
    interrupt_before=["tools"],
    # 注意:如果需要,也可以在动作 __之后__ 中断。
    # interrupt_after=["tools"]
)

第五部分:手动更新状态

在上一节中,我们展示了如何中断一个图,以便人类可以检查其操作。这样人类可以“读”出状态,但如果他们想要改变代理的行为,就需要“写”入权限。

幸运的是,LangGraph 允许您**手动更新状态**!更新状态可以通过修改代理的动作(甚至修改过去)来控制代理的轨迹。当您想要纠正代理的错误、探索替代路径或引导代理朝向特定目标时,这种能力尤其有用。

我们将在下面展示如何更新一个检查点状态。和之前一样,首先定义您的图。我们将重复使用之前的图。

from typing import Annotated

from langchain_anthropic import ChatAnthropic
from langchain_community.tools.tavily_search import TavilySearchResults
from typing_extensions import TypedDict

from langgraph.checkpoint.memory import MemorySaver
from langgraph.graph import StateGraph, START
from langgraph.graph.message import add_messages
from langgraph.prebuilt import ToolNode, tools_condition


class State(TypedDict):
    messages: Annotated[list, add_messages]


graph_builder = StateGraph(State)


tool = TavilySearchResults(max_results=2)
tools = [tool]
llm = ChatAnthropic(model="claude-3-5-sonnet-20240620")
llm_with_tools = llm.bind_tools(tools)


def chatbot(state: State):
    return {"messages": [llm_with_tools.invoke(state["messages"])]}


graph_builder.add_node("chatbot", chatbot)

tool_node = ToolNode(tools=[tool])
graph_builder.add_node("tools", tool_node)

graph_builder.add_conditional_edges(
    "chatbot",
    tools_condition,
)
graph_builder.add_edge("tools", "chatbot")
graph_builder.add_edge(START, "chatbot")
memory = MemorySaver()
graph = graph_builder.compile(
    checkpointer=memory,
    # 这真是新鲜事!
    interrupt_before=["tools"],
    # 注意:如果需要,也可以在动作**之后**中断。
    # interrupt_after=["tools"]
)

user_input = "I'm learning LangGraph. Could you do some research on it for me?"
config = {"configurable": {"thread_id": "1"}}
# 配置是 stream() 或 invoke() 的 **第二个位置参数**!
events = graph.stream({"messages": [("user", user_input)]}, config)
for event in events:
    if "messages" in event:
        event["messages"][-1].pretty_print()

snapshot = graph.get_state(config)
existing_message = snapshot.values["messages"][-1]
existing_message.pretty_print()
================================== Ai Message ==================================

[{'text': "Certainly! I'd be happy to research LangGraph for you. To get the most up-to-date and comprehensive information, I'll use the Tavily search engine to look this up. Let me do that for you now.", 'type': 'text'}, {'id': 'toolu_018YcbFR37CG8RRXnavH5fxZ', 'input': {'query': 'LangGraph: what is it, how is it used in AI development'}, 'name': 'tavily_search_results_json', 'type': 'tool_use'}]
Tool Calls:
  tavily_search_results_json (toolu_018YcbFR37CG8RRXnavH5fxZ)
 Call ID: toolu_018YcbFR37CG8RRXnavH5fxZ
  Args:
    query: LangGraph: what is it, how is it used in AI development
到目前为止,所有内容都是之前部分的_完全重复_。LLM刚刚请求使用搜索引擎工具,我们的图表被中断了。如果我们继续按照之前的方式进行,工具将被调用来搜索网页。

但如果用户想要插入呢?如果我们觉得聊天机器人不需要使用这个工具怎么办?

让我们直接提供正确的回应!

from langchain_core.messages import AIMessage, ToolMessage

answer = (
    "LangGraph is a library for building stateful, multi-actor applications with LLMs."
)
new_messages = [
    # The LLM API expects some ToolMessage to match its tool call. We'll satisfy that here.
    ToolMessage(content=answer, tool_call_id=existing_message.tool_calls[0]["id"]),
    # And then directly "put words in the LLM's mouth" by populating its response.
    AIMessage(content=answer),
]

new_messages[-1].pretty_print()
graph.update_state(
    # Which state to update
    config,
    # The updated values to provide. The messages in our `State` are "append-only", meaning this will be appended
    # to the existing state. We will review how to update existing messages in the next section!
    {"messages": new_messages},
)

print("\n\nLast 2 messages;")
print(graph.get_state(config).values["messages"][-2:])
================================== Ai Message ==================================

LangGraph is a library for building stateful, multi-actor applications with LLMs.


Last 2 messages;
[ToolMessage(content='LangGraph is a library for building stateful, multi-actor applications with LLMs.', id='675f7618-367f-44b7-b80e-2834afb02ac5', tool_call_id='toolu_018YcbFR37CG8RRXnavH5fxZ'), AIMessage(content='LangGraph is a library for building stateful, multi-actor applications with LLMs.', additional_kwargs={}, response_metadata={}, id='35fd5682-0c2a-4200-b192-71c59ac6d412')]

API Reference: AIMessage | ToolMessage

现在图已经完整,因为我们提供了最终的响应消息!由于状态更新模拟图的一个步骤,它们甚至生成相应的踪迹。请检查上述 update_state 调用的 LangSmith 追踪,看看发生了什么。

注意,我们新的消息是 附加 到已经在状态中的消息上。还记得我们是如何定义 State 类型的吗?

class State(TypedDict):
    messages: Annotated[list, add_messages]

我们用预构建的 add_messages 函数为 messages 添加了注释。这指示图始终将值附加到现有列表中,而不是直接覆盖列表。同样的逻辑在这里适用,因此我们传递给 update_state 的消息以相同的方式附加了!

update_state 函数的操作就像是您图中的一个节点!默认情况下,更新操作使用最后执行的节点,但您可以在下面手动指定它。让我们添加一个更新,并告诉图将其视为来自“聊天机器人”的数据。

graph.update_state(
    config,
    {"messages": [AIMessage(content="I'm an AI expert!")]},
    # 这个节点将作为哪个功能来运行。它将自动继续。
    # 像这个节点刚刚运行过一样进行处理。
    as_node="chatbot",
)
{'configurable': {'thread_id': '1',
  'checkpoint_ns': '',
  'checkpoint_id': '1ef7d134-3958-6412-8002-3f4b4112062f'}}

查看此更新调用的LangSmith 跟踪。**注意**从跟踪中可以看到图形继续到 tools_condition 边缘。我们刚刚告诉图形将更新 as_node="chatbot"。如果我们按照下面的图表开始从 chatbot 节点,自然会进入 tools_condition 边缘,然后是 __end__,因为我们更新的消息缺少工具调用。

from IPython.display import Image, display

try:
    display(Image(graph.get_graph().draw_mermaid_png()))
except Exception:
    # 这需要一些额外的依赖项,并且是可选的。
    pass

检查当前状态,如之前所做,以确认检查点反映了我们的手动更新。

snapshot = graph.get_state(config)
print(snapshot.values["messages"][-3:])
print(snapshot.next)
[ToolMessage(content='LangGraph is a library for building stateful, multi-actor applications with LLMs.', id='675f7618-367f-44b7-b80e-2834afb02ac5', tool_call_id='toolu_018YcbFR37CG8RRXnavH5fxZ'), AIMessage(content='LangGraph is a library for building stateful, multi-actor applications with LLMs.', additional_kwargs={}, response_metadata={}, id='35fd5682-0c2a-4200-b192-71c59ac6d412'), AIMessage(content="I'm an AI expert!", additional_kwargs={}, response_metadata={}, id='288e2f74-f1cb-4082-8c3c-af4695c83117')]
()
注意:我们已经继续将AI消息添加到状态中。由于我们作为聊天机器人在响应中使用不包含tool_calls的AIMessage,因此图表知道它已经进入了一个完成的状态(next为空)。

如果你想要**覆盖**现有消息,该怎么办?

我们用来注释图表状态add_messages函数控制如何对messages键进行更新。该函数查看新messages列表中的任何消息ID。如果ID与现有状态中的消息匹配,add_messages就会用新内容覆盖现有消息。

例如,让我们更新工具调用,以确保我们从搜索引擎获得良好的结果!首先,开始一个新线程:

user_input = "I'm learning LangGraph. Could you do some research on it for me?"
config = {"configurable": {"thread_id": "2"}}  # we'll use thread_id = 2 here
events = graph.stream(
    {"messages": [("user", user_input)]}, config, stream_mode="values"
)
for event in events:
    if "messages" in event:
        event["messages"][-1].pretty_print()
================================ Human Message =================================

I'm learning LangGraph. Could you do some research on it for me?
================================== Ai Message ==================================

[{'text': "Certainly! I'd be happy to research LangGraph for you. To get the most up-to-date and accurate information, I'll use the Tavily search engine to look this up. Let me do that for you now.", 'type': 'text'}, {'id': 'toolu_01TfAeisrpx4ddgJpoAxqrVh', 'input': {'query': 'LangGraph framework for language models'}, 'name': 'tavily_search_results_json', 'type': 'tool_use'}]
Tool Calls:
  tavily_search_results_json (toolu_01TfAeisrpx4ddgJpoAxqrVh)
 Call ID: toolu_01TfAeisrpx4ddgJpoAxqrVh
  Args:
    query: LangGraph framework for language models
**接下来,**我们来更新代理的工具调用。也许我们特别想要搜索人机协作的工作流程。

from langchain_core.messages import AIMessage

snapshot = graph.get_state(config)
existing_message = snapshot.values["messages"][-1]
print("Original")
print("Message ID", existing_message.id)
print(existing_message.tool_calls[0])
new_tool_call = existing_message.tool_calls[0].copy()
new_tool_call["args"]["query"] = "LangGraph human-in-the-loop workflow"
new_message = AIMessage(
    content=existing_message.content,
    tool_calls=[new_tool_call],
    # 重要!ID是LangGraph知道如何在状态中替换消息而不是追加这些消息的方式。
    id=existing_message.id,
)

print("Updated")
print(new_message.tool_calls[0])
print("Message ID", new_message.id)
graph.update_state(config, {"messages": [new_message]})

print("\n\nTool calls")
graph.get_state(config).values["messages"][-1].tool_calls
Original
Message ID run-342f3f54-356b-4cc1-b747-573f6aa31054-0
{'name': 'tavily_search_results_json', 'args': {'query': 'LangGraph framework for language models'}, 'id': 'toolu_01TfAeisrpx4ddgJpoAxqrVh', 'type': 'tool_call'}
Updated
{'name': 'tavily_search_results_json', 'args': {'query': 'LangGraph human-in-the-loop workflow'}, 'id': 'toolu_01TfAeisrpx4ddgJpoAxqrVh', 'type': 'tool_call'}
Message ID run-342f3f54-356b-4cc1-b747-573f6aa31054-0


Tool calls

[{'name': 'tavily_search_results_json',
  'args': {'query': 'LangGraph human-in-the-loop workflow'},
  'id': 'toolu_01TfAeisrpx4ddgJpoAxqrVh',
  'type': 'tool_call'}]
API Reference: AIMessage

注意 我们已经将人工智能的工具调用修改为搜索“LangGraph 人类在环工作流程”,而不是简单的“LangGraph”。

查看 LangSmith 跟踪 以查看状态更新调用 - 您可以看到我们的新消息成功更新了之前的人工智能消息。

通过输入 None 和现有配置恢复图形流。

events = graph.stream(None, config, stream_mode="values")
for event in events:
    if "messages" in event:
        event["messages"][-1].pretty_print()
================================== Ai Message ==================================

[{'text': "Certainly! I'd be happy to research LangGraph for you. To get the most up-to-date and accurate information, I'll use the Tavily search engine to look this up. Let me do that for you now.", 'type': 'text'}, {'id': 'toolu_01TfAeisrpx4ddgJpoAxqrVh', 'input': {'query': 'LangGraph framework for language models'}, 'name': 'tavily_search_results_json', 'type': 'tool_use'}]
Tool Calls:
  tavily_search_results_json (toolu_01TfAeisrpx4ddgJpoAxqrVh)
 Call ID: toolu_01TfAeisrpx4ddgJpoAxqrVh
  Args:
    query: LangGraph human-in-the-loop workflow
================================= Tool Message =================================
Name: tavily_search_results_json

[{"url": "https://www.youtube.com/watch?v=9BPCV5TYPmg", "content": "In this video, I'll show you how to handle persistence with LangGraph, enabling a unique Human-in-the-Loop workflow. This approach allows a human to grant an..."}, {"url": "https://medium.com/@kbdhunga/implementing-human-in-the-loop-with-langgraph-ccfde023385c", "content": "Implementing a Human-in-the-Loop (HIL) framework in LangGraph with the Streamlit app provides a robust mechanism for user engagement and decision-making. By incorporating breakpoints and ..."}]
================================== Ai Message ==================================

Thank you for your patience. I've found some information about LangGraph, particularly focusing on its human-in-the-loop workflow capabilities. Let me summarize what I've learned for you:

1. LangGraph Overview:
   LangGraph is a framework for building stateful, multi-actor applications with Large Language Models (LLMs). It's particularly useful for creating complex, interactive AI systems.

2. Human-in-the-Loop (HIL) Workflow:
   One of the key features of LangGraph is its support for human-in-the-loop workflows. This means that it allows for human intervention and decision-making within AI-driven processes.

3. Persistence Handling:
   LangGraph offers capabilities for handling persistence, which is crucial for maintaining state across interactions in a workflow.

4. Implementation with Streamlit:
   There are examples of implementing LangGraph's human-in-the-loop functionality using Streamlit, a popular Python library for creating web apps. This combination allows for the creation of interactive user interfaces for AI applications.

5. Breakpoints and User Engagement:
   LangGraph allows the incorporation of breakpoints in the workflow. These breakpoints are points where the system can pause and wait for human input or decision-making, enhancing user engagement and control over the AI process.

6. Decision-Making Mechanism:
   The human-in-the-loop framework in LangGraph provides a robust mechanism for integrating user decision-making into AI workflows. This is particularly useful in scenarios where human judgment or expertise is needed to guide or validate AI actions.

7. Flexibility and Customization:
   From the information available, it seems that LangGraph offers flexibility in how human-in-the-loop processes are implemented, allowing developers to customize the interaction points and the nature of human involvement based on their specific use case.

LangGraph appears to be a powerful tool for developers looking to create more interactive and controllable AI applications, especially those that benefit from human oversight or input at crucial stages of the process.

Would you like me to research any specific aspect of LangGraph in more detail, or do you have any questions about what I've found so far?
查看追踪,以查看工具调用和后来的LLM响应。注意,现在图形使用我们更新后的查询词查询搜索引擎——我们能够手动覆盖LLM的搜索!

所有这些都反映在图形的检查点内存中,这意味着如果我们继续对话,它将记住所有的_修改_状态。

events = graph.stream(
    {
        "messages": (
            "user",
            "Remember what I'm learning about?",
        )
    },
    config,
    stream_mode="values",
)
for event in events:
    if "messages" in event:
        event["messages"][-1].pretty_print()
================================ Human Message =================================

Remember what I'm learning about?
================================== Ai Message ==================================

I apologize for my oversight. You're absolutely right to remind me. You mentioned that you're learning LangGraph. Thank you for bringing that back into focus. 

Since you're in the process of learning LangGraph, it would be helpful to know more about your current level of understanding and what specific aspects of LangGraph you're most interested in or finding challenging. This way, I can provide more targeted information or explanations that align with your learning journey.

Are there any particular areas of LangGraph you'd like to explore further? For example:

1. Basic concepts and architecture of LangGraph
2. Setting up and getting started with LangGraph
3. Implementing specific features like the human-in-the-loop workflow
4. Best practices for using LangGraph in projects
5. Comparisons with other similar frameworks

Or if you have any specific questions about what you've learned so far, I'd be happy to help clarify or expand on those topics. Please let me know what would be most useful for your learning process.
恭喜您! 您已经使用 interrupt_beforeupdate_state 手动修改状态,作为人机协作工作流的一部分。中断和状态修改使您能够控制代理的行为。结合持久化检查点,这意味着您可以在任何时刻 暂停 一个动作并 恢复。您的用户在图形中断时不必在场!

本节的图形代码与之前的代码相同。需要记住的关键代码片段是,如果您想在图形到达某个节点时显式地暂停图形,请添加 .compile(..., interrupt_before=[...])(或 interrupt_after)。然后,您可以使用 update_state 来修改检查点并控制图形应该如何继续。

第六部分:自定义状态

到目前为止,我们依赖于一个简单的状态(这只是一个消息列表!)。依靠这个简单的状态可以走得很远,但如果你想定义复杂的行为而不依赖于消息列表,你可以向状态添加额外的字段。在这一部分中,我们将通过添加一个新节点来扩展我们的聊天机器人,以说明这一点。

在上面的示例中,我们以确定性的方式引入了人类:每当调用工具时,图形____总是____会中断。假设我们希望我们的聊天机器人可以选择依赖人类。

实现这一点的一种方法是创建一个通过的“人类”节点,在此节点之前图形将始终停止。只有当 LLM 调用“人类”工具时,我们才会执行此节点。为了方便起见,我们将在图形状态中包含一个“ask_human”标志,如果 LLM 调用此工具,我们将翻转该标志。

下面,定义这个新图形,并更新 State

from typing import Annotated

from langchain_anthropic import ChatAnthropic
from langchain_community.tools.tavily_search import TavilySearchResults
from typing_extensions import TypedDict

from langgraph.checkpoint.memory import MemorySaver
from langgraph.graph import StateGraph, START
from langgraph.graph.message import add_messages
from langgraph.prebuilt import ToolNode, tools_condition


class State(TypedDict):
    messages: Annotated[list, add_messages]
    # 这面旗帜是新的。
    ask_human: bool

接下来,定义一个模式来显示模型以决定请求帮助。

在LangChain中使用Pydantic

该笔记本使用Pydantic v2 BaseModel,需要 langchain-core >= 0.3。 使用 langchain-core < 0.3 会导致错误,这是因为Pydantic v1和v2 BaseModels的混合使用。

from pydantic import BaseModel


class RequestAssistance(BaseModel):
    """Escalate the conversation to an expert. Use this if you are unable to assist directly or if the user requires support beyond your permissions.

    To use this function, relay the user's 'request' so the expert can provide the right guidance.
    """

    request: str

接下来,定义聊天机器人节点。这里的主要修改是,如果我们发现聊天机器人调用了 RequestAssistance 标志,则翻转 ask_human 标志。

tool = TavilySearchResults(max_results=2)
tools = [tool]
llm = ChatAnthropic(model="claude-3-5-sonnet-20240620")
# 我们可以将大语言模型绑定到工具定义、Pydantic 模型或 JSON 模式上。
llm_with_tools = llm.bind_tools(tools + [RequestAssistance])


def chatbot(state: State):
    response = llm_with_tools.invoke(state["messages"])
    ask_human = False
    if (
        response.tool_calls
        and response.tool_calls[0]["name"] == RequestAssistance.__name__
    ):
        ask_human = True
    return {"messages": [response], "ask_human": ask_human}

接下来,创建图形构建器,并像之前一样将聊天机器人和工具节点添加到图形中。

graph_builder = StateGraph(State)

graph_builder.add_node("chatbot", chatbot)
graph_builder.add_node("tools", ToolNode(tools=[tool]))

接下来,创建“human” node。这个 node 函数在我们的图中主要是一个占位符,用于触发中断。如果人类在 interrupt 期间__没有__手动更新状态,它会插入一个工具消息,以便大型语言模型(LLM)知道用户已被请求但没有回应。此节点还会取消设置 ask_human 标志,以便图知道在未进一步请求的情况下不再访问该节点。

from langchain_core.messages import AIMessage, ToolMessage


def create_response(response: str, ai_message: AIMessage):
    return ToolMessage(
        content=response,
        tool_call_id=ai_message.tool_calls[0]["id"],
    )


def human_node(state: State):
    new_messages = []
    if not isinstance(state["messages"][-1], ToolMessage):
        # 通常,用户将在中断期间更新状态。
        # 如果他们选择不这样做,我们将包含一个占位符 ToolMessage 以便于。
        # 让大型语言模型继续。
        new_messages.append(
            create_response("No response from human.", state["messages"][-1])
        )
    return {
        # 添加新消息。
        "messages": new_messages,
        # 取消标志设置
        "ask_human": False,
    }


graph_builder.add_node("human", human_node)
API Reference: AIMessage | ToolMessage

接下来,定义条件逻辑。如果标志被设置,select_next_node 将路由到 human 节点。否则,它将让预构建的 tools_condition 函数选择下一个节点。

请记住,tools_condition 函数只是检查 chatbot 的响应消息中是否有任何 tool_calls。如果有,它将路由到 action 节点。否则,图形将结束。

def select_next_node(state: State):
    if state["ask_human"]:
        return "human"
    # 否则,我们可以像以前一样进行路由。
    return tools_condition(state)


graph_builder.add_conditional_edges(
    "chatbot",
    select_next_node,
    {"human": "human", "tools": "tools", END: END},
)

最后,添加简单的有向边并编译图。这些边指示图在每当 a 完成执行时**始终**从节点 a 流向节点 b

# 其余部分都是一样的。
graph_builder.add_edge("tools", "chatbot")
graph_builder.add_edge("human", "chatbot")
graph_builder.add_edge(START, "chatbot")
memory = MemorySaver()
graph = graph_builder.compile(
    checkpointer=memory,
    # We interrupt before 'human' here instead.
    interrupt_before=["human"],
)

如果您安装了可视化依赖项,您可以看到下面的图形结构:

from IPython.display import Image, display

try:
    display(Image(graph.get_graph().draw_mermaid_png()))
except Exception:
    # 这需要一些额外的依赖项且是可选的。
    pass

聊天机器人可以请求人类的帮助(聊天机器人->选择->人类)、调用搜索引擎工具(聊天机器人->选择->行动)或直接回应(聊天机器人->选择->结束)。一旦采取了行动或请求,图形将过渡回聊天机器人节点以继续操作。

让我们看看这个图形是如何运作的。我们将请求专家协助来展示我们的图形。

user_input = "I need some expert guidance for building this AI agent. Could you request assistance for me?"
config = {"configurable": {"thread_id": "1"}}
# config 是 stream() 或 invoke() 的 **第二个位置参数**!
events = graph.stream(
    {"messages": [("user", user_input)]}, config, stream_mode="values"
)
for event in events:
    if "messages" in event:
        event["messages"][-1].pretty_print()
================================ Human Message =================================

I need some expert guidance for building this AI agent. Could you request assistance for me?
================================== Ai Message ==================================

[{'text': "Certainly! I understand that you need expert guidance for building an AI agent. I'll use the RequestAssistance function to escalate your request to an expert who can provide you with the specialized knowledge and support you need. Let me do that for you right away.", 'type': 'text'}, {'id': 'toolu_01Mo3N2c1byuSZwT1vyJWRia', 'input': {'request': 'The user needs expert guidance for building an AI agent. They require specialized knowledge and support in AI development and implementation.'}, 'name': 'RequestAssistance', 'type': 'tool_use'}]
Tool Calls:
  RequestAssistance (toolu_01Mo3N2c1byuSZwT1vyJWRia)
 Call ID: toolu_01Mo3N2c1byuSZwT1vyJWRia
  Args:
    request: The user needs expert guidance for building an AI agent. They require specialized knowledge and support in AI development and implementation.
注意: LLM已经调用了我们提供的“RequestAssistance”工具,并且中断已被设置。让我们检查图形状态以确认。

snapshot = graph.get_state(config)
snapshot.next
('human',)

图状态在 'human' 节点之前确实是**中断的**。在这种情况下,我们可以充当“专家”,通过添加带有我们输入的新 ToolMessage 手动更新状态。

接下来,根据聊天机器人的请求进行响应: 1. 创建一个包含我们回应的 ToolMessage。这将被传递回 chatbot。 2. 调用 update_state 手动更新图状态。

ai_message = snapshot.values["messages"][-1]
human_response = (
    "We, the experts are here to help! We'd recommend you check out LangGraph to build your agent."
    " It's much more reliable and extensible than simple autonomous agents."
)
tool_message = create_response(human_response, ai_message)
graph.update_state(config, {"messages": [tool_message]})
{'configurable': {'thread_id': '1',
  'checkpoint_ns': '',
  'checkpoint_id': '1ef7d092-bb30-6bee-8002-015e7e1c56c0'}}

您可以检查状态以确认我们的响应已被添加。

graph.get_state(config).values["messages"]
[HumanMessage(content='I need some expert guidance for building this AI agent. Could you request assistance for me?', additional_kwargs={}, response_metadata={}, id='3f28f959-9ab7-489a-9c58-7ed1b49cedf3'),
 AIMessage(content=[{'text': "Certainly! I understand that you need expert guidance for building an AI agent. I'll use the RequestAssistance function to escalate your request to an expert who can provide you with the specialized knowledge and support you need. Let me do that for you right away.", 'type': 'text'}, {'id': 'toolu_01Mo3N2c1byuSZwT1vyJWRia', 'input': {'request': 'The user needs expert guidance for building an AI agent. They require specialized knowledge and support in AI development and implementation.'}, 'name': 'RequestAssistance', 'type': 'tool_use'}], additional_kwargs={}, response_metadata={'id': 'msg_01VRnZvVbgsVRbQaQuvsziDx', 'model': 'claude-3-5-sonnet-20240620', 'stop_reason': 'tool_use', 'stop_sequence': None, 'usage': {'input_tokens': 516, 'output_tokens': 130}}, id='run-4e3f7906-5887-40d9-9267-5beefe7b3b76-0', tool_calls=[{'name': 'RequestAssistance', 'args': {'request': 'The user needs expert guidance for building an AI agent. They require specialized knowledge and support in AI development and implementation.'}, 'id': 'toolu_01Mo3N2c1byuSZwT1vyJWRia', 'type': 'tool_call'}], usage_metadata={'input_tokens': 516, 'output_tokens': 130, 'total_tokens': 646}),
 ToolMessage(content="We, the experts are here to help! We'd recommend you check out LangGraph to build your agent. It's much more reliable and extensible than simple autonomous agents.", id='8583b899-d898-4051-9f36-f5e5d11e9a37', tool_call_id='toolu_01Mo3N2c1byuSZwT1vyJWRia')]

接下来,通过将 None 作为输入来恢复图形。

events = graph.stream(None, config, stream_mode="values")
for event in events:
    if "messages" in event:
        event["messages"][-1].pretty_print()
================================= Tool Message =================================

We, the experts are here to help! We'd recommend you check out LangGraph to build your agent. It's much more reliable and extensible than simple autonomous agents.
================================= Tool Message =================================

We, the experts are here to help! We'd recommend you check out LangGraph to build your agent. It's much more reliable and extensible than simple autonomous agents.
================================== Ai Message ==================================

Thank you for your patience. I've escalated your request to our expert team, and they have provided some initial guidance. Here's what they suggest:

The experts recommend that you check out LangGraph for building your AI agent. They mention that LangGraph is a more reliable and extensible option compared to simple autonomous agents.

LangGraph is likely a framework or tool designed specifically for creating complex AI agents. It seems to offer advantages in terms of reliability and extensibility, which are crucial factors when developing sophisticated AI systems.

To further assist you, I can provide some additional context and next steps:

1. Research LangGraph: Look up documentation, tutorials, and examples of LangGraph to understand its features and how it can help you build your AI agent.

2. Compare with other options: While the experts recommend LangGraph, it might be useful to understand how it compares to other AI agent development frameworks or tools you might have been considering.

3. Assess your requirements: Consider your specific needs for the AI agent you want to build. Think about the tasks it needs to perform, the level of complexity required, and how LangGraph's features align with these requirements.

4. Start with a small project: If you decide to use LangGraph, consider beginning with a small, manageable project to familiarize yourself with the framework.

5. Seek community support: Look for LangGraph user communities, forums, or discussion groups where you can ask questions and get additional support as you build your agent.

6. Consider additional training: Depending on your current skill level, you might want to look into courses or workshops that focus on AI agent development, particularly those that cover LangGraph.

Do you have any specific questions about LangGraph or AI agent development that you'd like me to try to answer? Or would you like me to search for more detailed information about LangGraph and its features?
注意:聊天机器人在其最终响应中融入了更新的状态。由于**所有**内容都已进行检查点处理,因此循环中的“专家”可以在任何时候执行更新,而不会影响图的执行。

恭喜! 你现在已经向你的助手图中添加了一个额外的节点,让聊天机器人自己决定是否需要中断执行。你通过在图状态中更新一个新的ask_human字段,并在编译图时修改中断逻辑来实现这一点。这让你在每次执行图时能够动态地引入一个人类参与,同时保持完整的**内存**。

我们快要完成教程了,但在结束之前还有一个概念需要复习,它连接了检查点处理状态更新

本节代码在下方供你参考。

完整代码

from typing import Annotated

from langchain_anthropic import ChatAnthropic
from langchain_community.tools.tavily_search import TavilySearchResults
from langchain_core.messages import BaseMessage
# 注意:你必须使用 langchain-core >= 0.3 与 Pydantic v2
from pydantic import BaseModel
from typing_extensions import TypedDict

from langgraph.checkpoint.memory import MemorySaver
from langgraph.graph import StateGraph
from langgraph.graph.message import add_messages
from langgraph.prebuilt import ToolNode, tools_condition


class State(TypedDict):
    messages: Annotated[list, add_messages]
    # 这个标志是新的
    ask_human: bool


class RequestAssistance(BaseModel):
    """将对话升级到专家。如果你无法直接提供帮助,或者用户需要超出你权限的支持,请使用此功能。

    使用此功能时,请传达用户的“请求”,以便专家提供正确的指导。
    """

    request: str


tool = TavilySearchResults(max_results=2)
tools = [tool]
llm = ChatAnthropic(model="claude-3-5-sonnet-20240620")
# 我们可以将llm绑定到工具定义、pydantic模型或json架构
llm_with_tools = llm.bind_tools(tools + [RequestAssistance])


def chatbot(state: State):
    response = llm_with_tools.invoke(state["messages"])
    ask_human = False
    if (
        response.tool_calls
        and response.tool_calls[0]["name"] == RequestAssistance.__name__
    ):
        ask_human = True
    return {"messages": [response], "ask_human": ask_human}


graph_builder = StateGraph(State)

graph_builder.add_node("chatbot", chatbot)
graph_builder.add_node("tools", ToolNode(tools=[tool]))


def create_response(response: str, ai_message: AIMessage):
    return ToolMessage(
        content=response,
        tool_call_id=ai_message.tool_calls[0]["id"],
    )


def human_node(state: State):
    new_messages = []
    if not isinstance(state["messages"][-1], ToolMessage):
        # 通常情况下,用户将在中断期间更新状态。
        # 如果他们选择不更新,我们将包含一个占位符ToolMessage以
        # 让LLM继续。
        new_messages.append(
            create_response("没有来自人类的响应。", state["messages"][-1])
        )
    return {
        # 附加新的消息
        "messages": new_messages,
        # 取消设置标志
        "ask_human": False,
    }


graph_builder.add_node("human", human_node)


def select_next_node(state: State):
    if state["ask_human"]:
        return "human"
    # 否则,我们可以按以前的方式路由
    return tools_condition(state)


graph_builder.add_conditional_edges(
    "chatbot",
    select_next_node,
    {"human": "human", "tools": "tools", "__end__": "__end__"},
)
graph_builder.add_edge("tools", "chatbot")
graph_builder.add_edge("human", "chatbot")
graph_builder.set_entry_point("chatbot")
memory = MemorySaver()
graph = graph_builder.compile(
    checkpointer=memory,
    interrupt_before=["human"],
)

第7部分:时间旅行

在典型的聊天机器人工作流程中,用户与机器人进行1次或多次交互以完成任务。在前面的部分中,我们看到了如何添加记忆和人工干预,以便能够检查我们的图状态并手动覆盖状态以控制未来的响应。

但如果您希望让用户从之前的响应开始并“分支”以探索不同的结果呢?或者,如果您希望用户能够“倒带”您助理的工作,以修复一些错误或尝试不同的策略(在诸如自主软件工程师等应用中常见)呢?

您可以使用LangGraph内置的“时间旅行”功能创建这两种体验以及更多体验。

在本节中,您将通过使用图的 get_state_history 方法获取检查点来“倒带”图。然后,您可以在这个之前的时间点恢复执行。

首先,回忆一下我们的聊天机器人图。我们不需要对之前的内容进行**任何**更改:

from typing import Annotated, Literal

from langchain_anthropic import ChatAnthropic
from langchain_community.tools.tavily_search import TavilySearchResults
from langchain_core.messages import AIMessage, ToolMessage

# 注意:您必须使用 langchain-core >= 0.3 以及 Pydantic v2。
from pydantic import BaseModel
from typing_extensions import TypedDict

from langgraph.checkpoint.memory import MemorySaver
from langgraph.graph import StateGraph, START
from langgraph.graph.message import add_messages
from langgraph.prebuilt import ToolNode, tools_condition


class State(TypedDict):
    messages: Annotated[list, add_messages]
    # 这面旗帜是新的。
    ask_human: bool


class RequestAssistance(BaseModel):
    """Escalate the conversation to an expert. Use this if you are unable to assist directly or if the user requires support beyond your permissions.

    To use this function, relay the user's 'request' so the expert can provide the right guidance.
    """

    request: str


tool = TavilySearchResults(max_results=2)
tools = [tool]
llm = ChatAnthropic(model="claude-3-5-sonnet-20240620")
# 我们可以将大型语言模型绑定到工具定义、pydantic模型或json模式。
llm_with_tools = llm.bind_tools(tools + [RequestAssistance])


def chatbot(state: State):
    response = llm_with_tools.invoke(state["messages"])
    ask_human = False
    if (
        response.tool_calls
        and response.tool_calls[0]["name"] == RequestAssistance.__name__
    ):
        ask_human = True
    return {"messages": [response], "ask_human": ask_human}


graph_builder = StateGraph(State)

graph_builder.add_node("chatbot", chatbot)
graph_builder.add_node("tools", ToolNode(tools=[tool]))


def create_response(response: str, ai_message: AIMessage):
    return ToolMessage(
        content=response,
        tool_call_id=ai_message.tool_calls[0]["id"],
    )


def human_node(state: State):
    new_messages = []
    if not isinstance(state["messages"][-1], ToolMessage):
        # 通常,用户在中断期间会更新状态。
        # 如果他们选择不这样做,我们将包含一个占位符 ToolMessage。
        # 让大型语言模型继续。
        new_messages.append(
            create_response("No response from human.", state["messages"][-1])
        )
    return {
        # 附加新消息。
        "messages": new_messages,
        # 取消标志设置
        "ask_human": False,
    }


graph_builder.add_node("human", human_node)


def select_next_node(state: State):
    if state["ask_human"]:
        return "human"
    # 否则,我们可以像以前一样进行路由。
    return tools_condition(state)


graph_builder.add_conditional_edges(
    "chatbot",
    select_next_node,
    {"human": "human", "tools": "tools", END: END},
)
graph_builder.add_edge("tools", "chatbot")
graph_builder.add_edge("human", "chatbot")
graph_builder.add_edge(START, "chatbot")
memory = MemorySaver()
graph = graph_builder.compile(
    checkpointer=memory,
    interrupt_before=["human"],
)
from IPython.display import Image, display

try:
    display(Image(graph.get_graph().draw_mermaid_png()))
except Exception:
    # 这需要一些额外的依赖项,并且是可选的。
    pass

让我们让我们的图形进行几步。每一步将在其状态历史中进行检查点记录:

config = {"configurable": {"thread_id": "1"}}
events = graph.stream(
    {
        "messages": [
            ("user", "I'm learning LangGraph. Could you do some research on it for me?")
        ]
    },
    config,
    stream_mode="values",
)
for event in events:
    if "messages" in event:
        event["messages"][-1].pretty_print()
================================ Human Message =================================

I'm learning LangGraph. Could you do some research on it for me?
================================== Ai Message ==================================

[{'text': "Certainly! I'd be happy to research LangGraph for you. To get the most up-to-date and accurate information, I'll use the Tavily search function to gather details about LangGraph. Let me do that for you now.", 'type': 'text'}, {'id': 'toolu_019HPZEw6v1eSLBXnwxk6MZm', 'input': {'query': 'LangGraph framework for language models'}, 'name': 'tavily_search_results_json', 'type': 'tool_use'}]
Tool Calls:
  tavily_search_results_json (toolu_019HPZEw6v1eSLBXnwxk6MZm)
 Call ID: toolu_019HPZEw6v1eSLBXnwxk6MZm
  Args:
    query: LangGraph framework for language models
================================= Tool Message =================================
Name: tavily_search_results_json

[{"url": "https://medium.com/@cplog/introduction-to-langgraph-a-beginners-guide-14f9be027141", "content": "LangGraph is a powerful tool for building stateful, multi-actor applications with Large Language Models (LLMs). It extends the LangChain library, allowing you to coordinate multiple chains (or ..."}, {"url": "https://towardsdatascience.com/from-basics-to-advanced-exploring-langgraph-e8c1cf4db787", "content": "LangChain is one of the leading frameworks for building applications powered by Lardge Language Models. With the LangChain Expression Language (LCEL), defining and executing step-by-step action sequences — also known as chains — becomes much simpler. In more technical terms, LangChain allows us to create DAGs (directed acyclic graphs)."}]
================================== Ai Message ==================================

Thank you for your patience. I've gathered some information about LangGraph for you. Let me summarize the key points:

1. What is LangGraph?
   LangGraph is a powerful tool designed for building stateful, multi-actor applications using Large Language Models (LLMs). It's an extension of the LangChain library, which is already a popular framework for developing LLM-powered applications.

2. Purpose and Functionality:
   - LangGraph allows developers to coordinate multiple chains or actors within a single application.
   - It enhances the capabilities of LangChain by introducing more complex, stateful workflows.

3. Relation to LangChain:
   - LangGraph builds upon LangChain, which is one of the leading frameworks for creating LLM-powered applications.
   - LangChain itself uses the LangChain Expression Language (LCEL) to define and execute step-by-step action sequences, also known as chains.
   - LangChain allows the creation of DAGs (Directed Acyclic Graphs), which represent the flow of operations in an application.

4. Key Features:
   - Stateful Applications: Unlike simple query-response models, LangGraph allows the creation of applications that maintain state across interactions.
   - Multi-Actor Systems: It supports coordinating multiple AI "actors" or components within a single application, enabling more complex interactions and workflows.

5. Use Cases:
   While not explicitly mentioned in the search results, LangGraph is typically used for creating more sophisticated AI applications such as:
   - Multi-turn conversational agents
   - Complex task-planning systems
   - Applications requiring memory and context management across multiple steps or actors

Learning LangGraph can be a valuable skill, especially if you're interested in developing advanced applications with LLMs that go beyond simple question-answering or text generation tasks. It allows for the creation of more dynamic, interactive, and stateful AI systems.

Is there any specific aspect of LangGraph you'd like to know more about, or do you have any questions about how it compares to or works with LangChain?

events = graph.stream(
    {
        "messages": [
            ("user", "Ya that's helpful. Maybe I'll build an autonomous agent with it!")
        ]
    },
    config,
    stream_mode="values",
)
for event in events:
    if "messages" in event:
        event["messages"][-1].pretty_print()
================================ Human Message =================================

Ya that's helpful. Maybe I'll build an autonomous agent with it!
================================== Ai Message ==================================

[{'text': "That's an excellent idea! Building an autonomous agent with LangGraph is a great way to explore its capabilities and learn about advanced AI application development. LangGraph's features make it well-suited for creating autonomous agents. Let me provide some additional insights and encouragement for your project.", 'type': 'text'}, {'id': 'toolu_017t6BS5rNCzFWcpxRizDKjE', 'input': {'query': 'building autonomous agents with LangGraph examples and tutorials'}, 'name': 'tavily_search_results_json', 'type': 'tool_use'}]
Tool Calls:
  tavily_search_results_json (toolu_017t6BS5rNCzFWcpxRizDKjE)
 Call ID: toolu_017t6BS5rNCzFWcpxRizDKjE
  Args:
    query: building autonomous agents with LangGraph examples and tutorials
================================= Tool Message =================================
Name: tavily_search_results_json

[{"url": "https://medium.com/@lucas.dahan/hands-on-langgraph-building-a-multi-agent-assistant-06aa68ed942f", "content": "Building the Graph. With our agents defined, we'll create a graph.py file to orchestrate their interactions. The basic graph structure in LangGraph is really simple, here we are going to use ..."}, {"url": "https://medium.com/@cplog/building-tool-calling-conversational-ai-with-langchain-and-langgraph-a-beginners-guide-8d6986cc589e", "content": "Introduction to AI Agent with LangChain and LangGraph: A Beginner’s Guide Two powerful tools revolutionizing this field are LangChain and LangGraph. In this guide, we’ll explore how these technologies can be combined to build a sophisticated AI assistant capable of handling complex conversations and tasks. Tool calling is a standout feature in agentic design, allowing the LLM to interact with external systems or perform specific tasks via the @tool decorator. While the Assistant class presented here is one approach, the flexibility of tool calling and LangGraph allows for a wide range of designs. With LangChain and LangGraph, you can build a powerful, flexible AI assistant capable of handling complex tasks and conversations. Tool calling significantly enhances the AI’s capabilities by enabling interaction with external systems."}]
================================== Ai Message ==================================

Your enthusiasm for building an autonomous agent with LangGraph is fantastic! This project will not only help you learn more about LangGraph but also give you hands-on experience with cutting-edge AI development. Here are some insights and tips to get you started:

1. Multi-Agent Systems:
   LangGraph excels at creating multi-agent systems. You could design your autonomous agent as a collection of specialized sub-agents, each handling different aspects of tasks or knowledge domains.

2. Graph Structure:
   The basic graph structure in LangGraph is straightforward. You'll create a graph.py file to orchestrate the interactions between your agents or components.

3. Tool Calling:
   A key feature you can incorporate is tool calling. This allows your LLM-based agent to interact with external systems or perform specific tasks. You can implement this using the @tool decorator in your code.

4. Flexibility in Design:
   LangGraph offers great flexibility in designing your agent. While there are example structures like the Assistant class, you have the freedom to create a wide range of designs tailored to your specific needs.

5. Complex Conversations and Tasks:
   Your autonomous agent can be designed to handle sophisticated conversations and complex tasks. This is where LangGraph's stateful nature really shines, allowing your agent to maintain context over extended interactions.

6. Integration with LangChain:
   Since LangGraph builds upon LangChain, you can leverage features from both. This combination allows for powerful, flexible AI assistants capable of managing intricate workflows.

7. External System Interaction:
   Consider incorporating external APIs or databases to enhance your agent's capabilities. This could include accessing real-time data, performing calculations, or interacting with other services.

8. Tutorial Resources:
   There are tutorials available that walk through the process of building AI assistants with LangChain and LangGraph. These can be excellent starting points for your project.

To get started, you might want to:
1. Set up your development environment with LangChain and LangGraph.
2. Define the core functionalities you want your autonomous agent to have.
3. Design the overall structure of your agent, possibly as a multi-agent system.
4. Implement basic interactions and gradually add more complex features like tool calling and state management.
5. Test your agent thoroughly with various scenarios to ensure robust performance.

Remember, building an autonomous agent is an iterative process. Start with a basic version and progressively enhance its capabilities. This approach will help you understand the intricacies of LangGraph while creating a sophisticated AI application.

Do you have any specific ideas about what kind of tasks or domain you want your autonomous agent to specialize in? This could help guide the design and implementation process.
现在我们已经让智能体采取了几步操作,我们可以replay完整的状态历史,以查看发生的所有事情。

to_replay = None
for state in graph.get_state_history(config):
    print("Num Messages: ", len(state.values["messages"]), "Next: ", state.next)
    print("-" * 80)
    if len(state.values["messages"]) == 6:
        # 我们根据该州的聊天消息数量有些随意地选择一个特定州。
        to_replay = state
Num Messages:  8 Next:  ()
--------------------------------------------------------------------------------
Num Messages:  7 Next:  ('chatbot',)
--------------------------------------------------------------------------------
Num Messages:  6 Next:  ('tools',)
--------------------------------------------------------------------------------
Num Messages:  5 Next:  ('chatbot',)
--------------------------------------------------------------------------------
Num Messages:  4 Next:  ('__start__',)
--------------------------------------------------------------------------------
Num Messages:  4 Next:  ()
--------------------------------------------------------------------------------
Num Messages:  3 Next:  ('chatbot',)
--------------------------------------------------------------------------------
Num Messages:  2 Next:  ('tools',)
--------------------------------------------------------------------------------
Num Messages:  1 Next:  ('chatbot',)
--------------------------------------------------------------------------------
Num Messages:  0 Next:  ('__start__',)
--------------------------------------------------------------------------------
注意,每一步的图都会保存检查点。这__跨越了调用__,因此您可以在一个完整线程的历史记录中回溯。我们选择了to_replay作为恢复的状态。这是上述第二个图调用中chatbot节点之后的状态。

从这一点恢复应该接着调用**动作**节点。

print(to_replay.next)
print(to_replay.config)
('tools',)
{'configurable': {'thread_id': '1', 'checkpoint_ns': '', 'checkpoint_id': '1ef7d094-2634-687c-8006-49ddde5b2f1c'}}
注意,检查点的配置 (to_replay.config) 包含一个 checkpoint_id 时间戳。提供这个 checkpoint_id 值可以告诉 LangGraph 的检查点管理器 加载 那个时刻的状态。我们在下面试一下:

# `to_replay.config`中的`checkpoint_id`对应于我们保存到检查点的一个状态。
for event in graph.stream(None, to_replay.config, stream_mode="values"):
    if "messages" in event:
        event["messages"][-1].pretty_print()
================================== Ai Message ==================================

[{'text': "That's an excellent idea! Building an autonomous agent with LangGraph is a great way to explore its capabilities and learn about advanced AI application development. LangGraph's features make it well-suited for creating autonomous agents. Let me provide some additional insights and encouragement for your project.", 'type': 'text'}, {'id': 'toolu_017t6BS5rNCzFWcpxRizDKjE', 'input': {'query': 'building autonomous agents with LangGraph examples and tutorials'}, 'name': 'tavily_search_results_json', 'type': 'tool_use'}]
Tool Calls:
  tavily_search_results_json (toolu_017t6BS5rNCzFWcpxRizDKjE)
 Call ID: toolu_017t6BS5rNCzFWcpxRizDKjE
  Args:
    query: building autonomous agents with LangGraph examples and tutorials
================================= Tool Message =================================
Name: tavily_search_results_json

[{"url": "https://blog.langchain.dev/how-to-build-the-ultimate-ai-automation-with-multi-agent-collaboration/", "content": "Learn how to create an autonomous research assistant using LangGraph, an extension of LangChain for agent and multi-agent flows. Follow the steps to define the graph state, initialize the graph, and run the agents for planning, research, review, writing and publishing."}, {"url": "https://medium.com/@lucas.dahan/hands-on-langgraph-building-a-multi-agent-assistant-06aa68ed942f", "content": "Building the Graph. With our agents defined, we'll create a graph.py file to orchestrate their interactions. The basic graph structure in LangGraph is really simple, here we are going to use ..."}]
================================== Ai Message ==================================

Great choice! Building an autonomous agent with LangGraph is an excellent way to dive deep into its capabilities. Based on the additional information I've found, here are some insights and steps to help you get started:

1. LangGraph for Autonomous Agents:
   LangGraph is particularly well-suited for creating autonomous agents, especially those involving multi-agent collaboration. It allows you to create complex, stateful workflows that can simulate autonomous behavior.

2. Example Project: Autonomous Research Assistant
   One popular example is building an autonomous research assistant. This type of project can help you understand the core concepts of LangGraph while creating something useful.

3. Key Steps in Building an Autonomous Agent:
   a. Define the Graph State: This involves setting up the structure that will hold the agent's state and context.
   b. Initialize the Graph: Set up the initial conditions and parameters for your agent.
   c. Create Multiple Agents: For a complex system, you might create several specialized agents, each with a specific role (e.g., planning, research, review, writing).
   d. Orchestrate Interactions: Use LangGraph to manage how these agents interact and collaborate.

4. Components of an Autonomous Agent:
   - Planning Agent: Determines the overall strategy and steps.
   - Research Agent: Gathers necessary information.
   - Review Agent: Evaluates and refines the work.
   - Writing Agent: Produces the final output.
   - Publishing Agent: Handles the final distribution or application of results.

5. Implementation Tips:
   - Start with a simple graph structure in LangGraph.
   - Define clear roles and responsibilities for each agent or component.
   - Use LangGraph's features to manage state and context across the different stages of your agent's workflow.

6. Learning Resources:
   - Look for tutorials and examples specifically on building multi-agent systems with LangGraph.
   - The LangChain documentation and community forums can be valuable resources, as LangGraph builds upon LangChain.

7. Potential Applications:
   - Autonomous research assistants
   - Complex task automation systems
   - Interactive storytelling agents
   - Autonomous problem-solving systems

Building an autonomous agent with LangGraph is an exciting project that will give you hands-on experience with advanced concepts in AI application development. It's a great way to learn about state management, multi-agent coordination, and complex workflow design in AI systems.

As you embark on this project, remember to start small and gradually increase complexity. You might begin with a simple autonomous agent that performs a specific task, then expand its capabilities and add more agents or components as you become more comfortable with LangGraph.

Do you have a specific type of autonomous agent in mind, or would you like some suggestions for beginner-friendly autonomous agent projects to start with?
请注意,图表从 **action** 节点恢复执行。您可以通过上面打印的第一个值是我们搜索引擎工具的响应来判断这一点。

恭喜你! 现在您已经在 LangGraph 中使用了时间旅行检查点遍历。能够回溯并探索替代路径为调试、实验和交互式应用程序打开了无限可能。

下一步

通过探索部署和高级功能,进一步拓展您的旅程:

服务器快速入门

LangGraph 云

LangGraph 框架

LangGraph 平台

使用这些资源扩展您的知识:

优云智算