订单状态与政策可以同时读取,但必须等两者都回来才能判断申请是否满足条件。图把这种依赖表达出来:节点做一段工作,边表达顺序或分支,状态保存节点之间需要传递的信息。
图不是可靠性的自动来源。一个节点里如果包含没有幂等保护的支付请求,画成矩形之后仍然会重复执行。先理解图解决的控制问题,再把持久化接进来。
节点返回更新,不一定返回完整状态
退款核验节点读取订单和金额,返回 eligible=true 或拒绝原因。它不需要把全部聊天、历史事件和其他字段重新复制一次。明确更新范围,有助于避免覆盖无关状态。
节点也不天然可重试。纯计算通常容易重做;调用模型可能得到不同输出并产生费用;写业务系统可能有副作用。设计节点边界时,应考虑中断后哪些工作会一起重做。
例如把“生成摘要”和“提交退款”放在同一节点,模型请求成功、退款响应丢失后重跑,两个动作都可能再次发生。拆分能缩小重做范围,但仍需要保存完成结果和保护外部动作。
条件路由应该依据什么
模型可把用户自然语言转换为候选意图,确定性路由读取已经校验的字段。若金额未提供,进入澄清;若超过剩余可退金额,拒绝或人工核查;若符合条件,进入批准。
不应让模型自行选择绕过不可省略的业务检查。一个路由字符串也要限制在已注册节点集合内,不能把任意模型文本当作函数名执行。
# 普通 Python 示意:金额和资格已由前置校验写入状态。
def route(state):
if state["amount_minor"] is None:
return "clarify"
if not state["eligible"]:
return "reject"
return "approval"
这里的布尔值必须来自可信校验,不是把模型字符串 "false" 转成 bool。类型正确只是第一层,字段来源仍然重要。
并行阶段怎样汇合
订单查询与政策查询没有依赖,可以并行。汇合节点应等待必要输入都已就绪,再运行资格判断。一个分支失败时,是重试该分支还是让整个任务失败,要由引擎语义与业务要求共同决定。
在 LangGraph 中,多个并行节点对同一个没有合并规则的字段写入,可能触发并发更新错误,而不是简单“后写覆盖前写”。不同执行系统的默认行为不同,因此不能把一个通用直觉当成框架保证。
Reducer 明确多个更新怎样合并。列表相加适合追加证据,但合并顺序不应被误当成事实优先级。如果两个来源都描述订单状态,应该保留来源和版本,再由业务规则选择权威值,而不是采用列表最后一个。
追加列表为什么仍可能出错
分支 A 返回证据 E1,重试后又返回 E1。普通列表相加得到两个相同项。若后续用数量作为“证据充分”的指标,就会高估支持程度。
可按稳定证据 ID 去重,并在相同 ID 内容不一致时显式报冲突。这里的 ID 应对应同一份证据版本,不能只用文件名,否则更新后的内容可能被错误丢弃。
Reducer 还应考虑结合律、交换律和幂等性。结合律意味着分批合并不会改变结果;交换律意味着到达顺序不影响结果;幂等性意味着重复更新不会增加效果。不是所有业务合并都能满足三者,但应知道自己依赖哪一种顺序。
配套 reducer 示例下载展示按 ID 合并与冲突拒绝。它只处理内存数据,不给外部文件或数据库加锁。
循环与子图怎样控制复杂度
模型调查资料可能需要循环,但必须有总调用、时间和重复失败限制。图递归限制保护执行推进,不等于精确费用预算;一次节点可能包含多次内部请求,统计需要下沉到实际调用入口。
子图适合复用审批或调查流程。需要定义输入、输出与状态映射,避免父子图通过大量隐含共享字段耦合。子图的 Checkpoint 命名空间与恢复方式也应按实际版本核对,不能仅因为函数被拆开就假定可以独立恢复。
人工暂停发生在子图内部时,还需要找到应该恢复的中断。多个并行审批不能只用一个不带标识的 true,后面的 HITL 篇会说明如何关联决定与具体提案。
图版本为什么影响旧任务
旧任务暂停在 approval 节点,新版本把节点删除或更名,恢复器可能找不到位置。字段结构变化也可能让旧状态不满足新代码要求。
可以保留旧流程处理已开始任务,或在明确边界迁移状态。迁移不仅修改 JSON,还要确认新规则是否仍承认旧批准和旧业务结果。直接从旧 Checkpoint 分叉也不会撤销此前的外部动作。
下一篇从三个具体崩溃时刻解释持久化与恢复,再在 LangGraph 实战中对应到真实执行机制。