Bootstrap

LangChain大模型应用开发:LangGraph快速入门

介绍

大家好,博主又来给大家分享知识了。一直以来,我都非常感激大家对我的支持与鼓励,大家的点赞、关注和收藏,不仅是对博主分享内容的认可,更是博主不断创作、坚持分享知识的永恒动力。

今天给大家分享的内容是使用LangChain进行大模型应用开发中的LangGraph,那么什么是LangGraph呢?我们直接进入正题。

LangGraph

LangGraph概述

LangGraph是一个用于借助大语言模型(LLMs)构建有状态的、多主体应用程序的库,可用于创建智能体和多智能体工作流程。

LangGraph的灵感源自PregelApache Beam。其公开接口借鉴了NetworkX的设计思路。LangGraphLangChain的开发者——LangChain公司所开发,但即便不使用LangChain,也能运用LangGraph

大家可选看:

  • Pregel:它是一种用于大规模图处理的分布式计算模型,由谷歌公司开发并在2010年左右提出,常用于处理超大规模的图数据,它为大规模图处理提供了一种高效、灵活且易于编程的解决方案,在许多领域都有广泛的应用。
  • Apache Beam:它是一个统一的开源编程模型,用于定义和执行数据处理工作流,支持批处理和流处理,可在多种分布式执行环境中运行。Apache Beam为数据处理提供了一个强大、灵活且可移植的编程模型,适用于各种数据处理场景,无论是批处理还是流处理,都能帮助开发者高效地实现数据处理工作流。
  • NetworkX:它是一个用Python语言编写的用于创建、操作和研究复杂网络的结构、动力学和功能的开源库。NetworkX为研究和分析复杂网络提供了一个强大而灵活的工具,广泛应用于各个领域的网络研究和分析中。

LangGraph优势

LangGraph使我们能够对智能体应用程序的流程和状态进行细粒度的控制。它实现了一个中央持久层,具备了大多数智能体架构共有的功能:

  • 记忆功能:LangGraph可以持久保存应用程序状态的任意方面,支持保存对话的记忆,以及用户交互过程中及不同交互之间的其他更新内容。
  • 人工介入:由于状态会被保存检查点,执行过程可以被中断和恢复,从而允许在关键阶段通过人工输入来进行决策、验证和修正。

LangGraphLangChain的高级库,为大型语言模型(LLM)带来循环计算能力。它超越了LangChain的线性工作流,通过循环支持复杂的任务处理。

  • 状态:维护计算过程中的上下文,实现基于累积数据的动态决策。
  • 节点:代表计算步骤,执行特定任务,可定制以适应不同工作流。
  • 边:连接节点,定义计算流程,支持条件逻辑,实现复杂工作流。

