freeCodeCamp.org

Why Software Automation Needs a Layer Between Intent and Execution

8.5内容质量

TL;DR · AI 摘要

软件自动化应在意图与执行间增加中间层,以明确操作意图,提升系统灵活性与可维护性。当前工具常将意图与执行融合,导致变更执行机制时意图可能被改变,而分离层可解决此问题。

核心要点

  • 自动化工具常将意图与执行融合,导致变更执行机制时意图可能被改变
  • 可执行的操作规范(executable operational specification)可明确操作意图,提升系统灵活性
  • 分离意图与执行步骤,有助于AI代理和复杂系统的管理

结构提纲

按章节快速跳转。

  1. 指出当前自动化工具在意图与执行融合中的局限性

  2. 提出可执行的操作规范作为意图与执行的分离层

  3. 分离层如何提升系统灵活性和可维护性

  4. 小规模部署示例说明分离层的应用

  5. 分离层如何帮助AI代理理解操作意图

  6. 当前分离层无法解决的问题

思维导图

用一张图看清主题之间的关系。

查看大纲文本(无障碍 / 无 JS 友好)
  • 意图与执行的分离
    • 核心机制
      • 可执行操作规范
    • 实践价值
      • 提升灵活性
      • AI代理支持
    • 挑战
      • 未解决的复杂性

金句 / Highlights

值得收藏与分享的关键句。

#软件架构#自动化#CI/CD#分布式系统
打开原文

为什么软件自动化需要在意图和执行之间增加一层

2026年9月29日

/

#software architecture

Hugo Teijiz

软件自动化擅长将指令转化为行动。

一个流水线可以部署发布版本,Terraform可以配置基础设施,操作手册可以重启服务,代理可以检查遥测数据并选择修复步骤。

但所有这些系统都有一个共同的弱点:它们通常更擅长执行指令,而不是表达这些指令背后的意图。

当自动化简单时,这个差距很容易被忽视。如果一个脚本只运行一个命令,脚本本身可能就足以解释应该发生什么。

但随着系统变得越来越分布化、越来越以策略驱动、越来越自主,执行逻辑开始承担它从未被设计用来承担的责任。

一个部署工作流现在可能需要编码:

code
应该部署什么
可以在哪里运行
必须满足哪些约束
必须检查哪些证据
谁可以批准更改
何时需要回滚
哪些失败是可以接受的

在这一点上,自动化不再仅仅是执行。它也成为了定义操作意图的地方。

而这正是一个问题。

因为当意图和执行融合在一起时,更改执行者也可能改变操作的含义。

在我之前的 文章 中,我介绍了可执行操作规范的概念:一种机器可读的描述,说明操作应该实现什么,以及如何评估其结果。

在本文中,我想更深入一层。

核心问题是:

在人类意图和执行它的系统之间,应该存在什么?

我将探讨:

  • 为什么自动化工具常常成为意外的规范
  • 为什么意图和执行以不同的速度演变
  • 当意图嵌入在流水线和脚本中时会丢失什么
  • 为什么期望状态是有用的但仍然不完整
  • 如何引入一个显式的操作意图层
  • 预条件、约束、证据和恢复规则如何融入这一层
  • 执行计划与操作规范有何不同
  • 这种结构如何帮助AI代理
  • 以及这种分离仍然无法解决的问题

目标不是为了抽象而添加另一个抽象层。目标是让操作决策更容易理解、评估和更改,而无需每次执行机制改变时都重新定义操作的含义。

先决条件

你应该熟悉:

  • 关键的软件架构概念
  • CI/CD
  • 基础设施自动化
  • 可观测性
  • 分布式系统
  • 基本的策略概念
  • TypeScript或类似语言

你不需要特定的编排工具或云平台。示例特意设计得小巧且与工具无关。

目录

  • 先决条件
  • 自动化工具常常成为意外的规范
  • 意图和执行以不同速度演变
  • 当意图嵌入在执行者中时会丢失什么
  • 期望状态有帮助,但不是完整的意图
  • 缺失的层
  • 一个小型部署示例
  • 将预条件与执行步骤分离
  • 将约束与机制分离
  • 明确证据要求
  • 在执行开始前定义恢复措施
  • 操作规范与执行计划
  • 为什么这对AI代理很重要
  • 一个最小的TypeScript模型
  • 一个实际的工作流程
  • 这种分离仍然无法解决的问题
  • 从自动化逻辑到操作合同
  • 结论

