土法炼钢兴趣小组的算法知识备份

【系统架构设计】防御性架构:用系统设计约束 AI 犯错

文章导航

分类入口
architecture
标签入口
#defensive-architecture#ai-codegen#contracts#least-privilege#schema#policy-as-code#blast-radius#type-system

目录

上一篇把穿越技术周期的不变量收束成可核对清单:失败常态、尾延迟、一致性谱系、爆炸半径、反馈控制。那些约束在「人写代码」时代已经成立;当 AI 默认参与生成补丁、补测试、改配置、甚至直接开 PR 时,它们不会消失,反而更尖锐——因为变更吞吐上升,而单次变更的「作者意图」变模糊。

95 AI 原生架构回答的是 LLM 作为在线运行时依赖 时的超时、成本与编排;本文回答的是另一条轴线:AI 作为开发期协作者 时,系统如何用设计约束把错误钉在可接受半径内。Agent 身份委托、MCP 与工具级授权见 Agent 身份与安全;训练与推理集群见 大模型基础设施。本文不写模型怎么训,也不写网关怎么限 token——只写 边界、契约与爆炸半径

系列导航: ← 架构的不变量 · 系统架构设计索引 · AI 时代工程基础设施 →

相关专题: 弹性设计模式 · 限流与过载 · ADR · 策略引擎 · 容器隔离强度


一、问题:AI 写代码之后,架构要防什么

1.1 三类典型失败

把「AI 帮忙写代码」拆成可观测的失败模式,比争论「会不会取代工程师」更有用:

失败模式 表象 典型爆炸半径
越权调用 生成客户端直接打到内部管理 API,或复用过高权限 token 跨租户读、误删、资金侧写
契约漂移 字段改名、可选变必填、枚举漏分支;编译过了、联调炸 下游解析失败、静默丢字段
校验缺失 「先跑通」的路径跳过鉴权、幂等键、输入边界 注入、重复扣款、资源耗尽

这三类失败在人工协作里也存在;AI 改变的是 速率与覆盖面:同一下午可以触及十几个模块,而作者对每个模块历史约束的记忆并不连续。架构若只依赖「评审时人眼盯住」,吞吐一上来就会漏。

1.2 本文核心问题

  1. 防御性架构与传统「代码防御式编程」差在哪一层?
  2. 类型、schema、契约测试、策略引擎、沙箱各自钉住什么、钉不住什么?
  3. 如何与 24/25 的「失败必发生」假设对齐?
  4. 强约束何时变成交付阻力?争论双方各自的证据是什么?

1.3 与 95 / agent-identity 的分工

主题 负责篇
LLM 在线依赖的超时、成本、编排 95
Token Exchange、MCP、工具级授权细节 agent-identity
生成代码的破坏半径与系统约束 本文(101)
CI / eval / 回滚兜底 102

二、谱系:从最小权限到契约

2.1 最小权限与深度防御

Saltzer 与 Schroeder 在 The Protection of Information in Computer Systems(CACM / 经典安全原则综述,1975)中归纳的 最小权限(least privilege)深度防御(defense in depth),至今仍是主机与分布式权限设计的默认词汇[1]。原则原文针对的是访问控制与多用户 OS,但其工程含义可直接迁移:

AI 生成代码并不改变原则;它改变的是 默认主体能力往往过大——开发者本机凭证、admin 角色的 service account、可写生产库的 migration 身份,都容易被「为了让 demo 跑通」写进脚手架。

2.2 Design by Contract

Bertrand Meyer 的 Design by Contract(DbC)把模块边界写成 前置条件、后置条件与不变式[2]。在现代栈里,DbC 很少以 Eiffel 原语出现,而以更碎片的形式落地:

对 AI 协作而言,契约的价值不在「文档好看」,而在于 机器可判定:生成物要么通过校验器,要么进不了主干。人可以争论「这个字段该不该可选」;机器只问「对照当前权威 schema,是否合法」。

2.3 与弹性工程的同构

24 弹性设计25 过载保护 的底层假设是:依赖会失败、流量会超预期。防御性架构对 AI 协作的同构假设是:

生成物会包含局部正确、全局危险的补丁;评审会漏;测试覆盖会有洞。

因此架构目标不是「保证 AI 永远正确」,而是像舱壁与限流一样,把错误 限制在已知舱室:错误 schema 进不了生产;高权限路径默认不可达;写操作必须有幂等与审计钩子。

flowchart TB
  Gen["AI generated change"] --> T["Type / compile gate"]
  T --> S["Schema / OpenAPI gate"]
  S --> C["Contract tests"]
  C --> P["Policy engine"]
  P --> X["Runtime sandbox / least privilege"]
  X --> Prod["Limited blast radius"]

三、约束谱系:每一层钉什么

3.1 类型系统与静态边界