对这些组件进行标准化,能让个人和团队专注于智能体的行为本身,而非其支持性的基础设施。通过LangGraph平台,LangGraph还提供了用于开发、部署、调试和监控应用程序的工具。
LangGraph可与LangChainLangSmith无缝集成(但并非必须使用它们)。

    LangGraph平台

    LangGraph平台是用于部署LangGraph智能体的基础设施。它是一个将智能体应用程序部署到生产环境的商业解决方案,构建在开源的LangGraph框架之上。

    LangGraph平台由多个组件组成,这些组件协同工作,为LangGraph应用程序的开发、部署、调试和监控提供支持,包括:LangGraph服务器(提供应用程序编程接口)、LangGraph软件开发工具包(用于调用这些接口的客户端)、LangGraph命令行界面(用于构建服务器的命令行工具)以及LangGraph工作室(用户界面兼调试器)。

    LangGraph平台的优势是它能够解决这些问题:

    • 流式传输支持:LangGraph服务器提供多种流式传输模式,针对各种应用程序需求进行了优化。
    • 后台运行:可在后台以异步方式运行智能体。
    • 支持长时间运行的智能体:具备能够处理长时间运行进程的基础设施。
    • 重复消息处理:处理在智能体做出响应之前收到用户两条消息的情况。
    • 应对突发流量:设有任务队列,即使在高负载情况下也能确保请求得到一致处理而不丢失。

    LangGraph安装

    我们使用pip命令安装,安装命令为:

    pip install langgraph

    LangGraph入门示例

    我们利用LangGraphLangChain构建了一个基于大语言模型的问答工作流应用,实现与用户进行多轮对话并根据用户问题调用工具查询信息的功能。

    我们构建一个简单的问答系统,通过工作流的方式管理对话流程,根据用户问题调用工具获取信息,并使用语言模型生成回复。

    完整代码

    # 导入Literal类型,用于类型注解,指定方法返回值只能是特定的几个值
    from typing import Literal
    # 从langchain_core.messages模块导入HumanMessage类,用于表示人类输入的消息
    from langchain_core.messages import HumanMessage
    # 从langchain_core.tools模块导入tool装饰器,用于将方法转换为工具
    from langchain_core.tools import tool
    # 从langchain_openai模块导入ChatOpenAI类,用于调用OpenAI的聊天模型
    from langchain_openai import ChatOpenAI
    # 从langgraph.checkpoint.memory模块导入MemorySaver类,用于保存工作流的状态
    from langgraph.checkpoint.memory import MemorySaver
    # 从langgraph.graph模块导入END常量、StateGraph类和MessagesState类,用于构建工作流图和管理消息状态
    from langgraph.graph import END, StateGraph, MessagesState
    # 从langgraph.prebuilt模块导入ToolNode类,用于创建工具节点
    from langgraph.prebuilt import ToolNode
    
    
    # 使用tool装饰器将search方法转换为工具
    @tool
    def search(query: str):
        """模拟一个搜索工具"""
        # 如果查询中包含“北京”或“Beijing”
        if "北京" in query.lower() or "Beijing" in query.lower():
            # 返回北京的天气信息
            return "现在31度,有风."
        # 否则返回默认的天气信息
        return "现在是35度,万里无云。"
    
    
    # 将search工具添加到列表中
    tools = [search]
    
    # 创建一个工具节点,用于处理工具调用
    tool_node = ToolNode(tools)
    
    # 初始化一个ChatOpenAI模型实例,使用gpt-3.5-turbo模型,设置temperature=0不考虑随机性,并将工具绑定到模型上
    chat_model = ChatOpenAI(model="gpt-3.5-turbo", temperature=0).bind_tools(tools)
    
    
    # 定义一个方法,用于判断是否需要继续调用工具
    def should_continue(state: MessagesState) -> Literal["tools", END]:
        # 从状态中获取消息列表
        messages = state['messages']
        # 获取最后一条消息
        last_message = messages[-1]
        # 如果最后一条消息包含工具调用
        if last_message.tool_calls:
            # 返回"tools",表示需要继续调用工具
            return "tools"
        # 否则返回END,表示工作流结束
        return END
    
    
    # 定义一个方法,用于调用语言模型处理消息
    def call_model(state: MessagesState):
        # 从状态中获取消息列表
        messages = state['messages']
        # 调用语言模型处理消息
        response = chat_model.invoke(messages)
        # 返回包含模型响应的新状态
        return {"messages": [response]}
    
    
    # 创建一个StateGraph实例,用于定义工作流的状态转换
    workflow = StateGraph(MessagesState)
    # 向工作流中添加一个名为"agent"的节点,该节点执行call_model方法
    workflow.add_node("agent", call_model)
    # 向工作流中添加一个名为"tools"的节点,该节点执行tool_node
    workflow.add_node("tools", tool_node)
    
    # 设置"agent"节点为工作流的入口点
    workflow.set_entry_point("agent")
    
    # 为"agent"节点添加条件边,根据should_continue方法的返回值决定流向
    workflow.add_conditional_edges(
        "agent",
        should_continue,
    )
    
    # 添加从"tools"节点到"agent"节点的边,使得工具调用完成后回到"agent"节点继续处理
    workflow.add_edge("tools", 'agent')
    
    # 创建一个MemorySaver实例,用于保存工作流的状态
    checkpointer = MemorySaver()
    
    # 使用checkpointer编译工作流,生成可执行的应用
    app = workflow.compile(checkpointer=checkpointer)
    
    # 调用应用,传入用户的问题和配置信息
    final_state = app.invoke(
        {"messages": [HumanMessage(content="北京的天气怎么样?")]},
        config={"configurable": {"thread_id": 21}}
    )
    
    # 从最终状态中提取模型的回复内容
    result = final_state["messages"][-1].content
    # 打印模型的回复
    print(result)
    
    # 再次调用应用,传入新的用户问题和配置信息
    final_state = app.invoke(
        {"messages": [HumanMessage(content="我问的哪个城市?")]},
        config={"configurable": {"thread_id": 21}}
    )
    
    # 从最终状态中提取模型的回复内容
    result = final_state["messages"][-1].content
    # 打印模型的回复
    print(result)

    运行结果

    北京的天气现在是31度,有风。
    您问的是北京城市的天气情况。
    
    进程已结束,退出代码为 0

    运行示例分析

    定义工作流控制方法
    def should_continue(state: MessagesState) -> Literal["tools", END]:
        messages = state['messages']
        last_message = messages[-1]
        if last_message.tool_calls:
            return "tools"
        return END
    
    def call_model(state: MessagesState):
        messages = state['messages']
        response = chat_model.invoke(messages)
        return {"messages": [response]}
    • should_continue方法:根据当前对话状态判断是否需要继续调用工具。如果最后一条消息包含工具调用,则返回"tools"继续调用工具;否则返回END表示工作流结束。
    • call_model方法:从传入的状态中获取消息列表,调用 chat_model 处理消息,并返回包含模型响应的新状态。
    构建工作流
    workflow = StateGraph(MessagesState)
    workflow.add_node("agent", call_model)
    workflow.add_node("tools", tool_node)
    workflow.set_entry_point("agent")
    workflow.add_conditional_edges(
        "agent",
        should_continue,
    )
    workflow.add_edge("tools", 'agent')
    • 创建一个StateGraph实例workflow,用于定义工作流的状态转换。
    • 添加两个节点:"agent" 节点执行call_model函数,"tools"节点执行tool_node
    • 设置"agent"节点为工作流的入口点。
    • 为"agent"节点添加条件边,根据should_continue函数的返回值决定流向。
    • 添加从"tools"节点到"agent"节点的边,使工具调用完成后回到"agent"节点继续处理。 
    编译工作流并保存状态
    checkpointer = MemorySaver()
    app = workflow.compile(checkpointer=checkpointer)
    • 创建一个MemorySaver实例checkpointer,用于保存工作流的状态。
    • 使用checkpointer编译工作流workflow,生成可执行的应用app。 
    处理用户问题
    final_state = app.invoke(
        {"messages": [HumanMessage(content="北京的天气怎么样?")]},
        config={"configurable": {"thread_id": 21}}
    )
    result = final_state["messages"][-1].content
    print(result)
    
    final_state = app.invoke(
        {"messages": [HumanMessage(content="我问的哪个城市?")]},
        config={"configurable": {"thread_id": 21}}
    )
    result = final_state["messages"][-1].content
    print(result)
    • 两次调用appinvoke方法,分别传入不同的用户问题和相同的配置信息,获取工作流的最终状态。
    • 从最终状态中提取模型的回复内容并打印输出,实现与用户的交互。 

    LangGraph智能体工作流构建管理者

    • 在上面代码中,config={"configurable": {"thread_id": 21}}中的"thread_id": 21起到唯一标识一次对话流程的作用。thread_id可理解为“对话线程ID”,如同专属编号,标记特定的对话线程,这里的21就是该对话线程对应的编号。
    • 代码中两次调用app.invoke方法时都传入相同的thread_id(21),这表明这两次调用属于同一轮对话。系统基于此thread_id保存和恢复对话状态,确保两次调用间的数据和上下文相互关联。
    • 从运行结果可知,第二次app.invoke传入相同thread_id,系统能依据该标识找到对应的对话上下文,这些上下文通过保存的状态(即存储的消息列表)留存,从而让智能体根据上下文准确回答用户问题(如确定用户询问的城市是北京)。

    结合实际应用,LangGraph的作用主要体现在以下几个方面:

    • 构建工作流:通过StateGraph类构建工作流图,定义了节点(如 “agent” 节点用于调用语言模型处理消息,“tools”节点用于处理工具调用)和边(包括条件边和普通边,决定工作流的流向和执行顺序),清晰地描述了智能体应用的执行流程,实现了从用户输入到调用工具、语言模型,再到生成回复的有序操作。
    • 管理对话状态:借助MessagesState管理消息状态,在工作流中传递和处理消息,同时使用MemorySaver保存工作流的状态,确保在应用运行过程中可以记录中间状态,方便后续恢复或调试,保障对话的连续性和上下文的关联。
    • 创建工具节点:利用ToolNode创建工具节点,使得在工作流中可以调用各种工具(如模拟的搜索工具),辅助智能体完成任务,根据用户输入动态调用工具获取信息,增强了智能体处理复杂任务的能力。
    • 实现应用架构可扩展可管理:通过定义节点、边和状态保存机制,搭建起一个可扩展、可管理的应用架构,使得智能体应用能够根据用户的输入灵活地调用工具和语言模型,完成复杂的任务,并且便于对整个应用进行管理和维护。

    结束

    好了,以上就是本次分享的全部内容了。希望大家对LangGraph有一定的了解和掌握,本次分享只是对LangGraph的一个入门讲解,让大家对它有个概念,然后通过示例代码来给大家演示如何去使用它的功能。

    LangGraph是一个用于构建具有LLMs的有状态、多角色应用程序的库,用于创建智能体和多智能体工作流。与其他LLM框架相比,它提供了循环、可控性和持久性的核心优势。

    LangGraph允许我们定义涉及循环的流程,这对于大多数智能体架构至关重要。作为一种非常底层的框架,它提供了对应用程序的流程和状态的精细控制,这对创建可靠的智能体至关重要。此外,LangGraph包含内置的持久性,可以实现高级的 “人机交互” 和内存功能。

    那么本次分享就到这里了。最后,博主还是那句话:请大家多去大胆的尝试和使用,成功总是在不断的失败中试验出来的,敢于尝试就已经成功了一半。如果大家对博主分享的内容感兴趣或有帮助,请点赞和关注。大家的点赞和关注是博主持续分享的动力🤭,博主也希望让更多的人学习到新的知识。

    ;