自动化工具往往无意中成为规范

考虑一个部署工作流。它可能最初很简单:

code
steps:
  - build
  - test
  - deploy

然后生产需求出现。

你添加了:

code
approval
region validation
health checks
canary percentage
rollback logic
error-rate thresholds
latency thresholds
artifact verification

最终,该流水线成为唯一完整描述操作的地方。

这会无意中形成一种架构:

code
操作意图
      ↓
流水线配置
      ↓
执行

现在流水线承担了两项职责:它既描述了操作的含义,又定义了操作的执行方式。

这些职责相关但不相同。

假设该工作流声明:

code
deploy:
  image: orders:v42

verify:
  errorRateBelow: 0.01

rollback:
  whenVerificationFails: true

这可能完美运行。但现在想象将部署迁移到另一个平台。

如果操作规则仅存在于该流水线中,迁移执行器时必须重新发现并重新实现这些规则。

真正的迁移变成:

code
迁移执行机制
+
重建操作意图

这比仅仅更换工具要危险得多。

意图与执行的演变速度不同

操作意图通常相对稳定。

例如:

code
部署已批准的版本。

保持可用性。

不要超过错误率阈值。

保持回滚可能性。

不要在批准区域外部署。

这些期望可能保持有效多年。但执行机制的演变频率要高得多。

团队可能经历:

code
shell脚本
Jenkins
GitHub Actions
Argo CD
Kubernetes操作符
云原生部署服务
AI代理

如果意图与这些机制紧密耦合,每次工具变更都可能引发操作重设计。

这会带来不必要的变更。

你希望:

code
稳定意图
     ↓
可替换执行

而不是:

code
新执行器
     ↓
重新发现操作的含义

随着自动化变得更复杂,这种分离变得越来越重要。

执行器包含的智能越多,意图越容易隐藏在实现细节中。

当意图嵌入执行器时会丢失什么

当操作意图直接嵌入脚本和流水线中时,以下问题会变得更困难。

1. 审查

审查者可能看到:

code
if: error_rate < 0.01

但不知道该阈值是:

code
业务需求
SRE策略
临时发布规则
历史变通方案

该值是可执行的。但其含义并不明确。

2. 可审计性

六个月后,有人问:

为什么允许这次部署?

回答可能需要重建:

code
流水线版本
功能标志
运行时变量
仪表板
审批
操作员决策

3. 可移植性

更换工具意味着需要重新实现规则。

4. 测试

你可以测试流水线是否正常工作。但测试操作意图本身是否完整要困难得多。

5. 治理

授权规则可能被埋在工作流代码中。

code
生产变更需要审批

可能被实现为平台特定的分支保护规则,而不是作为操作的一部分进行表述。

6. AI执行

AI 代理可能会在违反未明示的操作约束的情况下生成有效的命令序列。

问题不在于执行器能力不足,而在于意图没有独立表示。

期望状态有帮助,但并非完整意图

许多现代系统已经使用期望状态模型。

code
replicas: 3
image: orders:v42

这很有用。

系统可以比较:

code
期望状态
vs.
观察状态

并协调差异。

但操作意图通常包含的不仅是最终配置。

假设期望状态是:

code
orders:v42
replicas: 3

该操作还可能要求:

code
不可将可用性降低至99.9%以下
支付依赖降级时不可部署
保持错误率低于1%
生产部署需要审批
延迟超过400毫秒时需回滚

这些不仅仅是目标状态属性。

其中一些属于:

code
前提条件
运行时约束
证据要求
治理规则
恢复规则

期望状态是操作意图的一部分,但并非总是完整的操作意图。

缺失的层级

更明确的架构如下所示:

code
人类/组织意图
             ↓
操作规范
             ↓
执行计划/绑定
             ↓
执行器
             ↓
操作
             ↓
证据
             ↓
评估

操作规范是缺失的层级。

它的职责不是执行命令,而是独立于执行机制来保持操作的含义。

该层级可以包含:

code
目标
范围
前提条件
约束
所需证据
授权规则
恢复条件

执行器则回答不同的问题:

