# Refactor Notes — 重构建议与架构审核

> 生成日期: 2026-06-05 | 基于 chain-map 和 module-boundary 的架构分析

---

## 总体评估

三个仓库形成了一个**清晰的 3 层架构**：App → Bridge → (Providers + Gateway + Plugins)。架构在职责拆分上是合理的，但在以下几个方面存在可改进的空间：

1. **循环依赖已收敛**: Plugin → Bridge (HTTP) 反向调用已从目标架构中移除，Plugin 只保留被 Gateway 调用的 artifact adapter 职责
2. **协议层过多**: Chain 2 经过 4 层跳转 (App → Bridge → Gateway → Plugin)
3. **容错不足**: 多处依赖未处理的状态丢失场景
4. **配置分散**: App/Bridge/Plugin 各自维护连接配置，缺乏统一管理

---

## 问题 1: Plugin ↔ Bridge 循环依赖

### 现状

```
Bridge → (Gateway RPC) → Plugin (xworkmate.artifacts.*)
Plugin → (HTTP JSON-RPC) → Bridge (session.start, multiAgent)
```

### 问题

- 两个方向使用不同协议 (Gateway RPC vs HTTP JSON-RPC)，增加调试难度
- 循环引用导致: Bridge 故障 → Plugin 不可用 → Bridge 的 agent 调用失败 → Plugin 的 bridge agent 反向调用也无法工作
- 版本升级需要同步两个方向

### 建议

**方案 A (推荐): 统一为单向调用**

删除 Plugin 中的 `bridgeAgents.ts` 反向 HTTP 客户端；多 agent 编排归 Bridge 或 OpenClaw 原生 runtime：

```
Bridge
  internal/
    acp/
      orchestrator.go      ← 拥有 bridge 侧编排逻辑
      gateway.go            ← 保留 xworkmate.artifacts.* 调用
```

Plugin 变为纯工件管理 (只被调，不反向调用):

```
Plugin (简化后)
  src/exportArtifacts.ts   ← 只保留 prepare/export/list/read
  删除 src/bridgeAgents.ts  ← 不再从插件反向调用 Bridge
```

**方案 B: 统一协议方向**

Plugin ↔ Bridge 全部走 Gateway RPC (去掉 Plugin 中的 HTTP 调用):

- Bridge 新增 `xworkmate.bridge.*` 网关方法供 Plugin 调用
- Plugin 通过 `api.callGatewayMethod()` 而非 `fetch()` 调用 Bridge

### 影响范围

- 方案 A: 删除 Plugin 的 bridgeAgents.ts；Bridge/OpenClaw 原生 runtime 拥有编排
- 方案 B: 改动 Bridge (新增网关方法) + Plugin (改用网关调用)

---

## 问题 2: Chain 2 协议层过多

### 现状

```
App ──(ACP/WS)──► Bridge ──(GW RPC/WS)──► Gateway ──(Plugin API)──► Plugin
     跳数: 4          协议变换: 2 次
```

### 问题

- 每层增加延迟和故障点
- 错误信息层层包装，难以定位根因
- Gateway 层是性能瓶颈 (WebSocket 单连接复用)

### 建议

**短期优化**:

在 Bridge 中增加 Gateway 连接池（当前为单连接）:
```go
// gatewayruntime/pool.go (新增)
type GatewayPool struct {
    conns   []*GatewayRuntime
    maxSize int
    mu      sync.Mutex
}

func (p *GatewayPool) Acquire() *GatewayRuntime { ... }
func (p *GatewayPool) Release(rt *GatewayRuntime) { ... }
```

**中期优化**:

Bridge 缓存工件元数据，减少实时 Gateway 调用:

```go
// internal/acp/artifact_cache.go (新增)
type ArtifactCache struct {
    cache map[string]*CachedArtifact
    ttl   time.Duration
}

// 命中缓存时跳过 Gateway→Plugin 调用
func (c *ArtifactCache) GetOrFetch(sessionKey, runId string) { ... }
```

**长期考虑**:

如果 Plugin 始终与 Bridge 在同一主机，考虑内嵌 Plugin 为 Bridge 内部模块（避免 Gateway RPC 中转）。

---

## 问题 3: 容错与恢复

### 3.1 SSE 流中断降级为轮询

**现状** (`external_code_agent_acp_desktop_transport.dart`):
```
SSE 流中断 → 降级为 xworkmate.tasks.get 轮询 → 非实时
```

**建议**:
- 在 Bridge 中维护任务状态的增量日志，支持断点续传
- App 重连时发送 `session.resume` 而非重新 `session.start`

### 3.2 Gateway WebSocket 断连 → 任务状态丢失

**现状** (`gatewayruntime/runtime.go`): 断连后未持久化任务状态。

**建议**:
- Bridge 中维护 `tasks` map，断连时标记为 `STALE`
- 重连后自动查询任务最新状态
- 超过 TTL 的 STALE 任务发出 `session.cancel` 通知 App

### 3.3 Gemini/Hermes 子进程崩溃

**现状**: 子进程崩溃后无自动重启。

