Kiro CLI 封装 REST API 实战:ACP 协议双通道架构设计与 FastAPI 集成详解(附完整源码)
Kiro CLI 封装 REST API 实战ACP 协议双通道架构设计与 FastAPI 集成详解附完整源码Kiro CLI 只有 stdio 接口想让别的服务调它门都没有。我最近在搞一个自动化编码流水线需要把亚马逊云科技的 Kiro CLI 集成进来。结果发现这玩意儿只能在终端里交互没有 HTTP 接口没有 SDK连个 socket 都没有。说白了它就是个你打字我回答的命令行工具。但我就是要让它变成 REST API。折腾了一周踩了不少坑总算搞出来了。今天把整个过程分享出来。先说结论两个文件600 行搞定最终产出就两个文件kiro-api/ ├── acp_client.py # ACP JSON-RPC 2.0 客户端~300 行 └── server.py # FastAPI HTTP 服务~280 行用 FastAPI 起个服务端口 86427 个 REST 端点支持多轮会话和模型切换。完整实现代码见下文。ACP 协议Kiro CLI 的隐藏接口Kiro CLI 有个子命令kiro-cli acp启动后通过 stdio 进行 JSON-RPC 2.0 双向通信。这就是 ACPAgent Communication Protocol协议。核心方法就这几个方法方向干嘛的initializeClient → Kiro握手声明能力session/newClient → Kiro创建编码会话session/promptClient → Kiro发任务等结果session/updateKiro → Client流式推文本块通知session/request_permissionKiro → Client敏感操作审批看着挺清晰对吧但坑全藏在细节里。模型切换试了 8 种方式全失败我一开始的想法很简单通过 ACP 协议指定模型。比如在session/new里加个 model 参数。结果呢尝试方式结果session/new加 model 参数被忽略还是默认模型session/setModel方法Method not foundsession/configure方法Method not found_kiro.dev/commands/execute/model进程直接崩了反序列化错误kiro-cli acp --model Xunexpected argumentkiro-cli-chat acp --model Xunexpected argument环境变量KIRO_MODEL被忽略~/.kiro/settings/cli.json配置被忽略8 种方式全军覆没。差点掀桌子。抓包分析了半天结论是ACP v1.25 协议设计上就不支持运行时切换模型。这不是 bug是 feature苦笑。但我发现了个替代方案kiro-cli chat --model X --no-interactive。这个命令支持指定模型但只能一次性运行不支持多轮会话。双通道架构既要又要的工程方案既然一条路走不通那就两条路一起走。HTTP 客户端curl / Python / JS | | HTTP REST (port 8642) v FastAPI Server (server.py) | ------------------- | | | modelauto | model指定模型 v v ACP 通道 Chat 通道 acp_client.py subprocess (常驻进程) (一次性进程) 多轮会话 单次调用这就是双通道架构的核心思路ACP 通道Chat 通道协议JSON-RPC 2.0 over stdiosubprocess 文本解析模型选择不支持固定 auto支持--model参数多轮会话支持不支持性能无冷启动延迟低每次冷启动约 3 秒适用场景多轮对话、上下文复用指定模型的单次任务设计原则优先走 ACP 通道性能好、支持多轮只有在需要指定模型时才降级到 Chat 通道。关键实现Event Pending Map 同步语义ACP 底层是异步的 stdio 通信。你往 stdin 写一条请求stdout 上可能先来几条通知然后才是你的响应。怎么把异步变同步核心思路每个请求分配唯一 id用 Event Pending Map 做同步等待。# 发请求时注册等待self._pending[req_id](threading.Event(),[None,None])# 写入 stdinself._proc.stdin.write(json_msg\n)# 阻塞等待event.wait(timeout)# 读线程收到响应后按 id 匹配写入结果并唤醒ifmsg[id]inself._pending:holder[0]msg[result]event.set()# 唤醒等待线程这套机制保证了不管 Kiro 推了多少通知过来你的请求一定能拿到对应的响应。踩坑记录session/update 才是文本载体这个坑我踩了好几个小时。session/prompt发出去之后最终响应长这样{jsonrpc:2.0,id:3,result:{stopReason:end_turn}}里面没有文本内容一开始我以为是解析错了反复检查了好几遍。实际的文本是通过多个session/update通知逐块推过来的← {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()拼接。这个在官方文档里没明确说。得自己抓包才能发现。权限请求不回复就永远挂死又一个坑。Kiro 在执行文件写入、终端命令这些敏感操作之前会发一个权限请求{jsonrpc:2.0,id:99,method:session/request_permission,params:{toolCall:{title:Creating app.py}}}注意这是个带id的 request不是通知。你必须回复否则 Kiro 会永远等下去。整个请求就挂死了。在 headless 模式下我的处理很简单自动审批。def_handle_permission_request(self,msg_id,params):self._send_response(msg_id,{optionId:allow_always})Chat 通道的输出清洗Chat 通道的坑在于kiro-cli chat的 stdout 是给人看的不是给程序解析的。原始输出里混着这些东西ASCII 艺术 banner“Did you know?” 提示框“Model: claude-opus-4.6 | Plan: KIRO PRO” 信息行前缀的实际回复内容 ← 这才是我要的“▸ Credits: 0.09 • Time: 3s” 页脚清洗流程正则去掉所有 ANSI 转义码跳过 banner、提示框、Model/Plan 行找到标记提取后续内容遇到▸ Credits截止丢弃页脚说实话这种文本解析挺脆弱的。Kiro CLI 版本一更新输出格式可能就变了。但目前没有更好的办法。对外 REST API7 个端点最终 FastAPI 服务提供这些接口接口说明POST /prompt快捷调用支持指定模型POST /sessions创建多轮会话仅 auto 模型POST /sessions/{id}/prompt向已有会话发送任务GET /sessions列出所有活跃会话DELETE /sessions/{id}删除指定会话GET /models列出可用模型GET /健康检查调用示例# 快速提问curl-XPOST http://localhost:8642/prompt\-HContent-Type: application/json\-d{prompt: 写一个 Python 快速排序, model: auto}# 创建会话 → 多轮对话curl-XPOST http://localhost:8642/sessionscurl-XPOST http://localhost:8642/sessions/abc123/prompt\-d{prompt: 帮我重构这段代码}stdio 双向通信的死锁陷阱这个问题比较隐蔽。Kiro CLI 进程的 stdout 和 stderr 都有输出。如果你只读 stdout 不管 stderrstderr 的缓冲区满了之后整个进程会阻塞——因为写不进去了。所以 acp_client.py 里必须单独开一个线程来排空 stderr# 专门的 stderr 读取线程防止缓冲区满导致死锁def_drain_stderr(self):forlineinself._proc.stderr:pass# 读掉就行不需要处理这个坑在 Windows 上尤其明显因为 Windows 的管道缓冲区更小。Linux 上也不能忽视高频输出时照样会阻塞。已知限制说完好处也得说局限无鉴权API 没做认证只适合内网用别暴露到公网单 ACP 连接高并发请求需要排队处理会话不持久化服务重启后会话丢失Chat 模式无上下文每次都是新进程不支持多轮这些限制在个人使用和团队内网场景下可以接受。如果要上生产还需要加鉴权、连接池、持久化这些工程化的东西。总结类别结论协议限制ACP v1.25 不支持运行时切换模型协议设计限制架构决策双通道多轮走 ACP指定模型走 Chat关键发现session/update通知才是文本载体关键发现权限请求必须回复否则永久挂起关键发现Chat 输出混杂 UI 元素需专门清洗关键发现stdio 双向通信必须读写线程分离把 CLI 工具封装成 API核心难点不在 HTTP 层而在于理解协议的实际行为。官方文档没覆盖的部分只能自己调试和抓包。完整源码已在上文逐段展示可直接复制使用。本文基于亚马逊云科技官方博客内容整理结合实际开发经验撰写。原文参见将 Kiro CLI 封装为 REST API双通道架构实践