上一篇把穿越技术周期的不变量收束成可核对清单:失败常态、尾延迟、一致性谱系、爆炸半径、反馈控制。那些约束在「人写代码」时代已经成立;当 AI 默认参与生成补丁、补测试、改配置、甚至直接开 PR 时,它们不会消失,反而更尖锐——因为变更吞吐上升,而单次变更的「作者意图」变模糊。
95 AI 原生架构回答的是 LLM 作为在线运行时依赖 时的超时、成本与编排;本文回答的是另一条轴线:AI 作为开发期协作者 时,系统如何用设计约束把错误钉在可接受半径内。Agent 身份委托、MCP 与工具级授权见 Agent 身份与安全;训练与推理集群见 大模型基础设施。本文不写模型怎么训,也不写网关怎么限 token——只写 边界、契约与爆炸半径。
系列导航: ← 架构的不变量 · 系统架构设计索引 · AI 时代工程基础设施 →
相关专题: 弹性设计模式 · 限流与过载 · ADR · 策略引擎 · 容器隔离强度
一、问题:AI 写代码之后,架构要防什么
1.1 三类典型失败
把「AI 帮忙写代码」拆成可观测的失败模式,比争论「会不会取代工程师」更有用:
| 失败模式 | 表象 | 典型爆炸半径 |
|---|---|---|
| 越权调用 | 生成客户端直接打到内部管理 API,或复用过高权限 token | 跨租户读、误删、资金侧写 |
| 契约漂移 | 字段改名、可选变必填、枚举漏分支;编译过了、联调炸 | 下游解析失败、静默丢字段 |
| 校验缺失 | 「先跑通」的路径跳过鉴权、幂等键、输入边界 | 注入、重复扣款、资源耗尽 |
这三类失败在人工协作里也存在;AI 改变的是 速率与覆盖面:同一下午可以触及十几个模块,而作者对每个模块历史约束的记忆并不连续。架构若只依赖「评审时人眼盯住」,吞吐一上来就会漏。
1.2 本文核心问题
- 防御性架构与传统「代码防御式编程」差在哪一层?
- 类型、schema、契约测试、策略引擎、沙箱各自钉住什么、钉不住什么?
- 如何与 24/25 的「失败必发生」假设对齐?
- 强约束何时变成交付阻力?争论双方各自的证据是什么?
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,但其工程含义可直接迁移:
- 最小权限:主体(人、服务、Agent、生成代码所用的 CI 身份)只拿到完成任务所需的能力;
- 深度防御:一层失效时下一层仍限制损害——类型拦一层、契约测一层、运行时策略再拦一层。
AI 生成代码并不改变原则;它改变的是
默认主体能力往往过大——开发者本机凭证、admin
角色的 service account、可写生产库的 migration
身份,都容易被「为了让 demo 跑通」写进脚手架。
2.2 Design by Contract
Bertrand Meyer 的 Design by Contract(DbC)把模块边界写成 前置条件、后置条件与不变式[2]。在现代栈里,DbC 很少以 Eiffel 原语出现,而以更碎片的形式落地:
- API:OpenAPI / JSON Schema 描述请求响应形状;
- 服务间:契约测试(consumer-driven contract)防止提供方静默破坏消费方假设;
- 领域:不变量写在领域服务或数据库约束里(非空、外键、检查约束)。
对 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 相关的收益:
- 生成代码更难「顺便」加后门:权限决策不在业务函数里随手改;
- 策略变更可审:策略文件进 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 协作设计默认路径时,优先:
- 只读探索(读代码、读 metrics、读 schema)与 写变更 分身份;
- 生产写路径 必须经独立审批身份(人审或策略 + 短时 elevation),见 104 与 HITL;
- 脚手架模板 自带鉴权中间件、幂等键、OpenAPI 校验,而不是生成后再「记得补」。
4.3 可观测的约束违反
约束失败应变成 可聚合信号:schema 校验失败次数、策略拒绝次数、契约测试红灯。它们是变更质量的 SLI 候选,与 28 SLO 的错误预算叙事衔接——不是「AI 好不好用」的主观分,而是「破坏性变更被闸门拦住」的客观计数。细节的流水线编排留给 102。
五、争论:强约束是否扼杀速度
5.1 双方立场
| 立场 | 主张 | 常见证据类型 |
|---|---|---|
| 强约束派 | 没有机器可判定边界,AI 吞吐是负债加速器 | 安全原则文献[1];契约测试工业实践[5];生产越权事故复盘(B 级) |
| 轻约束派 | 过早 schema/策略使探索成本过高,阻碍原型 | 创业期速度叙事;部分平台工程文主张「先合并后治理」 |
5.2 可操作的折中(非「看场景」空话)
按 副作用不可逆性 分层,而不是按「团队是否喜欢严格」:
| 变更类 | 约束强度 | 理由 |
|---|---|---|
| 文档、注释、纯前端文案 | 低 | 爆炸半径小 |
| 内部只读 API、实验旗标后的功能 | 中:类型 + schema | 可快速回滚 |
| 支付、权限、多租户数据面、密钥 | 高:契约 + 策略 + 沙箱 + 人审 | 不可逆或合规敏感 |
这与 26 优雅降级 按业务优先级裁剪功能是同一把尺子:优先级高的路径,约束更重。
尚无跨组织的对照试验证明「某种约束组合最优」;能下的强结论只到:不可逆副作用路径上省略硬闸门,是已知反模式——与是否使用 AI 无关,AI 只是提高触发频率。
六、开放问题
生成代码的「证明义务」如何分配?
是生成端(Agent)提交机器可检查证明(类型、契约、策略 diff),还是接收端(CI)承担全部验证?二者组合是现状;最优分工缺少形式化框架。可读入口:软件供应链与 SLSA 类保证级别讨论(与 AI 无关的证明义务谱系可迁移)。语义层约束如何机器化?
Schema 钉形状,钉不住「退款对象是否正确」。属性测试、差分测试、业务 invariant fuzz 有局部成效,但覆盖「领域正确性」仍开放。约束本身的变更如何防 AI 误改?
策略文件与 OpenAPI 也可能被模型「为了让测试通过」放宽。需要对约束工件本身设更高审门槛——见 102/104 的门禁分层。
七、落地检查清单
- 生产写路径与 AI/CI 身份是否默认最小权限?
- 对外 API 是否有版本化 OpenAPI/JSON Schema,并在 CI 校验?
- 跨服务依赖是否有契约测试,而非只靠端到端快乐路径?
- 授权是否集中在策略引擎,业务代码是否「默认拒绝」?
- 高风险工具/迁移是否有沙箱与人审,而不是 prompt 里写「请小心」?
- 约束失败是否有 metrics,而不只是日志里一行字符串?
参考资料
规范与标准
- OpenAPI Initiative, OpenAPI Specification 3.1.(A 级)[3]
- Wright, A. et al., JSON Schema(IETF / 社区规范当前草案与发布轨)。(A 级)[4]
论文与经典文献
- Saltzer, J. H., Schroeder, M. D., “The Protection of Information in Computer Systems,” Proceedings of the IEEE, 1975.(A 级)[1]
- Meyer, B., Object-Oriented Software Construction, Prentice Hall — Design by Contract.(A 级)[2]
工程实践与官方文档
- Pact Foundation / consumer-driven contract 文档(按所用工具钉版本)。(B 级)[5]
- Open Policy Agent / Cedar 官方文档。(B 级)[6]
- Nygard, M., Release It!(2nd ed.)— 舱壁与爆炸半径。(B 级)
站内交叉
- 95 AI 原生架构 §9.4 与 Part XV 分工
- 100 架构不变量 — 爆炸半径
- agent-identity — 工具授权与 HITL
- iam/13 策略引擎
核心文献(本主题)
| 文献 | 年份 | 本文引用点 |
|---|---|---|
| Saltzer & Schroeder | 1975 | 最小权限、深度防御 |
| Meyer, DbC | 1988+ | 模块契约思维 |
| OpenAPI 3.1 | — | 机器可判定 API 形状 |
实验与工具
- 第三节 schema 必填字段检查:Python 3 标准库断言,写作环境实测通过;完整项目应换正式校验器。
系列导航: ← 架构的不变量 · 系统架构设计索引 · AI 时代工程基础设施 →
同主题继续阅读
把当前热点继续串成多页阅读,而不是停在单篇消费。
【系统架构设计】AI 时代的工程基础设施:让 pipeline 替你兜底
人审吞吐跟不上 AI 变更速率时,质量只能前移到流水线。本文建立变更威胁模型,拆解 lint、类型、契约、策略、eval 与 canary 的门禁分层与失败语义,对照 Continuous Delivery 与 SRE 变更管理,并讨论 eval 能否替代代码评审的争论与开放问题。
【身份与访问控制工程】OPA、Cedar 与策略引擎落地
OPA 是 CNCF 的策略引擎标准答案,Rego 是它的策略语言;Cedar 是 AWS 开源的新竞争者,基于 Rust 的 WASM 编译执行、语法更接近 SQL。两者在架构模式(sidecar vs 中心化)、策略语言设计哲学和性能特征上有根本差异。本文从策略引擎的架构模式出发,拆解 OPA Rego 的核心语义与性能限制、Cedar 的设计取舍,以及策略即代码(Policy as Code)在 CI/CD 中的落地。
【编译器与 MLIR】类型系统与属性
解析 MLIR 的类型体系:内建类型(Integer、Float、Tensor、MemRef)与自定义方言类型的注册机制;区分 Type 与 Attribute 的设计意图;通过 OpBuilder 理解类型和属性在 IR 构造中的实际角色。
【系统架构设计】架构的不变量:穿越技术周期的设计原则
单体、微服务、边缘、AI 原生换的是部署形态与运行时组件,失败、尾延迟、一致性权衡、爆炸半径、反馈控制、组织同构与可观测性前提在二十年间并未消失。本文把 Part XIV 前沿篇收束为可核对的不变量清单,逐条链回本系列已写篇章与经典文献,并给出开放问题与自检用法。