强类型语言(Rust、Go、TypeScript strict、Java)把一类错误前移到编译期:错误的枚举分支、空指针路径、错误的 ID 类型混用。对 AI 生成代码,类型检查是 最便宜的硬闸门——CI 里跑 tsc --noEmit / go test / cargo check 不依赖评测集,假阳性相对可控。

钉得住:签名不匹配、明显的空安全、部分不变量(newtype 包裹的 UserId vs OrderId)。

钉不住:业务语义错误(给错用户退款)、权限逻辑漏洞、「类型正确的危险 SQL」。

工程建议:把 易混标识符 做成不同名义类型;把「金额」「比率」从裸 float 中拆出——这类约束对生成模型特别有效,因为模型倾向复用最宽的数字类型。

3.2 Schema 与 API 契约

OpenAPI 3.x 与 JSON Schema 把 HTTP/事件载荷变成可验证工件[3][4]。权威源应是 仓库内版本化的 spec,而不是 Wiki 截图。生成客户端/服务端桩时,应以同一份 spec 为输入;手改生成物而不改 spec,是契约漂移的常见入口。

本地可复现的最小检查(标准库即可):给定必填字段集合,拒绝缺字段对象。

schema_required = ["order_id", "amount", "currency"]

def missing_fields(obj, required):
    return [k for k in required if k not in obj]

assert missing_fields(
    {"order_id": "o-1", "amount": 12.5, "currency": "CNY"},
    schema_required,
) == []
assert missing_fields(
    {"order_id": "o-2", "amount": 12.5},
    schema_required,
) == ["currency"]

以上断言在本文写作环境用 Python 3 实测通过。真实项目应换成完整 JSON Schema / OpenAPI 校验器,并在 CI 对 请求样例与响应样例 双侧跑。

钉得住:字段缺失、类型错误、枚举越界(在 schema 写清时)。

钉不住:语义正确但业务危险的合法请求(amount: -1 若 schema 只写 number);跨资源的授权关系。

3.3 契约测试

Consumer-driven contract(如 Pact 一类工具所代表的实践)让消费方把「我依赖的交互」固化成可回放用例,提供方变更时在 CI 失败[5]。对 AI 批量改多个服务尤其关键:模型可能「修好」提供方却不知道三个消费方的隐式假设。

钉得住:已登记的交互被破坏。

钉不住:从未被消费方声明的路径;生产独有的暗数据形状。

3.4 策略即代码

把「谁能对什么资源做什么」从散落的 if role == admin 收到策略引擎(OPA、Cedar 等)[6],有两个与 AI 相关的收益:

  1. 生成代码更难「顺便」加后门:权限决策不在业务函数里随手改;
  2. 策略变更可审:策略文件进 PR,与 102 的 policy gate 衔接。

站内细节见 策略引擎;Agent 工具级授权见 function calling 授权。本文只强调架构落点:默认拒绝 + 显式允许,且允许集与生成任务绑定(最小权限)。

3.5 运行时沙箱与爆炸半径

即使代码通过类型与契约,执行环境仍可限制损害:

机制 限制什么 站内锚点
只读 DB 角色 / RLS 生成迁移或报表路径误写 PG/InnoDB 内核系列;业务侧角色
网络 egress 白名单 SSRF、扫内网 零信任 / 容器网络
文件系统路径白名单 任意文件读写 Agent 工具边界
进程/容器隔离 逃逸后的主机损害 容器隔离强度
租户舱壁 跨租户数据面 99 多租户

沙箱是 最后一层,不是第一层:没有契约与策略,沙箱只能把「错得有限」从「错得无限」里救回来,仍可能毁掉当前租户的数据。


四、把约束设计进系统,而不是贴在提示词里

4.1 提示词不是控制面

「请遵守公司规范」写进 system prompt,属于 软约束:模型可能遵守,也可能在长上下文或工具循环中遗忘。架构硬约束必须落在 编译器、校验器、策略引擎、身份与隔离——提示词最多作为辅助,不能当控制面。

这一区分与 95 中「传输成功 ≠ 业务可接受输出」同构:开发期「生成成功」也不等于「可合并」。

4.2 默认安全路径

为 AI 协作设计默认路径时,优先:

  1. 只读探索(读代码、读 metrics、读 schema)与 写变更 分身份;
  2. 生产写路径 必须经独立审批身份(人审或策略 + 短时 elevation),见 104HITL
  3. 脚手架模板 自带鉴权中间件、幂等键、OpenAPI 校验,而不是生成后再「记得补」。

4.3 可观测的约束违反

约束失败应变成 可聚合信号:schema 校验失败次数、策略拒绝次数、契约测试红灯。它们是变更质量的 SLI 候选,与 28 SLO 的错误预算叙事衔接——不是「AI 好不好用」的主观分,而是「破坏性变更被闸门拦住」的客观计数。细节的流水线编排留给 102


五、争论:强约束是否扼杀速度

