16 KiB
Bridge(Octop ↔ Octop 实例桥)
状态:实现中(Phase 0–3 骨架已落地)。命名与范围以本文为准;实现落在
infra/bridge/。
与同机 Agent 互调无关:见 agent-call-agent.md(ask_agent不参与跨实例)。
用户级连接另一台 Octop(云端或另一台本地),在本机 Dashboard 查看对端专家列表、按需拉取对话记录,并像选择本地专家一样与对端专家对话(对称:对端也可经同一桥访问本机)。对话只在专家所在实例执行;发起侧不落库完整历史。
1. 需求结论
| 项 | 结论 |
|---|---|
| 连接粒度 | 用户级:地址 + 用户名 + 密码;密码加密存储便于自动重连 |
| 多节点 | 同一用户可同时连接多个远程 Octop |
| 拓扑 | 两边均可主动建连;同一逻辑连接共用一个 connection_id |
| 专家 | 在线拉取列表;影子入口;不复制 workspace;不安装为本地 runnable agent |
| 可见范围 | 与该账号在对端本机可见范围一致(无额外「联邦可见」勾选) |
| UI | 远程单独入口/分组,不与本地专家混排 |
| 对话执行 | 仅在专家所在侧跑 harness;发起侧不改 harness |
| 历史 | 按需拉取;权威在专家所在侧;发起侧仅临时会话、不写本机 threads |
| 附件 | 支持;文件落入专家所在侧 inbound/ |
| 前端调用 | 浏览器只打本机 API;本机识别远程后经桥转发 |
| 远程 agent id | bridge:{connection_id}:{remote_agent_id} |
| 传输 | 联邦长连接用 WebSocket;其上以通用 HTTP 隧道为主;特殊能力以后再补显式 RPC |
| 非目标 | 改造 ask_agent / harness 跨端协议;双边双写完整聊天记录;浏览器直连对端 baseURL |
2. 总体架构
┌─────────────────────┐ Bridge WS(双向可发起) ┌─────────────────────┐
│ 本机 Octop │◄─────────────────────────────────────►│ 对端 Octop │
│ │ HTTP 隧道 + turn 流式多路复用 │ │
│ Dashboard ──HTTP──► │ │ ◄──HTTP── Dashboard │
│ localhost API │ │ peer API │
│ │ │ │ │ │
│ infra/bridge │ │ infra/bridge │
│ (识别 bridge:* / │ │ (执行隧道请求) │
│ 转发 / 收隧道) │ │ │
│ │ │ │ │ │
│ 本地 agent+harness │ │ 对端 agent+harness │
└─────────────────────┘ └─────────────────────┘
- Harness / GlobalProcessor 保持单机语义;桥只负责把请求送到对端执行并把响应送回。
- Dashboard 的
WebSocketHub仅服务浏览器↔本机;机机总线是独立的 Bridge WS,不要复用 Hub。 - NAT:对端 HTTP 往往不可达时,业务走已建立的 Bridge WS 隧道(尤其云端访问家里的本地实例)。
3. 模块边界
| 路径 | 职责 |
|---|---|
infra/bridge/connections.py |
连接 CRUD、加密凭证、connection_id、多节点 |
infra/bridge/transport.py |
出站 WS 客户端 + 入站 WS 端点;重连;同 connection_id 去重合并 |
infra/bridge/http_tunnel.py |
通用 method/path/query/headers/body ↔ 响应;分片、超时、取消 |
infra/bridge/router.py |
本机侧:目标为 bridge:*(或显式 connection 上下文)时改走隧道 |
infra/bridge/chat_bridge.py |
远程对话:本机 chat WS ↔ 桥上对端 turn 流 |
api/routers/bridge.py |
连接管理 HTTP(探测/添加/列表/删除/连接/改名);Dashboard 入口在 设置 → 远程桥接(/bridge,知识库下方) |
cli/commands/bridge.py |
同一套管理面的 CLI:octop bridge(list/get/probe/create/patch/delete/connect/disconnect/agents) |
探测(probe,不落库)
添加连接前可先 POST /api/bridge/probe:用填写的 peer_base_url + 用户名/密码对端 HTTP 登录,再拉 GET /api/agents?scope=mine,返回专家摘要列表(agent_id / name / description / 绝对 icon_url 等)。不写入 bridge_connections,不建立 Bridge WS。Dashboard 添加抽屉里的「探测」按钮走此接口。
「保存并连接」仅在登录 + Bridge WS hello_ack 成功后落库;失败回滚。管理 API 按连接所有者鉴权(登录用户即可管理自己的桥)。入站隧道允许 agent 范围内 的读写(聊天、工作区、定时任务、状态,以及个性化里标记为 limited 的工具 / 插件 / 渠道,含 PATCH …/tool-settings 与 POST …/reload),外加只读 composer:GET /api/providers/resolved、GET /api/providers/active-model、GET /api/knowledge-bases、GET /api/knowledge-bases/capability,以及 Chat dock 浏览器 viewer:GET /api/browser/env-status、GET /api/browser/harness-sessions、POST /api/browser/sessions/{id}/handoff。技能包、全局 ACP、连接器管理、知识库管理在 Dashboard 标为 peer-only,应在对端操作。管理 / 认证 / Bridge 控制面不入隧道。history-migration 仅本机处理。入站 hello 不得抢占他人 connection_id。旧入口 /admin/advanced?tab=bridge 会重定向到 /bridge。
依赖:api → infra/bridge → 现有 infra(对端登录、对端执行 agents/history/upload)。
禁止:bridge → api/ / cli/ / launch.py(peer turn / browser runner 由 build_app 注入);禁止用 Dashboard Hub 做机机通道。
4. 连接与身份
4.1 配对流程
- 用户在本机填:
base_url、username、password。 - 发起方用 HTTP 调对端
POST /api/auth/login换 token(首次配对假定发起方能 HTTP 打到对端;若不可达需由另一侧发起或另做配对码——实现前写死一种冷启动路径)。 - 生成
connection_id(ULID);握手帧确认后两边各存同一 id。 - 建立 Bridge WS;之后业务优先走隧道(尤其反向访问 NAT 后实例)。
- 密码与 token 加密落库;重连优先 refresh,失败再用密码登录。
4.2 存储(示意)
表名建议:bridge_connections。
| 列 | 说明 |
|---|---|
connection_id |
双方一致的逻辑连接 id |
owner_user_id |
本机用户 |
peer_base_url |
对端地址 |
peer_username |
对端登录名 |
display_name |
必填、同一用户下唯一;聊天页分组切换用的显示名 |
notes |
可选备注,仅本机展示 |
password_encrypted |
加密密码 |
access_token_encrypted / refresh / 过期时间 |
会话 |
created_at / last_seen_at / status |
元数据 |
同一本机用户可有多行(多远程节点)。
4.3 鉴权与可见性
- 隧道内请求以对端该登录用户身份执行。
- 专家可见范围 = 该用户在对端本机可见范围。
- 本机用户 id 与对端用户 id 无对应关系。
4.4 双向主动连
- 任一侧可 dial;握手携带
connection_id+ 用户证明。 - 已存在同一
connection_id的活连接则合并或踢旧,避免双管道。
5. 通用 HTTP 隧道
机制通用:任意 method + path + query + headers + body 可经 Bridge WS 转发;产品 v1 先接专家列表、对话、历史上传、附件。不是「只服务这几类」的专用协议;可选 path 允许/拒绝仅作安全开关。
5.1 帧形态(示意)
→ tunnel.request { id, method, path, query, headers, body_b64? | chunks }
← tunnel.response { id, status, headers, body_b64? | chunks, done }
← tunnel.error { id, code, message }
→ tunnel.cancel { id }
- 大 body(上传)分片;支持取消与超时。
- SSE:v1 可不透传或单独标记;实时对话用 §6 的 turn 流,避免与 HTTP 隧道搅在一起。
5.2 本机路由(浏览器只打本机)
- Dashboard 请求本机(列表、history、upload、chat 等)。
- 若目标为
bridge:{connection_id}:{agent_id}(或带 connection 上下文):- path 内 agent id 改写为对端真实 id;
- 经该 connection 隧道发到对端;
- 将 status/body 原样返回浏览器。
- 本地真实 agent 仍走现有代码,零隧道。
6. 远程专家与对话
6.1 列表
- Dashboard「远程节点」按 connection 分组。
- 本机经隧道调对端
GET /api/agents(或等价列表),将 id 映射为bridge:{connection_id}:{id}再返回前端。 - 不写本地
agents表作为权威(进程内短缓存可选)。
6.2 开聊
- 前端仍连本机:
/api/agents/bridge:…/chat/ws(或等价入口)。 - 本机识别
bridge:*:不启本地 harness;将 user_turn / subscribe 转到对端对应 agent 的对话通道。 - 对端正常
GlobalProcessor → harness;chunk 经桥回传,本机再推给浏览器(帧形状尽量与现有 dashboard chat 一致)。 - 同一条 Bridge WS 上:HTTP 隧道与 turn 流式帧多路复用。
- Chat dock「远程浏览器」在
bridge:*专家下经隧道读对端env-status/harness-sessions/handoff,画面走显式browser.*中继(WS /api/bridge/connections/{id}/browser-stream/ws);独立 Remote Browser 页与 install/录制仍打本机。
6.3 会话与历史
- Thread 只在专家所在侧创建与持久化。
- 发起侧:连接/内存级临时会话(持有对端
thread_id),不写本机threads/thread_messages。 - 对话列表与历史:本机 API → 隧道 → 对端现有 history API。
- 对称:云端聊本地专家时,权威在本地。
6.4 附件
- 前端仍
POST本机/api/agents/bridge:…/upload。 - 本机隧道转发对端同名 upload,文件落入对端
inbound/。 - 预览/下载由本机代理(再隧道 GET),避免浏览器直连对端。
7. 与现有组件关系
| 现有 | Bridge 中的角色 |
|---|---|
Dashboard chat WS / WebSocketHub |
仅浏览器↔本机 |
GlobalProcessor / harness |
只在专家所在侧跑 |
ask_agent / Team |
不参与跨实例 |
POST …/upload + inbound/ |
对端执行;发起侧代理 |
| 用户 JWT | 本机≠对端;隧道用对端登录态 |
| Connector(OAuth/MCP) | 无关;勿与 bridge 混名 |
8. 分期
| 阶段 | 内容 |
|---|---|
| Phase 0 | 连接 CRUD、密码加密、登录换 token、双边 WS、connection_id 握手与去重、隧道 ping/health |
| Phase 1 | 远程分组列表、bridge:* 映射、经隧道拉 agents / history;Dashboard 远程入口 |
| Phase 2 | 本机 chat WS ↔ 对端 turn 中继;临时会话;取消/断线 |
| Phase 3 | upload/预览经隧道分片代理 |
| Phase 4 | 多连接稳定性、token 刷新、path 策略、可观测性;必要时对极少数能力补显式 RPC |
9. 风险与约束
- 配对冷启动:添加连接时对端 HTTP 不可达则无法用「登录换 token」完成首配——须约定由可达的一侧发起,或引入配对码/中继。
- 隧道面过大:协议通用后需身份隔离 + 可选 path 策略 + 审计,避免误暴露危险管理 API。
- 双连接竞态:两边同时 dial 同一
connection_id必须合并。 - 流式对话:不要把 turn 硬塞进模拟 HTTP SSE;HTTP 隧道与 turn 流分帧、同连接多路复用。
- 版本 skew:握手带 octop / bridge protocol version;不兼容则拒绝并提示升级。
10. 命名一览
| 用途 | 命名 |
|---|---|
| 包 | octop.infra.bridge |
| HTTP 管理 API | /api/bridge/... |
| CLI | octop bridge(与 Dashboard 对端管理同一套 BridgeManager) |
| 远程 agent id | bridge:{connection_id}:{remote_agent_id} |
| 表 | bridge_connections |
| 产品文案 | 「远程节点」/ Bridge(中英文另定) |
旧讨论中的 federation / fed: 不再使用。





