LangChain补充五:Agent之LangGraph的使用

news/2024/11/10 15:03:12/文章来源:https://www.cnblogs.com/ssyfj/p/18308248

一:LangGraph入门

https://www.51cto.com/article/781996.html
https://blog.csdn.net/weixin_41496173/article/details/139023846
https://blog.csdn.net/wjjc1017/article/details/138518087
https://langchain-ai.github.io/langgraph/
https://langchain-ai.github.io/langgraph/tutorials/
https://langchain-ai.github.io/langgraph/how-tos/

(一)Chain、LangChain代理、LangGraph

1.Chain:线性执行的顺序链

LangChain的核心优势在于其能够轻松构建自定义链,这些链通常是线性的,类似于有向无环图(DAG),每个步骤都严格按顺序执行,每个任务只有一个输出和一个后续任务,形成一个没有循环的线性流程。
在遇到复杂任务时,比如第一次搜索没有找到想要的内容,我们可能需要进行第二次、第三次搜索,甚至可能需要调用网络搜索来完成。在这种情况下,顺序执行的任务(DAG)显然无法满足需求。
请求方和搜索方之间需要经历多次来回沟通,请求方可能会要求搜索方根据反馈调整搜索策略,这种多次的循环沟通才能逐步逼近最终答案。

2.LangChain代理:循环图,但是无法控制

这种情况下,我们需要的不再是DAG,而是一个循环图,它能够描述多个参与者之间的多轮对话和互动,以确认最终的答案。这种循环图能够处理更模糊、更复杂的用例,因为它允许系统根据反馈进行调整和迭代。那么,在循环图的运行模式就是智能代理,也就是AI Agent。

3.LangGraph,可以对智能代理进行更多的控制

在实际应用过程中,我们发现需要对智能代理进行更多的控制。例如,我们可能希望智能代理始终首先调用特定工具,或者我们可能希望对工具的调用方式有更多的控制,甚至可能希望根据智能代理的状态使用不同的提示。为了解决这些问题,LangGraph提出了“状态机”的概念。通过状态机为图创建对应的状态机,这种方法可以更好地控制智能代理的行动流程,使其更加灵活和有效地处理复杂任务。

补充:LangChain代理和LangGraph对比

 
可靠性
灵活性
Langchain 代理
可靠性较低,因为 LLM 需要在每个步骤上做出正确的决策
更灵活,因为 LLM 可以选择任何动作序列
LangGraph
可靠性更高,因为控制流已经设置好,LLM 在每个节点上有具体的任务
灵活性较低,因为动作受限于在每个节点上设置控制流

(二)LangGraph概念

1.StateGraph(状态图):包含了状态表示的一个图结构

StateGraph是一个类,它负责表示整个图的结构;需要通过传入一个状态定义来初始化这个类(图状态),这个图状态代表了一个中心状态对象,它会在执行过程中不断更新。这个图状态对象由图中的节点更新,节点会以键值对的形式,返回对状态属性的操作。
状态对象的属性可以通过两种方式更新,在定义图状态的时候,需要指定属性的更新方式:(可以有多个属性,每个属性需要单独指定一种更新方式)
  • 覆盖更新:如果一个属性需要被新的值替换,我们可以让节点返回这个新值进行替换。
  • 增量更新:如果一个属性是一个动作列表(或类似的操作),我们可以在原有的列表上添加新的动作。
旅行为例:
1.输入目的地(Edges里面的起始边)
2.任务规划,包括预定航班、预订酒店(Nodes里面的各个节点)
3.任务规划之间是有关联关系的,比如航班夜晚,我们就不要酒店了,航班改中间位置就要修改酒店地址,之间有因果关系(Edges里面的边,分为普通边和条件边,普通边就是任务有序执行,条件边就是可能根据情况不一样)
4.StateGraph类就是这样的旅行计划,而节点就像是规划旅行的不同步骤,比如确定目的地、预定航班和预定酒店。每个步骤都会更新你的旅行计划,可能是完全替换旧的计划,也可能是添加新的信息到现有的计划中

2.Nodes(节点)

创建了StateGraph之后,需要向其中添加节点;每个节点都代表一个任务,它们执行的结果会影响StateGraph的状态。这些节点通过边相互连接,形成了一个有向无环图(DAG),确保了任务的正确执行顺序。
上面旅行计划的例子,Nodes(节点)就好像旅行计划中需要完成的任务,例如:预定航班、预订酒店。Nodes(节点)接受旅行计划(图状态对象)作为输入,并输出一个更新后的任务状态,例如:完成酒店的预订

