大模型 LangChain-LangGraph 初探
一、LangGraph 简介
LangGraph(https://langchain-ai.github.io/langgraph/)是一个用于构建有状态、多参与者应用程序的库,在创建代理和多代理工作流方面发挥着重要作用。与其他大语言模型(LLM)框架相比,它具备一些独特的核心优势:
(一)循环
LangGraph 允许定义涉及循环的流程,这一特性对于大多数代理架构而言是不可或缺的。许多实际应用场景中,任务的处理可能需要多次迭代或重复某些操作,LangGraph 的循环功能使得在这些情况下能够更加灵活地设计和实现工作流,使其区别于基于有向无环图(DAG)的解决方案。
(二)可控性
作为一个低级框架,LangGraph 提供了对应用程序流程和状态的细粒度控制。在开发复杂的代理应用时,开发人员需要精确掌控每一个环节和状态的变化,LangGraph 能够满足这一需求,这对于创建可靠、稳定且高效的代理应用至关重要。
(三)持久性
LangGraph 包含内置持久性,这一特性为实现高级的人机循环和记忆功能提供了有力支持。例如,在一个持续交互的聊天机器人应用中,它可以记住之前的对话内容和状态,从而为用户提供更加连贯和个性化的服务,提升用户体验。
LangGraph 主要用于搭建和设计 agents 的复杂工作流,比如调用搜索引擎 API、调用第三方接口等,并能够细致地设计各个节点细节和状态流转。
二、官方示例
(一)构建一个简单的聊天机器人
在官方示例中,遇到的坑:
最初尝试使用 deepseek 模型来构建聊天机器人,但遇到了 function calling 一直不返回结果的问题,这导致大模型节点和 tool 节点陷入循环无法结束。经过排查和尝试,将模型更换为豆包的大模型后,function calling 能够正常返回结果,从而使整个流程得以正常运行。这一过程凸显了在开发过程中模型选择和适配的重要性,不同模型在功能实现和性能表现上可能存在差异,需要根据具体需求和实际情况进行选择。
(二)代码实现细节
- 导入必要的模块和类
from langchain_core.runnables import RunnableLambda
from langgraph.prebuilt import ToolNode, tools_condition
from typing import Annotated
from typing_extensions import TypedDict
from langgraph.graph import StateGraph, START, END
from langgraph.graph.message import add_messages
from langchain_openai import ChatOpenAI
from IPython.display import Image, display
from langchain_community.tools import DuckDuckGoSearchRun, DuckDuckGoSearchResults
import json
from langchain_core.messages import ToolMessage
from langgraph.checkpoint.memory import MemorySaver
from langchain_core.messages import AIMessage, ToolMessage
这些导入的模块和类涵盖了构建 LangGraph 应用所需的各种功能,包括定义可运行对象、工具节点、状态图、消息处理、大模型调用、搜索工具、内存管理以及消息类型等。
- 定义状态类(State)
class State(TypedDict):
messages: Annotated[list, add_messages]
这里定义了一个名为 State 的类,它继承自 TypedDict。其中包含一个键为 messages 的属性,其值为一个列表,并且通过 add_messages 注解指定了新消息的添加方式(即每次添加新消息而不是覆盖原有消息)。这个状态类用于管理应用程序在运行过程中的状态信息,特别是与消息相关的状态。
- 定义聊天模型(chatModel 函数)
def chatModel():
llm = ChatOpenAI(
model='ep-20250106175109-npkpj',
openai_api_key='<api-key>',
openai_api_base='https://ark.cn-beijing.volces.com/api/v3',
max_tokens=1024)
return llm
该函数用于创建并返回一个 ChatOpenAI 模型实例,指定了模型的名称、API 密钥、API 基础地址以及最大令牌数。这个聊天模型将作为应用程序的核心组件,负责处理用户输入并生成相应的回复。
- 定义搜索工具(serach_tool 函数)
def serach_tool():
search = DuckDuckGoSearchResults(max_results=1)
return search
此函数创建并返回一个 DuckDuckGoSearchResults 实例,用于执行网络搜索操作。通过设置 max_results=1,限制每次搜索返回的结果数量为 1 个,以便在后续处理中更加方便地获取和处理最相关的搜索结果。
- 定义聊天机器人(chatbot 函数)
def chatbot(state: State):
return {"messages": [llm_with_tools.invoke(state["messages"])]}
聊天机器人函数接受一个 State 类型的参数,它通过调用绑定了工具的大模型(llm_with_tools)的 invoke 方法,传入当前状态中的消息列表,获取模型的回复,并将回复包装在一个包含 messages 键的字典中返回。
- 定义状态机图(graph 函数)
def graph():
memory = MemorySaver()
tool_node = ToolNode(tools=tools)
graph_builder = StateGraph(State)
graph_builder.add_node("tools", tool_node)
graph_builder.add_node("chatbot", chatbot)
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)
return graph
在 graph 函数中,首先创建了一个 MemorySaver 实例用于内存管理,接着定义了一个 ToolNode 工具节点并传入之前定义的搜索工具列表。然后构建了一个 StateGraph 状态机图,向其中添加了工具节点和聊天机器人节点,并设置了条件边和普通边来定义节点之间的流转关系。最后通过 compile 方法编译状态机图,并传入内存管理对象,返回构建好的状态机图。
- 定义聊天交互循环(chat 函数)
def chat():
while True:
try:
user_input = input("User: ")
if user_input.lower() in ["退下吧"]:
print("再见!")
break
print("User:", user_input)
stream_graph_updates(user_input)
except:
user_input = "no"
print("User: " + user_input)
stream_graph_updates(user_input)
break
聊天函数实现了一个持续循环,等待用户输入。当用户输入 “退下吧” 时,循环结束并打印再见信息。对于其他用户输入,首先打印用户输入内容,然后调用 stream_graph_updates 函数来更新状态机图并获取模型的回复。如果在输入过程中出现异常,将用户输入设置为 “no” 并继续处理。
- 定义工具集合(tools 列表)和绑定工具的大模型(llm_with_tools)
tools = [serach_tool()]
llm_with_tools = chatModel().bind_tools(tools)
graph = graph()
config = {"configurable": {"thread_id": "1"}}
这里创建了一个包含搜索工具的列表 tools,并通过调用 chatModel 函数获取大模型实例,然后使用 bind_tools 方法将工具绑定到大模型上,得到一个可以在处理过程中调用工具的模型实例。
- 主程序入口
if __name__ == '__main__':
show()
chat()
在主程序入口部分,首先调用 show 函数来展示状态机图(如果相关依赖满足),然后调用 chat 函数启动聊天交互循环,使整个应用程序开始运行并与用户进行交互。
(三)示例运行结果分析
结果:
User: 你好,我叫彦祖
Assistant: 你好,彦祖,很高兴认识你呀。
User: 北京的天气怎么样
Assistant:
当前提供了 1 个工具,分别是["duckduckgo_results_json"],需求是查询北京的天气,需要调用 duckduckgo_results_json 获取信息。
Assistant: snippet: 据最新天气预报显示,今天,北京天气晴转多云,最高气温6℃,最低气温零下6℃,昼夜温差较大,并且周末两天,北京以晴到多云天气为主,气温变化不大,最低气温零下3℃至零下4℃,而且根据天气预报还提供的其他气象信息,如空气质量、湿度等,公众在 ..., title: 北京 天气预报15天, link: https://www.tianqi.com/beijing/15/, snippet: 周末两天北京以晴到多云天气为主 气温变化不大. 据最新天气预报显示,今天,北京天气晴转多云,最高气温6℃,最低气温零下6℃,昼夜温差较大,并且周末两天,北京以晴到多云天气为主,气温变化不大,最低气温零下3℃至零下4℃,而且根据天气预报还提供的其他气象信息,如空气质量、湿度 ..., title: 【北京天气预报】北京天气预报一周,北京天气预报15天,30天,今天,明天,7天,10天,未来北京一周天气预报查询—天气网, link: https://www.tianqi.com/beijing/, snippet: Beijing Weather Forecasts. Weather Underground provides local & long-range weather forecasts, weatherreports, maps & tropical weather conditions for the Beijing area., title: Beijing, People's Republic of China Weather Conditions, link: https://www.wunderground.com/weather/cn/beijing, snippet: Weather report for Beijing. During the night and in the first hours of the day a few clouds are expected, the sky clears in the afternoon. It is a sunny day., title: Weather Beijing - meteoblue, link: https://www.meteoblue.com/en/weather/week/beijing_china_1816670
Assistant: 根据查询到的信息,当前北京天气晴转多云,最高气温 6℃,最低气温零下 6℃。周末两天北京以晴到多云天气为主,气温变化不大,最低气温在零下 3℃至零下 4℃。同时,你还可以通过相关的天气预报网站或应用获取更详细准确的实时天气信息。
User: 好的,我叫什么名字
Assistant: 你说你叫彦祖呀。
再见!
- 初次交互
当用户输入 “你好,我叫彦祖” 时,聊天机器人回复 “你好,彦祖,很高兴认识你呀。” 这表明聊天机器人能够正常理解用户的问候并做出相应的友好回复,说明大模型在基本的文本生成和对话处理方面功能正常。 - 查询北京天气
用户询问 “北京的天气怎么样” 时,聊天机器人首先识别出需要查询天气信息,然后利用绑定的 DuckDuckGoSearchResults 工具进行网络搜索。从返回的结果可以看到,它获取了多个来源的天气预报信息,包括天气网、Weather Underground 等。最终,聊天机器人整合这些信息并回复给用户:“根据查询到的信息,当前北京天气晴转多云,最高气温 6℃,最低气温零下 6℃。周末两天北京以晴到多云天气为主,气温变化不大,最低气温在零下 3℃至零下 4℃。同时,你还可以通过相关的天气预报网站或应用获取更详细准确的实时天气信息。” 这展示了 LangGraph 如何通过工具调用和大模型的信息处理能力,为用户提供准确的实时信息查询服务。 - 回忆用户姓名
当用户询问 “好的,我叫什么名字” 时,聊天机器人准确地回答 “你说你叫彦祖呀。” 这体现了聊天机器人在对话过程中能够保持一定的记忆能力,记住之前用户提到的姓名信息,这得益于 LangGraph 的内置持久性和状态管理机制。
(四)总结与思考
通过对 LangGraph 官方示例的详细分析,我们可以看到它在构建复杂的代理应用程序方面的强大功能和灵活性。它不仅能够整合大模型的语言处理能力,还能够方便地调用外部工具,实现诸如信息查询等功能,并通过状态管理和流程控制来确保整个应用的稳定运行和良好交互体验。然而,在实际应用中,仍然可能会遇到一些问题,如模型适配问题,这提醒我们在开发过程中需要充分测试和评估不同模型和工具的组合,以选择最适合项目需求的方案。同时,随着技术的不断发展,LangGraph 等类似框架在多模态处理、更复杂的工作流设计以及性能优化等方面还有很大的提升空间,值得我们持续关注和深入研究。