第 1 章:从 Agent 工具到托管 Agent 操作系统
1.1 先回答:Multica 到底是什么
Multica 不是模型 SDK,也不在服务端自己实现一套通用 LLM Agent Loop。它做的是更上层的问题:如何把已经存在的 Coding Agent CLI 变成可被团队长期管理的执行单元。
源码中的核心动作不是 model.generate(),而是:
- 人或自动规则产生一份工作意图。
- 服务端把意图冻结成持久化 Task。
- 某台机器上的 Daemon 证明自己具备对应 Provider 能力。
- Daemon 领取 Task,准备安全且可恢复的执行环境。
- Provider CLI 在该环境中运行,并通过 Multica CLI 回写业务动作。
- 服务端把状态、消息、结果和成本广播给所有相关界面。
所以更准确的定位是:
Multica 是一个面向人类与 Agent 混合团队的托管 Agent 控制系统。它把项目管理、权限、队列、执行、上下文、会话、协作和观测连成一条耐故障的闭环。
产品定位可以从根 README.md 看到;真正的执行闭环分散在 IssueService、TaskService、Daemon 和统一 agent.Backend 中。
1.2 为什么不能只做一个“远程运行按钮”
一条 POST /run 加一个 exec.Command 很容易做出演示,但它回答不了生产环境的问题:
- 浏览器断开后,工作还算不算存在?
- 两台 Daemon 同时看到任务,谁能执行?
- Daemon 领取后崩溃,任务如何恢复?
- 同一仓库能否同时被两个任务修改?
- Agent 在执行时代表谁?能访问谁的外部应用?
- 新评论到来时,是新开任务、合并任务,还是等待当前任务结束?
- Claude、Codex、Pi、Cursor 的流式协议不一样,界面如何显示统一状态?
- 旧 Desktop 对上新 Server,多一个字段会不会让整个页面白屏?
- 多个 Server 实例如何把事件发到连在另一实例上的浏览器或 Daemon?
Multica 的复杂度几乎都来自这些问题,而不是来自调用模型本身。
1.3 三个平面
控制平面
控制平面以 Go Server 和 PostgreSQL 为中心,负责:
- Workspace、Member、Issue、Comment、Agent、Squad、Skill、Project 等长期领域对象;
- Task 的排队、Claim、租约、状态迁移、取消与重试;
- 权限、归因、审计与任务级令牌;
- Browser WebSocket 和 Daemon WebSocket;
- Autopilot、Webhook、渠道连接和后台清理任务;
- 多实例下的 Redis 实时中继。
组合入口是 server/cmd/server/main.go 和 router.go。
执行平面
执行平面位于运行 multica daemon 的机器:
- 探测 Provider CLI;
- 为每个 Workspace / Profile / Provider 注册 Runtime;
- 通过 WebSocket 收到唤醒并优先使用 WS RPC Claim;
- 在本地槽位空闲后才领取任务;
- 准备 Worktree 或锁定
local_directory; - 注入 Brief、Skills、MCP、任务令牌和 Provider 专用配置;
- 启动 CLI,统一流式消息,监控取消、超时和语义停滞;
- 汇报消息、用量和终态。
核心在 server/internal/daemon 与 server/pkg/agent。
交互平面
交互平面不只有 Web:
- Next.js Web 是完整浏览器产品;
- Electron Desktop 复用 Core、UI、Views,同时管理本地 Daemon;
- Expo Mobile 独立拥有 Native UI、状态、路由和实时连接;
- CLI 同时服务于人类管理和任务内 Agent 操作;
- Slack / Feishu 把外部对话映射成 Multica Chat 与 Task。
这解释了为什么前端包要拆成 core、ui 和 views:同一业务表面需要落到多个宿主上。
1.4 一次任务的最短闭环
重要的是,任何一条虚线断掉都不应立刻丢失业务事实:
- 浏览器 WebSocket 断开,PostgreSQL 仍是事实源;
- Daemon WebSocket 断开,可以安全回落 HTTP;
- Daemon 崩溃,租约、Runtime 心跳与孤儿恢复会识别任务;
- Agent CLI 失败,终态与失败分类仍被记录;
- Server 多实例,Redis 把事件转发到持有连接的节点。
1.5 Multica 与 Pi Agent 的对应关系
参考教程里的 Pi 是“进程内 Agent Runtime”:消息、模型、工具和 Loop 在同一 SDK 内协同。Multica 把观察尺度拉到了分布式产品层。
| Pi 的概念 | Multica 中更接近的对象 | 关键差异 |
|---|---|---|
| Agent Loop | Provider CLI 自己的循环 | Multica 只统一启动、消息和终态,不重写所有 Provider 内核 |
| Agent state | agent_task_queue + Daemon 运行态 | 状态跨进程持久化,允许恢复与审计 |
| Tool | multica CLI、Provider 内建工具、MCP | 工具既作用于文件,也作用于 Multica 业务 API |
| Message | agent.Message、task_message、Comment、Chat Message | 运行流、业务对话和持久历史分层 |
| Session | Provider session_id + work_dir | 与 Task、Issue、Chat Session 都不是一回事 |
| Extension | Provider adapter、Skill、MCP、Channel、VCS adapter | 扩展跨服务端和本地端两个进程 |
| Event | Server Bus + Browser WS + Daemon WS | 事件既做进程内副作用,也跨节点同步缓存 |
Multica 并没有取代 Pi、Claude Code 或 Codex;它把这些 Agent 当作执行引擎。
1.6 七条贯穿源码的设计原则
原则一:业务事实先持久化,再发通知
Task、Comment、Autopilot Run、Webhook Delivery 都先写数据库,再广播事件。WebSocket 是加速同步的信号,不是唯一事实来源。
这使客户端可以通过重新查询恢复,而不是依赖“从未漏过任何事件”。
原则二:执行靠近代码与凭据
服务端不要求把所有仓库和 Provider 登录态上传到中心。Daemon 在本地使用已安装 CLI、Git 凭据和工作目录。
代价是系统必须解决 Runtime 发现、心跳、唤醒、跨版本兼容、磁盘清理和本地并发锁。
原则三:控制身份与执行身份分离
Daemon 的 mdt_ 令牌证明“这台 Daemon 能管理哪些 Runtime”;任务中的 mat_ 令牌证明“这个 Agent 正在代表哪位用户、哪项任务做什么”。
任务进程不能把 Daemon Owner 的长期令牌当作写操作兜底。这是第 4、11、20 章反复出现的信任边界。
原则四:把异构协议压到最窄接口
17 类 Provider 最终都收敛成:
Execute(ctx, prompt, options) -> Session{
Messages <-chan Message
Result <-chan Result
}
差异留在 adapter 内;Daemon 只处理统一消息、用量、取消与终态。
原则五:可恢复性不是“重跑”两个字
恢复至少包含:
- Task 失败后是否允许重试;
- Provider Session 是否仍安全;
- Workdir 是否存在且属于 Daemon 管理;
- Codex rollout 是否真的可见;
- 新评论是否已经交付;
- 本地目录锁是否重新获得;
- 重试的能力快照和归因是否仍正确。
所以源码把 session_id、work_dir、failure_reason、retry_of_task_id、force_fresh_session 等字段分开存储。
原则六:权限与归因不能靠 UI 猜
Agent 的调用权限由 permission_mode 与 agent_invocation_target 决定;任务还记录 originator_user_id、accountable_user_id、证据类型与委派血缘。
UI 是否展示按钮只是体验层;服务端每个入口仍要重新验证。
原则七:兼容性要允许局部退化
安装版 Desktop 可能晚于 Server 更新。前端通过 Zod 与 parseWithFallback 把异常响应降级成空列表或保守对象,同时记录诊断,而不是让一个新增字段或坏数据击穿整个页面。
Daemon 协议也通过能力字段、版本检查、WS/HTTP 回落和可选响应字段逐步演进。
1.7 一组有用的系统类比
可以把 Multica 类比为一个小型操作系统:
| 操作系统概念 | Multica 对应物 |
|---|---|
| 用户与组 | Member、Agent、Squad、调用目标 |
| 进程控制块 | agent_task_queue |
| 调度器 | Claim SQL、Runtime 容量、Autopilot Scheduler |
| 设备驱动 | Agent Provider adapter、Channel adapter、VCS adapter |
| 文件系统命名空间 | 每任务 env、Worktree、Task HOME、Provider HOME |
| IPC | Event Bus、Browser WS、Daemon WS、Redis Stream |
| 能力令牌 | PAT、Daemon Token、Agent Task Token |
| 守护进程 | multica daemon、后台 sweeper、webhook worker |
类比的价值不在于说两者完全相同,而在于提醒我们:任务可靠性来自大量边界协议,而不是来自某一个“聪明的 Agent 类”。
1.8 本章源码导航
- 产品定位:
README.md、README.zh-CN.md - Server 组合根:
server/cmd/server/main.go - 路由装配:
server/cmd/server/router.go - Issue 创建:
server/internal/service/issue.go的IssueService.Create - Task 编排:
server/internal/service/task.go - Daemon:
server/internal/daemon/daemon.go - Agent 统一契约:
server/pkg/agent/agent.go - 前端组合:
packages/core/platform/core-provider.tsx
1.9 本章结论
理解 Multica 的第一步,是把问题从“怎样调用一个 Coding Agent”改写为:
怎样让不同 Agent 在不同机器上,代表正确的人,携带正确的上下文与能力,在可恢复的工作区里执行,并把结果可靠地带回团队协作系统?
后面的每一章都在回答这个问题的一部分。下一章先从仓库骨架出发,确认这些责任为什么被放在不同包和不同进程中。
下一章:Monorepo 骨架与架构边界。