亚马逊AWS官方博客

将 Kiro CLI 封装为 REST API:双通道架构实践

摘要:Kiro CLI 是 AWS 推出的终端 AI 编码工具,原生只支持 stdio 交互,无法被程序化调用。本文介绍将其封装为标准 REST API 的完整实现方案,重点说明双通道架构的设计决策,以及 ACP 协议通信中的关键技术细节。


1. 引言

随着 AI 编码工具的普及,如何将这些工具集成到现有的自动化流程和团队工作流中,已成为工程实践中的实际需求。Kiro CLI 是 AWS 推出的终端 AI 编码工具,能够完成代码生成、分析与重构,但其原生设计面向终端用户交互,只暴露 stdio 接口,无法直接被脚本或其他服务调用。

本文介绍将 Kiro CLI 封装为标准 REST API 的工程实践,核心产出为两个文件:

kiro-api/
├── acp_client.py   # ACP JSON-RPC 2.0 客户端(~300 行,纯 stdlib)
└── server.py       # FastAPI HTTP 服务(~280 行)

封装完成后,任意 HTTP 客户端均可通过标准 REST 接口调用 Kiro 的 AI 能力,支持多轮会话与模型切换。

kiro-apc(完整源码)

2. 核心挑战:ACP 协议与模型切换

2.1 ACP 协议简介

Kiro CLI 通过 kiro-cli acp 子命令暴露了 ACP(Agent Communication Protocol)协议——基于 JSON-RPC 2.0 over stdio 的双向通信机制。其核心方法如下:

方法 方向 用途
initialize Client → Kiro 握手,声明 client 能力
session/new Client → Kiro 创建编码会话
session/prompt Client → Kiro 发送任务,阻塞等待结果
session/update (notify) Kiro → Client 流式推送文本块和工具调用
session/request_permission Kiro → Client 敏感操作审批请求
_kiro.dev/metadata (notify) Kiro → Client Credits 和上下文用量

ACP 协议支持多轮会话、流式输出和上下文保持,是封装的理想基础。

2.2 模型切换的限制与验证

ACP 协议不支持指定模型,验证过程中依次尝试了以下 8 种方式,均未成功:

尝试方式 结果
session/new 加 model 参数 忽略,仍用默认模型
session/setModel 方法 Method
not found
session/configure 方法 Method not found
_kiro.dev/commands/execute/model 进程崩溃(反序列化错误)
kiro-cli acp --model X unexpected argument
kiro-cli-chat acp --model X unexpected
argument
环境变量 KIRO_MODEL 忽略
~/.kiro/settings/cli.json 配置 忽略

结论:ACP 协议在 v1.25 不支持运行时模型切换,这是 Kiro CLI 的协议设计限制。唯一可行的模型指定方式是 kiro-cli chat --model X --no-interactive,但该命令以一次性子进程运行,不支持多轮会话。

3. 双通道架构设计

针对上述协议限制,采用双通道架构作为工程方案:

HTTP 客户端(curl / Python / JS)
         |
         | HTTP REST (port 8642)
         v
    FastAPI Server (server.py)
         |
    -----+--------------------
    |                        |
    | model=auto             | model=其他
    v                        v
 ACP 通道                 Chat 通道
 acp_client.py            subprocess
 (常驻进程)               (一次性进程)
 多轮会话                 单次调用
 JSON-RPC 2.0 over stdio  文本解析

两个通道的职责划分:

ACP 通道 Chat 通道
实现文件 acp_client.py server.py
协议 JSON-RPC 2.0 over stdio subprocess + 文本解析
模型选择 不支持(固定 auto) 支持 --model 参数
多轮会话 支持 不支持(每次新进程)
性能 无冷启动,延迟低 每次冷启动约 3 秒
适用场景 多轮对话、上下文复用 指定模型的单次任务

设计原则:优先使用 ACP 通道(性能好、支持多轮),仅在需要指定模型时降级到 Chat 通道,两条路径在 FastAPI 层统一对外接口。

kiro-apc(完整源码)

4. 关键实现细节

4.1 完整请求生命周期(ACP 通道)

一次完整的 ACP 请求经历以下步骤:

  1. 客户端发送 POST /prompt
  2. FastAPI 检测 model=auto,走 ACP 通道
  3. acp_client 调用 session/new,获取 session_id
  4. acp_client 构造 JSON-RPC 请求写入 stdin:
    {"method":"session/prompt","params":{"sessionId":"xxx","prompt":[...]}}
  5. Kiro 处理任务,通过 session/update 流式推送文本块(无 id,单向通知)
  6. 如遇敏感操作,Kiro 发送带 id 的 permission request,acp_client 自动审批
  7. Kiro 发送最终响应:{"result":{"stopReason":"end_turn"}}
  8. acp_client 拼接所有文本块,返回完整结果
  9. FastAPI 返回 JSON 响应给客户端

4.2 同步调用语义:Event + Pending Map

ACP 底层是异步 stdio 通信,但对调用方需要呈现同步语义。核心实现(acp_client.py):

# 每个请求分配唯一 id,注册 Event 等待
self._pending[req_id] = (threading.Event(), [None, None])

# 写入 stdin
self._proc.stdin.write(json_msg + "\n")

# 阻塞等待(最多 timeout 秒)
event.wait(timeout)

