565 lines
34 KiB
Markdown
565 lines
34 KiB
Markdown
# 多系统智能体平台 · 实施设计
|
||
|
||
> **来源**:飞书文档([原文](https://scneju2wtbd7.feishu.cn/docx/SNW8dLae7ozvBVxY7CDc2pLSnEe),A0-G 评审快照,2026-09-23 导入)
|
||
> **权威版本**:本仓库。导入后飞书副本停止更新,改动走仓库提交与 ADR 流程。
|
||
> **A0.10 补记**:1.4 已补入单机 SQLite 文件路径与跨库写法约定(ADR-0007);飞书副本未同步。
|
||
> **2026-09-24 修订(A3.1 前置)**:第 9 节按 ADR-0006 重写——引擎长驻子进程、平台装 npm 包只为拿 RpcClient 与类型、新增进程模型与扩展桥说明;原「进程内
|
||
ew Agent()」的写法作废。
|
||
|
||
承接《多系统智能体平台 · 架构设计》,把落地问题定到“谁做、放在哪、走什么流程”。本文只写落地约束,不重复分层原理。
|
||
|
||
章节按从基础到高级排列:先看数据与界面,再看 Provider 扩展,最后看多 Sub-agent 协作。
|
||
|
||
# 总览
|
||
|
||
先看两张图:一张是用户实际怎么用,一张是系统怎么搭。后面的章节都是这两张图的展开。
|
||
|
||
## 用户使用流程(桌面端)
|
||
|
||
```mermaid
|
||
flowchart TB
|
||
A["打开桌面端"] --> B["选项目 或 新建会话"]
|
||
B --> C["用自然语言说出目标"]
|
||
C --> D["服务端:Pi 理解目标并选能力"]
|
||
D --> E{"只读 还是 写操作"}
|
||
E -->|只读| F["工作区展示表格 / 甘特 / 图表"]
|
||
E -->|写入| G["生成变更草案"]
|
||
G --> H["确认卡:影响范围 · 参数 · 版本"]
|
||
H -->|拒绝| I["记录并说明原因"]
|
||
H -->|批准| J["Core 执行并留痕"]
|
||
J --> K["回执与证据回到对话"]
|
||
F --> L["追问 / 对比 / 导出"]
|
||
K --> L
|
||
|
||
classDef start fill:#ffffff,stroke:#9aa7b8,color:#243247;
|
||
classDef ui fill:#f5f8fc,stroke:#9aaec7,color:#243247;
|
||
classDef gate fill:#fff1e7,stroke:#f97316,color:#243247;
|
||
class A,B,C ui;
|
||
class E,H gate;
|
||
class D,F,G,I,J,K,L start;
|
||
```
|
||
|
||
关键点:只读直接出结果,写入一律先出草案再出确认卡;批准后由 Core 执行,回执与证据回到对话,不留在后台。
|
||
|
||
## 整体架构
|
||
|
||
```mermaid
|
||
flowchart TB
|
||
subgraph CLIENT[客户端]
|
||
DESK["桌面端 · Electron
|
||
界面 · 工作区 · 本地能力"]
|
||
WEBUI["浏览器"]
|
||
end
|
||
subgraph SERVER[服务端 · 引擎中立的平台服务]
|
||
APP["应用服务
|
||
会话 · 编排 · 能力查询 · 事件推送"]
|
||
ENGINE["引擎适配
|
||
统一接口:Pi 或 Codex"]
|
||
CORE["平台 Core
|
||
身份 · 策略 · 审批 · 执行 · 审计"]
|
||
SUITE["Provider 接入
|
||
契约 · 路由 · 适配"]
|
||
end
|
||
subgraph DATA[数据层]
|
||
PG[("PostgreSQL
|
||
项目 · 执行 · 审批 · 审计")]
|
||
JSONL[("会话记录
|
||
服务端权威,本地离线副本")]
|
||
OBJ[("文件存储
|
||
附件 · 产物")]
|
||
end
|
||
APS["APS 系统"]
|
||
ERP["ERP 系统"]
|
||
EMS["EMS 系统"]
|
||
DESK --> APP
|
||
WEBUI --> APP
|
||
APP --> ENGINE
|
||
ENGINE --> CORE
|
||
CORE --> SUITE
|
||
SUITE --> APS
|
||
SUITE --> ERP
|
||
SUITE --> EMS
|
||
APP --> PG
|
||
APP --> JSONL
|
||
APP --> OBJ
|
||
|
||
classDef ext fill:#fafafa,stroke:#7f8c9c,color:#243247,stroke-dasharray:5 4;
|
||
class APS,ERP,EMS ext;
|
||
```
|
||
|
||
\>
|
||
|
||
接入层只做界面;平台服务里入口、运行时、控制面、Provider 套件四段分开;数据层按结构化数据、会话文件、文件资产三类存放。桌面端与浏览器共用同一套平台服务。
|
||
|
||
# 1 数据管理
|
||
|
||
对象分五类:项目、会话、设置、工作区、运行与证据。
|
||
|
||
| 对象 | 作用域 | 关键内容 |
|
||
|-|-|-|
|
||
| 用户与租户 | 全局 | 身份、角色、所属租户 |
|
||
| 项目 | 租户内 | 工作目录、成员与角色(owner / editor / viewer)、业务上下文 |
|
||
| 会话 | 个人或项目 | 消息、工具调用、结论;个人会话私有且只读,写操作必须在项目会话内 |
|
||
| 工作区 | 用户 × 项目 | 打开的视图集合、当前选择、会话;可恢复上次状态 |
|
||
| 设置 | 系统 / 租户 / 项目 / 用户 / 会话 | 后一级覆盖前一级;会话级只在本次有效 |
|
||
| 运行与执行 | 会话内 | executionId、参数摘要、幂等键、状态、回执 |
|
||
| 审批 | 项目内 | 请求、一次性授权、决策事件 |
|
||
| 审计 | 全局 | 哈希链,只追加不可删 |
|
||
| 文件 | 项目内 | 元数据入库,内容放对象存储或本地目录 |
|
||
|
||
| 存储 | 放什么 | 备注 |
|
||
|-|-|-|
|
||
| PostgreSQL | 项目、权限、设置、运行、审批、审计等结构化数据;服务端模式的会话消息也在库里 | 桌面模式用 SQLite 承接同类数据 |
|
||
| 对象存储或本地目录 | 上传的文件、导出物 | 数据库只存元数据与校验和 |
|
||
| 会话 JSONL(桌面) | 每个会话一个文件,消息与工具调用按行追加 | 放 \~/.aps/sessions 下,见 1.4 |
|
||
| 桌面端 \~/.aps | 本机配置、缓存、日志、Provider 包 | 与服务器同构,便于同步 |
|
||
|
||
**四条规矩:**审计只追加不修改;写操作必须有项目归属(个人会话只读,写操作一律在项目会话内,因为审批、审计与证据都要求项目归属);会话删除是软删,运行与证据保留;文件删除要过审批。
|
||
|
||
## 1.1 会话里都有什么
|
||
|
||
| 内容 | 存什么 | 放哪 | 生命周期 |
|
||
|-|-|-|-|
|
||
| 消息 | 角色、内容块、时间、token 数 | 桌面 JSONL / 服务端 PostgreSQL,只追加 | 随会话软删 |
|
||
| 内容块 | 文本 / 图片 / 附件引用 / 工具调用 / 审批卡 / 视图引用 | 随消息 | 同上 |
|
||
| 上下文快照 | 实际发给模型的消息集合与压缩摘要 | 随会话文件 / 服务端库 | 用于复盘“当时模型看到了什么” |
|
||
| 工具调用 | capabilityId、参数摘要、结果引用、executionId | 随消息 | 只追加 |
|
||
| 审批记录 | 请求、决策、批准人、失效条件 | 关联消息与审批表 | 只追加 |
|
||
|
||
## 1.2 附件与产物
|
||
|
||
| 类型 | 来源 | 归属 | 存法 |
|
||
|-|-|-|-|
|
||
| 附件 | 用户上传 | 项目文件库 | 原文件 + 元数据 + 校验和;消息里只放引用 |
|
||
| 产物 | Agent 生成 | 项目 | 带版本号,记录产生它的 executionId,可对比与回滚 |
|
||
| 导出物 | 报告 / 表格 / 图纸导出 | 项目 | 发布为文件,保留生成参数与来源快照 |
|
||
| 视图快照 | 用户在工作区保存 | 用户 × 项目 | 只存定义与参数,不存渲染结果 |
|
||
|
||
| 规则 | 说明 |
|
||
|-|-|
|
||
| 大内容不进消息表 | 消息只放引用、摘要与校验和,正文与二进制进对象存储 |
|
||
| 附件按校验和去重 | 同一文件被多个会话引用只存一份 |
|
||
| 产物有版本 | 生成物不覆盖历史,能对比前后差异,能回滚到指定版本 |
|
||
| 权限跟着项目走 | 离开项目后失去项目文件访问权,个人会话里的引用同样失效 |
|
||
| 删除要留痕 | 附件删除需审批;产物保留版本历史,审计不删 |
|
||
|
||
## 1.3 上下文窗口
|
||
|
||
不把整段会话塞给模型。按 token 预算取「最近若干轮 + 滚动摘要 + 钉住的引用」,工具结果只进摘要与引用,需要细节时再调对应能力取回。压缩后的摘要与原始消息都保留,任何结论都能追到原始输入。
|
||
|
||
## 1.4 会话文件格式(服务端为主,本地 JSONL 副本)
|
||
|
||
架构改成客户端加服务端之后,会话默认由服务端保存;客户端本地用 JSONL 存一份离线副本,断网时可读、可导出。格式与 Pi 的会话文件一致:每个会话一个文件,一行一条 JSON,每行带 id 与 parentId 组成树,支持在同一个文件里原地分支。
|
||
|
||
| 存储形态 | 桌面端 | 服务端 |
|
||
|-|-|-|
|
||
| 会话消息 | \~/.aps/sessions/<项目>/<时间戳>\_<会话id>.jsonl | PostgreSQL |
|
||
| 会话索引 | 本地 SQLite(列表、搜索、未读) | PostgreSQL |
|
||
| 附件与产物 | 文件目录,JSONL 只存引用 | 对象存储 + 元数据表 |
|
||
| 执行、审批、审计 | 本地 SQLite,不放 JSONL | PostgreSQL |
|
||
| 单机库文件 | ~/.aps/aps.sqlite(与会话 JSONL 同级;备份即拷贝目录) | 不适用(用 PostgreSQL 自身的备份) |
|
||
| 导出与交换 | JSONL | JSONL |
|
||
|
||
**一行 JSON 的字段:**
|
||
|
||
```json
|
||
{"v":1,"type":"header","sessionId":"s-...","project":"...","ts":1757404800000}
|
||
{"v":1,"type":"message","id":"a1","parentId":null,"role":"user","ts":...,"content":[...]}
|
||
{"v":1,"type":"tool_call","id":"a2","parentId":"a1","capabilityId":"aps.schedule.preview","argsDigest":"sha256:..."}
|
||
{"v":1,"type":"tool_result","id":"a3","parentId":"a2","executionId":"exec-...","status":"accepted","jobId":"..."}
|
||
{"v":1,"type":"approval","id":"a4","parentId":"a3","approvalId":"apr-...","decision":"approved"}
|
||
{"v":1,"type":"summary","id":"a5","parentId":"a3","covers":["a1","a2","a3"],"text":"..."}
|
||
```
|
||
|
||
| 规则 | 说明 |
|
||
|-|-|
|
||
| 只追加 | 不改历史行;分支靠 id 与 parentId 组成树,不新建文件 |
|
||
| 带版本 | 文件头一行是会话元信息,含格式版本;加载时按版本迁移,旧文件可读 |
|
||
| 大内容在外 | 附件与二进制不进 JSONL,只放引用、摘要与校验和 |
|
||
| 不落敏感 | 参数只写摘要与规范化结果,凭据与敏感明文不落文件 |
|
||
| 索引另存 | 会话列表、搜索、未读数走本地 SQLite,不在打开时扫描全部文件 |
|
||
| 可直接使用 | 备份、导出、排查直接用文件;服务端导出也用同一格式,本地能打开 |
|
||
|
||
**为什么不把一切都放文件:**执行记录、审批与审计需要按条件查询、并发写入和防篡改,放进本地数据库更合适;JSONL 只承担会话消息流。共享与多端场景下,会话消息改由服务端 PostgreSQL 承接,本地 JSONL 作为离线副本与导出格式。两种存储实现同一个 SessionStore 接口,上层代码共用。
|
||
|
||
**跨库写法约定(ADR-0007):**时间统一存整数毫秒;主键由应用侧生成(ULID);JSON 列在 PostgreSQL 落 JSONB、在 SQLite 落 TEXT;金额与工时用整数最小单位;不用触发器、存储过程与数据库专属函数。一套方言无关 schema + 一个迁移工具,禁止两边各写一套。
|
||
|
||
## 1.5 谁是权威数据源
|
||
|
||
引擎在开发或部署期选定,会换;数据归属不能跟着换。应用服务持有会话与业务记录,Core 持有执行与审计,引擎的原生状态只用于同引擎恢复。
|
||
|
||
| 数据 | 权威在谁 | 说明 |
|
||
|-|-|-|
|
||
| 会话与消息 | 平台(应用服务) | 平台格式是权威;引擎只拿上下文,不拿所有权 |
|
||
| 上下文检查点与摘要 | 平台 | 发给模型的上下文由平台构建,压缩结果一并保存,可追溯 |
|
||
| 执行、审批与审计 | Core | 与引擎无关,进程重启或换引擎都继续存在 |
|
||
| 引擎原生状态 | 对应引擎 | Pi 的内存上下文、Codex 的 thread:只保存原生引用,用于同引擎恢复,不作为业务真相 |
|
||
| 客户端本地副本 | 平台(副本) | 桌面端 JSONL 只作离线读取与导出,冲突以服务端为准 |
|
||
|
||
# 2 界面设计
|
||
|
||
视觉与布局沿用现有 APS 项目的设计系统,不另起一套。现有界面已经跑过对话、视口、审批卡、证据链和桌面壳,直接继承比重新发明便宜,也让团队不用学第二套规范。
|
||
|
||
## 2.1 设计系统(沿用)
|
||
|
||
| 维度 | 规范 |
|
||
|-|-|
|
||
| 色彩 | 浅底 #FAFAF8 / 暗色 #1B1B1F;唯一强调色为工业橙 #F97316,只用于主操作与激活态;红黄绿只表达状态,不做装饰 |
|
||
| 侧栏 | 浅色微透明,激活项用浅色胶囊,层级靠明度差不靠边框 |
|
||
| 字体 | 系统栈 + Noto Sans SC;正文 14px / 1.65;数字用 tabular-nums 对齐 |
|
||
| 圆角 | 卡片 12px、对话气泡 16px、按钮与输入框 10px、胶囊 999px |
|
||
| 阴影与边框 | 低海拔柔和阴影,边框仅 1px 极浅色,不叠双重描边 |
|
||
| 间距 | 8pt 网格:8 / 12 / 16 / 24 / 32 |
|
||
| 暗色模式 | 跟随系统并支持手动切换,语义色配对暗色变体 |
|
||
| 动效 | 150–250ms ease-out;输出流式呈现;左说右动时右侧目标元素脉冲高亮一次 |
|
||
| 数据呈现 | KPI 用大数字卡片;表格弱化网格线;甘特用低饱和色板加悬浮细节卡 |
|
||
| 空态与加载 | 空态给引导与快捷指令;长任务给骨架屏与可取消进度 |
|
||
|
||
## 2.2 三栏布局(沿用)
|
||
|
||
桌面端与 Web 端同一套界面,只是外壳不同。右栏在现有项目里是排产视口,这一版泛化为工作区,可以承载任意 Provider 视图。
|
||
|
||
```text
|
||
┌──┬───────────────┬──────────────────────┬───────────────────────────┐
|
||
│ │ 项目/会话面板 │ 中:会话区 │ 右:工作区 │
|
||
│导│ ───────────── │ ┌────────────────┐ │ ┌─────────────────────┐ │
|
||
│航│ ▸ 新建会话 │ │ 面包屑:项目/会话│ │ 表格 / 甘特 / 图表 │ │
|
||
│轨│ ▸ 项目列表 │ │ 对话流(UI块) │ │ 对比 / 文件 / 网页 │ │
|
||
│ │ ▸ 待审批 │ │ 审批卡 / 证据链 │ │ 运行记录 │ │
|
||
│56│ ▸ 工作区 │ │ ────────────── │ │ │ │
|
||
│px│ ▸ Provider │ │ 输入框(多模态) │ │ │ │
|
||
│ │ ▸ 设置 │ │ │ │ │ │
|
||
└──┴───────────────┴──└────────────────┘──┴──└─────────────────────┘──┘
|
||
```
|
||
|
||
| 区域 | 职责 |
|
||
|-|-|
|
||
| 导航轨 56px | 新建会话、项目、待审批、工作区、Provider、设置,图标加悬浮提示 |
|
||
| 项目/会话面板 | 会话按项目分组,带状态徽章(运行中 / 待审批 / 已完成),支持搜索、归档、置顶 |
|
||
| 中栏会话区 | 对话流内嵌 UI 块;顶部面包屑(项目 / 会话 / 分支);底部多模态输入框 |
|
||
| 右栏工作区 | 可整栏收起或最大化,承载表格、甘特、图表、对比、文件与网页 |
|
||
|
||
## 2.3 UI 块协议(沿用并扩展)
|
||
|
||
模型不直接输出 HTML,而是输出结构化 UI 块,由前端按类型渲染。这套协议在现有项目里已经落地,继续沿用,只增加一个 Provider 类型。
|
||
|
||
| 块类型 | 用途 |
|
||
|-|-|
|
||
| text / markdown | 正文与说明 |
|
||
| confirm-card | 门禁确认卡:影响范围、参数摘要、目标版本 |
|
||
| evidence | 证据链:来源系统、时间、版本 |
|
||
| diff | 方案与分支对比 |
|
||
| gantt / load-heatmap / conflict-list | 排产类可视化 |
|
||
| scenario-cards / sensitivity | 方案卡组与敏感性分析 |
|
||
| report | 报告预览与导出 |
|
||
| media | 图片、音频转写、视频帧 |
|
||
| provider-view | Provider 声明的视图,由宿主转发数据(见第 4 节) |
|
||
|
||
和第 4 节的关系:Provider 可视化就是 UI 块协议的一个扩展点。Provider 只声明视图,平台用自己的组件渲染,不引入第二套渲染体系。
|
||
|
||
## 2.4 主要界面
|
||
|
||
| 界面 | 作用 | 关键交互 |
|
||
|-|-|-|
|
||
| 对话 | 主入口 | 消息流 + 工具调用卡 + 内联审批卡 + 内联图表 |
|
||
| 审批中心 | 待我审批 / 我发起的 / 已处理 | 影响范围、参数摘要、目标版本,可拒绝与转交 |
|
||
| 工作区 | 表格 / 甘特 / 图表 / 对比 / 文件 / 网页 | 与对话联动,点数字看来源 |
|
||
| Provider 管理台 | 安装、启停、健康、契约、日志 | 双面板:左清单右详情 |
|
||
| 运行记录 | 按 executionId 追溯 | 谁、什么时候、用什么参数、结果如何 |
|
||
| 设置 | 账号绑定、模型、安全、数据位置 | 账号绑定逐系统显示登录态 |
|
||
|
||
**与现有界面的差异只有四处:**右栏从排产视口泛化为工作区;导航轨增加工作区与 Provider 入口;UI 块协议增加 provider-view 类型,数据经宿主转发;审批聚合为独立界面,支持待我审批 / 我发起的 / 已处理。其余包括设计系统、三栏布局、自绘顶栏与桌面壳全部沿用。
|
||
|
||
## 2.5 交互原则
|
||
|
||
| 原则 | 说明 |
|
||
|-|-|
|
||
| 写操作都有确认卡 | 影响范围 + 参数摘要 + 目标版本,参数变了即失效 |
|
||
| 状态始终可见 | 排队、执行中、等待审批、已完成、结果未知 |
|
||
| 数字可追溯 | 关键数字能点开看到来源系统、时间与版本 |
|
||
| 失败显式 | 显示错误分类与是否可重试,不用话术圆场 |
|
||
| 不暴露实现细节 | 界面不出现凭据、内部 token、Provider 内部结构 |
|
||
|
||
# 3 工作区
|
||
|
||
工作区是右侧的多标签面板:把对话里的结果钉下来,慢慢看、反复比。它不是第二个业务系统,所有数据都经 Core 获取。
|
||
|
||
| 面板类型 | 内容 | 数据来源 | 可否编辑 |
|
||
|-|-|-|-|
|
||
| 表格 | 订单池、资源、物料清单 | 能力调用(只读) | 编辑生成变更草案,需审批 |
|
||
| 甘特 / 排程 | 计划、工序、资源占用 | 能力调用 | 拖拽生成草案,需审批 |
|
||
| 图表 | 负荷曲线、KPI、方案对比 | 能力调用 | 否 |
|
||
| 对比 | 方案 A/B、执行前后差异 | 两次执行结果 | 否 |
|
||
| 文档预览 | 报告、PDF、Word | 产物 | 否 |
|
||
| 文件浏览 | 项目目录树与文件预览 | 项目工作目录 | 上传、重命名走审批 |
|
||
| 图纸 / 图片 | CAD 转图、现场照片 | 项目文件 | 否 |
|
||
| 代码 / 文本 | 配置、工艺文件、脚本 | 项目文件 | 编辑走审批 |
|
||
| 网页 | 外部页面 | 系统浏览器或受控内嵌 | 否 |
|
||
| 运行记录 | 按 executionId 追溯 | 平台 | 否 |
|
||
|
||
组织方式:标签页 + 布局 + 当前选择,整体作为一个工作区状态保存,换设备也能恢复。对话里的卡片可以钉到工作区,工作区的当前选择也能回填给对话。
|
||
|
||
| 网页浏览 | 做法 | 说明 |
|
||
|-|-|-|
|
||
| 用户看网页 | 默认交给系统浏览器打开 | 最省事,也不把外部内容混进产品界面 |
|
||
| 需要内嵌时 | 受控面板,域名白名单,明确标识外部内容 | 禁止自动填表与凭据注入 |
|
||
| Agent 读网页 | 走 web.fetch 能力 | 域名白名单、体积上限、脱敏、全程审计;不给 Pi 通用 HTTP 工具 |
|
||
|
||
| 工作区边界 | 说明 |
|
||
|-|-|
|
||
| 不持有业务凭据 | 凭据只存在平台凭据代理,工作区只拿渲染所需的数据 |
|
||
| 数据都经 Core | 每次取数带用户身份与权限,越权直接拒绝 |
|
||
| 编辑先出草案 | 拖拽、改单元格生成变更草案,审批通过才写入业务系统 |
|
||
| 文件访问受限 | 文件浏览限制在项目工作目录内,不遍历整机 |
|
||
| 状态属于偏好 | 打开的标签、布局、列设置可跨端同步;不属于业务数据 |
|
||
|
||
# 4 Provider 可视化怎么集成
|
||
|
||
结论:平台定义视图契约,Provider 只做声明,渲染由平台负责;确有复杂交互时再开 iframe 沙箱。
|
||
|
||
| 方案 | 做法 | 适用 | 代价 |
|
||
|-|-|-|-|
|
||
| 平台渲染(推荐) | Provider 在 manifest 里声明视图,用平台的组件词汇表(表格 / 图表 / 表单 / 甘特 / 时间线 / 差异)描述界面;数据由 Core 提供 | 绝大多数业务视图 | 组件词汇表要覆盖得住场景,早期可能不够 |
|
||
| iframe 沙箱 | Provider 自带页面,平台嵌进面板 | 第三方 Provider、复杂交互 | 要严格 sandbox 与 CSP,且不能让它直接拿凭据 |
|
||
| 静态内容 | 只给 markdown 或图片 | 说明类内容 | 没有交互能力 |
|
||
|
||
数据只有一条通路:视图 → 宿主 postMessage → 平台校验身份 → Core.invoke → 返回。iframe 不允许直连业务系统,也不注入任何凭据。
|
||
|
||
| 视图契约 | 字段与取值 |
|
||
|-|-|
|
||
| 槽位 slot | nav.item / session.sidepanel / provider.detail.tab / message.inline |
|
||
| 类型 kind | schema(平台组件渲染)/ iframe(受信沙箱)/ markdown(说明) |
|
||
| 数据来源 | capabilityId + 参数模板,运行时由 Core 补齐身份与授权 |
|
||
| 刷新 | 手动 / 定时 / 订阅事件,统一由平台调度 |
|
||
|
||
首版只开两类视图:只读表格与图表,且只允许 schema 类型。iframe 留到接入第三方 Provider 时再开。
|
||
|
||
# 5 Provider 怎么开发与管理
|
||
|
||
Provider 先写适配器,再以包的形式安装和管理。开发步骤与运行机制如下,包结构与生命周期见后。
|
||
|
||
**开发步骤:**
|
||
|
||
```text
|
||
1. 定义能力:id、effect、mode、输入输出 Schema
|
||
2. 写 manifest:providerType、version、capabilities、auth 声明
|
||
3. 实现适配器:describe / invoke / health(+ preview、exchange)
|
||
4. 跑契约测试
|
||
5. 注册实例:地址、租户或工厂绑定(实例路由)、适配器版本
|
||
6. 账号绑定:用户在每个系统各自绑定一次登录态(ADR-0012)
|
||
7. 灰度开放给引擎
|
||
```
|
||
|
||
**账号绑定怎么走(ADR-0012):**平台负责发起、回调、兑换、加密存储、续期与失效判定,登录形态由 Provider 的 `auth` 声明决定(`oauth` / `password-exchange` / `apikey`)。
|
||
|
||
```text
|
||
用户点「绑定 APS」
|
||
→ 平台按 auth.kind 发起(跳转授权登录 / 收一次账号密码 / 用管理员配置的 key)
|
||
→ Provider.exchange() 把 code 或账号密码换成 token
|
||
→ 平台把 token 密文写进 identity_bindings,明文只在这一步的调用里经过
|
||
→ 设置页显示该系统的登录态:已绑定 / 已失效 / 未绑定
|
||
失效:token 过期或被业务系统踢下线 → 该系统的调用立即被拒,提示重新绑定;其他系统不受影响
|
||
解绑:删除绑定与密文;审计与执行历史保留
|
||
```
|
||
|
||
| 运行机制 | 约定 |
|
||
|-|-|
|
||
| 凭据边界 | 首版 Provider 运行在平台信任域内,Core 按 execution 注入短期凭据,Provider 不落库、不缓存。外部团队接入时走跨进程模式,双向认证加一次性凭据。 |
|
||
| 异步任务 | Core 登记 jobId 并负责轮询或接收回调,引擎只收到“已受理”和“终态”两类事件,不自己空转。 |
|
||
| 审批 | Core 生成审批单(请求人、能力、目标实例、参数摘要、资源版本、影响范围),通过后写入执行授权,与主体、实例、参数摘要、版本和有效期绑定;参数或目标改变即失效。 |
|
||
| 幂等与超时 | 发请求前持久化 executionId 与幂等键,一个授权只启动一个逻辑执行。超时先查回执,只有能确认未执行或下游支持可靠幂等时才重试。 |
|
||
| 错误分类 | 统一映射为 invalid_argument、forbidden、conflict、rate_limited、unavailable、unknown_result;只有 rate_limited 与 unavailable 允许自动重试。 |
|
||
| 契约测试 | 必须覆盖 Schema 校验、幂等重放、权限拒绝、错误映射、超时与结果未知、异步受理与查询。 |
|
||
|
||
| 包内容 | 说明 |
|
||
|-|-|
|
||
| provider.json | id、版本、供应方、能力清单、视图声明、兼容的平台版本 |
|
||
| adapter | describe / invoke / health 的实现 |
|
||
| ui/(可选) | 视图描述文件,走第 4 节的契约 |
|
||
| tests/(可选) | 该 Provider 的补充契约用例 |
|
||
| 签名与摘要 | 包签名 + SHA256,安装前必须校验 |
|
||
|
||
| 阶段 | 动作 | 权限级别 | 说明 |
|
||
|-|-|-|-|
|
||
| 发现 | 扫描目录或仓库,只读清单 | 只读 | 不执行任何代码 |
|
||
| 安装 | 校验签名与摘要 → 解包到临时目录 → 契约校验 → 权限面变化提示 → 审批 → 落盘注册 | 需审批 | 会扩大可用能力面,必须走确认 |
|
||
| 启用 / 停用 | 切换实例状态 | 需审批 | 停用立即阻断新调用,在途任务保留查询 |
|
||
| 升级 | 旁路安装新版本 → 契约回归 → 审批 → 切换 | 需审批 | 保留上一版本目录,支持一键回滚 |
|
||
| 卸载 | 先停用 → 检查在途执行与绑定 → 解除绑定 → 删除文件 | 需审批 | 执行历史与审计不删 |
|
||
| 健康 | 定时探测 + 手动触发 | 只读 | 保留最近 N 次结果 |
|
||
|
||
落盘位置:桌面模式放 \~/.aps/providers/<id>/<version>/,服务端模式放平台的 provider 目录;注册表与实例绑定存数据库。
|
||
|
||
供应链约束:只允许内网 registry 或签名包,不从公网直接安装。Pi 自带的 pi install 是给开发者 CLI 用的,包拥有完整系统权限,不能直接搬进产品。
|
||
|
||
# 6 多 Sub-agent 与 Goal
|
||
|
||
先确认一个事实:**引擎不提供平台级的 Goal 与 Task**。Pi 的 agent-core 只有单个 Agent 循环(消息、工具、事件、steering、follow-up、abort 与思考预算),Codex 同样把它自己的多任务能力留给宿主。所以 Goal、Plan、Task 由平台定义,Sub-agent 是平台创建的一次受控运行,与所选引擎无关;每个运行记录它用的是哪个引擎。
|
||
|
||
| 概念 | 定义 | 关键字段 |
|
||
|-|-|-|
|
||
| Goal | 用户想达成的一件事 | goalId、标题、发起人、成功判据、涉及系统、期限、状态 |
|
||
| Plan | Goal 下的步骤序列 | planId、步骤、每步输入输出、可回滚点 |
|
||
| Task | 一个可独立执行的小任务 | taskId、角色、输入、输出 schema、工具子集、超时、预算 |
|
||
| Sub-agent | 执行某个 Task 的一次 Agent 运行 | runId、taskId、能力子集、事件流、结果 |
|
||
|
||
| 规则 | 说明 |
|
||
|-|-|
|
||
| 权限只减不增 | Sub-agent 的能力子集必须是父级授权范围的子集,每次调用仍过 Core |
|
||
| 写操作串行 | 同一 Goal 内的写操作串行;同一目标资源不允许并发写 |
|
||
| 只读可并行 | 调研类只读任务可以并行,结果汇总回主 Agent |
|
||
| 预算硬上限 | 每个 Sub-agent 有轮次、工具调用次数、超时三重上限,超限中止并回报 |
|
||
| 输出结构化 | Sub-agent 必须按输出 schema 返回,不把整段对话塞回主上下文 |
|
||
| 首版不互相对话 | Sub-agent 之间不直接通信,由父级编排,避免循环与成本失控 |
|
||
|
||
首版只做一种形态:主 Agent 派发只读调研任务(每个子任务一个能力子集),汇总后由主 Agent 出方案并走审批;写操作全部回到主 Agent 串行执行。
|
||
|
||
# 7 文档与规范管理
|
||
|
||
这一节定文档的归属与流转:权威会随阶段转移,但任何时刻只有一个权威。
|
||
|
||
分两个阶段。现在处于第一阶段:设计在飞书评审,评审通过后沉淀到仓库;沉淀完成即以仓库为准。
|
||
|
||
| 问题 | 规定 |
|
||
|-|-|
|
||
| 当前阶段 | 设计文档在飞书维护,方便评审与批注;仓库里保留一份镜像 |
|
||
| 沉淀时机 | 评审通过后,用脚本把飞书文档导出为 Markdown 写入仓库,一次一份,不手工誊抄 |
|
||
| 沉淀之后 | 仓库是唯一真相,飞书只作对外读物;冲突以仓库为准 |
|
||
| 放在哪 | docs/ 一个入口,分 product / architecture / development / runbook / adr;本平台的设计放在 architecture/multi-system-agent-platform/ |
|
||
| 什么时候改 | 改代码、改门禁、改能力清单、改契约,同一轮改文档,否则视为未完成 |
|
||
| 决策留痕 | 重大选择写 ADR:背景、选项、结论、影响 |
|
||
| 防漂移 | 用测试卡住:能力清单 ↔ 文档表格、schema ↔ 代码,对不上就红 |
|
||
| 导出方式 | 脚本把正文导出为 Markdown,画板随正文内联为 mermaid 代码块;文件头写入来源与同步方式 |
|
||
|
||
# 8 测试体系
|
||
|
||
| 层 | 测什么 | 例子 | 运行时机 |
|
||
|-|-|-|-|
|
||
| 单元 | Core 纯逻辑 | 策略判定、幂等键、参数规范化、错误分类映射 | 每次提交 |
|
||
| 契约 | Provider 是否合格 | schema 校验、幂等重放、权限拒绝、错误分类、超时与未知、异步受理 | 每次提交,接入新 Provider 必跑 |
|
||
| 引擎适配 | 引擎实现是否合格 | 多轮对话、工具调用与回执、事件顺序、取消、错误分类、会话恢复;用假引擎回放 | 每次提交,接入新引擎必跑 |
|
||
| 集成 | Core + Provider + 假业务系统 | 审批全链路、授权失效、并发写冲突 | 每次提交 |
|
||
| 端到端 | 从对话到执行 | 固定模型响应回放,跑通查询、试排、发布三条主线 | 每日 |
|
||
| 安全 | 边界是否守住 | 越权、凭据不出域、策略绕过、重放、停用后仍被调用 | 每次提交 + 发版前 |
|
||
| 桌面 | 壳与本地服务 | 启停、单实例、离线升级与回滚、断网恢复 | 发版前 |
|
||
| 漂移 | 文档与契约是否同步 | 能力清单 ↔ 文档、schema ↔ 代码 | 每次提交 |
|
||
| 性能 | 求解与批量查询基线 | 黄金算例时限、并发会话数 | 每日 |
|
||
|
||
三条原则:测试模型只测边界不测措辞,用固定响应回放;所有写路径必须有“重试不重复生效”的用例;每条验收标准都要有一条自动化用例对应。
|
||
|
||
# 9 引擎怎么接入
|
||
|
||
架构设计只定了边界与契约,这里写怎么落地。**引擎以长驻子进程运行**(ADR-0006):平台拉起进程、发提示、收事件、发取消,不做进程内嵌入。
|
||
|
||
## 9.1 为什么是子进程
|
||
|
||
| 理由 | 说明 |
|
||
| --- | --- |
|
||
| Codex 只能子进程 | Rust 二进制,无法进程内嵌入 |
|
||
| 不占平台事件循环 | 智能体循环在独立进程,长任务不拖累其它会话的响应 |
|
||
| 进程模型统一 | 两个引擎一致,进程管理(启停、超时、崩溃重启、日志轮转)只写一套 |
|
||
| 历史在平台 | 平台保存会话与执行记录,引擎进程可以随时重启并按历史恢复 |
|
||
|
||
## 9.2 平台装什么依赖
|
||
|
||
平台依赖 Pi 的 npm 包,目的是**拿 `RpcClient` 与类型**,不是把 Agent 起在平台进程里。循环跑在子进程。
|
||
|
||
```json
|
||
{
|
||
"dependencies": {
|
||
"@earendil-works/pi-agent-core": "0.85.1",
|
||
"@earendil-works/pi-ai": "0.85.1",
|
||
"typebox": "1.3.7"
|
||
},
|
||
"engines": { "node": ">=22.19.0" }
|
||
}
|
||
```
|
||
|
||
**硬前提:** Node ≥ 22.19.0,部署镜像要跟上。
|
||
|
||
## 9.3 实现拆成五块
|
||
|
||
> ADR-0006 只定边界(进程模型、统一接口、工具面、遗留项),下面五块是按该决策拆出的实现视图。
|
||
|
||
| 模块 | 位置 | 职责 |
|
||
| --- | --- | --- |
|
||
| 引擎接口 | `server/agent/runtime.ts` | `AgentRuntime` / `RuntimeSession` / `RuntimeEvent`;平台只认这三个类型 |
|
||
| 进程管理 | `server/agent/process.ts` | 拉起、守护、崩溃重启、超时、日志轮转、父进程看门狗;两个引擎共用 |
|
||
| Pi 适配 | `server/agent/pi/adapter.ts` | 用官方 `RpcClient` 建会话、发提示、订阅事件、发取消 |
|
||
| 事件映射 | `server/agent/pi/events.ts` | Pi 事件 → `RuntimeEvent` → 协议 v1 的 `PlatformEvent`(见 `docs/spec/protocol.md`) |
|
||
| 工具桥 | `server/agent/pi/tool-bridge.ts` + Pi 侧扩展 | 扩展注册工具 → 回调平台 loopback → `Core.invoke` |
|
||
|
||
对应任务:接口与假引擎 A3.1 / A3.2;子进程接入 A3.3;工具桥 A3.4;进程管理 A3.10;扩展桥 A3.11。
|
||
|
||
## 9.4 一次对话的链路
|
||
|
||
1. 应用服务按身份取能力清单,过滤后作为工具描述交给适配层
|
||
2. 适配层拉起(或复用)Pi 子进程、建会话、注入 system prompt + 工具集 + 平台保存的历史
|
||
3. 发送用户消息,Pi 流式回事件
|
||
4. Pi 要用工具 → 扩展发本地 HTTP 到平台 → 平台走 `Core.invoke` → 回执回给扩展
|
||
5. 平台把事件映射成 `PlatformEvent`:落库、经 SSE 推给客户端
|
||
6. 取消:平台发 cancel,Pi 结束这一回合,推 `run.state = idle`
|
||
|
||
三条纪律(架构设计 5.1):工具只来自能力目录、每次都过 `Core.invoke`、凭据不进工具参数与模型上下文。
|
||
|
||
## 9.5 进程模型
|
||
|
||
| 项 | 首版做法 | 原因 |
|
||
| --- | --- | --- |
|
||
| 创建时机 | **首次发消息时懒启动**,不随平台启动预创建 | 建会话不需要引擎;懒启动让进程数跟着真实使用量走,避免为闲置会话养进程 |
|
||
| 进程粒度 | **一个会话一个引擎进程** | 会话间不串状态,崩溃互不影响 |
|
||
| 空闲回收 | A3.10 定:空闲时长与进程数上限 | 平台持有历史,进程随时可杀,下次按历史重建 |
|
||
| 进程复用 | 首版不做 | Pi 支持会话切换、Codex 支持多线程;进程数成为负担时再评估 |
|
||
|
||
先例数据:ChatGPT 客户端在本机实测 54 个进程、约 3.25 GB。工厂现场的单机配置要评估,这也是把空闲回收放进 A3.10 的原因。
|
||
|
||
## 9.6 工具面:Pi 需要扩展桥
|
||
|
||
事件面两个引擎一致,差异集中在工具面:
|
||
|
||
| 引擎 | 工具怎么进 | 状态 |
|
||
| --- | --- | --- |
|
||
| Codex | MCP 或实验性动态工具 | 切换时再写 |
|
||
| Pi | RPC 命令集没有「注册工具」通道,必须在 Pi 侧写扩展,把工具调用以本地 HTTP 回调平台 | A3.11 |
|
||
|
||
扩展桥的鉴权复用平台既有模式:loopback 随机端口 + nonce 注入 + origin 校验。原型可参考旧仓库 `poc/pi-aps/extensions/aps.ts`(只读、baseUrl + token + 超时)。
|
||
|
||
```typescript
|
||
// Pi 侧扩展示意:工具不自己发业务请求,只回调平台
|
||
registerTool("aps_schedule_preview", { schema }, async (args) => {
|
||
const res = await fetch(`${platformBase}/internal/tool-call`, {
|
||
method: "POST",
|
||
headers: { "content-type": "application/json", "x-nonce": nonce },
|
||
body: JSON.stringify({ capabilityId: "aps.schedule.preview", params: args }),
|
||
});
|
||
if (!res.ok) throw new Error(`tool bridge failed: ${res.status}`);
|
||
return await res.json(); // 平台侧收口到 Core.invoke
|
||
});
|
||
```
|
||
|
||
## 9.7 接入方式
|
||
|
||
| 方式 | 适用场景 | 说明 |
|
||
| --- | --- | --- |
|
||
| npm 依赖(推荐) | 常规开发 | 装包是为了 `RpcClient` 与类型;**循环跑在子进程**,不在平台进程内 |
|
||
| 改源码:fork | 必须改 Pi 源码时 | 构建与升级成本高,要自己维护 patch 并跟上游同步 |
|
||
| 子进程跑 pi-coding-agent CLI | 不建议 | 它自带 read / bash / edit / write,等于给模型开了本机 shell |
|
||
| 进程内嵌 Agent | 不采用 | ADR-0006 已否;会占平台事件循环,且与 Codex 的进程模型不一致 |
|
||
|
||
## 9.8 推进顺序
|
||
|
||
| 步 | 内容 | 工期 |
|
||
| --- | --- | --- |
|
||
| 1 | 定义引擎接口、守住三条纪律、写假引擎跑一致性测试 | 3–5 天(A3.1 / A3.2) |
|
||
| 2 | Pi 子进程接入 + 工具桥 + 扩展桥 | A3.3 / A3.4 / A3.11 |
|
||
| 3 | 进程管理与空闲回收 | A3.10 |
|
||
| 4 | Codex 驱动与工具面切换 | 真要切换时再写 |
|
||
# 10 配套资源
|
||
|
||
架构分层与目录结构见架构设计的第 1–3 节,引擎适配与 Provider 契约见第 5–6 节;本项目的任务分解、优先级、验收标准与推进状态见下面的任务表,默认按阶段分组查看。
|
||
|
||
[多业务系统智能体平台 · 架构设计](https://scneju2wtbd7.feishu.cn/docx/DPHSdeVtAo8bLExzbufcpRZ3nAb)
|
||
|
||
[多系统智能体平台 · 项目任务](https://scneju2wtbd7.feishu.cn/base/UTkYbMacdaz6O1sxzsEcd3BTnne)</bookmark>
|
||
|
||
任务表内含 1 条大任务、11 个里程碑、65 个工作包与 5 个验收门禁,另有 6 条待决策项;提供「按阶段分组」「按优先级」「待决策」三个视图。 |