**建议**:
```go
// internal/geminiadapter/process_manager.go (增强)
type ProcessManager struct {
    cmd        *exec.Cmd
    restartMax int           // 最大重启次数
    backoff    time.Duration  // 退避策略
}

func (pm *ProcessManager) Start() {
    for i := 0; i < pm.restartMax; i++ {
        if err := pm.run(); err != nil {
            time.Sleep(pm.backoff * time.Duration(1<<i))
            continue
        }
        break
    }
}
```

---

## 问题 4: 配置管理分散

### 现状

| 组件 | 配置位置 | 管理方式 |
|------|---------|---------|
| App | `config/settings.yaml` | 本地文件 |
| App | `config/feature_flags.yaml` | 本地文件 |
| Bridge | 环境变量 / `config.yaml` | Ansible 部署 |
| Plugin | `openclaw.plugin.json` / 环境变量 | 插件清单 |

### 问题

- 旧版 Plugin 的 `bridgeUrl` 和 `bridgeToken` 已从目标架构中移除；剩余连接配置集中在 App/Bridge/Gateway
- 配置不一致导致调试困难
- 无版本化配置管理

### 建议

**集中配置源**: 使用 accounts.svc.plus 作为配置中心。

```
accounts.svc.plus
  GET /api/config/bridge → { serverUrl, authToken, providers... }
  GET /api/config/plugin → { workspaceDir, artifact policy... }
```

**本地配置缓存**:
- App 首次启动从 accounts 拉取配置
- Bridge 启动时从 accounts 拉取 providers 配置
- Plugin 从 OpenClaw 网关配置中继承 bridge 连接信息

---

## 问题 5: 无版本兼容性检查

### 现状

三个仓库独立发布，无版本契约:
- xworkmate-app: 1.1.4
- xworkmate-bridge: (无显式版本)
- openclaw-multi-session-plugins: 0.1.15

### 建议

**在 ACP 协议中增加版本协商**:

```json
// acp.capabilities 响应中增加
{
  "protocolVersion": "1.0.0",
  "minCompatibleVersion": "1.0.0",
  "componentVersions": {
    "app": "1.1.4",
    "bridge": "0.2.0",
    "gateway": "2026.5.28",
    "plugins": "0.1.15"
  }
}
```

**App 启动时校验**:
```dart
// gateway_runtime_core.dart
final caps = await gatewayRuntime.getCapabilities();
if (!isCompatible(caps.minCompatibleVersion)) {
  showUpdateDialog();
  return;
}
```

---

## 重构优先级矩阵

| 优先级 | 问题 | 改动量 | 影响 | 建议时序 |
|--------|------|--------|------|---------|
| **P0** | Plugin↔Bridge 循环依赖 | 中 (删除 bridgeAgents) | 消除循环 + 简化协议 | 已落实为目标架构 |
| **P0** | Gateway 断连任务丢失 | 小 (增加持久化) | 核心可靠性 | 本周 |
| **P1** | Gemini/Hermes 进程崩溃 | 小 (增加重启) | 提供商可用性 | 本周 |
| **P1** | 配置管理分散 | 大 (集中配置) | 运维效率 | 本月 |
| **P2** | Chain 2 协议层过多 | 大 (连接池+缓存) | 性能 + 延迟 | 本月 |
| **P2** | 版本兼容性检查 | 小 (协议扩展) | 升级安全 | 本月 |
| **P3** | SSE 降级轮询优化 | 中 (增量日志) | 用户体验 | 下月 |

---

## 各仓库具体改动清单

### xworkmate-app

- [ ] `lib/runtime/gateway_acp_client.dart`: 增加版本兼容性检查
- [ ] `lib/runtime/external_code_agent_acp_desktop_transport.dart`: 支持 session.resume
- [ ] `lib/runtime/runtime_models_account.dart`: 从 accounts 拉取集中配置
- [ ] `config/settings.yaml`: 减少硬编码 URL，改为从 accounts 获取

### xworkmate-bridge

- [ ] `internal/acp/orchestrator.go`: 保持 Bridge 侧编排归属，不从 Plugin 反向调用 Bridge
- [ ] `internal/gatewayruntime/runtime.go`: 增加连接池 + 任务状态持久化
- [ ] `internal/gatewayruntime/pool.go`: 新增连接池
- [ ] `internal/acp/artifact_cache.go`: 新增工件缓存
- [ ] `internal/geminiadapter/` + `internal/hermesadapter/`: 增加子进程自动重启
- [ ] `internal/acp/rpc_handler.go`: 支持 session.resume

### openclaw-multi-session-plugins

- [x] `src/bridgeAgents.ts`: 删除反向 HTTP 调用边界
- [ ] `openclaw.plugin.json`: 简化配置（从网关继承 bridge 信息）
- [ ] `src/exportArtifacts.ts`: 增加 artifactRef TTL 可配置

---

## 不做的事情

| 项目 | 原因 |
|------|------|
| 引入 gRPC 替代 JSON-RPC | 现有协议工作正常，切换成本高于收益 |
| 合并 App 和 Bridge 为单体 | 拆分有明确的价值 (独立部署、技术栈隔离) |
| 引入消息队列 | 当前规模不需要，会增加运维复杂度 |
| 统一为单一编程语言 | Dart/Go/TS 各有适用场景，不必强行统一 |