我如何利用可用工具满足这个规范?

这是更清晰的分离。

一个小型部署示例

假设我们要部署订单服务的v42版本。

操作目标是:

code
将订单v42部署到生产环境。

但这还不够。

我们还知道:

code
支付依赖必须健康
至少要有3个副本保持可用
错误率必须保持在1%以下
p95延迟必须保持在400毫秒以下
生产部署需要审批
必须保留回滚的可能性

我们可以将这些关注点单独表示:

code
type DeploymentIntent = {
  service: string;
  version: string;
  environment: string;
  preconditions: {
    paymentDependencyHealthy: boolean;
    approved: boolean;
  };
  constraints: {
    minAvailableReplicas: number;
    maxErrorRate: number;
    maxP95LatencyMs: number;
  };
  recovery: {
    rollbackOnViolation: boolean;
  };
};

然后:

code
const intent: DeploymentIntent = {
  service: "orders",
  version: "v42",
  environment: "production",
  preconditions: {
    paymentDependencyHealthy: true,
    approved: true,
  },
  constraints: {
    minAvailableReplicas: 3,
    maxErrorRate: 0.01,
    maxP95LatencyMs: 400,
  },
  recovery: {
    rollbackOnViolation: true,
  },
};

注意缺失的部分。没有出现:

code
kubectl
Terraform
GitHub Actions
Argo
云API

意图目前还不关心部署如何发生。

这是有意为之。

将前提条件与执行步骤分离

前提条件回答的问题是:

此操作是否允许开始?

这不同于:

应该首先执行哪一步?

code
支付依赖必须健康
生产审批必须存在
制品必须已签名
维护窗口必须开启

这些不是执行指令,而是执行开始前应满足的条件。

一个简单的评估器可能如下所示:

code
type Preconditions = {
  paymentDependencyHealthy: boolean;
  approved: boolean;
};

function canStart(
  preconditions: Preconditions
): boolean {
  return (
    preconditions.paymentDependencyHealthy &&
    preconditions.approved
  );
}

如果:

code
canStart({
  paymentDependencyHealthy: false,
  approved: true,
});

返回:

code
false

则操作不应开始。

该决策不应取决于执行者是 Kubernetes、脚本还是 AI 代理。前置条件属于操作意图的一部分。

将约束与机制分离

约束回答在操作运行期间或之后必须保持为真的内容。

示例:

code
副本数 >= 3
错误率 <= 1%
延迟 <= 400 毫秒
成本增加 <= 20%
区域必须保持为 us-east-1

执行者可能使用许多不同的策略来满足这些约束。

例如,为了保持 3 个副本:

code
Kubernetes 可能使用滚动部署
另一个平台可能使用蓝/绿部署
代理可能在切换流量前临时扩展容量

机制可能不同,但约束本身不变。

这正是为什么约束应独立于执行者存在。

明确证据要求

只有在可以评估的情况下,约束才有用。

假设你定义:

code
错误率 <= 1%

那么你需要一个证据来源。

code
Prometheus
Datadog
CloudWatch
OpenTelemetry
自定义应用指标

规范不一定要硬编码某个供应商,但应说明需要证据。

code
type EvidenceRequirement = {
  name: string;
  metric: string;
};

const evidenceRequirements:
  EvidenceRequirement[] = [
    {
      name: "可用性",
      metric: "availableReplicas",
    },
    {
      name: "错误",
      metric: "errorRate",
    },
    {
      name: "延迟",
      metric: "p95LatencyMs",
    },
  ];

执行者或证据收集器可以决定这些值的来源。规范定义了必须知道的内容。这种区分非常重要。

在执行开始前定义恢复策略

许多操作工作流仅在出现问题后定义回滚。但这样做为时已晚。恢复是操作意图的一部分。

code
如果错误率超过 1%,停止发布。
如果 p95 延迟超过 400 毫秒,恢复到上一版本。
如果证据不可用,需要人工审核。

这些不仅仅是实现细节,它们表达了可接受的操作行为。

一个简单的表示方式可能是:

code
type RecoveryPolicy = {
  rollbackOnConstraintViolation: boolean;
  requireHumanReviewWhenEvidenceMissing:
    boolean;
};

执行者可以以不同方式实现回滚。该策略仍然属于操作的一部分。

