分享

彻底搞懂LangGraph:构建强大的Multi-Agent多智能体应用的LangChain新利器 【1】

 天承办公室 2024-04-12 发布于北京

上个月LangChain刚刚发布了正式的0.1稳定版本(没错,是0.1而不是1.0),在版本公告里面首当其冲宣布的最重要更新,是在这个版本里面引入了一个最新库 - LangGraph。这是一个面向当前LLM开发领域最火热的AI Agent开发与控制的开发库,也是LangChain试图用来弥补其在Agent开发、特别是复杂的多Agent系统定制方面的不足的重大尝试,相信也会成为LangChain在2024升级更新的重点领域!

图片

我们会用一系列文章深入LangGraph,结合官方例子介绍与剖析其在几个重点Agent方向的应用。

  • LangGraph诞生的动力及设计思想

  • LangGraph应用:增强的RAG应用

  • LangGraph应用:自修复代码助手

  • LangGraph应用:Multi-Agent系统

  • LangGraph应用:构建Web Agents

由于官方文档较为晦涩,加上LangChain一贯的“重量级”风格。为了更好地帮助深入浅出的理解LangGraph,并照顾到没有LangChain基础的朋友,我们首先来了解一些“预备知识”。


PART 01

图片
图片

预备知识


【LangChain中的链与LCEL】

Chain(链)是LangChain中最核心的概念之一(看名字就知道)。简单的说,就是把自然语言输入、关联知识检索、Prompt组装、可用Tools信息、大模型调用、输出格式化等这些LLM 应用中的常见动作,组装成一个可以运行的“链”式过程。链可以直接调用,也可以用来进一步构建更强大的Agent。

LCEL即LangChain Express Language,即LangChain表达语言。这是LangChain提供的一种简洁的、用于组装上述“链”的声明性方式。

我们看一个官方使用LCEL“组装”Chain的例子就明白:

prompt = ChatPromptTemplate.from_template('讲一个关于 {topic} 的笑话')
model = ChatOpenAI(model='gpt-4')
output_parser = StrOutputParser()
chain = prompt | model | output_parser
#调用chain
chain.invoke({'topic': '冰淇淋'})

这个官方的例子中,把提示(prompt)、大模型(model)、输出解析(output_parser)几个组件使用管道符号“|”链接在一起,上个组件的输出作为下一个组件的输入,一起形成了一个链。

对于最常见的RAG应用来说,使用LCEL也无非是在此之上增加一个检索相关文档的动作,类似:

chain = setup_and_retrieval | prompt | model | output_parser

这里很清晰地看到一个简单的RAG应用处理过程:检索关联文档 => 组装Prompt => 调用大模型 => 输出处理。

最后总结一下:LCEL就是LangChain提供用来组装Chain的一种简单表示方式。用这种方式组装链,可以自动获得诸如批量、流输出、并行、异步等一系列能力;而且链可以进一步通过LCEL组装成更复杂的链与Agent。

【LCEL构建与调度Agent】

那么如何用LCEL来创建一个AI Agent并调度运行呢?以最常见的React(推理&行动)范式的Agent来说,相对于Chain需要扩展的能力有:

  • 增加工具使用能力。这体现在Prompt中需要注入可用工具信息,并能自动调用工具获得结果。

  • 增加“循环”能力。Agent的运行通常需要多次Reason(推理)-Act(行动)的反复与循环,直到完成任务。

以LCEL来组装并创建运行一个Agent的简单过程如下:

'''
定义Agent需要使用的Tools
'''
@tool
def search(query: str) -> str:
'''此处省略'''

'''
LCEL创建一个Agent,与Chain类似
'''
agent = (
{input:{输入信息}, agent_scratchpad:{中间步骤}}
| prompt
| model
| AgentOutputParser()
)

'''
注意:Agent需要使用agent_executor调用,以增加上述两个能力
'''
agent_executor = AgentExecutor(agent=agent, tools=tool_list, verbose=True)
agent_executor.invoke({'input': 'whats the weather in New york?'})

注意到,相对于Chain.invoke()直接运行,这里的Agent_executor的作用就是为了能够实现多次循环ReAct的动作,以最终完成任务。

