摘要
AI 已经能写出正确的代码,但软件交付并没有因此变快——因为 AI 仍在为人类协作而设计的架构上工作。本文提出一种面向 AI 认知边界的架构模式:应用被拆解为页面,页面由相互独立的组件构成,每个组件由一段前端代码和一个专属后端函数组成,小到可以被完整装进模型的上下文窗口;页面由配置驱动的渲染框架动态组装。在这套架构上,AI 可以独立完成开发、部署与端到端验证,人从方案的产出者转变为方案的审核者。本文同时给出这套架构的开放标准草案(第十二章),任何团队都可以用自己的基础设施实现它。
最后一公里
过去两年,AI 生成代码的能力以肉眼可见的速度逼近甚至超过人类工程师。但一个尴尬的事实是:大多数团队的软件交付并没有变快。
产品经理用 AI 十分钟做出了以假乱真的产品原型,然后呢?然后这个原型被截图、被贴进需求文档、被排进下个双周迭代,由工程师在一个百万行的代码仓库里重新实现一遍。AI 在十分钟内完成的事情,组织需要一个月才能把它送到用户面前。
这就是「最后一公里」问题:从产品原型到生产级应用之间,隔着整个传统研发体系。
本宣言的三个主张:
- 问题不在模型,在架构。AI 很强,但它在为另一个时代设计的架构上工作。
- 存在一种为 AI 认知边界设计的架构,它能让 AI 独立完成开发、部署与验证的完整闭环。
- 这种架构可以被定义为一套开放标准,与任何公司的具体基础设施解耦。
痛点:AI 很强,交付没有变快
对产品经理而言:AI 生成产品原型已经不是问题,但从产品原型到可上线的应用,仍然需要技术团队介入。原型做得越快,等待排期的落差就越刺眼。
对工程师而言:AI 生成代码已经不是问题,但联调、测试、部署、验证仍需人工介入。Coding 往往只占一个研发周期 20% 的时间,AI 把这 20% 压缩到极致之后,剩下 80% 的流程成本纹丝不动。
对组织而言:AI 作为效率工具确实让产品、技术各自提效了,但组织形态没有改变——还是同样的角色分工、同样的协作链路、同样的迭代周期。最终的产能未见明显提升。
三个痛点指向同一个根因。
根因:架构在为另一种成本函数优化
AI 很强,但仍在传统人类的技术架构上 Coding,无法充分释放 AI 的潜能。
当下最主流的系统架构是微服务架构:一个逻辑应用由一系列微服务构成,每个微服务有独立的代码仓库,独立开发、部署、运维。它有两个与 AI 天然冲突的特征。
第一,代码仓库对 AI 来说仍然过大。 微服务虽然把庞大的应用拆成了若干「微」服务,但每个微服务的仓库仍有几万到几十万行。这对 AI 造成了很高的认知负载:它必须先在海量代码里检索、猜测、拼凑出与本次需求相关的上下文,才能开始工作。检索会遗漏,猜测会出错,所以 AI 改代码仍有相当比例的错误,仍需人类工程师兜底。今天业界的主流补救方案——更强的代码检索、更大的上下文窗口、仓库级索引——都是在为「装不下」打补丁。
第二,传统架构面向人类优化,而不是面向 AI。 传统架构强调复用性与可读性:系统要分层,越往下层越通用;每层之间定义不同的数据模型(DO、DTO、BO……),层与层之间的数据交换产生大量核心逻辑之外的胶水代码。这些设计在它的时代完全正确——因为一套系统最大的成本是工程师的人力成本,复用和规范能减少重复劳动、对抗人员流动。但代价是代码仓库无限膨胀,以及理解任何一个功能都需要跨越多层、多仓库的上下文。
一句话概括:架构是成本函数的影子。
人力昂贵的时代,我们发明了复用、分层、DRY,用「读懂成本」换「重写成本」。而 AI 时代的成本函数反转了:生成代码的边际成本趋近于零,真正稀缺的资源是单次任务所需的上下文。当成本函数变了,架构的优化目标就该变:
| 传统架构 | AI-friendly 架构 | |
|---|---|---|
| 稀缺资源 | 工程师人力 | 模型的有效上下文 |
| 优化目标 | 减少重复劳动(复用、分层) | 减少单次任务的认知负载(边界、自包含) |
| 重复代码 | 罪恶(DRY) | 可接受的成本 |
| 跨模块耦合 | 可管理的代价 | 头号债务 |
| 交付单元 | 应用 / 服务 | 组件 |
这个判断不必停留在推演——它可以被测量。为此我们构建了公开基准 Contextile-Bench:同一个 Coding Agent、同一组产品需求、黑盒评分,分别在共享分层的传统架构与组件自包含的 Contextile 上完成同样的任务,并把应用规模从 S 一路放大到 L。
实验结果画出了一条清晰的分界线。在万行以内的小仓库上,两种架构不相上下——无论代码怎么组织,Agent 的一次通过率都在 93% 以上。而当仓库跨过 10 万行,走势分化了:传统架构的一次通过率随规模一路下滑(100% → 96.9% → 91.7%),Contextile 则在最大的 L 规模上保持全对(24/24)。
解决之道不是让 AI 学会在百万行仓库里游泳,而是把海拆成鱼缸。
方案:AI-friendly Architecture
3.1核心模型:应用 → 页面 → 组件
这套架构的设计理念:一个应用由若干个页面构成,每个页面由若干个组件构成,不再有「应用」这个部署概念。组件是最小的开发、部署、运维单元,组件之间相互独立——可以把组件理解为一个更小粒度的应用。
每个组件包含两部分:
- 一段前端代码:负责渲染组件,开发完成后直接发布到 CDN;
- 一个专属后端函数:专门为这个组件提供动态数据,部署为 FaaS 服务。
前端组件与后端函数是一对一的组合关系:一个函数只服务于一个组件。
3.2页面渲染框架
页面不再是被「构建」出来的产物,而是被「配置」出来的运行时组装结果。
用户访问固定的页面 URL,URL 中携带 pageId。页面渲染框架根据 pageId 向页面配置服务查询该页面的配置——配置声明了这个页面由哪些组件构成、每个组件的前端代码地址、后端函数标识。框架并行加载所有组件代码、并行请求所有组件函数获取初始数据,数据到达即渲染,组件之间互不阻塞。
这带来一个关键性质:发布组件,而不是发布应用。新增或修改一个组件,只需发布该组件的前端产物和函数,再发布一个新的页面配置版本;回滚等于把配置指针切回上一个版本。整个过程不涉及任何「应用级」的构建与部署,也就不存在应用级的发布风险。
3.3七条设计原则
- 组件是最小交付单元。开发、部署、运维、回滚都以组件为单位,不再有应用级发布。
- 一个组件 = 一段前端 + 一个专属函数。前后端在同一个上下文里被理解和修改,联调消失了——因为「联」的两端本来就在一起。
- 全量上下文。组件必须小到能被完整装进模型的上下文窗口,并留出足够的推理余量。这是一条硬约束,也是整套架构的第一性原理。
- 上下文完整性优先于复用。允许组件之间重复,禁止组件之间耦合。重复是成本,耦合是债务;AI 把重复的成本打到了地板,却对耦合无能为力。
- 页面即配置。页面结构是数据而不是代码,变更页面 = 发布新版本配置,回滚 = 切换版本指针。
- 基建对机器可操作。部署、调用、测试、日志查询都必须是 AI 可调用的 API,而不是人类专属的控制台按钮。
- 环境是验证的唯一真相。AI 的自测闭环必须落在真实渲染和真实调用上——截图、DOM、控制台、网络请求——而不是模型对自己代码的自我评价。
为什么要组件化拆分
组件化是这套架构的核心理念。通过组件化,庞大的应用被拆分成若干独立的小组件,AI 基于组件维度 Coding,无需感知任何庞大的代码仓库。
认知负载的消除。组件本身很小(几百到几千行代码),AI 可以将组件的前后端代码全部加载进上下文。它不需要在数万行的仓库里 grep 本次需求要改的内容,不需要猜测某个函数在别处是否还有调用方,不需要理解五层抽象——它看到的就是全部。在 Contextile-Bench 的主实验中,Agent 就是这样在这套架构上独立承接产品需求的:96 次任务运行,94 次一次通过全部黑盒验收(98%),没有人类兜底。
爆炸半径的封锁。组件的边界就是爆炸半径。一次修改最多毁掉一个组件,而一个组件的故障在页面配置层有明确的降级策略(隐藏、占位、兜底文案)。这道边界在基准里同样看得见:Contextile-Bench 覆盖 S/M/L 三档规模的 80 条实验记录中,传统架构累计出现 8 次「改这里、坏那里」的净回归,Contextile 是 0 次。这让「让 AI 直接改生产代码」从冒险变成了工程上可控的决策。
非技术人员的直接驾驭。当单次变更的复杂度被压缩到「一个组件」的尺度,审核的单元也随之变小:人按组件验收结果即可,不必把整个应用装进脑子。这为没有技术背景的人直接驱动 AI Coding 打开了大门——比如产品经理一人驾驭产品的完整迭代。
为什么要 FaaS 化部署
每个组件要获取动态数据,就需要一个专属后端服务为它提供数据。这个专属后端服务由 FaaS 承载非常合适。
我敢说,FaaS 将是 AI 时代最重要的技术基础设施。
FaaS 的核心理念是函数是一等公民:没有应用的概念,每个服务是一个独立的函数,独立开发、独立部署、独立运维。这与「组件是最小交付单元」严丝合缝——组件的后端天然就该是一个函数。
一对一的组合关系带来了决定性的简化:AI 在编写组件函数时,完全不需要考虑系统级的复用性。它不需要设计给未来调用方使用的通用接口,不需要兼容其他场景,只解决当下这个组件的数据需求。函数的职责聚焦到极致,AI 根据组件需求直接产出这个函数是很容易的事。
需要说明的是:本架构对「FaaS」的要求是一份契约而非某个具体产品——凡是能提供「组件专属、可独立部署、可编程调用、带环境隔离」的函数运行时,无论是公有云函数计算、边缘函数、还是自建的轻量容器运行时,都可以实现这份契约(见第十二章 §5)。
为什么允许重复——复用的位置变了
「每个组件一个专属函数、组件之间不共享代码」——听到这里,任何受过训练的工程师都会本能地皱眉:这违反 DRY。
但 DRY 是有经济学前提的。重复的真正代价从来不是「多写了一遍」,而是「改的时候要改 N 处、还可能漏改」。当 AI 把「改 N 处」的边际成本压到接近于零(一条指令批量修改所有副本,且每个副本都在各自的小上下文里被正确理解),重复的代价就只剩下存储——而存储不要钱。
反过来,错误抽象的代价在 AI 时代被放大了。一个被五个场景共用的抽象层,是每个 Agent 每次任务都必须装载的上下文税;一次为了「未来可能的复用」而做的过度设计,会让之后每一次修改都要先理解这份设计。Sandi Metz 的名言在 AI 时代更加成立:错误的抽象远比重复昂贵。
所以本架构的主张不是「不要复用」,而是复用上移:
- 代码不复用:组件之间通过复制而非依赖来共享实现。拷贝即所有,改坏了只坏自己。
- 接口复用:真正沉淀业务能力的地方是领域服务和存量接口,组件函数是它们的轻量编排层(如何让 AI 找到该用的接口,见下一章)。
- 资产复用:视觉一致性靠设计 tokens、组件模板、脚手架来保证——复用发生在「生成时」而不是「运行时」。
让复用发生在接口和设计资产层,而不是代码层。
组件函数的依赖从哪儿来:AIRS
组件函数不生产核心业务逻辑,它编排存量能力:查商品、查库存、查优惠券、下单——这些能力以 HTTP / RPC 接口的形式存在于企业的领域服务中,成千上万。
于是出现了 AI 时代的一个新问题:AI 怎么在海量存量接口中,快速准确地找到当前需求真正该用的那一个?接口文档散落各处、口径腐烂、命名各异,人类工程师靠部落知识和口口相传解决这个问题,AI 没有部落。
为此,本架构定义了一套 AIRS——Agentic Interface Retrieval Specification(AI 编程接口检索规范):
- 每个接口按统一的元数据标准登记:语义化描述、参数与返回值 Schema、真实调用示例、负责人、SLA、稳定性等级;
- 提供一个面向 Agent 的检索服务:Agent 用自然语言描述数据需求,检索服务返回排序后的候选接口及其完整契约;
- AIRS 是研发期设施,不是运行时设施:代码生成并上线后,组件函数仍通过原生 HTTP / RPC 直连目标服务,AIRS 不代理任何线上流量,不引入任何运行时依赖。
与 MCP 的关系:MCP 解决的是「Agent 在运行时如何调用工具」,AIRS 解决的是「Agent 在编码时如何发现存量接口、并生成原生调用代码」。两者互补,作用在软件生命周期的不同阶段。
AIRS 的详细契约见第十二章 §6。
配套基础设施:让基建对机器可操作
前几章解决的是 AI 在 Coding 环节的问题。但 Coding 往往只占研发周期 20% 的时间——要让 AI 真正接管交付,就必须把测试、部署这些依赖人力的环节也变成 AI 可以独立操作的接口。
部署接口。基础设施层必须提供组件的程序化部署能力:前端代码发布至 CDN(内容寻址、URL 不可变)、后端代码部署为函数、部署状态可查询且有确定性的成功/失败判据。「确定性判据」不是洁癖:如果部署没有机器可判定的终态(接口调用成功不代表部署成功),自动化调用方就无法可靠区分成功、失败与仍在进行,Agent 的自动化链路会在这里反复翻车。
模拟器。要让 AI 端到端自测,必须给 AI 一个能渲染真实页面的模拟器:加载指定版本的页面配置与组件代码、注入测试身份与参数、真实调用指定环境的函数,返回截图、DOM 结构、控制台日志与网络请求记录。模拟器同时服务两个客户:AI 用它做功能验证(原则七:环境是验证的唯一真相),人用它做审核预览——两者看到的是同一个真实渲染结果。
可观测。每次函数调用携带贯穿式 traceId,日志可按 traceId / 时间范围程序化查询。Agent 排障的闭环是「调用失败 → 查日志 → 定位 → 修复 → 重试」,其中每一步都必须是 API。
环境模型。基础设施必须提供环境隔离语义(至少:开发环境 / 预发环境 / 生产环境),且部署范围与调用白名单是两个独立的控制面——允许自动部署到多环境,不等于允许 Agent 调用任意环境。生产环境的发布必须经过人审门禁。
一个需求的完整旅程
以一个通用电商场景走一遍全流程。需求:「在商品详情页增加一个『到手价计算器』组件,展示叠加优惠券后的最终价格。」
- 理解页面:Agent 读取
product-detail的页面配置,了解现有组件清单,确定新组件的插入位置。 - 创建组件:按组件标准(§1)生成脚手架——
price-calculator/目录,含前端入口、函数入口、组件说明文件、测试。 - 检索接口:通过 AIRS 检索「查询用户在指定商品上可用的优惠券」,获得候选接口的 Schema 与调用示例,将接口契约快照存入组件目录(上下文自包含)。
- 编写代码:前端组件 + 专属函数合计几百行,全量在上下文中一次写就。函数编排优惠券接口与价格接口,前端按设计 tokens 渲染。
- 部署开发环境:调用部署接口发布前端产物与函数,轮询部署状态至确定性成功。
- 模拟器自测:注入测试用户渲染真实页面,断言截图与 DOM、检查控制台无错误。发现「无可用券」时组件白屏——补上兜底展示,重新部署、重测通过。
- 提交人审:产出物 = 代码 diff + 模拟器预览链接 + 自测记录。人审核的是交付物,不是过程。
- 发布上线:审核通过后发布新版本页面配置,线上生效。如需回滚,把配置指针切回上一版本即可,秒级完成。
整个旅程中,人只出现在第 7 步。
人才与组织的转变
这套架构屏蔽了产品的技术实现细节,通过降低 AI 的认知负载,让「编码、测试、部署交给 AI 独立完成」成为可能。人的注意力从「怎么实现」转向「该做什么」——聚焦产品本身、聚焦用户价值本身。
在这套架构之上,组织所需要的人才画像也发生了变化,我们称之为「AI 产品全栈工程师」:
- 具备产品思维,产品的迭代进化由 TA 一人驱动;
- 具备基本的技术素养,当 AI 在少数情况下出错时,有能力定位和解决问题;
- 核心工作是设定方向、审核产出、把控风险——人从方案的产出者,转变为方案的审核者。
组织不再需要把岗位切分得很细(运营、产品、前端、后端、算法、测试、BI、SRE 各司其职),一个产品方向只需一名 AI 产品全栈工程师,借助 Agent 集群推动产品进化。审核的粒度可以随信任度演进:初期逐产出物审核,随着质量数据积累,逐步放权到只审关键节点。
弊端、质疑与回应
一个诚实的架构宣言必须直面自己的代价。
1. FaaS 成本会上升。成立。一对一的函数不考虑复用,独立部署运维,机器成本高于传统的共享服务。我们的回应:这是用机器成本置换人力成本与迭代速度,而三者的价格趋势一目了然;同时函数粒度的弹性伸缩(闲时缩容到零)能覆盖大部分长尾组件的成本。
2. 人不再了解实现细节,风险如何控制?成立,且必须严肃对待。整个迭代过程人只关注最终交付物,中间实现交由 AI 完成,可能存在人未察觉的分支缺陷。我们的回应是纵深防御:确定性测试 + 模拟器真实环境验证(不是 LLM 自评)+ 人审门禁 + 组件级爆炸半径 + 秒级配置回滚 + 全链路可观测。信任,但验证。
3. 组件各写各的,视觉和体验会漂移。会,如果什么都不做。所以复用要上移(第六章):设计 tokens 和组件模板在生成时约束视觉,页面框架统一壳层行为,架构标准中的确定性校验(lint 级规则)在部署前拦截越界。
4. N 个函数是不是 N 倍的安全面?函数数量确实变多,但每个函数的权限面变小了。回应:统一的请求信封与网关鉴权、函数最小权限原则、部署门禁中内置静态安全扫描(这些扫描本身也可以是 Agent)、AIRS 登记接口的调用方审计。
5. 这不就是低代码?不是。低代码约束「人」用可视化拼装代替代码,牺牲表达力换取门槛;本架构中代码仍然是全表达力的代码,只是由 AI 来写。被约束的是边界(组件多小、依赖什么),不是表达(能写什么逻辑)。
6. 这不就是微前端 / Server-Driven UI?机制上确有交集(运行时组装、配置驱动),但目标函数不同。微前端解决的是多团队并行与渐进式升级,所以它容忍巨大的子应用;SDUI 解决的是发版灵活性。本架构解决的是 AI 的认知负载,所以它强制小组件、强制前后端 1:1、强制无代码复用——这些约束在前两者的目标下没有必要,在本架构的目标下缺一不可。
7. 复杂业务逻辑和跨组件联动怎么办?架构划定了自己的适用边界,见下。组件间联动通过页面框架的受限事件总线完成(广播语义、可序列化载荷、互不假设对方存在),禁止组件直接依赖。真正的重业务逻辑本来就不该写在组件函数里——它属于领域服务,组件函数只做编排。
8. 既然 Provider 接口(§5)要使用者自行实现,参考实现里的 MCP Server 是做什么的?两者处在依赖倒置的两侧,面向不同的消费者。Provider 接口是面向基建的 SPI,回答「部署、调用、渲染这些动作由谁实现」;MCP Server 是面向 Coding Agent 的 API,回答「Agent 怎么使用这些动作」——它把 Provider 之上的交付工作流编排成少数几个稳定的工具(例如 deploy_component = 发布前端资产 → 部署函数 → 轮询部署状态至确定性终态 → 更新页面配置),自身不实现任何基建。这带来两个结果:其一,§5 的流程纪律(如 MUST NOT 以调用成功推断部署成功)固化在工具实现里,无需每个团队在 Agent 提示词中重新发明;其二,Agent 侧的工具签名与工作流不随基建变化——本地开发时对接参考实现内置的本地 Provider,上生产换成自家适配器,Agent 无感。
适用边界。本架构最适合:C 端展示与营销页面、运营活动、增长实验、内容型页面、数据看板、管理后台——一切「页面结构清晰、迭代频繁、试错价值高」的场景。需要谨慎评估:强事务一致性的核心域(交易、账务、库存扣减本体)、重交互的工具型单页应用(编辑器、IDE)、极致性能敏感场景。我们不主张用它重写你的核心域——它是核心域外围那层需要快速进化的皮肤,而皮肤恰恰是绝大多数产品迭代发生的地方。
架构标准(v0.1 草案)
规范文本以 contextile/spec 仓库为准,本章为其阅读快照。 本章是规范性内容。关键词 MUST(必须)/ MUST NOT(禁止)/ SHOULD(应当)/ MAY(可以) 按 RFC 2119 解释。完整规范在独立仓库中以 RFC 流程演进。 一个团队可以只采纳部分标准,合规性分级见 §8。
§0术语
| 术语 | 定义 |
|---|---|
| 页面(Page) | 由 pageId 标识的一次运行时组装结果,结构由页面配置声明 |
| 组件(Component) | 最小交付单元,= 前端产物 + 专属函数 + 清单文件 |
| 组件函数(Component Function) | 专属于某一个组件的后端函数,1:1 绑定 |
| 页面配置(Page Config) | 声明页面组件清单及其装配参数的版本化 JSON 文档 |
| 渲染框架(Renderer) | 解析页面配置、加载组件、编排数据、处理降级的通用运行时 |
| Provider | 基础设施的适配接口(配置、函数、资产、模拟器四类) |
| 数据信封(Envelope) | 渲染框架与组件函数之间的标准请求/响应格式 |
§1组件标准
1.1 目录结构。组件 MUST 是一个自包含目录:
price-calculator/
├── component.json # 组件清单(见 1.2)
├── CLAUDE.md # 面向 Agent 的组件说明(见 §7)
├── frontend/
│ ├── index.jsx # 前端入口,ESM 默认导出
│ └── styles.css # 作用域隔离的样式
├── backend/
│ ├── handler.ts # 组件专属函数入口
│ └── apis.md # 该函数依赖的外部接口契约快照(AIRS 检索结果落档)
└── tests/
├── frontend.test.jsx
└── backend.test.ts
1.2 组件清单。component.json MUST 存在:
{
"componentId": "price-calculator",
"name": "到手价计算器",
"owner": "example-team",
"frontend": { "entry": "frontend/index.jsx" },
"backend": { "entry": "backend/handler.ts", "runtime": "node20" },
"budget": { "maxSourceLines": 3000 },
"test": { "command": "npm test" }
}
1.3 尺寸预算。组件前后端源码合计 SHOULD ≤ 3,000 行。这个数字是经验值,其背后的硬规则是:组件全量源码装入主流模型上下文后,MUST 仍保留至少一半空间用于推理、对话与工具输出。超出预算的组件 MUST 拆分为多个组件。
1.4 隔离性。
- 组件 MUST NOT import 其他组件的任何代码;
- 组件 MUST NOT 读写其他组件的 DOM 或全局状态;
- 组件间通信 MUST 经由渲染框架的事件总线(§3.4);
- 样式 MUST 作用域隔离,MUST NOT 使用裸元素选择器或修改
:root; - 基础运行时(react / react-dom / 设计系统基础包)由渲染框架经 import map 统一提供,组件 MAY 依赖这份白名单,其余依赖 MUST 打包进组件自身产物。
§2页面配置契约
页面配置是一份版本化的 JSON 文档:
{
"pageId": "product-detail",
"version": "42",
"meta": { "title": "商品详情" },
"components": [
{
"componentId": "price-calculator",
"frontend": {
"format": "esm",
"url": "https://assets.example.com/components/price-calculator/42/index.js",
"integrity": "sha384-…"
},
"function": {
"name": "shop.product-detail.price-calculator",
"timeoutMs": 3000,
"required": false
},
"props": { "showDiscounts": true },
"fallback": "hide"
}
]
}
规则:
- 配置版本 MUST 不可变:发布 = 产生新版本,回滚 = 移动活跃版本指针(§5.1);
frontend.urlMUST 内容寻址(哈希入路径或文件名),SHOULD 携带子资源完整性校验(integrity);fallbackMUST ∈hide | skeleton | message,声明该组件失败时的降级表现;required: true的组件失败时,渲染框架 MUST 执行页面级降级策略;required: false的组件失败 MUST NOT 阻塞其他组件渲染;- 渲染框架 MUST 并行发起所有组件的初始取数,逐组件流式渲染。
§3数据契约
3.1 请求信封(渲染框架 → 组件函数):
{
"page": { "pageId": "product-detail", "version": "42", "query": { "productId": "1234" } },
"component": { "componentId": "price-calculator", "props": { "showDiscounts": true } },
"context": { "traceId": "ctx-9f3c…", "user": { "id": "u_888" }, "device": "mobile", "locale": "zh-CN" },
"action": "initial",
"params": {}
}
action = "initial" 表示首屏取数;交互期二次取数使用组件自定义的 action 名,路由在函数内部完成。
3.2 响应信封(组件函数 → 渲染框架):
{
"success": true,
"data": { "finalPrice": 128.0, "discounts": ["…"] },
"errorCode": null,
"errorMessage": null,
"traceId": "ctx-9f3c…",
"costMs": 87
}
失败时 success=false,errorCode MUST 机器可判定,errorMessage SHOULD 人类可读且可行动。
3.3 前端组件契约。前端入口 MUST 是 ESM 默认导出的组件,签名如下:
export default function PriceCalculator({ data, props, context, actions }) {
// data: 函数返回的动态数据(首屏由框架并行预取)
// props: 页面配置中的静态配置
// context: 页面上下文(用户、设备、locale——只读)
// actions: 框架注入的受限能力:
// actions.invoke(action, params) → 调用本组件专属函数(二次取数)
// actions.emit(event, payload) → 页面事件总线广播
// actions.on(event, handler) → 订阅页面事件
// actions.track(event, payload) → 埋点
return <div>…</div>;
}
actions.invoke MUST 只能调用本组件的专属函数——跨组件取数在协议层即不可表达。
3.4 事件总线。事件名 MUST 带组件命名空间;载荷 MUST 可 JSON 序列化;广播语义,无应答保证;组件 MUST NOT 假设某个事件一定有订阅者或发布者。
§4组件函数契约
- 函数 MUST 无状态,实例间不共享内存状态;
- 函数 MUST 只服务一个组件(1:1),MUST NOT 被多个组件共享——需要共享的逻辑属于领域服务;
- 函数 MUST 接受 §3.1 请求信封、返回 §3.2 响应信封,协议为 HTTP POST + JSON(满足此契约即语言无关);
- 函数名 SHOULD 采用
{命名空间}.{pageId}.{componentId}形式,MUST 全局唯一; - 函数 MUST 在
timeoutMs预算内响应,读操作 SHOULD 幂等; - 日志 MUST 携带请求信封中的 traceId;
- 对领域服务的调用 MUST 使用原生 HTTP / RPC 直连(不经过 AIRS 或其他研发期设施)。
§5Provider 接口标准
基础设施依赖被倒置为四个 Provider 接口。实现了这四个接口,任何公司的基建都能承载本架构;参考实现提供开箱即用的本地版(配置存文件、函数跑本地进程、静态资源本地伺服),用于开发与体验。
5.1 PageConfigProvider
interface PageConfigProvider {
getPage(pageId: string, version?: string): Promise<PageConfig>; // 缺省返回活跃版本
publishPage(config: PageConfig): Promise<{ version: string }>; // 版本不可变
listVersions(pageId: string): Promise<VersionMeta[]>;
setActiveVersion(pageId: string, version: string): Promise<void>; // 发布/回滚 = 移动指针
}
5.2 FunctionProvider
interface FunctionProvider {
deploy(bundle: ComponentBundle, env: Env): Promise<{ deployId: string }>;
status(deployId: string): Promise<DeployStatus>; // 终态判据 MUST 确定;FAILED MUST 携带机器可读原因
invoke(fn: string, envelope: RequestEnvelope, env: Env): Promise<ResponseEnvelope>;
logs(fn: string, filter: { traceId?: string; range?: TimeRange }): Promise<LogEntry[]>;
}
环境语义:Provider MUST 提供环境隔离(至少 DEV / STAGING / PROD);部署范围与调用白名单 MUST 是两个独立配置;status MUST 给出确定性的终态判据,MUST NOT 要求调用方以「接口调用成功」推断「部署成功」。
5.3 AssetPublisher
interface AssetPublisher {
publish(files: FileMap): Promise<{ urls: Record<string, string> }>;
// URL MUST 内容寻址且不可变;SHOULD 返回 integrity 哈希
}
5.4 SimulatorProvider
interface SimulatorProvider {
render(input: {
pageId: string;
overrides?: PageConfigPatch; // 未发布的草稿配置可叠加渲染
context: RenderContext; // 注入测试身份与参数
env: Env; // 函数调用环境(受调用白名单约束)
}): Promise<{
screenshot: Bytes;
dom: string;
console: ConsoleMessage[];
network: NetworkRecord[];
errors: RenderError[];
}>;
}
模拟器 MUST 使用与生产一致的渲染框架;MUST 区分「数据已返回」与「视图已可见」两个就绪信号——白屏但接口 200 不是就绪。
§6AIRS 接口检索标准
6.1 接口元数据。每个登记接口 MUST 提供:
以下记录仅用于说明契约结构,是完全合成的示例:
{
"name": "demo.discount.list-eligible",
"protocol": "http",
"endpoint": "POST /example-api/discounts/eligible",
"semantic": "列出合成示例中指定用户可用于指定商品的价格优惠,供演示资格判断",
"tags": ["demo", "discount", "eligibility"],
"request": { "$schema": "…", "properties": { "userId": {}, "productId": {} } },
"response": { "$schema": "…", "properties": { "discounts": {} } },
"examples": [ { "request": { "userId": "demo-user", "productId": "demo-product" }, "response": { "discounts": [] } } ],
"owner": "example-team",
"stability": "stable",
"sla": { "p99Ms": 150, "availability": "99.95%" },
"deprecated": false
}
6.2 检索协议。
POST /airs/search
{ "query": "演示用户可以在演示商品上使用哪些价格优惠", "topK": 5 }
→ 返回按相关度排序的候选接口,含完整元数据
6.3 纪律。
- AIRS MUST 仅在研发期使用;生成代码 MUST 直连原生协议,MUST NOT 让线上流量经过检索服务;
- Agent SHOULD 将选中接口的契约快照写入组件目录(
backend/apis.md),保证组件上下文自包含; semantic字段是检索质量的生命线,MUST 面向「使用场景」而非「实现描述」撰写。
§7Agent 工作区标准
7.1 组件说明文件。每个组件 MUST 包含面向 Agent 的说明文件(CLAUDE.md / AGENTS.md),内容 MUST 覆盖:组件职责与业务背景、数据契约摘要、依赖接口清单、测试与验证命令、明确的修改边界。
7.2 上下文自包含。理解并修改组件所需的全部信息 MUST 存在于组件目录内——包括其依赖的外部接口契约快照。Agent 完成一次组件任务 MUST NOT 需要读取组件目录之外的业务代码。
7.3 修改边界。一次 Agent 任务 MUST 只修改一个组件目录;跨组件需求 MUST 拆分为多个任务(这同时是多 Agent 并行的安全前提)。
7.4 验证闭环。组件 SHOULD 提供确定性测试命令(component.json 的 test.command);上线前 MUST 通过模拟器完成一次真实渲染检查(截图 + DOM + 控制台无错误)。验证的终点是环境,不是模型的自我评价。
§8合规性分级
| 级别 | 名称 | 要求 | 达到的能力 |
|---|---|---|---|
| L1 | 渲染合规 | §2 + §3 | 页面按配置组装渲染;后端可以是任何存量服务 |
| L2 | 交付合规 | L1 + §1 + §4 + §5 | Agent 可通过 Provider 接口端到端部署组件 |
| L3 | AI 原生合规 | L2 + §6 + §7(含模拟器) | Agent 可独立完成 开发 → 部署 → 自测 → 提审 的完整闭环 |
配套的一致性测试套件(TCK)将随参考实现发布:跑通测试套件,即可声明相应级别的合规。
从组件交付到产品迭代
架构标准回答的是 Agent 如何在明确边界内交付组件。要将这种能力用于产品迭代,研发系统还需要提供几类通用机制:将需求与交付状态表示为机器可读信息;在关键决策处保留可审计的人类审核;为每次变更提供隔离执行、确定性验证与可回滚发布;并将上线后的核心业务指标用于下一轮决策。
这些机制不规定某种平台形态:可以由现有的 CI/CD、项目管理、Agent 工具与可观测系统组合实现,也可以由一体化平台提供。架构标准定义组件交付的边界与接口,不依赖任何具体的编排产品。任何团队都可以在 L2 / L3 合规的基建上实现同样的「开发 → 审核 → 上线」闭环。
一起来定义 AI 时代的架构
过去五十年,我们的每一代架构——结构化、面向对象、SOA、微服务——都在为同一个约束优化:人类工程师的协作成本。这个约束刚刚松动了。这是五十年一遇的、重新思考架构第一性原理的机会。
本宣言给出的答案是:为 AI 的认知边界设计架构——组件小到装得进上下文,边界硬到锁得住爆炸半径,基建平到机器可操作,验证真到环境说了算。
这套标准将以开放的方式演进。你可以从以下入口阅读、试用或参与共建:
- contextile.dev:阅读双语宣言、Quickstart 与项目最新入口;
- contextile/contextile:参考实现,包含渲染器 SDK、本地一体化运行时、CLI 与 MCP Server,用于端到端交付 Contextile 组件;
- contextile/contextile-bench:公开基准评测,提供跨架构、跨规模的实验任务、原始评分、修正账本与分析代码,支持复跑和扩展;
- contextile/manifesto:本宣言的双语源文、图表与版本记录;
- contextile/spec:Contextile 的规范性标准,包含规范章节、JSON Schema、类型定义与 RFC 流程。
Provider 适配器将由官方样例与社区实现共同扩展。
如果你也相信「架构应该为 AI 重新设计」,欢迎从任何一个层面参与:实现一个 Provider 适配器、提交一份 RFC、或者仅仅是在你的团队里划出一个页面试一试。
你指明方向,让产品自己生长。
—— Asher Chai(@asher-chai)