首页 > 其他分享 >LangChain补充五:Agent之LangGraph的使用

LangChain补充五:Agent之LangGraph的使用

时间:2024-07-17 20:40:43浏览次数:6  
标签:node 状态 LangGraph tool LangChain state Agent tools 节点

一: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 + b
    return a

class State(TypedDict): #这里是图中状态的定义,也是标识运行时的input中哪些字段可以透传给状态图
    #https://blog.csdn.net/randy521520/article/details/133826255
    x: 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 Literal

from 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)
  <iframe style="display: none !important"></iframe>

标签:node,状态,LangGraph,tool,LangChain,state,Agent,tools,节点
From: https://www.cnblogs.com/ssyfj/p/18308248

相关文章

  • LangChain补充四:Agent知识点和案例补充
    https://www.alang.ai/langchain/101/lc07一:基本流程和概念(一)概念LangChainAgent的核心思想是,使用大语言模型选择一系列要执行的动作。在Chain中,一系列动作是硬编码在代码中的。在Agent中,大语言模型被用作推理引擎,以确定要采取的动作及其顺序。它包括3个组件:规划:将任......
  • LangChain让LLM带上记忆
    最近两年,我们见识了“百模大战”,领略到了大型语言模型(LLM)的风采,但它们也存在一个显著的缺陷:没有记忆。在对话中,无法记住上下文的LLM常常会让用户感到困扰。本文探讨如何利用LangChain,快速为LLM添加记忆能力,提升对话体验。LangChain是LLM应用开发领域的最大社区和......
  • 把LangChain跑起来的3个方法
    使用LangChain开发LLM应用时,需要机器进行GLM部署,好多同学第一步就被劝退了,那么如何绕过这个步骤先学习LLM模型的应用,对Langchain进行快速上手?本片讲解3个把LangChain跑起来的方法,如有错误欢迎纠正。Langchain官方文档地址:https://python.langchain.com/基......
  • LangChain 快速入门:构建你的第一个智能应用
    引言随着大型语言模型(LLM)的崛起,开发人员现在可以利用这些强大的工具来创建一系列创新的应用程序,从自动文档摘要到聊天机器人,再到智能客服系统。LangChain是一个开源框架,旨在简化与LLM的交互,帮助开发者轻松地构建和部署基于LLM的应用程序。本文将带你快速入门LangChain,通......
  • 简单剖析qwen-agent回答是怎么获取tool的
    openai是一家伟大的公司(虽然是closedai),当他们提出agent的概念后,就很神奇。之前通过langchain的langgraph进行写demo,就很好奇,他是怎么基于我的话自动去识别这句话是大模型的闲聊,那句话是大模型去调用tool1.现象1.和大模型打招呼,大模型知道回答,这没啥稀奇2.可是当问它某个地方的......
  • GPT-4从0到1搭建一个Agent简介
    GPT-4从0到1搭建一个Agent简介1.引言在人工智能领域,Agent是一种能够感知环境并采取行动以实现特定目标的系统。本文将简单介绍如何基于GPT-4搭建一个Agent。2.Agent的基本原理Agent的核心是感知-行动循环(Perception-ActionLoop),该循环可以描述如下:感知:Agent通过传感......
  • LangChain与RESTful API的交响曲:开发集成新篇章
    LangChain与RESTfulAPI的交响曲:开发集成新篇章在软件开发中,API(应用程序编程接口)是系统间交互的桥梁。RESTfulAPI作为API的一种风格,以其简洁、无状态和可缓存性而广受欢迎。LangChain作为一个多功能的语言处理工具链,其是否支持RESTfulAPI开发取决于其设计目标和集成的组......
  • 函数式编程的交响曲:探索LangChain对函数式编程特性的支持
    函数式编程的交响曲:探索LangChain对函数式编程特性的支持引言在现代软件开发中,函数式编程(FunctionalProgramming,FP)以其独特的优势,如无副作用、易于并行处理等,逐渐受到开发者的青睐。LangChain作为一个多语言编程工具链,其设计理念在于支持多种编程范式,包括函数式编程。......
  • 探索Web开发的无限可能:LangChain支持的Web框架全景
    探索Web开发的无限可能:LangChain支持的Web框架全景引言在现代Web开发中,选择合适的Web框架对于项目的成功至关重要。LangChain作为一个多语言编程工具链,提供了对多种编程语言的Web框架支持,使得开发者可以根据项目需求和技术栈偏好选择合适的框架。本文将详细介绍LangChain......
  • 释放LangChain潜能:精通性能优化的高级技巧
    释放LangChain潜能:精通性能优化的高级技巧引言LangChain作为一个多语言编程工具链,提供了强大的功能来简化开发流程和增强代码的执行效率。然而,随着项目规模的扩大和需求的增长,性能优化成为保持LangChain项目竞争力的关键。本文将深入探讨LangChain的性能优化技巧,包括代码......