3.Edges(边)

Edges(边)是连接Nodes(节点)并定义StateGraph(状态图)中节点执行顺序的关键部分。边主要分为以下几种:
  • 起始边(Starting Edge):作为图的开始,比如在旅行计划中,起始边就是确定你的目的地。一旦目的地被确定,你的旅行计划就可以开始执行了。
  • 普通边(Normal Edges):这些边表示一个节点总是要在另一个节点之后被调用。在旅行计划中,普通边就像是确定了任务执行的顺序。例如,在找到合适的航班之后,你可能会决定预订酒店。这个顺序确保了任务的有序执行。
  • 条件边(Conditional Edges):使用函数(通常由LLM提供)来确定首先调用哪个节点。在旅行计划中,条件边就像是根据你的喜好或者天气情况来决定你的下一步行动。比如,如果你发现没有合适的航班,你可能会选择推迟预订酒店,而去查找火车车票。条件边提供了灵活性,使得系统可以根据不同的情况来调整执行的顺序。
边定义了节点之间的依赖关系和执行顺序。起始边确定了图的开始,普通边确保了任务的正确执行顺序,而条件边则根据特定的条件来决定下一步的操作

二:LangGraph使用

(一)步骤归纳

1.初始化model和tools,可以通过agent关联models和tools
2.定义图状态,包括各个属性比如:用户输入(覆盖),聊天历史(增量),中间步骤(代理采取的行动和相应的观察),代理结果(代理的响应)
3.定义图节点,包括1中的代理、工具
4.定义边的逻辑判断(条件边)
5.定义工作流状态图a.通过图状态初始化工作流b.添加节点、边c.编译工作流成runnable
6.执行状态图

(二)实例一:状态图stateGraph和节点node的使用(不含图状态和注解)

from langgraph.graph import START, StateGraph#1.不涉及model和tool#2.不涉及图状态#3.定义图节点,定义节点需要设置一个节点的action(函数)
def my_node(state):return {"x": state["x"] + 1, "y": state["y"]-1,"z":10}#4.不需要条件边#5.定义工作流状态图
builder = StateGraph(dict)  #注意:没有传入图状态,dict默认全部input的字段都作为图状态属性传入,比如后面的x,y都是StateGraph的state
builder.add_node("my_fair_node", my_node)   #添加节点
builder.add_edge(START, "my_fair_node") #添加起始边#6.编译
graph = builder.compile()   
step1 = graph.invoke({"x": 1,"y":2})
print(step1)
如果我们尝试更新节点的action,会发现每一步的图状态更新和节点的返回结果相关
def my_node(state):return {"x": state["x"] + 1}
我们传递了y,但是节点没有返回

(二)实例二:状态图stateGraph和节点node的使用(包含图状态和注解)

from langchain_core.runnables import RunnableConfig
from typing_extensions import Annotated, TypedDict
from langgraph.graph import StateGraph#1.不涉及model和tool#2.定义图状态
def reducer(a: list, b: list | None) -> list: #进行追加if b is not None:return a + breturn aclass State(TypedDict): #这里是图中状态的定义,也是标识运行时的input中哪些字段可以透传给状态图#https://blog.csdn.net/randy521520/article/details/133826255x: Annotated[list, reducer] #Annotated: 用于添加类型注解的装饰器。可以在类型提示中添加额外的元数据信息;这里表示更新list数据的时候,通过reducer方法进行更新(第一个参数时原数据list,第二个参数是要更新的数据int)class ConfigSchema(TypedDict):  #这里是标识运行时的RunnableConfig中哪些字段可以透传给状态图r: float#3.定义图节点,定义节点需要设置一个节点的action(函数)
def node_runner(state: State, config: RunnableConfig) -> dict: #传入图状态,可以进行修改;还可以传入config,config在调用时传递,同时还需要在创建状态图时再声明哪些变量可以传递到图中r = config["configurable"].get("r", 1.0) #从config中获取配置字段值print(state["x"])x = state["x"][-1] #获取最新的状态next_value = x * r * (1 - x)    #随便的操作return {"x": [next_value],"y":10}    #注意:返回的值,会通过注解里面的reducer进行更新(作为第二个参数),所以类型要一致#4.不需要条件边#5.定义工作流状态图,没有边
graph = StateGraph(State, config_schema=ConfigSchema)
graph.add_node("A", node_runner) #添加节点,包括节点的action
graph.set_entry_point("A")  #设置进入节点
graph.set_finish_point("A")  #设置结束节点#6.编译
compiled = graph.compile()
step1 = compiled.invoke({"x": [0.5,1],"y":0.3}, {"configurable": {"r": 3.0}})
print(step1)
可以看到,我们显式定义了图状态里面的属性后,节点返回的数据在更新图状态时只会去获取图状态定义中的字段进行更新