5.1 双方立场

立场 主张 常见证据类型
强约束派 没有机器可判定边界,AI 吞吐是负债加速器 安全原则文献[1];契约测试工业实践[5];生产越权事故复盘(B 级)
轻约束派 过早 schema/策略使探索成本过高,阻碍原型 创业期速度叙事;部分平台工程文主张「先合并后治理」

5.2 可操作的折中(非「看场景」空话)

副作用不可逆性 分层,而不是按「团队是否喜欢严格」:

变更类 约束强度 理由
文档、注释、纯前端文案 爆炸半径小
内部只读 API、实验旗标后的功能 中:类型 + schema 可快速回滚
支付、权限、多租户数据面、密钥 高:契约 + 策略 + 沙箱 + 人审 不可逆或合规敏感

这与 26 优雅降级 按业务优先级裁剪功能是同一把尺子:优先级高的路径,约束更重

尚无跨组织的对照试验证明「某种约束组合最优」;能下的强结论只到:不可逆副作用路径上省略硬闸门,是已知反模式——与是否使用 AI 无关,AI 只是提高触发频率。


六、开放问题

  1. 生成代码的「证明义务」如何分配?
    是生成端(Agent)提交机器可检查证明(类型、契约、策略 diff),还是接收端(CI)承担全部验证?二者组合是现状;最优分工缺少形式化框架。可读入口:软件供应链与 SLSA 类保证级别讨论(与 AI 无关的证明义务谱系可迁移)。

  2. 语义层约束如何机器化?
    Schema 钉形状,钉不住「退款对象是否正确」。属性测试、差分测试、业务 invariant fuzz 有局部成效,但覆盖「领域正确性」仍开放。

  3. 约束本身的变更如何防 AI 误改?
    策略文件与 OpenAPI 也可能被模型「为了让测试通过」放宽。需要对约束工件本身设更高审门槛——见 102/104 的门禁分层。


七、落地检查清单

  1. 生产写路径与 AI/CI 身份是否默认最小权限?
  2. 对外 API 是否有版本化 OpenAPI/JSON Schema,并在 CI 校验?
  3. 跨服务依赖是否有契约测试,而非只靠端到端快乐路径?
  4. 授权是否集中在策略引擎,业务代码是否「默认拒绝」?
  5. 高风险工具/迁移是否有沙箱与人审,而不是 prompt 里写「请小心」?
  6. 约束失败是否有 metrics,而不只是日志里一行字符串?

参考资料

规范与标准

论文与经典文献

工程实践与官方文档

站内交叉

核心文献(本主题)

文献 年份 本文引用点
Saltzer & Schroeder 1975 最小权限、深度防御
Meyer, DbC 1988+ 模块契约思维
OpenAPI 3.1 机器可判定 API 形状

实验与工具


系列导航: ← 架构的不变量 · 系统架构设计索引 · AI 时代工程基础设施 →

同主题继续阅读

把当前热点继续串成多页阅读,而不是停在单篇消费。

2026-08-03 · architecture

【系统架构设计】AI 时代的工程基础设施:让 pipeline 替你兜底

人审吞吐跟不上 AI 变更速率时,质量只能前移到流水线。本文建立变更威胁模型,拆解 lint、类型、契约、策略、eval 与 canary 的门禁分层与失败语义,对照 Continuous Delivery 与 SRE 变更管理,并讨论 eval 能否替代代码评审的争论与开放问题。

2026-06-18 · architecture / security

【身份与访问控制工程】OPA、Cedar 与策略引擎落地

OPA 是 CNCF 的策略引擎标准答案,Rego 是它的策略语言;Cedar 是 AWS 开源的新竞争者,基于 Rust 的 WASM 编译执行、语法更接近 SQL。两者在架构模式(sidecar vs 中心化)、策略语言设计哲学和性能特征上有根本差异。本文从策略引擎的架构模式出发,拆解 OPA Rego 的核心语义与性能限制、Cedar 的设计取舍,以及策略即代码(Policy as Code)在 CI/CD 中的落地。

2026-06-09 · compiler / architecture

【编译器与 MLIR】类型系统与属性

解析 MLIR 的类型体系:内建类型(Integer、Float、Tensor、MemRef)与自定义方言类型的注册机制;区分 Type 与 Attribute 的设计意图;通过 OpBuilder 理解类型和属性在 IR 构造中的实际角色。

2026-07-29 · architecture

【系统架构设计】架构的不变量:穿越技术周期的设计原则

单体、微服务、边缘、AI 原生换的是部署形态与运行时组件,失败、尾延迟、一致性权衡、爆炸半径、反馈控制、组织同构与可观测性前提在二十年间并未消失。本文把 Part XIV 前沿篇收束为可核对的不变量清单,逐条链回本系列已写篇章与经典文献,并给出开放问题与自检用法。


By .