AI agent DAG Node Contracts:输入输出边界不清,是工作流漂移的根因

HTMLPAGE 团队
14 分钟阅读

工作流失控常见根因不是模型能力不足,而是节点契约缺失。本文讲清 DAG 节点输入输出边界、前后条件和版本治理。

#AI agent #DAG #Node Contract #Workflow Reliability

很多团队有 DAG,但没有真正的节点契约。节点看起来能跑,系统却经常在跨节点交接时出错:字段缺失、状态歧义、重放失败。

如果把这件事讲得更直白:多数“工作流漂移”不是模型在胡说,而是节点边界从未被系统正式定义。

契约不是一层,而是三层

实际落地里,节点契约至少分为三层:

层级关注点由谁维护
数据契约输入/输出字段与类型工程团队
执行契约前置条件、重试、超时平台团队
风险契约副作用、补偿、审批治理/业务团队

只做数据契约,系统仍会在执行与风险层面失控。

最小节点契约模板

建议每个节点都具备以下字段:

  • inputSchema
  • outputSchema
  • preconditions
  • postconditions
  • timeoutMs
  • retryPolicy
  • sideEffects
  • compensationPolicy

其中 sideEffects 建议强制结构化,例如:

字段含义
effectType写库、外发、状态变更
target影响对象
idempotencyKey幂等识别
reversible是否可逆

没有这些字段,平台无法做统一补偿治理。

常见边界错位,不是“偶发 bug”

以下三类问题最常见,且会反复出现:

  1. 推断字段污染:上游把可选字段当推断结果,下游当必填输入。
  2. 输出落地不一致:输出写在临时上下文而非共享状态,导致重放丢失。
  3. 异常语义不一致:有的节点返回错误码,有的只返回文本。

这些问题本质上都是契约未被 runtime 强制执行。

契约验证应在两个时机执行

时机目标
发布前(静态验证)schema、前后条件、策略配置完整性
运行时(动态验证)输入合法性、状态合法性、副作用合法性

只做静态验证会漏掉状态类问题;只做运行时验证会把低级错误带到线上。

失败案例:版本升级后“兼容成功”,业务却失真

某团队将节点输出字段 risk_score 改为 risk.level,并加了兼容适配层,技术上未报错。问题是下游审批规则仍读取旧字段,导致高风险内容被当低风险通过。这个问题持续两周才被业务发现。

根因不是字段重命名,而是契约变更缺少“语义级回归”。

修复动作:

  • 在契约变更流程中引入语义回归样本。
  • 将关键字段标记为“策略依赖字段”。
  • 变更后自动验证相关规则命中情况。

契约发布治理:像 API 一样治理节点

建议引入以下规则:

  • 向后兼容:新增字段可选,删除字段走废弃周期。
  • 版本语义:major 破坏性、minor 增量、patch 修复。
  • 依赖声明:下游节点声明依赖字段与语义。

这样做的结果,是节点不再是“局部实现细节”,而是平台级可治理接口。

看板应直接暴露契约健康度

推荐新增三类指标:

  • 契约校验失败率
  • 语义回归失败率
  • 兼容适配触发率

兼容适配触发率长期高,通常说明该进入正式迁移,而不是无限期临时兼容。

一个节点契约示例

下面是一个“风险评估节点”的简化契约。重点不是字段形式,而是它把输入、输出、前置条件和副作用分开声明:

{
  "nodeId": "risk_review.v2",
  "inputSchema": {
    "draftArtifactId": "string",
    "evidencePackId": "string",
    "tenantPolicyVersion": "string"
  },
  "outputSchema": {
    "riskLevel": "low | medium | high",
    "blockedReasons": "string[]",
    "requiresHumanApproval": "boolean"
  },
  "preconditions": [
    "draftArtifact.status == 'ready'",
    "evidencePack.status == 'verified'"
  ],
  "postconditions": [
    "riskDecisionId is persisted"
  ],
  "sideEffects": [],
  "failurePolicy": {
    "validation_error": "stop",
    "policy_timeout": "human_review"
  }
}

这里最重要的设计是:风险评估节点不直接发布、不改客户状态、不写外部系统。它只产出可审计的 riskDecisionId。这样下游审批和发布节点可以引用同一个稳定事实,而不是重新解释一段文本。

契约评审应该问的 8 个问题

每个新节点上线前,至少要过一轮契约评审:

  1. 这个节点是否真的需要独立存在?
  2. 它的输出是否会被两个以上下游节点复用?
  3. 输入字段里哪些是用户提供,哪些是系统推断?
  4. 输出字段里哪些会影响策略或计费?
  5. 失败时是否返回结构化错误?
  6. 是否产生外部副作用?如果有,补偿动作是什么?
  7. 契约升级是否影响旧 run 恢复?
  8. 是否有语义回归样本覆盖关键字段?

这 8 个问题能拦住很多“写起来方便,跑起来混乱”的节点。

版本兼容:不要只看字段是否存在

很多团队以为字段还在就是兼容。实际上,语义变化比字段变化更危险。例如 riskLevel: medium 在 v1 表示“需要人工复核”,在 v2 表示“允许自动发布但记录风险”,字段没变,业务后果完全变了。

因此,节点契约版本至少要区分两类变化:

变化类型示例处理方式
结构变化新增字段、重命名字段schema 兼容与适配层
语义变化风险等级含义变化major 版本 + 回归样本

语义变化必须走更严格发布流程,不应该混在 patch 里悄悄上线。

Contract Registry 的作用

当节点数量超过十几个时,建议建立 contract registry。它至少提供:

  • 节点契约版本查询
  • 上下游依赖图
  • 废弃字段和废弃节点列表
  • 契约变更历史
  • 回归样本绑定关系

没有 registry,团队会很快失去对“哪些节点依赖哪些字段”的判断力。到那时,每次改节点都像在拆一座看不见结构的楼。

契约治理 Checklist

  • 节点输入输出均可 schema 校验
  • pre/post 条件由 runtime 强制检查
  • 失败输出同样结构化,不混杂自由文本
  • side effect 和补偿策略可验证
  • 节点契约变更有版本化与废弃周期
  • 关键字段变更触发语义回归测试

延伸阅读: