【AI开发】LangGraph基础

在LangGraph中有三个重要元素

  • StateGraph
  • Node
  • Edge

StateGraph

首先stategraph是用来描述整个图的,图中的状态会随着多个agent的工作不断的更新,节点node就是用来更新状态的
如何来定义一张图中的状态
每个应用的状态可能不同,所以我们需要根据具体应用场景来决定状态

from langgraph.graph import StateGraph
from typing import TypedDict, List, Annotated
import operator


class State(TypedDict):#该示例中状态定义了两个字段,输入input,allaction
    input: str
    all_actions: Annotated[List[str], operator.add]

graph = StateGraph(State)

Node

当我们创建好stategraph后,就可以向其中添加node(graph.add_node(name,value))

graph.add_node("model", model)
graph.add_node("tools",tool_executor)

同时node中还有个特殊的节点END,表示状态终止

Edge

先定义从哪个节点开始

graph.set_entry_point("节点名字")

分为normal edge 和conditional edge
normal edge定义了两个节点之间必然的先后关系
conditional edge通过条件来决定下一步的节点是哪里,需要三个东西:上游节点(upstream node)、判定函数、映射关系。
分别表示来自哪里、如何判断去哪、条件结果返回

graph.add_conditional_edge(
	"model", #model是上游节点的名字
	should_continue,  #这是
	{
		"end":END,
		"continue":"tools"
	}
)

上述代码中,上游节点是model,判定条件是should_continue,如果should_continue返回end,那就是END节点,如果返回continue那就去tools节点

示例

我们来用一个具体的示例来展示langgraph
首先先定义stategraph

from langgraph.graph import StateGraph
from typing import TypedDict, List, Annotated,Union
import operator
from langchain_core.agents import AgentAction,AgentFinish
from langchain_core.messages import BaseMessage
####################################初始化一个stategraph
class AgentState(TypedDict):
    #定义一些初始化的功能
    #用户输入字符串
    input:str
    #对话中之前的消息列表
    chat_history:list[BaseMessage]
    #agent执行过后的结果,是要继续执行action还是完成finish,还是没有任何的状态None
    agent_outcom: Union[AgentAction,AgentFinish,None]
    #动作列表和相应的观察结果
    #operator.add表明对这个状态的操作是添加到现有值上而不是覆盖掉

再定义两个工具

##################外部的工具,应包含工具描述,工具输入内容的JSON模式,调用的函数
from langchain.tools import BaseTool, StructuredTool, Tool, tool
import random

@tool("lower_case", return_direct=True)
def to_lower_case(input:str) -> str:
    """返回全部小写的输入"""
    return input.lower()

@tool("random_number",return_direct=True)
def random_number_maker(input:str) -> str:
    """返回0-100之间的随机数"""
    return random.randint(0,100)

tools = [to_lower_case,random_number_maker]

to_lower_case.run('ROONIE')

最后就会输出roonie

现在我们来写一个完整的demo

from langgraph.graph import END, StateGraph
from langgraph.graph import StateGraph
from typing import TypedDict, List, Annotated,Union
import operator
from langchain_core.agents import AgentAction,AgentFinish
from langchain_core.messages import BaseMessage
from langchain_core.agents import AgentFinish
from langgraph.prebuilt.tool_executor import ToolExecutor
from langchain.tools.render import format_tool_to_openai_function

####################################初始化一个stategraph##############################
class AgentState(TypedDict):
    #定义一些初始化的功能
    #用户输入字符串
    input:str
    #对话中之前的消息列表
    chat_history:list[BaseMessage]
    #agent执行过后的结果,是要继续执行action还是完成finish,还是没有任何的状态None
    agent_outcome: Union[AgentAction,AgentFinish,None]
    #动作列表和相应的观察结果
    #operator.add表明对这个状态的操作是添加到现有值上而不是覆盖掉
    intermediate_steps: Annotated[list[tuple[AgentAction, str]], operator.add]
#############定义一些自定义的tool##############################
@tool("lower_case", return_direct=True)
def to_lower_case(input:str) -> str:
    """返回全部小写的输入"""
    return input.lower()

@tool("random_number",return_direct=True)
def random_number_maker(input:str) -> str:
    """返回0-100之间的随机数"""
    return random.randint(0,100)

tools = [to_lower_case,random_number_maker]

tool_executor = ToolExecutor(tools)


####################################定义另一个stategraph################################
from typing import TypedDict, Annotated, Sequence
import operator
from langchain_core.messages import BaseMessage
#不需要中间步骤,全部都在message里面
class AgentState_chatmodel(TypedDict):
    message: Annotated[Sequence[BaseMessage], operator.add]
     


###################################初始化节点############################################

#定义agent
def run_agent(data):
    #调用agent可执行对象,并传入数据
    agent_outcome = agent_runnable.invoke(data)
    #返回代理的结果
    return {"agent_outcome":agent_outcome}

#定义执行工具的函数
def execute_tools(data):
    #获取最近的代理结果 - 这是在上面的agent中添加的关键字
    agent_action = data['agent_outcome']
    #执行工具
    output = tool_executor.invoke(agent_action)
    #打印代理操作
    print(f"The agent action is {agent_action}")
    #打印工具结果
    print(f"The tool result is {output}")
    #返回输出
    return {"intermediate_steps": [(agent_action, str(output))]}

#定义用于确定哪条conditional edge该走的逻辑
def should_continue(data):
    #如果代理结果是AgentFinish,那么返回“end”字符串
    #在设置图的流程时将使用这个
    if isinstance(data['agent_outcome'], AgentFinish):
        return "end"
    #否则,返回一个AgentAction
    #这里我们返回‘continue’字符串
    #在设置图的流程时也将使用这个
    else:
        return "continue"


#######################################################定义一个新的图(工作流)##################################################


workflow = StateGraph(AgentState)  #agentstate是上面初始化过的一个stategraph

#定义两个节点,我们将在他们之间循环
workflow.add_node("agent", run_agent)
workflow.add_node("action", execute_tools) #agent输出不满足我们的要求,那就继续调用它的action

#设置初始节点entrypoint
#将入口节点设为agent,表示这个节点是第一个被调用的
workflow.set_entry_point("agent")

#添加一个conditional egde
workflow.add_conditional_edges(
    #首先定义边的起始节点,我们用‘agent’,表示在调用agent节点之后将采取这些边
    "agent",
    #接下来我们加载将决定下一个调用哪个节点的函数
    should_continue,
    #最后我们传入一个映射,key是字符串,value是其他节点
    #将会发生的是,我们调用“should_continue”,然后其输出将与此映射中的key匹配,根据匹配情况调用相应的节点
    {
        "continue": "action", #如果是“continue”,则调用工具节点
        "end": END #END是一个特殊节点,表示图的结束
    }
)

#agent到action是需要conditional egde,但action到agent是不用的,因为action之后肯定要回到agent节点,所以加一个normal edge就行
workflow.add_edge('action', 'agent')

#最后进行编译compile,将其编译成一个LangChain可运行对象
app = workflow.compile()

我们来总结一下一个简单的LangGraph怎么写

  1. 定义一个stateGraph的类,以及它的一些初始化的功能,比如用户输入、中间步骤、对话历史、agent执行后的结果等
  2. 自定义一些tool,不定义的话相当于直接让调用的LLM来完成。把tool放入executor里
  3. 初始化节点:包含定义agent、定义工具函数、定义条件边的逻辑(比如agent返回结果是continue就继续,返回finish就end
  4. 开始实例化一个workflow:定义agent和action节点、设置初始节点、添加agent起始的conditional edge,添加action到agent的normal edge
  5. compile
  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值