From e144429763f77b2b601859f87f2305404dc4d6e0 Mon Sep 17 00:00:00 2001 From: zh Date: Wed, 23 Sep 2026 11:25:31 +0800 Subject: [PATCH] =?UTF-8?q?docs(adr):=20=E8=A1=A5=E9=BD=90=20ADR-0006?= =?UTF-8?q?=EF=BC=88=E5=BC=95=E6=93=8E=E4=BB=A5=E9=95=BF=E9=A9=BB=E5=AD=90?= =?UTF-8?q?=E8=BF=9B=E7=A8=8B=E8=BF=90=E8=A1=8C=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/adr/0006-engine-as-subprocess.md | 46 +++++++++++++++++++++++++++ docs/adr/README.md | 1 + 2 files changed, 47 insertions(+) create mode 100644 docs/adr/0006-engine-as-subprocess.md diff --git a/docs/adr/0006-engine-as-subprocess.md b/docs/adr/0006-engine-as-subprocess.md new file mode 100644 index 0000000..f3ff96b --- /dev/null +++ b/docs/adr/0006-engine-as-subprocess.md @@ -0,0 +1,46 @@ +# 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 设计时定 +- 引擎进程的复用策略(一进程一会话还是一款多会话)首版不做,观察实际进程数后再评估 diff --git a/docs/adr/README.md b/docs/adr/README.md index bf0fb3c..6606ed9 100644 --- a/docs/adr/README.md +++ b/docs/adr/README.md @@ -43,5 +43,6 @@ ADR 写完不再修改。要改结论就**新写一份**,并在两份里互相 | [0003](./0003-data-scope-and-authority.md) | 数据作用域与权威数据源 | 已接受 | 任务表 A0.4(决策 D2) | | [0004](./0004-desktop-and-local-capabilities.md) | 桌面端与本地能力边界 | 已接受 | 任务表 A0.5(决策 D4) | | [0005](./0005-cutover-strategy.md) | 直接切换,不并行、不迁移 | 已接受 | 任务表 A0.9(决策 D7) | +| [0006](./0006-engine-as-subprocess.md) | 引擎以长驻子进程运行 | 已接受 | 任务表 A3(决策 D10) | 编号在写之前先在本表登记,避免多人并行时撞号。