KEEL · 龙骨 · A CURRICULUM FOR THE AI ERA
04. 执行前,先让变化变得可见 — keel 龙骨
直接把 Command 交给外部 API,会错过最后一次低成本发现问题的机会。Execution Gateway 在动作目录、预检、幂等存储和领域 Adapter 之间建立统一边界。
直接把 Command 交给外部 API,会错过最后一次低成本发现问题的机会。Execution Gateway 在动作目录、预检、幂等存储和领域 Adapter 之间建立统一边界。
Preview 应展示实际变化
对配置动作,好的 preview 不是重复用户原话,而是计算差异:
target: tenant-a/staging/payment-service
current: pool_limit=20, resource_version=cfg-7
desired: pool_limit=40
effect: write
reversible: true
expected next version: unknown until commit
对于通知动作,preview 应展示收件渠道、脱敏后的正文、是否可撤回和预计去重键。
Preflight 检查四类条件
- 契约:动作是否注册,参数是否满足 schema;
- 资源:目标是否存在,版本是否仍匹配;
- 依赖:外部服务是否可达,必要凭据是否可用;
- 运行:deadline、预算和取消状态是否允许继续。
权限和审批由 Safety Controls 判断,但执行点必须消费其可验证结果,不能只相信调用方传来的 approved=true。
Gateway 的处理顺序
flowchart TD
C[ActionCommand] --> V[查 ActionCatalog 并校验]
V --> F[计算 fingerprint]
F --> I{幂等记录存在?}
I -->|同指纹| R[返回已有 Receipt / Operation]
I -->|异指纹| X[idempotency_conflict]
I -->|不存在| P[preflight + preview]
P --> E[执行 Adapter]
E --> S[保存 Receipt 与外部引用]
幂等检查和保存必须靠近提交点。在多个 worker 并发执行时,简单的“先查后写”仍会竞态,生产存储需要唯一约束或原子 compare-and-set。
Dry-run 不是执行承诺
preview 在 10:00:00 读取到版本 cfg-7,正式执行在 10:00:03 开始。中间可能已有其他人把版本改成 cfg-8。因此 Adapter 应带上期望版本做条件写入:
update ... if resource_version == cfg-7
版本不匹配时返回 precondition_failed,重新读取和重新审批,而不是静默覆盖新状态。这正是 TOCTOU 问题。
运行示例
python courses/advanced/real-world-execution/course/project/examples/03_preview_and_execute.py
先观察 preview,再执行。然后把预期资源版本改旧,确认动作在提交点被拒绝。
检查理解
- preview 为什么要显示 before/after,而不是只显示动作名称?
- preflight 成功后为什么仍可能执行失败?
- 并发 worker 下,内存字典为什么不能提供生产级幂等?