aps-agent/docs/adr/0006-engine-as-subprocess.md

47 lines
2.9 KiB
Markdown
Raw Permalink Normal View History

# ADR-0006:引擎以长驻子进程运行
- **状态**:已接受
- **日期**:2026-09-23
- **决策人**:技术负责人
- **相关**:任务表 A3(决策 D10);设计文档《架构设计》5.1、5.2
## 背景
平台需要一个智能体循环来理解意图、挑工具、解释结果。这一循环跑在哪,决定了三件事:故障隔离、进程模型的复杂度、以及工具怎么交给引擎。
两项事实约束着选择:Codex 是 Rust 二进制,只能以子进程驱动,无法进程内嵌入;Pi 提供两种形态——进程内库(SDK),以及长驻子进程(RPC 模式,官方附带 `RpcClient` 客户端库)。若一个进程内、一个子进程,适配层要维护两套进程模型。
另有一个运行期问题:Node 是单线程。智能体循环与平台服务同进程时,长任务会挤占事件循环,影响所有会话的响应。
## 选项
| 选项 | 做法 | 代价 | 不选的理由 |
| --- | --- | --- | --- |
| A 引擎都进程内 | Pi 进程内、Codex 也进程内 | 无 IPC 开销 | Codex 无法进程内嵌入,此路不通 |
| B 引擎都子进程 | 两个引擎都以长驻子进程运行,平台通过各自官方客户端通信 | Pi 侧要写扩展桥与进程管理 | 选定 |
| C Pi 进程内 + Codex 子进程 | 维持现状 | 无需新增工作 | 两套进程模型;智能体循环与平台争抢事件循环 |
## 结论
选 B:**两个引擎都以长驻子进程运行**,由平台拉起与守护。
- Pi:子进程 + `RpcClient`,协议是标准输入输出的 JSONL
- Codex:子进程 + app-server 协议
- 平台侧只认统一的 `AgentRuntime` 接口,不感知两者协议差异
首版一个会话对应一个引擎进程;若进程数成为负担,再评估进程复用(Pi 支持会话切换,Codex 支持多线程复用)。
## 影响
- **隔离**:智能体循环不再占用平台的事件循环,长任务不拖累其他会话
- **工具面仍然不同,没有统一**:Codex 可通过 MCP 或实验性动态工具接收平台能力;Pi 需要在 pi 侧写一个扩展,注册工具并把调用以本地 HTTP 回调平台,平台再走 `Core.invoke`。该链路的鉴权复用现有模式:loopback 随机端口 + nonce 注入 + origin 校验
- **进程管理是共用的**:启停、超时、崩溃重启、日志轮转、父进程看门狗只写一套;可借鉴现有 Electron sidecar 的实现
- **对代码**:`server/agent` 增加引擎进程管理与 Pi 扩展桥;Pi 扩展放在 pi 侧的扩展目录,由平台启动子进程时指定
- **对任务**:A3.3 改为以子进程接入;新增两个工作包(引擎进程管理、Pi 扩展桥)
- **不可省**:无论哪个引擎,工具调用只有一个出口 `Core.invoke`
## 遗留
- Pi 扩展桥的鉴权细节:nonce 轮换周期、端口预留策略,在 A3 设计时定
- 引擎进程的复用策略(一进程一会话还是一款多会话)首版不做,观察实际进程数后再评估