# _read_loop 线程收到响应后按 id 匹配,写入结果并唤醒
if msg["id"] in self._pending:
    holder[0] = msg["result"]  # 存结果
    event.set()                # 唤醒等待线程

4.3 文本内容的流式收集

session/prompt 的最终响应仅包含结束标志,不含实际文本:

{"jsonrpc": "2.0", "id": 3, "result": {"stopReason": "end_turn"}}

实际文本通过多个 session/update 通知(无 id,单向推送)逐块到达:

← {"method": "session/update", ..., "content": {"type": "text", "text": "```python"}}
← {"method": "session/update", ..., "content": {"type": "text", "text": "\ndef fibonacci(n):"}}
← ...(更多文本块)
← {"jsonrpc": "2.0", "id": 3, "result": {"stopReason": "end_turn"}}

处理方式:在 _session_updates[session_id] 列表中收集所有 chunk,收到 end_turn 后 "".join() 拼接成完整文本。

4.4 权限请求的同步处理

Kiro 在执行文件写入、终端命令等操作前,会发送带idsession/request_permission request(非单向通知,需要回复才能继续执行):

{"jsonrpc": "2.0", "id": 99, "method": "session/request_permission",
 "params": {"toolCall": {"title": "Creating app.py"}}}

若不回复,Kiro 将永久等待,请求挂死。acp_client.py 在 headless 模式下自动审批:

def _handle_permission_request(self, msg_id, params):
    # headless 模式:自动审批所有权限请求
    self._send_response(msg_id, {"optionId": "allow_always"})

4.5 Chat 通道的输出清洗

kiro-cli chat 的 stdout 面向终端用户设计,混杂了大量 UI 元素:

stdout 原始输出:
  - ASCII 艺术 banner
  - "Did you know?" 提示框
  - "Model: claude-opus-4.6 | Plan: KIRO PRO+"
  - "> " 前缀的实际回复内容    ← 目标内容
  - "▸ Credits: 0.09 • Time: 3s" 页脚

server.py 中 _clean_chat_output() 的清洗流程:

1. 正则去除所有 ANSI 转义码(颜色、光标控制符)

2. 跳过 banner、”Did you know?” 提示框、Model/Plan 行

3. 找到 > 标记,提取后续内容

4. 遇到 ▸ Credits 截止,丢弃页脚

kiro-apc/server.py

5. 对外接口与已知限制

5.1 REST API 接口列表

FastAPI 服务运行在 8642 端口,提供以下接口:

接口 说明
POST /prompt 快捷调用,支持指定模型
POST /sessions 创建多轮会话(仅 auto 模型)
POST /sessions/{id}/prompt 向已有会话发送任务
GET /sessions 列出所有活跃会话
DELETE /sessions/{id} 删除指定会话
GET /models 列出可用模型
GET / 健康检查

5.2 已知限制

限制 说明
API 无鉴权 仅限内网使用,不建议暴露公网
ACP 连接 高并发请求需顺序处理
Session 不持久化 服务重启后会话丢失
Chat 模式无上下文 每次启动新进程,不支持多轮对话

6. 总结

类别 结论
协议限制 ACP v1.25 不支持运行时切换模型,是 CLI 协议设计限制,不可绕过
架构决策 双通道方案:多轮对话走 ACP,指定模型走 Chat subprocess
关键发现 session/update 流式通知才是文本载体,最终响应仅含 stopReason
关键发现 权限请求是同步阻塞 request,必须回复,否则进程永久挂起
关键发现 Chat 通道 stdout 混杂 UI 元素,需专门清洗才能提取有效内容
关键发现 stdio 双向通信必须读写线程分离,并单独排空 stderr 防死锁
最终产出 两个文件,约 600 行 Python,标准 REST API,支持多模型与多轮会话

将 CLI 工具封装为可编程 API,核心难点在于理解协议的实际行为——官方文档未覆盖的部分,需要通过调试和源码分析来确认。

➡️ 下一步行动:

相关产品:

相关文章:

7. 致谢与参考

参考 说明
Kiro CLI 官方文档 ACP 协议基础参考
FastAPI 文档 HTTP 服务层实现
Python threading 文档 多线程读写分离方案

*前述特定亚马逊云科技生成式人工智能相关的服务目前在亚马逊云科技海外区域可用。亚马逊云科技中国区域相关云服务由西云数据和光环新网运营,具体信息以中国区域官网为准。

本篇作者

张振华

亚马逊云科技解决方案架构师,负责基于亚马逊云科技的云计算方案的架构和设计,在 Edge、Serverless 、容器化,微服务架构,云原生 DevOps 等方向具有丰富的实践经验。自加入亚马逊云科技后,专注于游戏行业,以及 GenAI 在游戏行业的应用。

韩坤尧

亚马逊云科技解决方案架构师,负责基于亚马逊云科技方案架构的咨询、设计和评估。在运维、安全、网络方面有丰富的经验,目前侧重于AI/大数据领域的研究。在加入 AWS 之前曾就职于 Juniper、Cisco 等公司,担任高级系统工程师,主要服务于国内外企业客户。


AWS 架构师中心:云端创新的引领者

探索 AWS 架构师中心,获取经实战验证的最佳实践与架构指南,助您高效构建安全、可靠的云上应用