操作规范与执行计划

这种区分非常重要。

操作规范回答:

code
应该发生什么?
必须保持为真?
需要哪些证据?
如果约束失败会发生什么?

执行计划回答:

code
我们将执行哪些操作?
顺序如何?
使用哪些工具?

规范

code
部署订单 v42。

保持至少 3 个副本可用。

错误率 <= 1%。

如果约束被违反,执行回滚。

执行计划

code
1. 将订单扩展到6个副本。
2. 将v42部署到1个副本。
3. 路由10%的流量。
4. 等待5分钟。
5. 检查指标。
6. 增加流量。
7. 移除旧副本。

该计划是满足规范的一种可能方式。

另一个执行器可能会生成不同的计划。这很有用。这意味着相同的操作意图可以适应工具和执行策略的变化。

对AI代理的重要性

当执行器是AI代理时,这种分离变得尤为重要。

传统脚本通常有可预测的执行路径,而代理可能会动态决策。

对于同一事件,它可能会选择:

code
重启
扩展
重新路由
禁用功能
更改配置

如果正确性被定义为遵循一个确切的顺序,动态执行将难以验证。

但如果正确性被定义为操作规范,代理将拥有灵活性。

code
目标:
恢复结账可用性

前提条件:
支付数据库可访问

约束条件:
认证功能保持启用
无支付重复
错误率 <= 1%
成本增加 <= 20%

证据:
结账成功率
重复支付次数
错误率
成本变化

恢复:
如果无法满足约束条件则升级处理

代理可以选择不同操作,但其行为仍然受到约束。

这为我们提供了:

code
意图
↓
规范
↓
代理生成的计划
↓
执行
↓
证据
↓
评估

代理可以自由决定如何执行。但它不能重新定义成功的含义。

随着自主系统获得更多操作权限,这种区分将变得越来越重要。

一个最小的TypeScript模型

我们可以将这些概念整合成一个小型通用模型。

code
type OperationalSpec<TContext, TEvidence> = {
  name: string;

  canStart(
    context: TContext
  ): boolean;

  evaluate(
    evidence: TEvidence
  ): EvaluationResult;

  recovery: RecoveryPolicy;
};

type EvaluationResult = {
  conformant: boolean;
  failures: string[];
};

type RecoveryPolicy = {
  rollbackOnConstraintViolation: boolean;
};

对于部署:

code
type DeploymentContext = {
  paymentDependencyHealthy: boolean;
  approved: boolean;
};

type DeploymentEvidence = {
  version: string;
  replicas: number;
  errorRate: number;
  p95LatencyMs: number;
};
code
const deploymentSpec:
  OperationalSpec<
    DeploymentContext,
    DeploymentEvidence
  > = {
    name: "deploy-orders-v42",

    canStart: (context) =>
      context.paymentDependencyHealthy &&
      context.approved,

    evaluate: (evidence) => {
      const failures: string[] = [];

      if (evidence.version !== "v42") {
        failures.push(
          "wrong-version"
        );
      }

      if (evidence.replicas < 3) {
        failures.push(
          "insufficient-replicas"
        );
      }

      if (evidence.errorRate > 0.01) {
        failures.push(
          "error-rate-too-high"
        );
      }

      if (
        evidence.p95LatencyMs > 400
      ) {
        failures.push(
          "latency-too-high"
        );
      }

      return {
        conformant:
          failures.length === 0,
        failures,
      };
    },

    recovery: {
      rollbackOnConstraintViolation:
        true,
    },
  };

现在执行可以在其他地方处理。

code
interface DeploymentExecutor {
  execute(
    spec: typeof deploymentSpec
  ): Promise<DeploymentEvidence>;
}

该执行器可以由:

code
Kubernetes
云平台
测试框架
本地模拟器
AI 代理

规范始终是参考标准。

实践工作流程

如果你想将这种分离引入现有自动化系统,从一个操作开始。

1. 选择一个重复的操作

code
部署服务
轮换证书
扩展工作池
恢复备份

2. 提取目标

写下该操作试图实现的目标。避免使用命令。

3. 提取前提条件

提问:

在此操作开始之前,必须已经成立的条件是什么?

4. 提取约束条件

提问:

在执行过程中或执行后必须保持成立的条件是什么?

