Orca 看着像 IDE,源码里真正值钱的是一套 Agent 控制平面

发布于 · 3,167 字 · 约 8 分钟#Github 解读#Agent 框架原文链接
Orca 看着像 IDE,源码里真正值钱的是一套 Agent 控制平面 封面图
  • 使用 Git worktree 为每个 Agent 创建独立分支和文件系统视图,实现并行开发隔离
  • 通过独立 daemon 管理 PTY 生命周期,实现终端进程与 UI 分离,支持跨窗口和远程恢复
  • 基于 SQLite 构建 Run、Task、Dispatch、Delivery 任务状态机,确保消息可靠投递和冲突处理
  • 自动编排尚未完全实现,任务分解需预先创建,底层只负责派发和回执的扎实执行
  • 以控制平面优先思路,用可验证事实(文件、进程、回执)管理非确定性 Agent 执行

大家好,我是若风。

8 月 12 日,我把 Orca 的 main 分支浅克隆到本地。Git 展开了 13316 个文件,光 src/ 就有 11013 个文件,TypeScript 和 TSX 加起来 263598 行。

我原本以为,它大概就是一个给 Claude Code、Codex 多开终端的 Electron 壳。

结果顺着源码读下去,看到的却是一整套进程托管、Git worktree 隔离、任务状态机、远程运行时和终端恢复协议。README 那句「The AI Orchestrator for 100x builders」挺会营销,但 Orca 真正有意思的地方,不在「100x」,也不在又造了一个聊天框。

它在给 coding agent 补操作系统。

这个仓库创建于 2026 年 3 月 17 日。不到 5 个月,已经积累 43663 个 Star、3043 个 Fork,8 月 11 日发布到 v1.4.180,第二天还在连续修 WSL watcher、远程浏览器和渲染性能。速度很猛,代价也很直白,后面会聊。

多开终端不等于并行开发

假设你同时扔给 5 个 Agent 五项任务。

终端当然能开 5 个。麻烦是,它们要是都站在同一个 checkout 里,Agent A 刚改完依赖,Agent B 的测试环境就变了。Agent C 正在重构组件,Agent D 顺手格式化了同一个文件。等你回来,不是 5 份成果,而是一锅不知道该找谁负责的 diff。

真正的难题现在才出现。

并行开发至少要管住四件事,文件写入要隔开,进程要知道归谁,任务完成要有可信状态,人在离开电脑后仍能接住提问和结果。

Orca 的选择很工程化。模型继续跑在原来的 CLI 里,它不接管 Claude Code 或 Codex 的推理。确定性的脏活则交给控制平面,创建工作树、拉起 PTY、等待 TUI 就绪、注入任务协议、记录 heartbeat、保存滚动历史、回收终端。

你想想看,这和「做一个更强的 Agent」是两条完全不同的路。

第一层隔离,Git worktree 不是装饰

先看 src/main/git/worktree.ts。

performAddWorktree() 最终执行的是 git worktree add。新任务默认带 --no-track -b 创建独立分支,再从指定 base ref 建工作树。这里的 --no-track 很细,它避免新分支继承 base 的 upstream,免得 git status 一上来就显示自己落后若干提交。第一次 push 怎么办,代码会在仓库级配置里补 push.autoSetupRemote=true,让普通的 git push 自动建立远端跟踪关系。

其实吧,能把 worktree 建出来不难,难的是删的时候不丢用户代码。

同一个文件里的 removeWorktree() 会先检查 Git lock,非强制删除前还会跑 git status --porcelain --untracked-files=all。只要有未提交或未跟踪文件,就拒绝清理。遇到体积很大的目录,它会先把工作树改名到相邻的 trash 目录,清掉 Git 注册,再异步删文件,避免 UI 卡在几 GB 的递归删除上。

还有一个容易忽略的细节,注释要求 SSH relay 里的 worktree 实现和本地逻辑同步修改。Orca 从一开始就没把远程 Linux 当附加功能,它把本地、WSL、SSH 都当成执行宿主。

所以每个 Agent 拿到的不是一个新窗口,而是一套有独立路径、分支和生命周期的文件系统视图。

第二层隔离,终端进程不能跟着窗口一起死

很多 Agent GUI 有个尴尬问题,界面关了,任务也断了。Orca 绕开的办法是把终端从 renderer 里拿出来,交给独立 daemon。

src/main/daemon/terminal-host.ts 的 TerminalHost.createOrAttach() 不是简单 spawn。它先用 session owner 和 process generation 判断现有 PTY 能不能继续认领,只有所有权不冲突时才创建或附着。后面的 write()、resize()、signal()、detach() 操作全围绕 session id 路由。renderer 只是客户端,不是进程本体。

