muse-agent-example/src/muse/编排/槽位约束.py
zizi e99a952dd0 W05 任务状态、租约、事件与恢复:任务状态机、短事务租约、事件续接、恢复控制与作用域互斥。
按 R2 串行阶段整理提交;包内文件为该阶段交付(含后续小增量),状态以工作包清单为准。
2026-09-10 19:25:40 +08:00

47 lines
2.0 KiB
Python

"""依赖是全部满足才可执行;保护节点必须覆盖每个终点的依赖闭包。"""
from __future__ import annotations
from muse.任务运行.接口 import 执行计划, 步骤处理器
from muse.共享.错误 import Muse错误
class 流程校验错误(Muse错误):
错误码 = "MUSE_FLOW_INVALID"
def 核对流程(计划: 执行计划, 处理器: dict[str, 步骤处理器], 必需保护: tuple[str, ...]) -> None:
节点 = {项.步骤ID: 项 for 项 in 计划.步骤}
if not 节点 or len(节点) != len(计划.步骤):
raise 流程校验错误("流程步骤不能为空或重名")
祖先: dict[str, set[str]] = {}
访问中: set[str] = set()
def 遍历(身份: str) -> set[str]:
if 身份 in 访问中:
raise 流程校验错误("流程依赖存在环")
if 身份 not in 节点:
raise 流程校验错误("依赖节点不存在")
if 身份 in 祖先:
return 祖先[身份]
访问中.add(身份)
结果: set[str] = set()
for 依赖 in 节点[身份].依赖:
结果 |= 遍历(依赖) | {依赖}
if 节点[依赖].输出合同 != 节点[身份].输入合同:
raise 流程校验错误("相邻步骤的输入输出合同不兼容")
访问中.remove(身份)
祖先[身份] = 结果
return 结果
for 身份 in 节点:
遍历(身份)
处理 = 处理器[身份]
if 节点[身份].角色 != 处理.角色 or not set(节点[身份].工具) <= set(处理.允许工具):
raise 流程校验错误("槽位角色或工具许可不兼容")
已被依赖 = {依赖 for 项 in 计划.步骤 for 依赖 in 项.依赖}
for 终点 in 节点.keys() - 已被依赖:
路径保护 = {处理器[身份].保护职责 for 身份 in 祖先[终点] | {终点}}
if not set(必需保护) <= 路径保护:
raise 流程校验错误("存在绕过必需保护的流程终点")