(三)实例三:包含了model和tool的使用

这里用到了一个新的模型anthropic.claude-3-5-sonnet,Claude 3 是一系列最先进的人工智能(AI)模型,可让您根据自己的需求,从智能、速度和成本方面考虑选择最适合的组合。并且该模型能够可靠地从图像等非结构化数据中提取信息。
https://docs.anthropic.com/zh-CN/docs/intro-to-claude
但是有国家限制,离谱了,得到API Key也没有用,还要配合技术才行
from typing import Literalfrom langchain_core.messages import ToolMessage
from langchain_openai import ChatOpenAI
from langchain_core.tools import tool
from langgraph.checkpoint import MemorySaver
from langgraph.graph import END, StateGraph, MessagesState
from langgraph.prebuilt import ToolNode
from langchain.prompts import MessagesPlaceholder,ChatPromptTemplate#1.初始化model和tools,可以通过agent关联models和tools
@tool
def search(query: str):"""Call to surf the web."""# This is a placeholder, but don't tell the LLM that...if "sf" in query.lower() or "san francisco" in query.lower():return ["It's 60 degrees and foggy."]return ["It's 90 degrees and sunny."]tools = [search]prompt = ChatPromptTemplate.from_messages([("system", "You are a helpful assistant"),MessagesPlaceholder("chat_history", optional=True),("human", "{messages}"),MessagesPlaceholder("agent_scratchpad", optional=True),])#model在这里用于选取tools
model = prompt | ChatOpenAI(model="gpt-3.5-turbo", temperature=0).bind_tools(tools)#2.定义图状态,这里使用的MessagesState,属性如下:
# class MessagesState(TypedDict):
#     messages: Annotated[list[AnyMessage], add_messages]# 3.定义图节点
# tool_node = ToolNode(tools) 这也是一种方法,定义tool和model的运行action
tools_by_name = {tool.name: tool for tool in tools}
def tool_node(state: dict):result = []for tool_call in state["messages"][-1].tool_calls:tool = tools_by_name[tool_call["name"]]observation = tool.invoke(tool_call["args"])result.append(ToolMessage(content=observation, tool_call_id=tool_call["id"]))return {"messages": result}def call_model(state: MessagesState):response = model.invoke(state)return {"messages": [response]} #add_messages兼容list和非list,都会转成list# 4.定义边的逻辑判断(条件边),判断是否继续
def should_continue(state: MessagesState) -> Literal["tools", "__end__"]: #Literal用于限制返回的值的可选值messages = state['messages']last_message = messages[-1]if last_message.tool_calls: #判断models是否返回tools调用,有则告诉调用tools节点,否则结束return "tools"return END# 5.定义工作流状态图
# 5.a.通过图状态初始化工作流
workflow = StateGraph(MessagesState)# 5.b.添加节点、边
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')# 5.c.编译工作流成一个runnable,通过invoke调用
app = workflow.compile()# 6.执行状态图
final_state = app.invoke({"messages": "what is the weather in sf"},config={"configurable": {"thread_id": 42}}
)print(final_state["messages"][-1].content)
 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/745424.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

LangChain补充四:Agent知识点和案例补充

https://www.alang.ai/langchain/101/lc07 一:基本流程和概念 (一)概念 LangChain Agent的核心思想是,使用大语言模型选择一系列要执行的动作。 在Chain中,一系列动作是硬编码在代码中的。 在Agent中,大语言模型被用作推理引擎,以确定要采取的动作及其顺序。 它包括 3 个…

2024-07-17 如何在vscode部署你的代码块,从而在新建页面时能快速搭建模板(windows环境)

