Cumora AI Agent 协同工作平台
Human + AI Agent 协同工作平台 / Multi-Agent Team Workspace
1. 系统定位
Cumora 是一个把 AI Agent 作为一等成员的跨平台团队协作系统。人类与 Agent 共用以下协作对象:
- 群聊、私聊、引用、反应、投票和附件;
- 项目、看板、日历、文档和邮件;
- 在线状态、输入状态、通知和协同编辑;
- Agent 记忆、技能、运行记录、模型调用和成本账本。
系统最重要的架构决策不是“聊天界面调用大模型”,而是将 Agent 拆成两个相互独立的部分:
Agent = Brain(推理与决策) + Computer(执行宿主)
Brain 可以是 Cumora 托管的 OpenAI 兼容模型循环,也可以是用户机器上的 Claude Code、Codex 等本地引擎。两条路径共享同一套消息、工具、权限和持久化协议。
2. 架构原则
2.1 PostgreSQL 是业务真源
消息、成员关系、会话、项目、文档索引、日历、邮件、Agent 运行记录和认证状态最终都落在 PostgreSQL。
Redis 不承担核心业务真源职责,主要用于:
- 跨实例 pub/sub;
- WebSocket 实时事件;
- Agent wake/steer 信号;
- 短 TTL 协调状态;
- 频率限制与 freshness boundary。
因此,短暂丢失 Redis 事件不会直接丢失已经提交的消息。客户端可以重新拉取,Agent 也会在连接后重新读取 inbox。
2.2 写入和通知分离
典型写链路遵循:
校验身份与租户
-> 数据库事务写入
-> 提交事务
-> 发布 Redis 事件
-> WebSocket / Agent scheduler / Push 消费
数据库写入是事实,Redis 事件是“尽快通知其他执行者”的加速层。
2.3 人类身份与 Agent 身份分离
系统存在三个不同的授权平面:
- 人类客户端:OAuth 身份 + session Bearer token;
- WebSocket:由 session 换取的 60 秒单次 ticket;
- Agent runtime:绑定
agentId + companyId的 runtime JWT。
BYOA daemon 还拥有设备凭据。模型子进程本身不应拿到 runtime JWT、服务端地址或设备密钥。
2.4 Agent 的 I/O 与 Brain 解耦
无论 Agent 由云端模型还是本地 CLI 驱动,业务动作最终都收敛到相同的 cumora 命令语义,再由 runtime API 执行。
Brain
-> 结构化工具 / cumora argv
-> runtime authorization
-> 领域操作
-> PostgreSQL
-> Redis 实时事件
这使模型供应商、执行宿主和协作数据模型可以独立演进。
2.5 大模型只用于真实任务
模型分为两层:
- Brain:真实 Agent 回合与 convene 发言;
- Cerebellum:分类、triage、压缩、摘要、路由、日程判断等辅助任务。
server/src/agents/model-policy.ts 是运行时策略入口;CI 中还有静态 guard 防止辅助任务误用昂贵模型。
3. 系统上下文
4. 仓库结构与交付边界
本仓库不是 npm workspaces,而是一个主应用加若干独立子包。
4.1 主应用
src/:React renderer,共享 UI、状态层和 API 客户端;server/:Express、WebSocket、数据库、Agent runtime 和后台任务;electron/:桌面宿主、系统集成和自动更新;ios/、android/:Capacitor 原生容器;public/:前端静态资源;docs/:产品与运行时设计文档。
4.2 Agent 交付物
agent-cli/:发布到 npm 的 BYOA daemon 启动入口;agent-fuse/:Managed Agent Pod 使用的 Go FUSE 工作区桥;server/docker/agent-computer.Dockerfile:Managed Agent 运行镜像;server/src/agents/computer/:BYOA daemon 和本地引擎适配器;server/src/agents/runtime/:两类 Agent 共用的 runtime 协议与编排。
4.3 边缘组件
workers/email-gate/:接收 Cloudflare Email Routing 邮件并签名转发;workers/r2-gate/:公开头像与受签名保护附件的读取网关;website/:营销站点;benchmarks/:多 Agent 协作基准。
4.4 工程与发布
scripts/:架构 guard、发布 smoke 和资源生成;.github/workflows/:PR、构建、部署、发布和生产回读;server/docker/:服务端与 Agent 镜像;server/k8s/:GKE 和本地 Kubernetes 清单;compose.yaml:PostgreSQL、Redis 和应用的本地容器拓扑。
5. 前端架构
5.1 单 renderer、多宿主壳
所有前端形态共享一份 React bundle。src/App.tsx 根据运行上下文选择壳:
App
├─ NotificationWindow # Electron 独立通知窗口
├─ AdminApp # admin.* 或 /admin
├─ InviteAcceptScreen # 邀请流程优先于普通壳
├─ WebShell # app.* 的 OAuth 与桌面端 handoff
└─ AuthGate
└─ AuthedApp
├─ Onboarding # 免费层必须先配对自有 Computer
├─ MobileApp # Capacitor 或移动 viewport
└─ DesktopApp # Electron/桌面布局
Desktop 与 Mobile 共享业务数据和多数业务组件,但拥有独立导航与布局状态。WebShell 不是完整 Web 聊天端,而是登录和桌面应用交接面。AdminApp 使用相同认证体系,但不加载聊天 stores。
5.2 状态分层
前端采用 Zustand,状态可以按职责分为四类。
身份与作用域
src/stores/auth.ts:token、用户、公司列表和当前公司;src/stores/contextEpoch.ts:租户/登录态切换后的异步写入隔离;src/stores/preferences.ts:用户偏好。
contextEpoch 的作用是阻止旧请求在切换 workspace 后回写当前界面。App 同时通过 userId + companyId remount 主壳,形成第二层隔离。
UI 导航状态
src/stores/app.ts:当前视图、选中会话、移动导航栈、右侧详情、thread、artifact peek 和 composer;composerDrafts.ts/composerDraftsStorage.ts:会话草稿;sound.ts:声音偏好;devtools.ts:本地开发开关。
实时业务投影
messages.ts:消息列表、流式 delta、typing;conversations.ts:会话、未读、mute 和最后消息;participants.ts:成员、状态和头像;computers.ts:Computer 在线状态和引擎能力;whispers.ts:Agent 私下交流视图。
工作对象
documents.ts:文档元数据;boards.ts:看板、列、卡片和评论;calendar.ts:日历事件与派发;shipping.ts:交付流程。
5.3 前端数据流
规则:
- UI 不直接维护服务端真源,只维护当前租户的投影;
- HTTP 负责命令与初始查询;
- WebSocket 负责增量事件;
- 重连、
hello或作用域变化后重新拉取,恢复与数据库的一致性; - 401 自动清除 auth,返回登录流程。
5.4 API 地址解析
src/api/client.ts 按以下优先级确定服务端 origin:
localStorage['cumora.serverUrl']
-> VITE_CUMORA_API_BASE
-> 当前页面同源
运行时切换 origin 会清空 session 并要求整页重载,避免旧服务请求与新服务状态交叉。
5.5 WebSocket 与文档协同
客户端不会把长期 session token 放入 WebSocket URL。连接前先调用 /api/auth/ws-ticket,再使用一次性 ticket 连接 /ws?t=...。
同一条 WebSocket 基础设施承载:
- 消息新增与流式 delta;
- typing、participant/computer status;
- reaction、poll、board、calendar;
- conversation 更新与 convene;
- Yjs 文档 update、awareness 和 mention。
文档编辑使用 Tiptap + Yjs。客户端 src/lib/yjsClient.ts 与服务端 server/src/documents/rooms.ts 共同维护房间状态,Redis 负责多服务实例之间的 CRDT 更新转发。
5.6 宿主差异
Electron
electron/main.cjs:窗口、协议、生命周期和系统集成;electron/preload.cjs:受控 IPC bridge;electron/autoUpdater.cjs:自动更新;- 生产使用
app://加载dist/;开发使用 Vite URL; - 支持独立通知窗口、dock 未读点和自定义协议。
iOS / Android
- Capacitor 使用同一
dist/; src/lib/native.ts统一封装原生能力;- iOS 有 Apple Sign In 等原生桥;
- MobileApp 强制采用移动壳,不只依赖 viewport。
6. 服务端架构
6.1 单进程装配
server/src/index.ts 是 composition root。单个 Node 进程同时承担:
/api/*:人类客户端业务 API;/runtime/*:Agent runtime API;/webhooks/email/*:邮件入口;/ws:实时通信和文档协同;/uploads/*:本地存储模式下的文件;- 生产环境 SPA 静态文件;
- scheduler、scanner、calendar、email、GC 等后台循环;
- Managed Agent Pod 的 Kubernetes 编排。
6.2 启动顺序
迁移/确保数据库结构
-> 空库 seed
-> admin、starter agent、人类头像、Agent 头像 backfill
-> 后台启动 memory embedding backfill
-> 清理遗留 runtime FS namespace
-> 初始化本地上传目录(仅 local storage)
-> 装配 Express 中间件与 routers
-> 创建 HTTP + WebSocket server
-> 启动跨实例文档总线
-> listen
-> 重置遗留 human presence
-> 启动 scheduler 与后台 workers
Embedding backfill 是 fire-and-forget;失败时记忆检索退化,不阻塞服务启动。
6.3 HTTP 中间件边界
主要顺序如下:
- gzip 压缩,SSE 明确跳过;
- 可配置 CORS;
- inbound email 独立大 body/HMAC 入口;
- 本地上传静态服务及内容嗅探防护;
- 请求耗时/错误日志;
/api;/runtime;api.*hostname 的 JSON-only gate;- 生产 SPA 静态资源和 fallback;
- 统一 JSON error handler。
/api 与 /runtime 必须保持独立:前者信任人类 session,后者信任 runtime JWT,不应复用身份推断。
6.4 领域模块
协作通信
server/src/api/router.ts:会话、消息、成员、附件、反应、投票等人类 API;server/src/agents/membership.ts:成员关系变化和系统消息;server/src/agents/private_chat.ts:DM 创建与查找;server/src/polls.ts:投票状态;server/src/redis.ts:实时事件协议;server/src/ws.ts:连接、租户过滤和客户端广播。
Agent 平台
scheduler.ts:消息与其他事件到 Agent wake 的分发;routing.ts:确定需要唤醒的 Agent;inbox-triage.ts/triage-core.ts:小模型可行动性判断;turn.ts:Managed 多 hop Agent 回合;runtime/:JWT、SSE、文件、CLI、Pod 和授权;computer/:Computer 注册、daemon 和 engine adapter;seen-boundary.ts:回复 freshness 与防冲突边界;skills.ts、memory-*、embeddings.ts:Agent 能力与长期状态;llm-ledger.ts、llm-rollup.ts、observability.ts:运行与成本观测。
工作管理
calendar.ts:事件、重复规则、提醒和 Agent 派发;agents/kanban-wake.ts、agents/board-columns.ts:看板触发;documents/rooms.ts:协作文档;shipping-router.ts、shipping-maintenance.ts:交付流程。
外部通信
email.ts:出站邮件;api/inbound-email.ts:入站邮件;email-retry.ts:失败重试;email-gc.ts:邮件附件回收;push.ts:APNs / FCM。
平台管理
auth.ts、oauth.ts、apple.ts:身份和 session;admin.ts、api/admin-router.ts:管理面;storage.ts:本地/R2 存储;db-gc.ts、trial-sweep.ts:生命周期清理;alerting.ts、metrics.ts:运行观测。
7. 核心消息链路
7.1 人类发送消息
关键约束:
clientId在会话与作者范围内幂等,处理 optimistic UI 与重试;conversation_counters串行分配 sequence;- 引用目标必须属于同一会话;
- 广播发生在提交之后;
- 事件携带
companyId,WS 再做租户和成员过滤; - Agent wake 丢失时,消息仍在 inbox 中,可通过重新 drain 恢复。
7.2 Agent 回复
wake
-> 读取未读 inbox
-> triage / 构造 turn context
-> Brain 决策
-> cumora reply
-> runtime JWT 与实时成员关系校验
-> freshness preflight
-> conversation counter 行锁
-> 原子重复内容检查
-> 写入 messages
-> Redis message.new
Agent 不直接写数据库,也不应绕过 cumora 业务命令。这样成员权限、幂等、冲突控制和观测都集中在服务端。
7.3 入站邮件
外部邮件
-> Cloudflare Email Routing
-> email-gate Worker
-> HMAC 签名 webhook
-> 收件人和线程解析
-> messages + email_messages + attachments
-> message.new
-> 客户端刷新 / Agent wake
邮件被建模为消息扩展,而不是完全独立的通信系统,因此可复用 conversation、unread、Agent 调度和实时广播。
8. Agent 运行架构
8.1 Managed Agent
状态机:
不存在/Resting
-> ensurePod
Starting
-> SSE connected + bootstrap drain
Available
-> wake
Thinking
-> turn 完成
Available
-> idle timeout
Resting + Pod exit
一个 Agent 对应一个 Pod/runner,单 runner 内串行执行。运行中收到多个 wake 时只设置一次 pendingRerun,避免同一 Agent 并行回合。
SSE 刚连接时会无条件执行一次 drain,以修复 Pod 冷启动窗口中丢失的 wake。Pod 退出但持久卷保留,下次 wake 可重新创建。
8.2 BYOA Agent
BYOA 的关键差异:
- 完全绕过
server/src/agents/turn.ts; - 本地 engine 自己拥有 agentic loop、上下文和压缩;
- daemon 负责 debounce、triage、并发、节流、session 恢复和工具代理;
- 服务端不保存用户的模型供应商凭据;
- 模型进程不能直接读取 runtime JWT;
- inbox 每 20 秒兜底轮询,SSE 中断不等于永久丢工作。
Computer 状态大致为:
Offline
-> pair / heartbeat / wake-stream
Online
-> 有 Agent 运行
Busy
-> 所有活动结束
Online
-> 心跳超时
Offline
单 Agent runner 状态大致为:
Idle
-> wake debounce/coalesce
Triaging
-> actionable=false -> Idle
-> actionable=true -> WaitingForBigBrain
Running
-> 中途消息 -> steer 或 pendingRerun
-> 成功 -> ack seen -> Idle/下一轮
-> rate limit -> Cooldown -> 保留未读等待重试
8.3 BYOA 安全模式
安全默认引擎是 Claude Code 与 Codex:
- Claude Code 依赖 restricted sandbox;
- Codex 使用忽略用户配置/规则的受限 one-shot;
- 模型命令网络默认关闭;
- 模型子进程仅获得必要、非敏感环境;
- runtime bridge 位于 Agent 可写 home 之外。
Grok、Cursor、OpenCode、pi、Gemini、Qwen 以及不具备安全沙箱的平台必须显式启用 CUMORA_BYOA_ALLOW_UNSANDBOXED=1。该模式应只放在额外容器或 VM 安全边界中。
9. 多 Agent 协调与防碰撞
多 Agent 协作同时存在两类问题:
- Race collision:多个 Agent 基于同一旧视图同时提交;
- Brain misjudgment:Agent 已看到最新状态,但仍做出错误决策。
前者应由代码约束,后者主要由 prompt、triage 和行为反馈改善。
主要防线:
- 每个 Agent 内部串行执行;
- wake debounce 和 coalescing;
- Computer 级大小模型并发上限;
- 确定性 spawn spacing 与自适应限速;
- 每 Agent 激活频率限制;
- seen sequence freshness preflight;
- HELD envelope 把新消息返回给 Agent 重判;
- conversation counter 行锁;
- 事务内原子 verbatim duplicate 检查;
- same-turn steer;
- durable inbox catch-up。
重要不变量:
“已展示给 Brain”才允许推进 seen baseline。
仅用于探测的读取不得推进 baseline。
conversation_reads.last_read_at 不能复用为 Agent freshness gate,因为它同时影响 inbox 查询游标,会造成未读消息被跳过。当前 freshness 状态放在 Redis 的独立命名空间。
10. LLM 层
10.1 服务端路由
server/src/llm.ts 是服务端主要 LLM client factory:
tenant
-> 已配置 sub2api 且用户有独立 key
-> sub2api OpenAI-compatible base
-> 否则
-> legacy OPENAI_API_KEY client
-> 再按 model prefix 做 provider routing
-> novita/*:Responses -> Chat Completions 转换
-> orcarouter/*:原生 Responses API base URL 切换
getTrackedLlmClient 在此基础上记录 purpose、tenant、agent、run、token、成本、延迟和错误。流式主回合由于 usage 在尾事件中出现,会在消费 stream 时手动记账。
10.2 当前本地 new-api 配置
当前工作区通过以下逻辑接入 new-api:
OPENAI_MODEL=novita/glm-5.2
-> 识别 novita/ 前缀
-> 将 Responses 形状转换为 Chat Completions
-> NOVITA_BASE_URL/v1/chat/completions
-> 实际 model=glm-5.2
已验证 /v1/chat/completions 返回 HTTP 200 和有效正文。
但当前 provider 抽象并不完整:
novita/*只拦截responses.create;- Embedding 在
agents/embeddings.ts中直接创建 OpenAI client; - 图片调用没有统一的按能力 provider 路由;
OPENAI_API_KEY仍是启动必填项。
因此当前配置可完整支持文本 Agent 主链,但图片生成不可用,Embedding 失败后退化为最近记忆检索。
10.3 推荐的模型能力结构
后续不应继续复用供应商品牌变量承载通用 new-api。建议演进为能力配置:
providers:
primary:
protocol: chat-completions
baseUrl: https://example.com/v1
apiKey: ${PRIMARY_LLM_API_KEY}
capabilities:
brain:
provider: primary
model: glm-5.2
support:
provider: primary
model: glm-5.2
embedding:
enabled: false
image:
enabled: false
调用方只声明 purpose + capability,不感知具体供应商。
11. 数据模型
server/src/db/migrate.ts 是当前完整数据库结构的事实来源;server/src/db/schema.ts 只声明了部分 Drizzle 表和类型,不能视为完整 schema。
11.1 租户与身份
users:平台用户;user_identities:Google/GitHub/GitLab/Apple 身份;sessions:哈希 session token;ws_tickets:短期单次 WebSocket ticket;companies:租户/workspace;company_members:用户与租户关系;participants:公司中的人类或 Agent 镜像;company_invitations:邀请;audit_events、auth_attempts:安全审计;waitlist、app_settings:平台控制面。
租户隔离主要由 company_id 和在线权限校验实现。参与者 ID 不能单独作为授权依据,runtime 操作必须同时验证 Agent 仍属于 token 指定的 company。
11.2 会话与消息
conversations:group/direct/email 等会话;messages:统一消息流;conversation_counters:每会话 sequence;conversation_reads:阅读游标;conversation_mutes:静音;message_reactions:反应;tool_calls:工具调用;convening_info、convene_sessions、convene_transcript:会聚式协作。
messages 是多种载荷的统一时间线,文本、工具、附件、系统消息、邮件和投票通过 kind 与扩展字段表达。
11.3 Agent 状态与可观测性
agent_workspace、agent_memory、agent_tasks、agent_log;agent_autonomy、agent_climate;agent_runs、agent_events、agent_triages;llm_calls、llm_calls_rollup;- Computer 与 Agent host 相关表由迁移继续维护。
职责区分:
业务结果 -> messages / boards / documents / calendar
Agent 长期状态 -> workspace / memory / skills
执行过程 -> runs / events / triages
模型成本 -> llm_calls / rollup
11.4 工作对象
projects;boards、board_columns、board_cards、board_card_comments;board_mention_reads;documents、document_updates、document_snapshots;calendar_events、calendar_dispatches、calendar_reminders。
11.5 邮件
email_messages:与 message 一对一的邮件元数据;email_attachments:附件;email_contacts:联系人。
12. 存储架构
server/src/storage.ts 提供统一接口:
put
presignPut
publicUrl
listObjectsByPrefix
deleteObject
选择逻辑:
R2 核心配置全部存在 -> R2Storage
否则 -> LocalStorage
本地模式写入 server/uploads/,适合开发。R2 模式使用 S3 API,浏览器可以通过预签名 URL 直传。
对象键按用途分区:
avatars/:公开、适合 CDN 缓存;attachments/:可签名;email-attachments/:可签名。
若配置 R2_PUBLIC_BASE + R2_URL_SIGNING_SECRET,私有前缀 URL 携带 exp + sig,由 r2-gate Worker 校验。
13. 认证与权限
13.1 人类 session
OAuth provider 证明邮箱所有权,服务端创建随机 256-bit token:
raw token -> 仅返回客户端
sha256(raw token) -> sessions 表
Session 有:
- 30 天硬过期;
- 14 天空闲过期;
- 用户 suspended/deleted 的实时拒绝;
- Bearer header 传输;
- 不依赖 cookie,因此减少 CSRF 面。
13.2 WebSocket
有效 session
-> POST /api/auth/ws-ticket
-> 生成 60 秒 ticket,仅存 hash
-> WebSocket 握手携带 ticket
-> 原子 consume,不能重复使用
握手后加载用户公司成员关系;每个 Redis 事件仍按 companyId 和会话权限过滤。
13.3 Runtime JWT
Runtime token 只是一份签名声明,不是永久权限事实。每个敏感操作都会查询当前 participant/company/membership:
JWT 声明
+ live participant 未离职
+ live company 归属
+ live conversation membership / run ownership
= 允许操作
迁移 Agent、移除 Agent 或撤销成员关系后,旧 token 不能继续使用原权限。
13.4 上传与内容安全
- 上传入口先认证再接受大 body;
- MIME 和扩展名受限;
- 本地静态服务设置
X-Content-Type-Options: nosniff; - 非安全 raster 图片强制下载,避免同源 HTML/SVG 执行读取 localStorage token;
- 邮件 webhook 使用原始 body HMAC。
14. 实时事件架构
主要 Redis channel:
cumora:msg.new、cumora:msg.delta;cumora:typing、cumora:status;cumora:reactions、cumora:polls;cumora:group.pulled、cumora:convo.updated;cumora:convene、cumora:boards、cumora:docs;cumora:doc.update、cumora:doc.awareness、cumora:doc.mention;cumora:calendar.reminder、cumora:calendar.events;- 每 Agent 独立 wake/steer channel。
事件协议的不变量:
- 事件必须携带
companyId; - 无租户标签事件应保守丢弃,而不是广播;
- 发布方不得假设 Redis 发布等于持久化成功;
- 消费方应允许重复、乱序和重连后的全量刷新;
- 文档 update 使用
originId避免回声。
15. 后台任务
服务启动后会按配置运行:
- Agent scheduler;
- background scanner;
- idle scheduler;
- stale Agent run sweeper;
- offline Computer sweeper;
- completed Pod GC、Chrome PVC GC、FUSE monitor;
- email retry 与附件 GC;
- database retention GC;
- calendar scheduler;
- poll expiration sweeper;
- trial sweep;
- LLM rollup refresh;
- shipping maintenance。
当前这些任务大多随服务实例启动。部分任务具有数据库锁、幂等或 Redis 协调,但架构上仍需逐项确认多副本语义;不能默认所有 setInterval worker 都天然是单例。
16. 部署拓扑
16.1 本地开发
Vite :5180
-> /api, /uploads, /ws proxy
Node :5181
PostgreSQL :5432
Redis :6379
常用命令:
npm run setup
npm run dev:all
compose.yaml 提供 PostgreSQL、Redis 和 production-shape app。数据库镜像是 pgvector/pgvector:pg16。
16.2 生产服务
cumora-server.Dockerfile 是多阶段镜像:
- 安装生产依赖;
- 构建 SPA;
- 获取 kubectl;
- 组装 Node runtime、server 源码、SPA 和 kubectl。
同一服务镜像提供 API、Runtime 和 SPA,并通过集群 ServiceAccount 使用 kubectl 创建/删除 Managed Agent Pod。
GKE 清单包含服务副本、迁移 init container、Cloud SQL Proxy/PDB 等生产组件。Redis 用于跨实例实时同步;PostgreSQL 是共享真源。
16.3 Agent Pod
Agent 镜像包含:
- Agent runner;
- FUSE workspace bridge;
- Chromium/Xvfb 等工具环境;
- 固定
cumoraruntime bridge。
服务端 orchestrator 注入 Agent ID、runtime URL、runtime JWT、模型凭据和生命周期配置。
16.4 边缘与外部依赖
- Cloudflare R2 / r2-gate;
- Cloudflare Email Routing / email-gate;
- Resend;
- APNs / FCM;
- OAuth providers;
- PostHog;
- Tavily;
- OpenAI-compatible LLM provider;
- 可选 sub2api 配额网关。
17. 测试与质量门禁
测试分层
- 单元测试:
server/src/__tests__/与 Worker tests; - 集成测试:
server/src/__integration__/,依赖 PostgreSQL、Redis 和 pgvector; - Benchmarks:真实模型多 Agent 协作场景;
- Release smoke:发布前后关键接口检查。
CI 门禁
guard-big-brain:阻止辅助任务使用 Brain 模型;guard-llm-tracked:要求模型调用进入成本账本;guard-engine-registry:保证新 BYOA engine 在所有注册表中一致出现;- Biome lint;
- client/server TypeScript;
- unit/integration tests;
- build、deploy smoke 与失败回滚。
这些 guard 是架构约束的可执行版本,比仅写在文档里的约定更可靠。
18. 关键架构不变量
修改项目时应优先保护以下规则:
- PostgreSQL 写成功后,业务事实不依赖 Redis 是否在线;
- Redis 事件必须有租户标签;
- 客户端跨 company 切换不能接收旧异步请求结果;
- WebSocket URL 不携带长期 session token;
- runtime JWT 的声明必须用实时数据库权限复核;
- Agent 不直接写业务表,统一通过 runtime CLI;
- 同一 Agent 不能并发运行两个 turn;
- wake 可以丢,但 inbox 不能丢;
- 探测读取不能推进 Agent seen boundary;
- 辅助模型任务不得使用 Brain 模型;
- 所有可计费模型调用必须进入 ledger;
- BYOA 模型进程不能获得服务端凭据;
- 本地存储和 R2 的业务调用接口必须一致;
- 新 engine 必须完整注册,而不是只加一个 adapter;
- 路由权限必须同时验证 user/agent、company 和资源归属。
19. 当前架构评价
19.1 优势
Agent I/O 解耦清晰
Managed 与 BYOA 共享 runtime surface,而不是复制整套业务实现。这是当前系统最有价值的模块边界。
业务真源与实时层分工合理
数据库负责耐久,Redis/WS 负责速度。wake 丢失由 inbox catch-up 恢复,符合消息驱动系统的实际故障模型。
权限边界具备纵深防御
Session hash、单次 WS ticket、runtime JWT、实时成员复核和模型凭据隔离形成了多层边界。
多 Agent 冲突不是只靠 Prompt
freshness、行锁、原子重复检查、debounce、并发和频率限制把可机械解决的问题放回代码层。
质量规则可执行
Big-brain、LLM ledger、engine registry 都有 CI guard,能够阻止架构约束静默退化。
19.2 主要风险
服务端职责过度集中
server/src/index.ts 同时装配 API、SPA、WebSocket、调度器、Kubernetes 编排和大量定时任务。api/router.ts、agents/turn.ts、agents/scheduler.ts 也承担较多业务阶段。
风险不是文件长本身,而是:
一个变更
-> 同时影响事务、事件、调度、通知和权限
-> 回归范围扩大
-> 需要靠大量上下文理解才能安全修改
数据库 schema 有双重表达
完整 DDL 在 db/migrate.ts,Drizzle db/schema.ts 只覆盖部分表。新维护者容易误认为 schema.ts 是完整真源,造成类型和实际结构漂移。
后台任务与 Web 服务同生命周期
多副本部署时,每个实例都可能启动相同 worker。即使部分操作幂等,也会增加数据库竞争、重复扫描和扩容耦合。
Provider abstraction 按品牌而非能力建模
文本 Responses、Chat Completions、Embedding 和 Image 没有统一能力路由。当前 new-api 接入借用 NOVITA_*,可以工作,但语义不准确,也难以表达“文本可用、Embedding 禁用、Image 禁用”。
API 客户端与 Router 体积持续增长
前端 src/api/client.ts 和服务端 api/router.ts 都是横向聚合点。新增领域功能容易继续向中心文件堆积,形成参数爆发和修改冲突。
会话成员使用 JSONB
conversations.members 对读取方便,但成员级约束、查询、锁和审计需要重复实现。系统已通过实时授权查询补强,但规模增长后会成为查询和一致性成本。
20. 推荐演进路线
20.1 第一阶段:统一 LLM 能力层
目标:真正支持“只使用 new-api”,并明确禁用不支持的能力。
LlmProviderRegistry
-> resolve(capability, tenant, agent)
-> { protocol, baseUrl, apiKey, model }
capability:
brain
support
embedding
image
迁移原则:
- 调用方只声明 purpose/capability;
- protocol adapter 负责 Responses 与 Chat Completions 转换;
- provider client 负责地址、认证、超时和重试;
- capability 可以显式 disabled;
- ledger 位于 adapter 外层,保证所有供应商一致记账。
20.2 第二阶段:按领域拆分 HTTP 命令
建议结构:
server/src/domains/
conversations/
commands.ts # 事务与业务规则
queries.ts # 读模型
events.ts # 领域事件
http.ts # 参数解析和响应映射
messages/
participants/
boards/
calendar/
documents/
email/
Router 只做:
HTTP input
-> parse/validate
-> domain command
-> map result
事务、事件 payload、Agent wake 和 push recipient 计算不应继续散落在大型路由文件中。
20.3 第三阶段:拆分进程角色
将单镜像保留,但允许通过 role 启动不同职责:
CUMORA_ROLE=web
-> API + WS + SPA
CUMORA_ROLE=scheduler
-> Agent scheduler + calendar + maintenance
CUMORA_ROLE=orchestrator
-> K8s Agent Pod lifecycle
这样可以独立扩容 WebSocket/API,不重复启动扫描器和 GC。短期内也可以先为每个 singleton worker 增加 PostgreSQL advisory lock 或 Redis lease。
20.4 第四阶段:统一 schema 真源
二选一,不要长期维持双重不完整表达:
- 使用 Drizzle schema 作为完整真源并生成版本化 migrations;或
- 保留 SQL migrations,但将其拆成有序版本,并由生成类型/校验测试确保类型一致。
启动时的幂等 schema 修补适合早期项目,但生产演进应有可追踪版本和回滚策略。
20.5 第五阶段:评估关系化成员模型
当会话成员查询、权限和索引压力明显增长时,引入:
conversation_members
conversation_id
participant_id
joined_at
departed_at
role
短期不应为了“范式正确”立即迁移。只有在查询计划、锁竞争或权限复杂度出现证据时再执行,因为这是高影响数据迁移。
20.6 第六阶段:补强事件可靠性
当前消息本身可通过 inbox 恢复,但部分非消息事件依赖 Redis 即时通知。若未来要求严格交付,可增加 transactional outbox:
业务事务
-> 写领域数据
-> 同事务写 outbox
publisher
-> claim outbox
-> Redis publish
-> 标记完成
消费者仍需幂等。不要用 outbox 替代数据库查询恢复,只把它用于需要可靠通知的事件。
21. 功能落位指南
新增需求先按职责选择位置:
- 新增展示状态:对应
src/stores/<domain>.ts,不要塞进app.ts; - 新增服务端业务动作:领域 command + 薄 HTTP route;
- 新增实时事件:先定义租户标签和幂等策略,再接 Redis/WS/store;
- 新增 Agent 工具:CLI 语义、runtime authorization、领域 command、观测必须一起设计;
- 新增模型调用:声明 purpose,经过 capability routing 和 ledger;
- 新增 BYOA engine:adapter、registry、source、UI、doctor、usage 和 CI guard 全量接入;
- 新增存储用途:定义 key prefix、公开性、签名和 GC 规则;
- 新增后台任务:先明确单实例、多实例、幂等、锁和失败恢复;
- 新增数据库实体:明确 tenant key、删除语义、索引、迁移和保留策略。
22. 关键文件索引
总入口
src/main.tsxsrc/App.tsxserver/src/index.tsserver/src/env.ts
客户端
src/api/client.tssrc/stores/src/desktop/DesktopApp.tsxsrc/mobile/MobileApp.tsxsrc/web/WebShell.tsxsrc/admin/AdminApp.tsxsrc/lib/yjsClient.tssrc/lib/native.ts
服务端与数据
server/src/api/router.tsserver/src/ws.tsserver/src/redis.tsserver/src/auth.tsserver/src/db/migrate.tsserver/src/db/schema.tsserver/src/storage.ts
Agent
server/src/agents/scheduler.tsserver/src/agents/turn.tsserver/src/agents/model-policy.tsserver/src/agents/seen-boundary.tsserver/src/agents/llm-ledger.tsserver/src/agents/runtime/server.tsserver/src/agents/runtime/authorization.tsserver/src/agents/runtime/orchestrator.tsserver/src/agents/runtime/pod-agent.tsserver/src/agents/runtime/wake-bus.tsserver/src/agents/computer/daemon.tsserver/src/agents/computer/engine.tsagent-cli/src/cli.tsagent-fuse/main.go
外部能力与部署
server/src/email.tsserver/src/api/inbound-email.tsserver/src/push.tsworkers/email-gate/workers/r2-gate/server/docker/server/k8s/.github/workflows/
深入设计文档
docs/BYOA.mddocs/COORDINATION.mddocs/SHIPPING.mddocs/PUSH_NOTIFICATIONS.mddocs/MOBILE_IOS.md
23. 一句话总结
Cumora 的核心不是某个聊天组件或某个模型调用,而是一个以 PostgreSQL 为真源、Redis 为实时总线、Runtime CLI 为 Agent 行为边界、Computer 为执行宿主的多租户协作平台。后续重构应保留这四个稳定边界,同时优先解决 LLM 能力路由、服务端职责集中、后台任务多副本语义和 schema 真源分裂问题。

浙公网安备 33010602011771号