这里很关键。

界面切换、手机接入、远程客户端重连,都不该重新启动 coding agent。PTY 要有稳定身份,谁在写、谁只是看,也得分清。

滚动历史则由 src/main/daemon/history-manager.ts 接管。appendIncrements() 平时追加增量日志,5 秒 tick 不会反复写完整快照。只有干净断开、缓冲区溢出或日志达到上限,checkpoint() 才写全量状态,而且采用临时文件加 rename 的原子替换。项目注释说得很直接,坏掉的 checkpoint 比旧 checkpoint 更糟。

说真的,这些代码没有 Agent demo 那么吸睛,却决定了你第二天打开应用时,昨晚那几个会话还在不在。

第三层隔离,任务不能只靠一段 Prompt 记住

Orca 最不像普通 IDE 的部分,在 src/main/runtime/orchestration/。

它没有把「正在执行」塞进 React state,而是用 SQLite 建了一组明确对象。Run 是一次协作的命名空间和收件箱,Task 是带依赖的工作项,Dispatch 是某次任务尝试和某个终端之间的绑定,Delivery 负责可确认的消息投递。src/main/runtime/orchestration/db.ts 的 schema 已经迭代到 26 版,里面还有 decision gate、durable question、mutation receipt、worker terminal ownership 和 federated dispatch。

这套模型解决了一个很现实的问题。同一个 Task 失败后可能被重新派发,旧 Agent 迟到的「完成了」不能把新尝试误标完成。所以 src/main/runtime/orchestration/preamble.ts 生成的任务前言,要求 worker 每次上报都同时携带 taskId 和 dispatchId。完成消息只能发一次,长任务每 5 分钟发 heartbeat。一个已经失败的旧 Dispatch,即使晚到,也不能刷新当前重试的存活时间。

src/main/runtime/rpc/methods/orchestration-workers.ts 里的 orchestration.workerStart 把整条链串了起来。它先确认当前协调终端确实绑定了这个 Run,再决定复用还是创建 worktree,创建 Agent 终端,等待 tui-idle,生成 dispatch capability,最后才把任务前言写进终端。

不是「开个 shell 然后把 Prompt 粘进去」这么简单。

src/main/runtime/rpc/methods/orchestration-federation.ts 又把同一套 Dispatch 延伸到另一台 Orca server。任务的 Run 仍留在当前主机,远端只创建 attachment、worktree 和终端,后续消息按 Dispatch id 回家。移动端和 VPS 能成立,靠的就是这层身份和路由,而不是把 SSH 窗口塞进手机。

Orca Agent 控制平面架构
Orca Agent 控制平面架构

图里最底下的 Claude Code、Codex、OpenCode 仍是原来的 CLI 进程。Orca 没有把它们改造成自家 SDK。它控制的是进程周围的边界。

自动编排还没有 README 看起来那么自动

读到这里,很容易把 Orca 想成一个能接收大目标、自动拆任务、分配 5 个 Agent、最后合并结果的总管。

这不是自动驾驶。

src/main/runtime/orchestration/coordinator.ts 的 decompose() 留了一句很诚实的注释,自动 decomposition 还没实现,任务必须在 coordinator 启动前预先创建。默认最大并发是 4,不是 README 演示里的 5。工作树如果比 base 落后超过 20 个 commit,且任务没有显式写 allow-stale-base,派发会被拒绝并留在 ready 状态。

我读到这句时,反而松了口气。

它对「Agent 卡死」也很保守。worker 5 分钟发一次 heartbeat,10 分钟没消息只记 warning,不会自动判死。原因写在注释里,误杀一个慢但正确的 worker,比让一个挂起 worker 暂时占住槽位更贵。连续 3 次失败后,Dispatch 才触发 circuit breaker。

坦白讲,我反而喜欢这种克制。Agent 编排最危险的不是少自动一步,而是系统把猜测包装成确定状态。Orca 让任务拆分仍由人或上层 Agent 决定,底层只负责把派发和回执做扎实。

但产品文案和当前能力之间,确实要留这条线。结构化 orca orchestration 仍要求打开 Experimental 设置。普通 worktree、终端和 diff 能力已经是产品主体,Run、Task、Dispatch 这套自动协调层还在快速施工。

同类工具都在管 worktree,差别在管到哪一层

Orca 不是唯一看到并行 Agent 混乱的人。Conductor 也给 Claude Code、Codex 和 Cursor 分配独立 worktree,把 setup、diff、测试和 review 连起来。cmux 则更像一台为 coding agent 优化的 macOS 终端,任何能跑在 CLI 里的 Agent 都能接入。

我把它们和手工方案放在一起看,差别就清楚了。