【什么是图(Graph)】

图是计算机科学中的一种数据结构。大部分人可能都接触过一些基本的数据结构,比如队列(Queue)、堆栈(Stack)、链表(List)或者树(Tree)等,图(Graph)也是其中的一种相对复杂的数据结构。我们无意在此普及图的数据结构知识,你只需要了解的图的几个基本知识:

  • 图是表示多个元素及其之间关系的一种结构。其特点是,任何两个元素之间都可以直接发生联系,所以适合表达更复杂的元素关系。

  • 图的基本表示就是N个元素(节点/顶点)及这些元素之间的关系(边)的集合。

    图片

  • 有向无环图(Directed Acyclic Graph,DAG):有向指的是图中的“边”有方向;无环指的是无法从某个节点经过若干“边”返回这个节点。

    其他的一些图的理论,包括不同类型图的存储结构、相关算法等,这里对理解LangGraph无关紧要。


PART 02

图片
图片

LangGraph的驱动力


即然上文介绍的LCEL已经很强大,但是为什么还需要LangGraph呢?基于LCEL构建的Chain与Agent又存在哪些不足呢?

* 链(Chain):无法满足在循环中调用LLM以完成任务。

上文中,我们可以轻易地使用LCEL来快速创建一个链,但是很显然的一个问题是:如果我们把链中的组件想象成Graph中的节点,组件之间的联系想象成Graph中的边,那么这个链就是一个有向无环图(DAG)。即在一次Chain运行中,一个调用节点无法重复/循环进入。

图片

那么为什么需要将循环引入运行时呢?考虑一个增强的RAG应用:

图片

在这个RAG应用设计中,我们可以对语义检索出来的关联文档(上下文)进行评估:如果评估的文档质量很差,可以对检索的问题进行重写(Rewrite,比如把输入的问题结合对话历史用更精确的方式来表达),并把重写结果重新交给检索器,检索出新的关联文档,这样有助于获得更精确的结果。

这里把Rewrite的问题重新交给检索器,就是一个典型的“循环”动作。而在目前LangChain的简单链中是无法支持的。

其他一些典型的依赖“循环”的场景包括:

  • 代码生成时的自我纠正:当借助LLM自动生成软件代码时,根据代码执行的结果进行自我反省,并要求LLM重新生成代码。

  • Web访问自动导航:每当进入下一界面时,需要借助多模态模型来决定下一步的动作(点击、滚动、输入等),直至完成导航。

* AgentExecutor:尽管支持“循环”,但缺乏精确控制能力。

那么,如果我们需要在循环中调用LLM能力,就需要借助于AgentExecutor。其调用的过程主要就是两个步骤:

  1. 通过大模型来决定采取什么行动,使用什么工具,或者向用户输出响应(如运行结束时);

  2. 执行1步骤中的行动,比如调用某个工具,并把结果继续交给大模型来决定,即返回步骤1;

这里的AgentExecute存在的问题是:过于黑盒,所有的决策过程隐藏在AgentExecutor背后,缺乏更精细的控制能力,在构建复杂Agent的时候受限。这些精细化的控制要求比如:

  • 某个Agent要求首先强制调用某个Tool

  • 在 Agent运行过程中增加人机交互步骤

  • 能够灵活更换Prompt或者背后的LLM

  • 多Agent(Multi-Agent)智能体构建的需求,即多个Agent协作完成任务的场景支持。(这也是Langchain相对于竞争对手Autogen等最薄弱的能力之一,也是众多开发者千呼万唤的特性)

所以,让我们简单总结LangGraph诞生的动力:LangChain简单的链(Chain)不具备“循环”能力;而AgentExecutor调度的Agent运行又过于“黑盒”。因此需要一个具备更精细控制能力的框架来支持更复杂场景的LLM应用。


PART 03

图片
图片

LangGraph的设计思想


LangGraph并非一个独立于Langchain的新框架,它是基于Langchain之上构建的一个扩展库,可以与Langchain现有的链、LCEL等无缝协作。LangGraph能够协调多个Chain、Agent、Tool等共同协作来完成输入任务,支持LLM调用“循环”以及Agent过程的更精细化的控制。