5. 定义证据

针对每个约束条件,识别用于评估该条件的观测信息。

6. 定义恢复机制

决定当证据显示约束条件被违反时应采取的措施。

7. 保持执行独立

让当前的自动化工具继续作为执行者。不要一次性重写所有内容。

8. 评估现有执行者

判断当前脚本、流水线或代理是否满足提取出的规范。

这种分离可以逐步引入,因此不需要立即构建新的操作平台。

这种分离无法解决的问题

将意图与执行分离并不会自动使操作正确。

你仍然可能遇到:

code
错误的目标
不合理的阈值
缺失的证据
误导性的指标
冲突的约束
不安全的执行者
安全漏洞
错误的恢复策略

规范本身可能有错误。这很重要。

形式化表示不会使假设成立,它只是让假设更容易被检查和评估。

这也会带来成本。更明确的操作意图意味着需要维护更多工件。

如果规范未进行版本控制和审查,它们可能会过时。如果证据定义薄弱,符合性结果可能变得误导。某些操作决策仍然难以形式化。

人类判断不会消失。其价值在于让更多的判断变得明确。

从自动化逻辑到操作契约

还有另一种思考这种分离的方式。

成熟的操作规范开始表现出类似契约的特性。

它声明:

code
在这些条件下,该操作可以开始。

必须保持这些约束。

必须产生这些证据。

其结果将根据这些规则进行评估。

如果失败,将适用这些恢复条件。

这比以下内容强大得多:

code
运行此工作流

工作流是实现。操作契约描述了该实现需要负责实现的目标。

这为我们提供了如下结构:

code
意图
↓
操作契约
↓
执行计划
↓
执行者
↓
证据
↓
评估

这种分离为以下内容创造了空间:

code
针对相同操作意图的多种执行策略

一旦实现这一点,另一个问题浮现:

两个不同的执行者能否满足相同的操作规范?

这是我想接下来探讨的问题。

结论

自动化使软件操作变得更加快速和可重复。

但执行只是操作的一部分。我们仍然需要知道:

code
为什么允许执行该操作
预期的成果是什么
必须满足哪些约束
需要哪些证据
当约束失败时会发生什么
code

当所有这些逻辑都封装在流水线、脚本或代理中时,执行器会无意间成为操作语义的拥有者。

这种耦合使得系统更难被审查、迁移、审计和验证。

更合理的分层方式如下:

意图 ↓ 操作规范 ↓ 执行计划 ↓ 执行器 ↓ 证据 ↓ 评估

code

规范描述了成功的定义,执行器决定如何实现目标。

随着执行机制变得越来越动态,尤其是当执行器是AI代理时,这种区分变得越来越有价值。

因为当我们给予系统更多决定如何完成工作的自由时,就越需要明确工作应该实现的目标。

合作伙伴推荐课程

(在新标签页中打开)

广告

- Udemy Apache Airflow:实践指南 Marc Lamberti 4.6(满分5星)(5,751人评价) 10小时54分钟 初学者 证书 查看课程 (在新标签页中打开)

- Coursera 高级Linux故障排除与系统恢复 Packt - 课程讲师 3周,每周4小时 高级 证书 查看课程 (在新标签页中打开)

- Coursera 编写自动化脚本 业内人士 2周,每周1小时 中级 证书 查看课程 (在新标签页中打开)

- Coursera 搜索与展示广告 Chris J. Vargo 和 Harsha Gangadharbatla 4.5(满分5星)(99人评价) 5周,每周2小时 初学者 证书 查看课程 (在新标签页中打开)

更多Selenium WebDriver课程请访问Class Central

→

技术与产品负责人,拥有25年以上经验,曾主导构建和扩展拉丁美洲的B2B平台、运营系统、云架构和技术团队。我撰写关于软件架构、技术债务、遗留系统现代化、AI、工程领导力和技术决策的内容——尤其是团队投入时间、金钱和代码之前就应该做出的决策。OrkestCloud和Collabai创始人。兼职CTO和技术顾问。我的核心原则:构建成本低廉;糟糕决策代价高昂。

如果你读到这里,请感谢作者以表达你的支持。说声谢谢

免费学习编程。freeCodeCamp的开源课程已帮助超过40,000人成为开发者。立即开始