前言
LangGraph
是一个使用 LLM 和 LangChain 构建有状态多参与者应用程序的库。
LangChain 允许您使用 LCEL(LangChain 表达式语言)构建链
AI菜鸟向前飞 — LangChain系列之六 - 深入浅出LCEL与Chain(上篇)
AI菜鸟向前飞 — LangChain系列之七 - 深入浅出LCEL与Chain(中篇)
AI菜鸟向前飞 — LangChain系列之八 - 深入浅出LCEL与Chain(下篇)
但是,用它只能构建一个DAG(有向无环图),不能有环。
例如,在这种情况下,使用 RAG 的应用程序可能无法正常工作。
这是因为,即使在第一个数据获取步骤中返回无用信息,处理也不会重新获取数据而继续到下一步。
这个问题的解决方案LangGraph
,它扩展了 LCEL,能够循环调整跨越多个步骤的 Chain(或 Actor)。
如何更好地理解Graph
接下来,我会用一种易于大家理解的方式来介绍,
首先,本篇将两个重要要素:Node(节点)和Edge(边),如图所示:
这样就可以“组装”所谓的“graph”,即LangGraph的“原始”形态
一个最简单的形态
看图说话:
node_1、node_2顾名思义,即为Node(节点)
通常用add_node来为graph添加节点
带方向的连接线为边Edge(边)
通常用add_edge来连接各个节点,进而形成“边”
代码如下
from langgraph.graph import Graph
def node1(input1: str) -> str:
return f"Hello, {input1}"
def node2(input2: str) -> str:
return f"{input2},有什么我可以帮忙的吗?"
graph = Graph()
graph.add_node("node1", node1)
graph.add_node("node2", node2)
graph.add_edge("node1", "node2")
graph.set_entry_point("node1")
graph.set_finish_point("node2")
app = graph.compile()
app.invoke("Song榆钱儿")
输出结果
Hello, Song榆钱儿, 有什么我可以帮忙的吗?
🏁小技巧:可以把程序稍微改动一下,可以看到程序在每一个Node(节点)的输出结果
for output in app.stream("Song榆钱儿"):
for key, value in output.items():
print(key ,"----", value)
输出结果
node1 ---- Hello, Song榆钱儿
node2 ---- Hello, Song榆钱儿,有什么我可以帮忙的吗?
当看到这里,大家会想用这个与用传统方式(如:正常写一段LLM代码逻辑,或采用LCEL)有什么区别?
不就是顺序执行,只不过强行套了一个“Graph”壳子而已...上面的例子确实是这样,不过你往下看:
再看一种“分支”“选择”模式,看过我之前发的这篇文档的读者,可能会想到这篇RouterChain.
AI菜鸟向前飞 — LangChain系列之九 - RouterChain的四种实现方式
在graph中称作“条件边”,即add_conditional_edges
代码如下所示:
from langgraph.graph import Graph, END
def node_entry(input: str) -> str:
return f"{input}"
def decide_which(input: str) -> str:
if "你好" in input:
return "greeting"
elif "家乡" in input:
return "hometown"
return "end"
def node_greeting(input: str) -> str:
return f"你说的是:{input}\n我回答你:你好啊,我的朋友"
def node_hometown(input: str) -> str:
return f"你所提到的内容是: {input}\n我回答你:家乡在通化市"
graph = Graph()
graph.add_node("entry", node_entry)
graph.add_node("node_greeting", node_greeting)
graph.add_node("node_hometown", node_hometown)
graph.set_entry_point("entry")
graph.add_conditional_edges(
source="entry",
path=decide_which,
path_map={
"greeting": "node_greeting",
"hometown": "node_hometown",
"end": END
},
)
graph.add_edge("node_greeting", END)
graph.add_edge("node_hometown", END)
app = graph.compile()
app.invoke("家乡")
输出结果
请思考
最后,让我们再看这样一种结构,并试着把写出代码。
注意:node_greeting和node_hometown各有一条往回指的Edge,为什么会有它的存在呢?