方案文件隔离Agent 范围持久化任务协议远程与移动更像什么
tmux 加 git worktree手工任意 CLI自己约定自己搭一箱零件
Conductor自动 worktreeClaude Code、Codex、Cursor围绕 workspace 工作流以 Mac 桌面为主并行开发工作台
cmuxworkspace 管理任意 CLI终端通知为主macOS 终端路线Agent 终端
Orca本地、WSL、SSH worktree任意 CLIRun、Task、Dispatch、Delivery桌面、手机、VPSAgent 控制平面

老实说,如果你只是偶尔开两个 Claude Code,会写几条 worktree 脚本,Orca 的体量明显过重。一个 Electron 主应用、独立 daemon、SQLite、移动端、relay、原生辅助程序,全都要维护。

可一旦你开始混用 Codex、Claude Code 和远程 Linux,终端多到记不清哪个任务在哪个分支,手工脚本省下来的复杂度就会从别处回来。到那个阶段,Orca 管的已经不是窗口数量,而是所有权。

快速发布把问题也放大了

项目很活跃,活跃不等于稳定。

我抓取仓库时,GitHub 搜索到 1751 个 open issue。数量里有大量快速导入的用户反馈,不能直接等同于 1751 个严重 bug,但它足以说明 Orca 的平台组合有多宽。macOS、Windows、Linux、WSL、SSH、多种 shell、几十种 Agent,再加上桌面和移动端,每多一层,状态同步就多一组边界。

issue #6357 很能说明 worktree 的边界。用户导入一个包含 4 个独立仓库的顶层文件夹后,folder workspace 并不会自动给每个子仓创建隔离 worktree,Agent 可能直接站在原始目录工作。部分文件浏览问题后来修了,但 Git tab、diff 和多仓 worktree 设计在采集时仍没完全收口。

并行不会替你消灭冲突。

你还是要把任务切到尽量不重叠的文件面,给每个 worktree 配好 setup,并把端口、数据库和缓存也隔开。Git 只隔离工作目录,它不会自动给 5 个开发服务器分配 5 个端口。

更尖锐的是 issue #9138。报告者在 macOS 上发现 4 代 daemon 同时存活,界面只显示 8 个 session,系统里却有约 46 个 Claude Code 进程。清理旧 daemon 树时一共终止 370 个进程,swap 从 25 GB 降到 5 GB 以下。这个数字来自 issue 报告,不是我本机复现,但截至 8 月 13 日问题仍是 open。

这刚好打中了 Orca 最值钱的设计。daemon 让 Agent 跨窗口、跨重启存活,生命周期回收一旦出错,优点会翻成隐形资源泄漏。我的建议很具体,正式接管生产工作前,先用小仓库跑一周,升级后检查旧 daemon 和孤儿进程,再决定要不要把常驻任务迁进去。

怎么开始,才不会一上来就把自己绕晕

安装桌面版很简单。

brew install --cask stablyai/orca/orca

第一次使用,我建议先只导入一个普通 Git 仓库,创建两个互不重叠的工作树,一个跑 Claude Code,一个跑 Codex。确认 setup 能在新工作树完成,diff 和终端恢复符合预期,再碰 SSH、手机端和多仓 project group。

如果要试结构化编排,先在设置里打开 Experimental,然后从一个小 DAG 开始。

orca orchestration run-create --objective "review and fix one bug" --json
orca orchestration task-create --spec "reproduce the bug and record evidence" --json
orca orchestration task-create --spec "implement the verified fix" --deps '["task_id_here"]' --json

反正别一上来就把 5 个 Agent 扇出去。先确认任务边界、setup 成本和合并路径,再加并发。并行系统最怕的不是机器跑不满,是人最后审不过来。

Control Plane First

我一直觉得,Orca 最值得带走的不是某个按钮,而是一个叫 Control Plane First 的判断方法。

coding agent 是非确定性的执行面。它会慢,会跑偏,会在错误的分支上自信地完成任务。控制平面不要再去猜它脑子里发生了什么,只需要守住几条可以验证的事实,文件写在哪个 worktree,进程属于哪个 Dispatch,回执是否来自当前尝试,问题有没有经过 decision gate,终端退出后资源有没有被回收。

模型负责创造,系统负责记账。

如果你只有一个 Agent、一个仓库和一项任务,继续用原来的终端就好。要是你已经在本地和远程同时跑多个 coding agent,开始被 worktree、端口、会话和回执淹没,Orca 才进入它真正擅长的区间。

它还没做到全自动总管。

但它已经把「同时开 5 个 Agent」从一个炫技 demo,推进成了一道可以认真讨论所有权、恢复和审计的工程题。

评论互动

© 2026 王若风的技术博客 · Powered by Astro