(十五)给执行过程设置停止条件
1. 先处理可以重复的读取
临时网络超时可以重试;字段不合法、没有权限、引用不存在,需要回到相应输入或逻辑。本例只对 TimeoutError 重试,最多三次,包括第一次执行:
"""读取超时后重试同一个节点;第三次读取成功。"""
from typing import TypedDict
from langgraph.graph import END, START, StateGraph
from langgraph.types import RetryPolicy
class State(TypedDict):
text: str
attempts = 0
def read(state: State):
global attempts
attempts += 1
# 用确定的失败次数模拟读取超时,便于实际核对调用了几次。
if attempts < 3:
raise TimeoutError("模拟读取超时")
return {"text": "第三次读取成功"}
builder = StateGraph(State)
builder.add_node("read", read, retry_policy=RetryPolicy(
max_attempts=3, initial_interval=0.01, jitter=False, retry_on=TimeoutError))
builder.add_edge(START, "read")
builder.add_edge("read", END)
result = builder.compile().invoke({"text": ""})
assert attempts == 3
print(attempts, result)RetryPolicy 为节点指定重试条件、次数和间隔。这里第一次和第二次超时,第三次返回文本,最终打印 attempts=3。实际读取还应把超时传给 HTTP 客户端,让一次等待本身有机会结束。
2. 分别记录图步数与模型工作量
recursion_limit 限制一次图执行经过的 supersteps,可以理解为图调度的一轮轮推进;它不等于 Python 递归深度,也不等于 token 数或工具总次数。并行节点可能在同一 superstep 中运行,模型内部也可能花很长时间才返回。
本项目的固定路线用 recursion_limit=12;工具循环还需要分别记录模型调用次数、工具调用次数、消耗 token 和总时长。某个计数达到任务设定值时,由程序结束循环并返回已完成的结果。读取超时后重试次数有限;写入动作先有幂等识别,再考虑重试。
对照第八节的一轮调用:第一次模型调用决定检索,工具执行一次,第二次模型调用形成回答。因此完成这一轮后,模型调用数是 2,工具调用数是 1。把它们混成同一个计数,就无法看出时间花在模型还是工具上;token 用量则从实际模型响应读取,离线回放没有真实模型的计费用量。
执行中的取消也要沿调用链传递。异步 HTTP 客户端可以接收取消,已经交给外部服务的工作却未必随本地等待一起停止;重开任务时先查看保存状态与业务记录,再决定继续执行哪项动作。