LangGraph的实现方式是把之前基于AgentExecutor的黑盒调用过程用一种新的形式来构建:状态图(StateGraph)。把基于LLM的任务(比如RAG、代码生成等)细节用Graph进行精确的定义(定义图的节点与边),最后基于这个图来编译生成应用;在任务运行过程中,维持一个中央状态对象(state),会根据节点的跳转不断更新,状态包含的属性可自行定义。

我们用官方的一个增强的RAG应用的Graph来帮助理解:

图片

这个Graph中体现了LangGraph的几个基本概念:

  • StateGraph:这是代表整个状态图的基础类。

  • Nodes:节点。在有了图之后,可以向图中添加节点,节点通常是一个可调用的函数、一个可运行的Chain或者Agent。有一个特殊的节点叫END,进入这个节点,代表运行结束。

  • 在上图中,推理函数调用、调用检索器、生成响应内容、问题重写等都是其中的任务节点。

  • Edges:边。有了节点后,需要向图中添加边,边代表从上一个节点跳转到下一个节点的关系。目前有三种类型的边:

  • Starting Edge:一种特殊的边。用来定义任务运行的开始节点,所以它没有上一个节点。

  • Normal Edge:普通边。代表上一个节点运行完成后立即进入下一个节点。比如在调用Tools后获得结果后,立刻进入LLM推理节点。

  • Conditional Edge:条件边。代表上一个节点运行完成后,需要根据条件跳转到某个节点,因此这种边不仅需要上游节点、下游节点,还需要一个条件函数,根据条件函数的返回来决定下游节点。

在上图中,Check Relevance就是一个条件边,它的上游节点是检索相关文档,条件函数是判断文档是否相关,如果相关,则进入下游节点【产生回答】;如果不相关,则进入下游节点【重写输入问题】。

在构建好StateGraph,并增加Node和Edge后,可以通过compile编译成可运行的应用:

app = graph.compile()

接下来你就可以调用这个app来完成你的任务。


PART 04

图片
图片

LangGraph构建基础Agent


我们可以粗暴的认为LangGraph就是把现在黑盒的AgentExecutor揉碎掰开,允许你定义内部的细节结构(用图的方式),从而实现更强大的功能。那么我们当然可以用LangGraph来重新实现原来的AgentExecutor,即实现一个最基础的ReAct范式的Agent应用。

对应的Graph如下:

图片

简单的实现代码如下(省略了部分细节):

# 定义一个Graph,传入state定义(参考上图state属性)
workflow = StateGraph(AgentState)

# 两个节点

#节点1: 推理节点,调用LLM决定action,省略了runreason细节
workflow.add_node('reason', run_reason)

#节点2: 行动节点,调用tools执行action,省略executetools细节
workflow.add_node('action', execute_tools)

#入口节点:总是从推理节点开始
workflow.set_entry_point('reason')

#条件边:根据推理节点的结果决定下一步
workflow.add_conditional_edges(
'reason',
should_continue, #条件判断函数(自定义,根据状态中的推理结果判断)
{
'continue': 'action', #如果条件函数返回continue,进action节点
'end': END, #如果条件函数返回end,进END节点
},
)

#普通边:action结束后,总是返回reason
workflow.add_edge('action', 'reason')

#编译成app
app = workflow.compile()

#可以调用app了,并使用流式输出
inputs = {'input': 'you task description', 'chat_history': []}
for s in app.stream(inputs):
print(list(s.values())[0])
print('----')

代码中的注释对graph构建的细节做了解释。显然,这要比简单的使用agentExecutor要复杂的多,但同时也展示了LangGraph在构建LLM应用时强大的控制能力:通过Graph的定义,可以对一个LLM应用的处理过程进行非常细节的编排设计,从而满足大量复杂场景的AI Agent应用。

由于LangGraph刚推出不久,一些细节与易用性在后期也会不断完善。比如未来是否会提供更直观的定义界面等,也值得期待。在后续的文章中,我们将逐渐实践几个代表性场景下的LangGraph的应用,比如代码助手,自省式RAG,多Agent应用等,敬请期待。

图片

END

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多