langgraph中的接续运行

        在langgraph很多的人把Durable Execution译做持久化执行,容易与前面将的持久化混淆,其本意是当程序在某个点中断,可能是有意为之,比如让用户介入,也可能是无意为之,比如调用远程大模型超时,langgraph提供了机制,保证可以从中断点进行执行,更像下载时的断点续传,所以感觉翻译成接续运行更贴切。

     1.接续一致性问题

        为了理解接续运行机理,首先必须弄明白从中断中恢复运行时接续点在哪里?在langgraph中,并非从中断执行的那行代码开始,针对以下三种不同的情况,接续点定义如下:

        1)当使用Graph API时,接续点为发生中断的节点的第一行代码

        2)当在一个节点内部调用一个子图发生中断时,接续点为调用子图节点的父节点,而在子图内部恢复运行时从发生中断的子图节点第一行代码开始

        3)当使用函数式API时,接续点为发生中断的主函数的第一行代码

        从以上分析可知,在恢复运行时的接续点与发生中断的代码之间有其他的操作步骤,工作流恢复运行时会重放起始点到中断代码之间的所有步骤,因此必须保证重放的步骤的执行结果与中断前的执行结果完全一致。

        以下三种情况会导致接续的不一致性,

        1)非确定性操作,比如生成随机数,每次调用会生成不同的随机数;

        2)有副作用的操作,比如写文件;

        3)第三就是非幂等操作,

        这三类操作如果已经执行了一次,当从中断中恢复运行时,如果再次执行,则产生不可预料的后果。

     2.实现接续一致性

        为实现接续一致性,针对上面所说的导致接续不一致性的三种情况采取不同的处理策略:

        1)针对非确定性操作,封装到一个任务或节点内,从而借助langgraph的持久化层保存运行的结果

        2)针对有副作用的操作,封装到单独的任务中,从而在接续运行时不再重复执行这些任务

        3)针对非幂等的操作,通过使用幂等的键并根据已有数据进行验证,实现操作幂等

      2.1非确定性操作封装

        以下代码为包含非确定性操作的示例。如果发生中断后,再次进入时random会是一个与中断前执行不同的值,执行的分支与第一次可能是不同的。

from langgraph.func import entrypoint
import random

@entrypoint(checkpointer=checkpointer)
def workflow(inputs: dict) -> int:
    input_value = inputs["input_value"]
    random = random.randint(1, 10)

    if random > input_value:
        result = slow_task(1).result()
        value = interrupt("question")
    else:
        result = slow_task(2).result()
        value = interrupt("question")

    return {
        "result": result,
        "value": value
    }

        以下代码把获取随机数非确定性操作封装到一个任务内,因为任务的执行结果会被持久化,所以如果传入的参数中input_value值不变,random从持久化层获取,也是不变的,所以执行路径也是不变的,具体如下:

from langgraph.func import entrypoint
import random
@task
def get_random():#该任务用于获取随机数
    return random.randint(1, 10)
@entrypoint(checkpointer=checkpointer)
def my_workflow(inputs: dict) -> int:
    input_value = inputs["input_value"]
    random = get_random().result()

    if random > input_value:
        result = slow_task(1).result()
        value = interrupt("question")
    else:
        result = slow_task(2).result()
        value = interrupt("question")

    return {
        "result": result,
        "value": value
    }

     2.2有副作用操作封装

        以下代码访问一个网络地址并把结果保存在result中,如果发生中断后恢复执行,还需要再次请求同一个地址:

#只有一个节点的图,在节点中访问一个网络地址

from typing_extensions import TypedDict, NotRequired
from langgraph.graph import StateGraph, START, END
from langgraph.checkpoint.memory import InMemorySaver
import requests

class State(TypedDict):
    url: str
    result: NotRequired[str]
def my_node(state: State):
    result = requests.get(state['url']).text[:1000] 
    return {
        "result": result
    }   
checkpointer = InMemorySaver()
graph_builder = StateGraph(State)
graph_builder.add_node("my_node", my_node)
graph_builder.add_edge(START, "my_node")
graph_builder.add_edge("my_node", END)
graph = graph_builder.compile(checkpointer=checkpointer)

config = {"configurable": {"thread_id": 1}}
result = graph.invoke({'url': 'https://www.baidu.com'}, config=config)
print(result)

       以下代码把网络请求封装到一个任务内,从而接续运行时不需再次进行网络请求:

from typing_extensions import TypedDict, NotRequired
from langgraph.graph import StateGraph, START, END
from langgraph.checkpoint.memory import InMemorySaver
import requests

class State(TypedDict):
    url: str
    result: NotRequired[str]
@task 
def http_request(url: str)->str:
    return requests.get(url).text[:1000] 
    
def my_node(state: State):
    result = http_request(state['url']).result()
    return {
        "result": result
    }   
checkpointer = InMemorySaver()
graph_builder = StateGraph(State)
graph_builder.add_node("my_node", my_node)
graph_builder.add_edge(START, "my_node")
graph_builder.add_edge("my_node", END)
graph = graph_builder.compile(checkpointer=checkpointer)

config = {"configurable": {"thread_id": 1}}
result = graph.invoke({'url': 'https://www.baidu.com'}, config=config)
print(result)

     2.3非幂等操作处理

        把非幂等操作转换为幂等操作的思路与传统编程思路一致,此处不再举例。

     3.恢复工作流

        针对实现了继续一致性的工作流,可以从中断或者错误中恢复工作流的运行。

        1)从中断中恢复

        在工作流中调用interrupt暂停工作流的执行,并等待用户的输入。用户输入后,使用携带更新后状态的Command继续执行工作流,并且保证与预期的一致

        2)从错误中恢复

        如果在执行工作流时报错了,重新执行工作流时(调用invoke、stream或者astrem),调用的输入传入None,并且携带上次执行工作流相同的thread_id

     4.持久化模式

        langgraph基于持久化实现了接续运行中的数据一致性,langgraph同时提供了三种持久化模式供程序员选择,三种持久化模式是性能和一致性的权衡,程序员可根据具体的需求做选择。

        三种持久化模式为:

        1)exit:整个工作流执行全部完成后才做持久化,中间状态并不保存。该模式适合需要长时间运行的任务,但不能支持终端,也不支持从错误中恢复

        2)async:执行完一个超步后,在执行下一个超步时,对本超步状态进行持久化。采该改模式时,如果在执行一个超步时程序崩溃,前一超步的状态可能未持久化

        3)sync:执行完一个超步后,在执行下一个超步前,对本超步的状态进行持久化。该模式提供了最高级别的持久化,但带来很大的性能消耗

        

        

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值