# 系统架构设计 ## 1. 架构分层 ``` ┌─────────────────────────────────────────────────────────────┐ │ Renderer(React SPA) │ │ pages / components / store / wizard │ ├─────────────────────────────────────────────────────────────┤ │ Preload(contextBridge) │ │ window.aibox.* 类型安全封装 │ ├─────────────────────────────────────────────────────────────┤ │ Main(Electron 主进程) │ │ ipc.ts(白名单) → services/(业务逻辑) │ ├─────────────────────────────────────────────────────────────┤ │ Shared(纯类型层) │ │ types.ts — 领域模型,无 Node/Electron 依赖 │ └─────────────────────────────────────────────────────────────┘ ``` **依赖方向**: renderer → preload → main → shared(单向,shared 不依赖任何层) ## 2. 四层状态模型 系统采用严格分层的状态模型,四层状态互不混用: ### 2.1 Agent 生命周期(AgentLifecycle) ``` DISABLED → STARTING → READY → STOPPING ↘ ERROR ↗ ``` ### 2.2 任务状态机(TaskStatus) ``` QUEUED → RUNNING → COMPLETED / FAILED / CANCELLED / INTERRUPTED ↕ WAITING_APPROVAL / PAUSED ``` ### 2.3 引擎状态(EngineStatus) ``` NOT_INSTALLED → INSTALLING → AUTH_REQUIRED → HEALTHY / DEGRADED / ERROR ↕ SETUP_REQUIRED ``` ### 2.4 渠道状态(ChannelStatus) ``` UNCONFIGURED → CONNECTING → ONLINE / RECONNECTING / AUTH_EXPIRED / DISABLED / ERROR ``` ### 2.5 首页派生状态(DerivedAgentStatus) 由编排器计算,互斥优先级:`error > running > paused > starting > idle` ## 3. 核心服务模块 | 服务 | 文件 | 职责 | |------|------|------| | Orchestrator | `orchestrator.ts` | DispatchPlan 校验与提交、Agent/Task 状态机、FIFO 调度;任务状态的唯一写入者 | | ChannelIngressService | `channelIngressService.ts` | 渠道身份、会话、消息归一化与幂等入站 | | DesktopIngressService | `desktopIngressService.ts` | 桌面会话、本地管理员身份、消息归一化与幂等入站 | | ChannelControlPlane | `channelControlPlane.ts` | canonical memory 召回、控制核规划、计划持久化与 Orchestrator 提交 | | DesktopControlPlane | `desktopControlPlane.ts` | 将桌面聊天接入与渠道相同的 canonical 控制路径 | | KernelRouter | `kernel/kernelRouter.ts` | Hermes 主控制核、确定性 Nexus 备用控制核与 DSH 顾问/复核器的单 Leader 路由 | | MemoryService | `memoryService.ts` | OPC-Nexus 持有的分域长期记忆、版本、召回与遗忘 | | MemoryProposalService | `memoryProposalService.ts` | 控制核记忆建议的持久化、审核、接受/拒绝与启动恢复 | | TaskScheduleProposalService | `taskScheduleProposalService.ts` | 控制核定时任务建议的持久化、审核及 Scheduler 提交 | | Database | `database.ts` | sql.js 持久化、Schema 迁移、审计日志 | | ExecutorRegistry | `executor/index.ts` | 执行器选择与路由 | | EngineManager | `engineManager.ts` | 引擎检测/安装/认证/配置 | | ProviderManager | `providerManager.ts` | 多供应商 CRUD、密钥管理、模型路由 | | ChannelManager | `channelManager.ts` | 消息渠道生命周期 | | WorkflowEngine | `workflowEngine.ts` | DAG 工作流调度与执行 | | TeamEngine | `teamEngine.ts` | 专家团流水线执行 | | CollabManager | `collabManager.ts` | 多机协同(Git + MCP) | | McpManager | `mcpManager.ts` | MCP 服务器进程管理与工具调用 | | SkillManager | `skillManager.ts` | 可复用技能模板管理 | | Scheduler | `scheduler.ts` | 定时任务扫描与派发 | | ApprovalBroker | `approvalBroker.ts` | 人工审批挂起/唤醒 | | ResourceMonitor | `resourceMonitor.ts` | CPU/内存/GPU/磁盘采集与告警 | | BrowserManager | `browserManager.ts` | Playwright 浏览器自动化 | | WebServer | `webServer.ts` | 局域网 REST API 管理服务 | | ApiBridge | `apiBridge.ts` | OpenAI 兼容 API 反向代理 | ## 4. 控制面与执行器架构 ```mermaid flowchart TD C["渠道适配器
微信 / 企业微信 / 飞书"] --> CI["ChannelIngressService
canonical identity / conversation / message"] D["桌面聊天"] --> DI["DesktopIngressService
org-local / principal-local-admin / message"] V["语音确认"] --> DI WEB["Web API"] --> DI CI --> CP["Canonical Control Plane"] DI --> CP M["OPC MemoryService
长期记忆唯一权威"] -->|"按租户与作用域召回"| CP CP --> KR["KernelRouter
每个请求只有一个 Leader"] KR -. "复杂任务建议与计划复核" .-> DSH["DeepSeek Harness
Advisor / Reviewer"] KR --> H["Hermes Agent
首选 Leader"] KR -->|"Hermes 不可用、超时、鉴权失败或输出非法"| N["Nexus
本地确定性备用 Leader"] H --> O["Orchestrator
DispatchPlan 唯一提交点"] N --> O O --> ER["ExecutorRegistry"] ER --> W["Codex / Claude Code / Pi / DSH / Hermes Workers"] O --> MP["MemoryProposalService
pending review"] MP -->|"接受后"| M O --> SP["TaskScheduleProposalService
pending review"] SP -->|"接受后"| S["OPC Scheduler
定时任务唯一所有者"] ``` ### 4.1 Canonical 入站 - 渠道、桌面、语音与 Web API 请求都先落为 canonical organization、principal、conversation 与 message,再进入同一个 `dispatchCanonical()` 路径;语音和 Web API 复用 `DesktopIngressService`,不再直接调用旧的任务创建路径。 - 渠道命令、审批回复和任务控制按 canonical conversation 限定作用域,不能跨会话操作其他发送者的任务。 - 渠道命令与审批先把固定目标和回复写成 durable claim,再执行幂等状态转换并将 receipt 标为完成;进程崩溃后的重投按原目标对账,不会重新查询“当前最新任务/审批”。微信仅在 canonical 派单或控制动作已经耐久化后提交 seen id 与上游 cursor,失败时保留原 cursor 并退避重试。 - 渠道消息在适配器提供稳定上游 message key 时,按 organization、channel、external identity、conversation、direction 与 message key 组成去重作用域;缺少稳定 key 的渠道只能提供 at-least-once 语义。桌面与语音为一次用户确认复用稳定 UUID,Web 客户端需在重投时复用 `Idempotency-Key`。消息收据、DispatchPlan 和 Task 的唯一约束共同提供重投幂等性。 - `dispatch_plans`、`kernel_attempts` 和 `kernel_sessions` 保存计划、组件尝试及原生会话锚点;它们是恢复证据,不赋予控制核直接写 Task 状态的权限。 ### 4.2 控制核职责 1. `KernelRouter` 对同一 conversation 串行规划,并确保每个请求只有一个 Leader。 2. Hermes 是首选 Leader。它只返回结构化 DispatchPlan,不直接创建任务、写记忆、创建定时任务或执行渠道副作用。 3. DSH 仅在复杂任务上作为可选 Advisor/Reviewer;成功建议由 Router 传给 Leader,确定性 Nexus 不依赖该建议派单。已参与规划的 DSH reviewer 若拒绝、超时、掉线或返回非法结果,均 fail-closed 到人工审批而不是自动执行;预检阶段本就不就绪的 DSH 不参与该次计划。DSH 无派单权,但可经 `AcpExecutor` 作为普通 Worker 执行已提交任务。 4. Nexus 是无 Provider 依赖的本地确定性回退:优先选择当前入口绑定的员工,否则按角色/能力稳定匹配;高风险指令自动要求审批。它没有长期记忆。 5. Orchestrator 是 DispatchPlan、Task 创建和状态转换的唯一提交点;控制核、Advisor、Worker 和入口适配器都不能绕过它。 ### 4.3 Worker 边界 | Worker | 执行适配器 | 会话边界 | |------|------|------| | Codex CLI | `CliExecutor` | 支持保存 thread id,并在后续任务中 `exec resume` | | Claude Code | `CliExecutor` | 支持保存 session id,并在后续任务中 `--resume` | | Pi Agent CLI | `PiAgentExecutor` | 使用 OPC 管理的独立 profile 与显式 Provider 配置 | | DeepSeek Harness | `AcpExecutor` | 每次任务创建新的 ACP session;当前不恢复上一次任务 | | Hermes Agent | `HermesAgentExecutor` | 使用 OPC 管理的员工 profile;原生 session 仅作续接缓存 | 控制核身份与 Worker 身份互相独立:Hermes 可以同时实现首选控制核和 Hermes Worker,DSH 可以同时实现 Advisor/Reviewer 和 DSH Worker,但两种角色不会共享派单权限。 canonical DispatchPlan 会固定 `(workerAgentId, workerEngineId)`。Orchestrator 提交前重新核验员工、候选引擎与组织边界,并将批准引擎写入 Task;ExecutorRegistry 对 canonical Task 禁止静默 fallback 或模拟执行,批准引擎不可用时如实失败。只有不来自 canonical 控制面的旧式内部任务才保留主/辅执行引擎回退。 Android 手机操作员是更严格的专用 Worker:当前仅允许 `eng-hermes-cli`,由 `HermesAgentExecutor` 注入任务级 Mobile Gateway 地址和短期 Token,并加载受管 `android_*` 工具插件。DSH rc.6 的 ACP 入口拒绝非空 `mcpServers`,OPC 的 DSH sidecar 也不启用 Shell,因此 DSH Worker 当前不能操控手机。即使 Hermes 未就绪,手机任务也不会回退到 DSH 或其他执行器;旧数据库或任务级 override 若形成错误组合,会以明确配置错误失败。已绑定但离线的手机不会被误判为执行失败:任务保持 `QUEUED` 并显示“手机离线,等待连接”,收到 `device_connected` 后由 Orchestrator 自动唤醒。 ### 4.4 记忆所有权与提案 - OPC-Nexus 数据库是身份、会话、长期记忆、定时任务、审批和审计的唯一事实源。`MemoryService` 按 organization 以及 principal、channel、conversation、agent、project 作用域隔离数据,并提供版本化的 recall/remember/update/forget。 - Hermes controller 按 `(organization, principal, conversation)` 使用独立 `HERMES_HOME`,Hermes Worker 使用员工级独立 profile。普通与 Android Hermes Worker 都通过同一个员工 → 引擎 → 默认 Provider 解析得到原子化的 Provider/model/key/base URL 绑定;Provider/model 被显式固定为 OPC 管理的 `opcnexus` Provider,密钥只注入子进程环境,从而避免用户全局 Hermes/OpenRouter 配置引起的 401/403 串线。缺少完整绑定时任务 fail-closed,不继承用户全局 Hermes 配置。 - Hermes 原生 session、profile memory 与 `kernel_sessions` 锚点只作为可丢弃的连续对话缓存。每次规划仍显式接收 `MemoryService` 召回的 canonical memory;删除或升级 Hermes profile 不会改变 OPC 的长期记忆事实。 - DSH 上游具备 JSONL 历史、checkpoint、SQLite/FTS 会话索引等持久化基础组件,但这些不等于跨会话用户长期记忆。当前集成没有长期记忆或跨任务 resume:`AcpExecutor` 每次调用 `session/new`,为任务建立独立 session root,并在进程结束后清理;没有接入 session list/resume/fork/delete 或跨会话语义召回。 - 控制核只能在 DispatchPlan 中返回 `memoryProposals`。计划成功提交后,`MemoryProposalService` 以 `(request_id, proposal_index)` 幂等捕获为 `pending`;用户可在审核队列接受或拒绝。接受操作在同一事务中通过 `MemoryService` 创建 canonical memory 并标记 `accepted`,拒绝只标记 `rejected`。启动恢复会补捕获已提交计划中遗漏的提案。 - 仅 conversation 作用域可通过显式本地设置启用策略自动接受;默认仍需人工审核。未经接受的提案不会参与长期记忆召回。 ### 4.5 定时任务提案 - Hermes/Nexus 只能在 DispatchPlan 中返回 `taskScheduleProposals`,操作类型限定为 `create_task_schedule`。提案不能自行指定员工或经营报表类型,最终员工固定为该计划已经选择并提交的 Worker。 - KernelRouter 会校验标题、任务内容和 cron:支持 0.5-168 小时间隔、每日、每周和每月任务。控制核不能直接写 `schedules`。 - DispatchPlan 成功提交后,`TaskScheduleProposalService` 以 `(request_id, proposal_index)` 幂等捕获为 `pending`;启动恢复会补捕获已提交计划中遗漏的提案。 - 接受时再次校验 organization、员工和 project 归属,并通过 `Scheduler.createWithCommit()` 在同一事务中创建 `automationKind='task'` 的 schedule、记录审计并将提案标为 `accepted`;拒绝只标为 `rejected`。重复接受返回同一 schedule,不会创建第二份计划。 - Scheduler 是定时任务的唯一所有者,负责 cron 校验、`next_run_at` 计算、到期扫描和任务创建。Hermes 的原生定时能力不参与 OPC 调度,控制核升级或替换不会丢失已接受的计划。 ## 5. 安全基线 ### 5.1 进程隔离 - `contextIsolation: true` + `nodeIntegration: false`(不可关闭) - Renderer 无法访问 Node.js API - 外部链接一律 `shell.openExternal`,禁止 BrowserWindow 内导航 ### 5.2 IPC 白名单 - 所有 Renderer→Main 通信必须通过 `ipc.ts` 中 `ipcMain.handle` 显式注册 - Channel 命名规范: `aibox:<动作>` - Preload 只暴露类型安全函数封装,禁止暴露 `ipcRenderer` 本体 ### 5.3 密钥管理(safeStorage) - 密钥绝不进入 Renderer 进程或 localStorage - 存储路径: `safeStorage.encryptString()` → base64 → SQLite settings 表(key 前缀 `secret:`) - Renderer 仅可调用各业务域的专用凭据方法;不暴露可选择任意命名空间的通用密钥接口 - 每次密钥操作写入 AuditLog ### 5.4 工作目录安全 - 工作目录必须通过 `pickDirectory` 对话框由用户选择 - 单实例锁防止 SQLite 争用 ## 6. 数据持久化 - **引擎**: sql.js(SQLite WASM),零原生编译 - **存储路径**: `userData/aibox-data/aibox.db` - **持久化策略**: 变更后防抖导出 - **Schema 版本**: 38(真正空库按 v0 初始化;非空库缺失版本、版本非法或高于当前版本时拒绝打开) - **一致性**: 每次连接启用 SQLite 外键,迁移提交前执行 `foreign_key_check` - **数据保留**: 资源样本 7 天、任务明细 90 天、审计日志 365 天;canonical 入站 Task/Message/Plan 的身份收据永久保留,90 天后只清空任务载荷 ### Schema 可靠性演进 - v36 建立 canonical Task/Message/DispatchPlan 外键语义、`input_message_id` exactly-once 约束和迁移时外键校验;无法唯一重建的旧数据会中止迁移,而不是猜测关联。 - v37 将 project、agent、channel 纳入 organization 边界,使 `dispatch_plans.channel_id` 可为空以支持桌面入口,补齐本地管理员 principal,并加入 durable memory proposal 审核队列。 - v38 加入 durable task schedule proposal 审核队列;控制核输出与最终 `schedules` 记录分离,只有 OPC Scheduler 能把已接受提案转成可运行计划。 - 数据保留不会删除 canonical 入站任务的幂等身份。过期任务只清空标题、内容、结果、错误与 session 等载荷,避免同一上游消息在清理后被再次执行。 ### 核心数据表 | 表名 | 用途 | |------|------| | agents | 数字员工配置 | | tasks | 任务记录 | | task_events | 任务执行事件(审计) | | engines | 引擎配置 | | channels | 渠道配置 | | organizations / principals / channel_identities | canonical 租户与渠道身份 | | conversations / messages | canonical 会话和消息 | | dispatch_plans / kernel_attempts / kernel_sessions | 控制核计划、尝试和原生会话锚点 | | memory_items / memory_scopes / memory_versions / memory_terms | 分域长期记忆及版本索引 | | memory_proposals | 控制核记忆提案及 pending/accepted/rejected 审核状态 | | task_schedule_proposals | 控制核定时任务提案、审核状态及最终 schedule 引用 | | schedules | 定时任务 | | approvals | 审批记录 | | audit_logs | 操作审计日志 | | settings | 键值设置(含密钥引用) | | providers | 模型供应商 | | mcp_servers | MCP 服务器配置 | | skills / agent_skills | 技能及绑定 | | workflows | 工作流定义 | | workflow_runs | 工作流执行历史 | | teams / team_runs | 专家团及执行记录 | | collab_workspaces / collab_tasks / collab_agents | 多机协同 | | task_messages | 单次任务内部的执行消息 | | usage_records | Token 用量统计 | | resource_samples | 资源监控采样 | | prompt_templates | Prompt 模板 | ## 7. 实时通信机制 | 通道 | 方向 | 用途 | |------|------|------| | `aibox:snapshot` | Main → Renderer | 全量状态快照推送 | | `aibox:taskOutput` | Main → Renderer | 任务输出流式推送 | | `aibox:resources` | Main → Renderer | 资源监控实时数据 | | `aibox:wfNodeEvent` | Main → Renderer | 工作流节点执行状态 | ## 8. 权限模型 四级权限模式(`PermissionMode`): | 模式 | 说明 | |------|------| | `readonly` | 只读,不可写入任何文件 | | `standard` | 写入需审批 | | `trusted` | 全信任,无需审批 | | `autonomous` | 完全自主,无需任何审批 | 能力开关(`AgentCapabilities`)独立于权限模式: | 能力 | 说明 | |------|------| | `network` | HTTP/HTTPS 网络请求 | | `shell` | 系统命令执行 | | `install` | 软件包安装 | | `browser` | 浏览器自动化(Playwright/CDP) | | `computer` | 桌面操控(Computer Use) | ## 9. 当前运行时验证边界 以下结果记录于 2026-08-15。它们描述当前开发机的真实运行证据,不代替目标部署机器上的引擎检测与 Provider 鉴权。 | Runtime | 当前证据 | 仍需注意 | |------|------|------| | Hermes Agent v0.19.0 | **PASS**:真实首轮 `-z + --usage-file` 和第二轮 `chat -Q -q --resume` 使用同一原生 session 成功 | 证明原生续接可用,不代表 Hermes profile 是 canonical 长期记忆;生产仍依赖 OPC Provider 与密钥可用 | | DeepSeek Harness v0.1.0-rc.6 | **PASS**:内置 sidecar 完成 Provider 校验和真实 ACP 模型任务 | 它不是 PATH 中的 `dsh` CLI;当前 ACP 不支持 resume 或非空 `mcpServers`,进程结束会删除任务 session root,也不能作为 Android 手机操作员 | | Pi Agent v0.84.2 | **PASS**:认证检查、真实任务和两轮 session 续接成功 | 目标机器仍需安装 Pi CLI,并由 OPC 管理的 profile 注入匹配 Provider | | Claude Code v2.1.220 | **部分通过**:CLI 已安装,参数协议修复和单元测试通过 | 本轮没有重新执行在线真实模型 smoke,不能仅凭安装状态宣称端到端可用 | | Codex CLI | 执行适配与 thread resume 路径已接入 | 本轮未重新执行独立在线 smoke;应由目标机器的 EngineManager 探测确认鉴权和最小任务 | 验证分三层解释:单元测试证明参数构造、状态映射、解析和脱敏;`npm run harness:verify` 证明已准备的 DSH sidecar 依赖、原生模块与 ACP `initialize/session/new` 能工作;只有带实际 Provider 凭据的最小模型任务才能证明该 Runtime 当下端到端可用。