步骤一:打开vscode,按住ctrl+shif+p唤出命令窗口 步骤二:在窗口中输入命令,并回车Preferences: Open User Snippets 对,就是这个代码片段,接着输入你想添加代码的某某语言or脚本,比如我要添加vue的代码片段输入vue,回车,会显示vue.json文件出来给你更改,我的是这样 注…

[题解]POJ3675 Telescope——求多边形与圆相交部分的面积

POJ3675 Telescope 题意简述 多测。每次给定一个\(N\)边形(保证相邻输入的顶点在多边形上也是邻接的),再给定一个以\((0,0)\)为圆心,半径为\(r\)的圆。 请计算出多边形和圆相交部分的面积(保留\(2\)位小数)。\(3\le N\le 50\) \(0.1\le r\le 1000\) \(-1000\le x_i,y_i\l…

【专题】2023中国机器人产业分析报告PDF合集分享(附原数据表)

原文链接:https://tecdat.cn/?p=34144 原文出处:拓端数据部落公众号 仿生机器人作为一类结合了仿生学原理的机器人,具备自主决策和规划行动的能力,正逐渐进入大众视野。它们的核心技术要素包括感知与认知技术、运动与控制技术、人机交互技术和自主决策技术。 阅读原文,获…

Splay 学习笔记

Splay 树, 或 伸展树,是一种平衡二叉查找树,它通过 Splay/伸展操作 不断将某个节点旋转到根节点,使得整棵树仍然满足二叉查找树的性质,能够在均摊 O(\log N) 时间内完成插入,查找和删除操作,并且保持平衡而不至于退化为链。 Splay 树由 Daniel Sleator 和 Robert Tarjan …

redis学习-12(实现分布式锁、消息队列、缓存一致性问题、单线程快的原因、跳跃表)

引用以下内容: redis实现分布式锁:Redis分布式锁-这一篇全了解(Redission实现分布式锁完美方案) Redis实现分布式锁的7种方案,及正确使用姿势! redis实现消息队列Redis 的学习教程(十)之使用 Redis 实现消息队列 缓存一致性问题 想要保证数据库和 Redis 缓存一致性,推荐…

数据的运算(上)

逻辑门电路多路选择器和三态门加法器 一位全加器并行进位加法器

idea开发工具配置git,连接到gitee远程仓库

1. 打开idea,Settings里找到如下位置,正常idea会自动找到git,test测试,显示版本号说明正常 2. 创建本地Git仓库,默认就是当前项目路径, 不要修改,直接创建 3. 创建后自动识别出待提交的文件,输入说明,提交,提交后让输入git名称和邮箱,设置并提交,提交成功。我这个项…

使用Apache POI 处理Miscrosoft Office各种格式文件

介绍 Apache POI 是一个处理Miscrosoft Office各种文件格式的开源项目。简单来说就是,我们可以使用 POI 在 Java 程序中对Miscrosoft Office各种文件进行读写操作。一般情况下,POI 都是用于操作 Excel 文件。Apache POI 的应用场景: ● 银行网银系统导出交易明细 ● 各种业务…

闲话 717 - LGV 引理的小应用

717这是我们的某一天的联考题目:\(n\le 500\)。 显然使用平面图完美匹配计数可以获得 \(O(n^6)\),但是有一种神秘的对路径的双射。当时我们都认为这是超级人类智慧,但是今天看书发现是书上的某个例的题的方法(有不同)。。 考虑对正六边形的菱形密铺方案数(上图)。可以等…

useHeadSafe:安全生成HTML头部元素

title: useHeadSafe:安全生成HTML头部元素 date: 2024/7/17 updated: 2024/7/17 author: cmdragon excerpt: 摘要:“useHeadSafe”是Vue.js组合函数,用于安全生成HTML头部元素,通过限制输入值格式避免XSS等安全风险,提供了安全值白名单确保只有安全属性被添加。 categor…

Goby漏洞发布 | CVE-2024-4879 ServiceNowUI /login.do Jelly模板注入漏洞【已复现】

漏洞名称:ServiceNowUI /login.do Jelly模板注入漏洞(CVE-2024-4879) English Name:ServiceNowUI /login.do Input Validation Vulnerability(CVE-2024-4879) CVSS core: 9.3 漏洞描述: ServiceNow 是一个业务转型平台。通过平台上的各个模块,ServiceNow 可用于从人力资…