目录
文章目录
- 目录
- [AWS Kiro 架构解析](#AWS Kiro 架构解析)
-
- [统一之前:三套 harness 的代价](#统一之前:三套 harness 的代价)
- [架构分析:harness 与客户端的边界](#架构分析:harness 与客户端的边界)
-
- 官方给出的两侧清单
- 为什么是独立进程而不是库
- 客户端可以覆盖内置工具
- [一次 turn 的六个阶段](#一次 turn 的六个阶段)
- [ACP 与 Kiro 的扩展](#ACP 与 Kiro 的扩展)
- 规格驱动开发:把需求写成可追溯的三份文件
-
- 三份文件
- [EARS 与术语表:让验收标准的主语可判定](#EARS 与术语表:让验收标准的主语可判定)
- 正确性属性:设计文件里的一节全称命题
- 任务文件:可选任务与需求回指
- 四种模式
- 审批门
- 与另外三家的对照
- [steering 与 hooks:上下文注入与生命周期挂点](#steering 与 hooks:上下文注入与生命周期挂点)
-
- [steering 的四种纳入方式](#steering 的四种纳入方式)
- [文件引用:把 steering 变成索引](#文件引用:把 steering 变成索引)
- [hooks 的十个触发器](#hooks 的十个触发器)
- 参考引用
AWS Kiro 架构解析
Codex 把 core 引擎做成一个 Rust crate,三个前端并列依赖它;dsh 把一切做成插件,由插件装配决定形态;Claude Code 把内核做成一个原生可执行文件,由 SDK 驱动。而 Kiro 把 harness 做成一个独立的服务进程,客户端经 ACP 协议与它通信。
统一之前:三套 harness 的代价
要理解 Kiro 现在的形态,得先看它之前的形态。这一节的材料全部来自官方博客。
统一之前 Kiro 有三套各自独立的 harness,且语言各异。
- IDE 那套用 TypeScript,理由是贴合 Code OSS 的扩展模型;
- CLI 那套用 Rust,理由是性能;
- Web 那套用 Python,理由是贴近 agent 研究。
三个选择各自都说得通,但分裂的后果官方也逐条列了出来:
- 权限的表达方式不同:CLI 用的是基于正则的允许与拒绝命令列表,而 IDE 用的是前缀匹配的可信命令列表加子串匹配的拒绝列表。也就是说同一个安全需求在两个形态上要写两遍,且语义还不一样。
- 功能覆盖不同:Spec 规格驱动开发只有 IDE 有,而 Plan 模式只有 CLI 有。
- 由此产生的用户困惑是结构性的,即:同一个产品名之下,换一个入口就换一套能力与一套配置。

架构分析:harness 与客户端的边界
官方给出的两侧清单

- Kiro harness 一侧共七项,即:Agent 循环、工具与子 Agent、会话状态、MCP 客户端、配置与 steering、权限、遥测。
- 客户端一侧共三项,即:UX 与呈现、用户交互,以及可选的平台原生工具覆盖。
官方文档有一份更细的清单,它把 harness 列成 14 项,每一项都对应一个功能文档页[1](#1)。这十四项是:规格、steering、hooks、MCP、权限、自定义 Agent、Agent Skills、Powers、云会话、压缩、忽略文件、检查点与回退、内置工具、模型。
可以看见 Kiro 对 harness 边界的划法相当激进,凡是与 "Agent 能做什么" 有关的东西全在 harness 侧,客户端只剩下人机交互。

值得注意的是,这并不意味着客户端就只是 "一层皮",实际上 IDE 补全了行内差异、检查点界面、规格面板与把编辑器诊断共享给 Agent,CLI 补全了终端界面、无头模式、自动补全与从 shell 管理会话。
为什么是独立进程而不是库
官方文档特别提到 harness 是一个独立进程,而不是被编译进每个应用(IDE、CLI、Web)的库。两者的区别如下图所示:

客户端可以覆盖内置工具
若客户端只剩人机交互,则有一个具体问题需要回答,即:IDE 里的文件编辑该由谁做,而 Kiro 的答案是客户端可以提供自己的工具并覆盖 Harness 内置的那一个实现。这一设计让客户端具有更高的对用户优先级。

官方给出了一个具体的例子:IDE 使用 Code OSS 的 API 做文件操作,因此它提供自己的文件读写工具,取代 harness 那两个直接操作文件系统的内置工具。当 Agent Client 需要运行一个由客户端提供的工具时,harness 通知客户端,由客户端执行并把结果返回。
这个机制在协议层有对应的落点,即握手时客户端声明的能力里包含读文本文件、写文本文件与终端三项[2](#2)。也就是说 "谁来执行" 这件事是握手协商出来的,而不是硬编码的。
这一处设计解决的是一个真实矛盾。IDE 里的文件修改若绕过编辑器的 API 直接写盘,则未保存的缓冲区、撤销栈与文件监视都会错位,而若为此在 harness 里塞一套 IDE 专用逻辑,那条边界就破了。把工具的实现权交给客户端、而把工具的调度权留在 harness,是这个矛盾唯一干净的解法。
一次 turn 的六个阶段
harness 侧的 turn 流程被文档归纳为六个阶段,全部发生在 harness 之内。
| 阶段 | 内容 |
|---|---|
| 1 | 组装上下文,提示词提交类 hook 在此注入。 |
| 2 | 模型规划,或由自动路由选择模型。 |
| 3 | 逐个检查工具调用,含权限判定、忽略文件 与工具使用前 hook。 |
| 4 | 执行工具,含内置工具、MCP、Powers 与子 Agent,随后是工具使用后 hook。 |
| 5 | 结果回灌,随后是 Agent 停止类 hook。 |
| 6 | 上下文维护,即压缩。 |

如上图所示,这六个阶段的价值在于它把前面那份十四项功能清单串成了一条线。规格、steering 与 skills 落在第一阶段,模型与路由落在第二阶段,权限、忽略文件 与 hooks 落在第三阶段,工具与子 Agent 落在第四阶段,检查点与回退落在第五阶段之后,而压缩落在第六阶段。也就是说那十四项不是并列的功能列表,而是这条循环上的十四个挂点。
ACP 与 Kiro 的扩展
harness 与客户端之间的协议是 ACP(Agent Client Protocol)标准。这一节交代 Kiro 用了它的哪一部分、扩展了哪一部分,以及公开到什么程度。
标准协议这一侧
ACP 协议模块启动方式是一条子命令,通信走标准输入与标准输出上的 JSON-RPC 2.0。第三方编辑器接入时的配置键是一个 Agent 服务器清单,因此 Kiro 可以作为一个 ACP Agent 被别的编辑器驱动。
- Kiro 实现的 ACP 标准方法有七个,即:初始化、新建会话、加载会话、提交提示词、取消、设置模式、设置模型;
- 它向客户端声明的能力包括:支持加载会话、支持图片输入;
- 会话通知的更新类型有四种,即:助手消息分块、工具调用、工具调用更新、轮次结束。
两种传输
ACP 传输方式有两种,本地客户端走标准输入输出,而 Web 与移动端走 WebSocket。且 WebSocket 是 Kiro 自建的扩展,官方原话:ACP 支持标准输入输出作为传输,而对于 Web 与手机这类远程客户端,他们需要另一种传输,因此添加了一个基于 WebSocket 的自定义传输。
远程那一侧还有一处形态差异值得记录,即 Web 与移动端的 harness 跑在托管的云沙箱里,本机不执行任何东西,产出以拉取请求的形式交回。
扩展命名空间与公开程度
Kiro 在 ACP 标准之外扩展了自己的方法,而这些方法放在一个保留前缀之下。ACP 的可扩展性规则是任何以下划线开头的方法名都保留给自定义扩展[3](#3)。
Kiro 的前缀写法在官方文档里提到了 2 种:
- 文档的协议页与实际方法名用的是带域名的完整形式;
- 文档的架构页与博客用的是省略域名的简写。
Kiro 声称在 ACP 协议之上共加了二十余个可被 Agent 调用的方法、十五个可被客户端调用的方法与二十种通知类型,合计约五十五个扩展点。

规格驱动开发:把需求写成可追溯的三份文件
Kiro 是最早 build-in 实现 Spec 规格驱动开发思想的 AI Coding Agent,它把需求、设计与任务分别落成 specs 文件并置于版本控制之下[4](#4)。
三份文件
一个规格是仓库内 .kiro/specs/<名称>/ 目录下的一组文件,其构成为需求、设计与任务三份。其中,需求文件按功能与缺陷分成两个名字,而非同一个文件里加一个类型字段,这一处选择本身就说明两类工作的表达结构不同。
| 文件 | 角色 | 关键约定 |
|---|---|---|
requirements.md |
需求,新功能用此名 | User Story 加 EARS 验收标准,含术语表。 |
bugfix.md |
需求,缺陷修复用此名 | 三段式,含回归防护段。 |
design.md |
设计 | 架构、接口、数据模型,可含正确性属性。 |
tasks.md |
任务 | 复选框列表,每项回指需求编号。 |

EARS 与术语表:让验收标准的主语可判定
spec 规格驱动的术语表中明确 EARS 主语、正确性属性、验收标准分列、任务回指需求编号、可选任务显式标记、缺陷规格必须写未变行为,这五条都是纯写作约定。
其中,需求的验收标准采用 EARS 句式,即 Easy Approach to Requirements Syntax,其形态是当某个条件成立时系统应当做某件事。
代码仓库里有 AWS 用 Kiro 写的真实 specs 示例,其需求文件的组织方式是先给出一节术语表,再让每条 EARS 标准的主语落在术语表已定义的实体上[5](#5)。换言之,若一条标准写的是当某个实体处于某个状态时系统应当如何处理它,这个实体是术语表里有定义的名词,而不是一个临时措辞。
这一处约定的价值在于它把歧义的消除位置前移了。若验收标准的主语是自然语言里的泛称,则同一条标准在两个人读来可以指向不同的对象,而模型读来又是第三种;而若主语被术语表钉住了,则这条标准的判定范围是唯一的,与读它的是人还是模型无关。这是一个不需要工具支持、纯靠写作约定就能拿到的收益。
正确性属性:设计文件里的一节全称命题
设计文件里有一节全称命题,这一节的写法有两个固定成分。
- 其一是每条属性以一个表示任意的量词短语开头,即对于任何满足某个前提的输入,系统的行为都满足某个后置条件;
- 其二是每条属性末尾标注它所验证的需求编号,形如验证需求一点二。
这一节与 EARS 验收标准的分工需要说清,因为二者容易被看成重复。验收标准是存在量化的,即存在某个场景使系统表现出某个行为,它天然对应一条测试用例;而正确性属性是全称量化的,即对任何输入都成立,它对应的是不变量;前者可以逐条验收,而后者只能靠推理或性质测试来支撑。把两者分开写,实际上是把可枚举的期望与不可枚举的期望分开放。
据此可以推断,末尾那条需求编号标注是为了让不变量与它所服务的需求之间保持双向可追溯。一条不变量若不指向任何需求,则它是设计者的偏好而非需求的推论,而这一区分在评审时是有意义的。
任务文件:可选任务与需求回指
任务文件是一份复选框列表,其中两处标记是格式层面的约定。
- 复选框之后带一个星号的任务为可选任务,即该任务不完成也不阻塞整个规格的达成。
- 每个任务条目末尾以斜体标注其所实现的需求编号,形如需求一点二,从而任务与需求之间双向可查。
把这两处标记与设计文件里的需求编号标注放在一起看,可以得到一条完整的追溯链,即需求到不变量、需求到任务、任务到代码。这条链的每一环都只是一行文本约定,没有任何工具强制,然而它足以回答评审时最常问的那个问题,即某一条需求究竟由哪些改动落实。
值得注意的是,可选任务这个标记解决的是一个很具体的协作问题。一份任务列表若全部为必做项,则实现者遇到其中一项难以完成时只有两个选择,即硬做或私自跳过;而若可选性在规格里就被声明,则跳过是一个被授权的动作,不需要回到需求方确认。
四种模式
spec 的创建有 4 种方式:
| 模式 | 起点 | 适用 |
|---|---|---|
| 从零创建 | 一句功能描述 | 新功能 |
| 从已有代码 | 现有实现 | 为遗留功能补规格 |
| 从缺陷 | 一个 bug 现象 | 生成 bugfix.md |
| 从对话 | 一段已发生的会话 | 把对话结论固化 |
其中缺陷模式的产物结构与功能模式不同,它是三段式,而第三段是最值得关注的一处,即:未变行为,用于回归防护。
这一段要求写下修复之后哪些行为必须保持不变。它的必要性来自缺陷修复的特有风险。功能开发的失败形态是功能没做出来,而缺陷修复的失败形态更多是修好了这一处、碰坏了另一处,而后者之所以频繁发生,正是因为没有人写下过那些不该变的行为,而把它列为规格的固定一段,等于要求在动手之前先把回归面圈出来。
审批门
三份文件之间有审批门,需求经确认之后才能进入设计,设计经确认之后才进入任务。这一处设置的意义在于它把人的确认动作放在了信息量最大的位置,其背后的逻辑是需求阶段改一句话的成本远低于任务阶段改一批代码,而审批门保证了修改发生在前者。
与另外三家的对照
| 维度 | Codex | dsh | Claude Code | Kiro |
|---|---|---|---|---|
| 需求是否落盘 | 否 | 否 | 否 | 是,requirements.md |
| 计划是否落盘 | 否 | 否 | 是,任务文件 | 是,tasks.md |
| 需求与任务是否互指 | 无 | 无 | 无 | 是,需求编号回指 |
| 不变量是否有专门位置 | 无 | 无 | 无 | 是,正确性属性一节 |
| 落盘物是否进版本控制 | 规则文件进 | 无 | 会话与任务在用户目录 | 规格在仓库内 |
这张表里最后一行值得关注。Claude Code 的任务文件位于用户目录下的会话数据里,因而它是运行时状态,而 Kiro 的规格位于代码仓库内,因而它是代码库的一部分,会随分支走、进代码评审、被历史记录。同一类信息放在两个位置,其可协作程度完全不同。
steering 与 hooks:上下文注入与生命周期挂点
steering 的四种纳入方式
steering 是把 specs 项目约定持续供给 Agent 的机制,其载体是若干 Markdown 文件,而每个文件的头部声明它以何种方式被纳入上下文[6](#6),纳入方式共四种。
| 纳入方式 | 语义 |
|---|---|
| 始终纳入 | 每次请求都进上下文。 |
| 按文件匹配 | 仅当本轮涉及的文件命中给定模式时纳入。 |
| 手动 | 仅当用户显式引用时纳入。 |
| 按需 | 由模型自行判断是否取用。 |
这四种方式的差别在于纳入的决定权归谁,即分别归配置、归本轮的文件集合、归用户与归模型。实际上大多数自研 Agent 只实现了第一种,即把项目约定一次性全塞进系统提示词。

文件引用:把 steering 变成索引
按文件匹配就需要一个模式字段配合,其判定依据是本轮涉及的文件路径。这一种的价值在于它使上下文预算按需分配,即前端约定只在改前端文件时占位。就上下文成本而言,四种方式里只有这一种在项目规模增长时不会线性推高每轮的固定开销。
steering 文件里可以用一个专门的引用语法把代码仓库内的其他文件拉进来,其写法是一个井号加双方括号包裹的文件路径。这一处机制使 steering 文件可以只做索引而不做副本。若没有它,则项目约定要么复制一份进 steering、从此与真实文件不同步,要么完全不进上下文;而有了它,steering 文件里写的是指向真实文件的引用,真实文件改了引用自然跟着变。
其实质是把上下文注入从传值改成传引用,是一个成本很低而收益明确的机制。
hooks 的十个触发器
hooks 是在 Agent 生命周期的特定时点执行外部命令的机制,其触发器共十个,命名采用首字母大写的驼峰形式[7](#7),而这十个触发器覆盖:会话开始、提示词提交、工具使用前、工具使用后、Agent 停止、子 Agent 停止、压缩前、文件保存、文件创建与文件删除一类时点。
把这十个触发器与前文那六个 turn 阶段对照,可以看出 hooks 的挂点是沿着那条循环分布的,即:提示词提交类挂在组装上下文之后,工具使用前后类夹在权限判定与工具执行两侧,而 Agent 停止类挂在结果回灌之后。换言之,hooks 不是一组零散的回调,而是那条循环上的取样点。
一个 hook 是仓库内 hooks 目录下的一个 JSON 文件,其文件名即该 hook 的标识。配置里声明触发器、匹配条件与要执行的命令。hook 的返回通道是退出码,而其中有一个取值具有阻断语义,即:退出码为二时该次工具调用被阻止。
参考引用
https://aws.amazon.com/blogs/devops/one-agent-many-surfaces-inside-kiros-unified-agent-architecture/
https://kiro.dev/docs/how-kiro-works/
https://kiro.dev/docs/cli/acp/
https://agentclientprotocol.com/protocol/extensibility
https://agentclientprotocol.com/updates
https://kiro.dev/docs/permissions/
https://kiro.dev/docs/steering/
https://kiro.dev/docs/subagents/
https://kiro.dev/docs/reference/configuration/
https://github.com/kirodotdev/Kiro
https://github.com/kirodotdev/Kiro/tree/main/.kiro/specs