找回密码
立即注册
搜索
发回帖 发新帖

5372

积分

0

好友

690

主题
发表于 4 小时前 | 查看: 11| 回复: 0

1 个 Agent 能干一段活,3 个 Agent 一起干就容易乱。OpenBot 想解决的正是这件事:把多个 AI Agent 放进同一张桌面工作台,各有工位、各有队列,互相还能递东西。

OpenBot 一句话就能说清:一个 local-first 的桌面工作台,让多个 AI Agent 常驻在你的机器上,各自有工作区和会话,彼此能发消息、传文件。

「local-first」指程序和数据都跑在本机,只有账号体系那一小段联网。它不等于纯离线:登录与账号相关的请求,走 Cloudflare Workers 上的接口。这个区别得先记住,后面讲权限和同步时会绕回来。

先说为什么现在聊这个——因为开发者手里的 Agent 变多了。

一年前多数人只开 1 个命令行 Agent,问一句答一句,关掉窗口就结束。现在常见的画面是:一个 Codex 在改后端,一个 Claude 在写测试,还有一个在浏览器里翻文档。3 个终端标签,3 套上下文,彼此不认识。

能力早就不是瓶颈了,秩序才是。

所以这篇不打算夸它多强。我把它当成一张工位图来拆:谁坐哪、活怎么排队、话怎么传、出事谁负责、哪里会疼。每一节都按四步走——先说它是什么,再说为什么需要,然后看怎么实现,最后算一笔值不值的账。

一、Agent 不缺能力,缺的是一张固定工位

先给定义。OpenBot 给每个 Agent 分配一个长期存在的「工位」:一个工作目录、一段能持续下去的会话、一条属于自己的任务队列。

它用 TypeScript 写成,代码在 nightly-labs/openbot,产品页是 openbot.run。

为什么要工位?因为终端天生不保存身份。

在终端里,一个 Agent 的上下文活在那个进程里。进程一关,它做过什么、动过哪些文件、还欠哪一步,全凭你脑子里那本账。3 个 Agent 并行的时候,这本账很快就记不住了。

更麻烦的是串味。同一个目录里起两个 Agent,它们看到同一批文件,改同一份配置,最后谁覆盖了谁说不清。你回头翻记录,只看到两份不一样的结果,中间发生了什么,没有痕迹。

OpenBot 的做法,是把「身份」和「现场」一起固化下来。

进程层面分两层:Electron 主进程管窗口、文件、子进程和系统权限;SolidJS 渲染层只负责界面。Agent 的子进程挂在主进程下面,界面怎么刷新都不影响它们继续跑。

数据层面把这些状态写进本地 SQLite,文件名是 openbot.db。写的是命令日志,读的是投影。

这两个词值得停下来。「命令日志」是只追加的记录,谁在什么时候要求谁做什么,按顺序落盘。「读投影」是把日志折叠成当前状态,界面查的是这一份。

分开的好处是崩溃后能重放,坏处是要维护两边一致。日志写成功、投影没更新,界面就会显示一个旧状态。这类问题不难修,但排查时容易先怀疑错地方——看到界面卡在旧进度,先想投影,别急着说 Agent 卡死了。

工位还带来一个隐性收益:可中断。

人在中途离开是常态。接个电话、开个会、下班。工位把「离开」这件事变得没事——Agent 继续跑,队列继续排,回来接着看。终端里做不到这一点,标签一关,或者笔记本一合,回来就得重开一轮。

代价也在这里:它得常驻。主进程活着、SQLite 在写、Agent 子进程在跑,风扇会比平时响一点,磁盘也会慢慢长。你在用一台 8G 内存的机器时,得算一算能常驻几个 Agent。

值不值?如果你只是偶尔问一句,装个桌面应用去换掉终端标签,成本大于收益。

如果你每天都在 3 个 Agent 之间来回切,这套工位的价值就很直接:上下文不丢,文件归属清楚,谁欠着活一眼能看到。

开一堆终端与一张工作台对比:OpenBot 把散落的终端窗口收敛为统一的多 Agent 布局

权限开到最大这件事,得单独说

README 顶部有一条警告,值得原样转述:这个项目现在是 development preview,Agent 目前以 danger-full-access 运行,审批策略是 never。

翻译成具体行为:首次启动有一个同意步骤,点完之后,Agent 可以读文件、改文件、执行命令、访问网络、控制内嵌浏览器,每一步都不再单独向你确认。

这个设计选择有它的道理。多 Agent 场景下,如果每个 Agent 每做一步都弹一次确认,你会被通知淹没,最后条件反射地点同意——那种「确认」比不确认更糟,因为它给你一种被保护的错觉。

但代价是真实的。一个 Agent 误解了任务,跑一条 rm 或者一次带 force 的 git 操作,它不会问你。这也是为什么 README 直接写了两条建议:只跑你信任的 Agent 和任务,做好备份。

我的读法是:把它当成一个「在你机器上有完整权限的远程同事」来配置,而不是当成一个沙箱应用。想清楚这一点,很多决策会自然做对——比如它会建议你单独开一个用户账号,或者干脆放在一台不装重要东西的机器上。

顺带说,这类工具的安全边界通常不在产品里,在使用者的部署方式里。同一个软件,跑在你日常工作的那台机器上,和跑在一台专门的机器上,风险差一个量级。

二、常驻:一个 Agent 一套工作区

工位落到磁盘上,就是一组目录。

OpenBot 给每个 Agent 一个独立工作区:~/OpenBot/Agents/<agent-id>。这里的 <agent-id> 是它在系统里的身份,不是昵称。

另外两个位置跟具体 Agent 无关:~/OpenBot/Shared 放跨 Agent 的公共素材,~/OpenBot/Downloads 放从外面拉下来的东西。

为什么要这么切?因为混在一起会出事。

把所有 Agent 都指向同一个项目目录,第一天看着方便,第三天就开始互相踩。A 改了一半的文件被 B 读到,B 根据半成品写了新代码,最后两条错误的路一起往前走。等到编译不过,你已经分不清是哪一步进的坑。

隔离之后,边界很清楚:谁的目录谁负责,要共享就走 Shared。

这里有个容易忽略的细节:目录路径里带着 agent-id,意味着工作区和身份是绑死的。

换一个 Agent 就是换一个目录,不会出现「同一个目录两个身份」的模糊状态。这对排错特别有用——看到一份奇怪的文件,先看它在谁的目录里,责任范围立刻收窄。

那 Shared 怎么用才不出事?

我的建议是把它当收发室,不当草稿纸。往里放的是「已经定稿、别人要用」的东西,比如一份 schema、一张图、一个构建产物。半成品放进去,等于把私货摆到了公共桌面,别人拿起来就用,错就跟着扩散。

会话也是一样。每个 Agent 的会话跟着它的工作区走,不是一个全局大对话。

全局大对话听起来省事,实际会把上下文搞脏。你在讨论前端样式的时候,后端 Agent 的报错信息也在这个窗口里,模型会被带偏,回答开始照顾两边,最后两边都不满意。

分开之后,切换 Agent 等于切换上下文,思路是断开的。这个「断」是好事——它逼你把要交接的东西说清楚,而不是指望对方从一大堆背景里自己捞。

再往下一层,是持久化。openbot.db 里存的不只是聊天记录,还有命令日志。

为什么不直接存最终状态?因为状态是推导出来的,日志才是事实。日志能回答「这条指令是什么时候、由谁、以什么参数下发的」,状态表回答不了。出问题时,你需要的是当时,不是此刻。

代价也要说清。日志只追加,文件只增不减,长期使用要有清理预期。投影要跟着日志更新,写路径多了,一致性就靠代码纪律兜。

还有一个实际影响:备份。想给工作区做快照,得连数据库一起带上,只拷 ~/OpenBot/Agents 是不够的。反过来,想彻底重来一次,删目录还不够,数据库里的身份和队列也得清。

如果你打算把它当日常工具,先想好三件事:磁盘留多少,日志留多久,备份带不带库。

OpenBot Agent 目录结构图:一个 Agent 一套工作区,Shared 与 Downloads 独立挂在根目录下

为什么「工位」不只是个比喻

先把这个词落实。传统多开终端时,每个 Agent 的处境是这样的:它在同一个工作目录里跑,能看见同一个仓库的所有文件,读写的是同一份 node_modules,两个 Agent 同时改一个文件时后写的覆盖前面的。

问题不是它们会不会打架,是你没法知道它们打了架。终端里的输出各滚各的,没有任何一个地方记录「谁在什么时候动了哪个文件」。

OpenBot 的工位概念对应三个具体的分离:

目录分离。 每个 Agent 有自己的工作目录,路径是 ~/OpenBot/Agents/<agent-id>。这个目录归属它自己,别的 Agent 默认看不到。要共享得进 ~/OpenBot/Shared。

会话分离。 每个 Agent 有自己的会话。你在 A 里聊的内容,不会出现在 B 的上下文里。

队列分离。 每个 Agent 有自己的消息队列,互不阻塞。A 卡在一个长任务上,B 该干什么还干什么。

三样加起来,才构成一个「工位」。少了任何一样,多 Agent 就会退化成「多个窗口」。

这也是为什么它敢叫自己「工作台」而不是「启动器」。启动器管的是把进程拉起来,工作台管的是把一群进程的边界划清楚。

三、接进来:一个入口接住各种 Agent

工位有了,下一个问题是:谁的工位?OpenBot 不自己造模型,它做的是接入。

接入分成两条路。第一条是原生集成:本地 Codex App Server 和 Claude 那一侧。

Codex 走的是 stdio 上的 JSONL。简单说,主进程起一个子进程,两边用标准输入输出逐行收发 JSON,一行一条消息。这种方式不占端口,不需要鉴权握手,进程死了通道就断,状态很好判断。

Claude 那侧接的是 Claude Agent SDK,消息以 stream JSON 的形式回来。流式的好处是过程可见——工具调用的中间态能显示出来,而不是等一个最终答案。你能看到它先读了哪个文件,再决定改哪里。

第二条路是 ACP,全称 Agent Client Protocol,一套让客户端和 Agent 对话的通用协议。

在 OpenBot 里,走 ACP 的客户端共用同一个适配层,启动命令由各家自己给:

  • Grok CLI
  • OpenCode
  • Gemini,通过 Antigravity 的 ACP server,路径从环境变量 OPENBOT_ANTIGRAVITY_PATH 读
  • Cursor CLI,启动命令是 cursor-agent acp
  • Cline CLI,启动命令是 cline --acp

除了这两条,还有两个兜底口子:自定义的 OpenAI 兼容端点,以及自定义 ACP agent。本机装了 Ollama 或 LM Studio,OpenBot 会探测到。

为什么要这么多入口?因为不同活适合不同 Agent。

长上下文读代码,和快速改一个配置,是两种任务。有人习惯用 Cursor 那套,有人手里只有 Cline。如果工作台只认一家,其余的人就得在终端和桌面之间来回跳,工位的意义就没了。

协议统一之后,界面只有一套:任务怎么发、队列怎么排、文件怎么传,都走同一套逻辑。差异被压到适配层里。

这里要说清一个边界:接入不等于能力对等。

ACP 是传输协议,它规定消息怎么走,不规定 Agent 能干到什么程度。同一个「读文件」动作,不同 CLI 的实现深度、权限模型、上下文窗口都不一样。工作台能保证的是流程一致,不能保证产出质量一致。

实测里最容易踩的坑,是路径。Antigravity 靠环境变量定位,环境变量没设,表现是连不上而不是报错清晰。这类问题先查环境,再怀疑协议。

另一个坑是启动方式。cursor-agent acp 和 cline --acp 都是把 CLI 拉进 ACP 模式,你手动在终端敲一次能进,工作台里进不去,多半是 PATH 不同——图形界面启动的进程,拿不到你 shell 里的那套环境。

解决办法很土但有效:把 CLI 装在图形环境也能找到的位置,或者在配置里写绝对路径。别指望工作台替你去 source 你的 .zshrc。

还有一个坑是「接上了但状态不对」。

免登录的本地服务、需要 token 的远端服务、需要先跑一次授权的 CLI,这三种的失败表现不一样。第一种是端口不通,第二种是 401,第三种最麻烦——连接成功,但每个动作都返回权限错误。看到权限错误先别改协议配置,去 CLI 里手动授权一次。

值不值?多入口的代价是配置面变宽,每个 Provider 都有自己的脾气。

但对「多个 Agent 常驻」这个前提来说,这是必须付的钱。只支持一家的工作台,本质上还是一个加壳的客户端——你把终端换成了窗口,别的东西都没变。

OpenBot 接入层示意:Codex、Claude 原生集成与 Grok、OpenCode、Gemini、Cursor、Cline 的 ACP 客户端统一汇入同一张桌面

四、消息队列:FIFO、可暂停、崩溃后还在

一个 Agent 同时被 3 个人催,会发生什么?在终端里,答案是三份需求混成一段乱序文本。

OpenBot 用队列回答这个问题。每个 Agent 有一条自己的消息队列,按 FIFO 排队——先到先做,后来的等着。

FIFO 这个词很朴素,但它解决了一个真问题:顺序可预期。

不排队的时候,你补一句「顺便把测试也跑了」,模型可能把它当成本轮的补充要求,也可能当成下一轮的。排队之后,「顺便」就是一条新任务,位置明确,落在哪一格你能看见。

队列的三个动作是这套机制的核心:

  • 暂停:这一条先别动
  • 恢复:接着来
  • 取消:不用做了

暂停和取消的区别在于语义。暂停是「等一等,我可能还要」,取消是「这件事不存在了」。取消之后,队列后面的任务会往前顶,不会留一个空洞等它。

为什么需要暂停?因为 Agent 干活的时候,人常常会中途改主意。

比如它在批量重命名文件,你看到第三个名字不对,想先说清楚规则再放行。没有暂停,你要么打断(它可能停在半路),要么等它跑完再回滚(代价更大)。

有了暂停,你可以把它摁住,补一条说明,再放开。它不会跳步,也不会把中间态当成品。

更硬的一层是崩溃安全。

队列是持久化的,写进本地存储。程序被强杀、机器断电,重开之后队列还在,未完成的任务不会凭空消失。这个特性听起来像基础设施的常规要求,但对 Agent 工作台很关键——Agent 干的活往往有副作用,改文件、发请求、跑构建,丢了队列就等于丢了「还剩什么没干」的账。

这里的取舍值得讲透。

持久化队列要付出写盘成本,每条消息落盘一次,高频小任务会有额外开销。它也带来一个语义问题:重启后那条「做到一半」的任务算什么?

OpenBot 的选择是把未完成的任务留在队列里,由人来决定继续还是取消。这是偏保守的设计——它不替你做幂等判断,也不假装知道副作用做到哪一步。

这个保守是对的。让程序猜「这条重跑会不会重复提交」,猜错的代价比多问一句大得多。宁可让人花 10 秒确认,也不要让程序花 1 小时补数据。

坑在哪?在「取消」的边界。

取消是取消排队中的任务,不是撤销已经发生的动作。文件已经改了,请求已经发了,取消只保证后面的不再执行。把它当回滚用,一定失望。

所以我的用法是:破坏性动作前面放一条暂停点。先让队列停住,确认清楚,再放行。这个习惯跟队列本身无关,但它是队列能不能用好的分水岭。

另一个坑是队列的长度感知。

队列排了 8 条还是 2 条,界面上要能看清。排太长了,任务之间会互相影响——前面那条改了文件,后面那条的假设就过时了。我一般控制在 3 条以内,做不完就先停,重新想一遍顺序。

OpenBot 消息队列状态机:FIFO 先进先出,支持暂停、恢复、取消,未完成任务写盘后崩溃也能恢复

队列为什么需要「崩溃后还在」

这一点容易被当成实现细节跳过,但它是常驻 Agent 能不能用的分水岭。

先想一个场景:你给 Agent 排了五个任务,它跑到第三个,你合上笔记本。半小时后打开,如果队列只在内存里,剩下两个任务没了,而且它不会告诉你它们没了——它只是安静地停在那里,等你以为它还在跑。

更隐蔽的情况是进程崩了但你没注意。Agent 的面板还开着,状态显示「空闲」,你以为活干完了,实际上队列里的东西在崩溃那一刻全丢了。

所以「队列落盘」不是一个性能优化,是正确性问题。它保证的是:你说过的话不会因为一次意外消失。

对应的另一面是恢复语义。落盘之后,重启时它得知道每个任务处于什么状态——没开始的可以重跑,跑到一半的不能简单重跑(可能产生了副作用),已经完成的不能重复执行。这套状态机如果设计得草,落盘反而会带来重复执行。

从 README 的描述看,它做的是 FIFO 队列加崩溃安全的持久化,并且支持暂停、恢复、取消。这三个动作配合落盘,实际给出了「可控」这件事的最小集合:你想停能停、想继续能继续、想撤能撤。

这也解释了为什么它把队列做成每个 Agent 独立,而不是全局一个。全局队列在某个 Agent 卡住时会堵住所有人;独立队列各堵各的,你最多损失一个工位的产出。

五、递东西:Agent 之间怎么传消息和文件

队列解决的是「人对 Agent」,还差一半:「Agent 对 Agent」。

真实项目里,一个任务经常要跨 Agent。A 在写接口,B 在写调用方,中间那份类型定义归谁写?在终端方案里,答案是复制粘贴——人当路由器。

OpenBot 把这层搬进了工作台。Agent 之间可以直接发消息,也可以传文件。

传文件的落点在 ~/OpenBot/Shared/Transfers。往这个目录写文件时,会有 1 个清单文件 .openbot-transfer.json 记录这次传输,内容是所有权、接收方、大小和 SHA-256。

为什么要搞一份清单,而不是直接放到共享目录?

因为共享目录只解决「放得进去」,不解决「算谁的」。

没有清单的时候,一个文件出现在共享区,谁放的、给谁的、是不是传了一半,全靠猜。SHA-256 在这里是干活的:它让接收方能验证拿到的东西和发出的一致,传输被截断或者被改写,一算就知道。

「所有权」这一项更微妙。它意味着文件在系统里有一个明确的归属者,不是一堆无主文件堆在共享盘上。

这在排错时的差别很大。假设下游拿到一份旧 schema,你得先判断:是发送方发错了版本,还是接收方缓存了旧的?有所有权和摘要,就能把范围切到一侧,不用两边一起查。

具体到用法,我建议把传输当「交接单」,不当「同步盘」。

一张交接单要有三样:对象是什么(文件名)、谁给的(所有权)、对不对(摘要)。这三样齐了,接收方可以自己完成校验,不需要再开一个对话问「你这个是最新的吗」。

而同步盘的心智是「文件会自己出现」,两边都不用管过程。这种心智在多人协作里很舒服,在多 Agent 里很危险——Agent 不会主动去看共享目录有没有更新,它只在被要求的时候才读。

所以流程要显式:A 写完,发一条消息给 B,说清文件名和用途;B 收到消息,去 Transfers 取,校验摘要,再用。

三步都不能省。省掉消息,B 不知道有东西;省掉校验,B 可能用一份坏文件;省掉用途说明,B 会猜错该怎么用。

消息本身也是这个道理。

Agent 之间的消息不是闲聊,它应该带着结构:要什么、给什么、什么时候要。写成一坨自然语言,接收方得先理解再拆解,多一层损耗就多一层错。

坑在这里:传递会放大错误。

一个 Agent 判断错了字段类型,传给下一个 Agent,下一个基于错误前提写了实现,再传给第三个。等到编译报错,错误已经在 3 个工位之间转了一圈,你可能要回翻 3 段记录才能定位。

传递链越长,越需要在每一跳设检查点。我的做法是让每次传递都附一条「可验证的断言」,比如「这个接口返回 3 个字段」,接收方第一步就是验证这句话,不成立就退回去,不让错误往下走。

还有一层是隐私边界。

本机跑不等于没有外传。Agent 调用远端模型的时候,你递给它的文件内容会跟着请求出去。传文件之前想一下:这份东西能不能出本机?

工作台能管的是流程,管不了判断。判断在你这边。

OpenBot 文件传输流程:发送方写入 Transfers 目录,清单记录所有权、收件人、大小与 SHA-256,接收方校验后使用

传输带元数据这件事,为什么值得

~/OpenBot/Shared/Transfers 这个目录里,每次传输都带一个 .openbot-transfer.json,里面写归属、收件人、大小、SHA-256。

四个字段,各自解决一个问题。

归属 回答「谁生成的」。多 Agent 场景下,一份产物出问题时第一个要问的就是这个。

收件人 回答「给谁的」。同一份文件可能发给多个 Agent,也可能只发给某一个。这个字段也决定了删除 Agent 时的清理范围——README 说得很清楚:删一个 Agent 会删掉它的工作区、它生成的附件、以及「只发给它」的传输;如果还有别的 Agent 在用同一条消息,传输保留。

这条清理规则是我认为设计得比较克制的地方。很多工具删除时的默认行为是「连带清理」,看起来干净,实际会误删别人还在用的东西。

大小和 SHA-256 回答「有没有被改过」。Agent 之间传文件不像 HTTP 有内容协商,传过去的东西可能被中间某个环节动过。有了哈希,接收方可以验。

四个字段加起来,把「文件传输」从「扔过去」变成了「有凭据的交付」。这个差别在调试时特别明显——出问题时你知道该问谁、该验什么。

六、共享频道:一个任务只能有一个 owner

多个 Agent 看同一件事,最容易出的问题是「都以为别人在做」。

OpenBot 的答案是一个共享桌面频道:同一个任务,只有一个 owner。

owner 之外的人想参与,得走显式委派。委派是一个动作,不是一句「你也看看」。它有明确的指向:从谁到谁,做哪一段。

频道上的五个操作把权责说清楚了:

  • Stop:停下
  • Resume:接着来
  • Reassign:换人
  • Archive:归档
  • Restore:恢复

这五个词选得挺讲究。Stop 和 Archive 分开,是因为「暂停」和「收工」是两件事。停下的任务还在场,归档的任务退出视野但不销毁。

Reassign 单独存在,说明换 owner 是常规操作,不是异常处理。

为什么要这么设计?因为并行协作里,最贵的成本不是算力,是「谁在做什么」的不确定性。

人多的团队靠会议同步。Agent 没有会议,只能靠状态。状态不显式,就会退化成互相猜测:A 觉得 B 在改,B 觉得 A 已经改完,两边都不动,任务静默停住。

单一 owner 把这个模糊地带砍掉了。任何时刻,一个任务有一个负责人,其他人要么旁观,要么被委派。

这条规则听起来限制协作,实际是让协作可算。

我在实际用的时候,会把它当成「值日表」。一个任务开出来,第一件事是认领,认领之后就写清范围。范围写不清,后面全是扯皮——不是 Agent 扯皮,是你自己在两份不一致的产出之间做仲裁。

Archive 和 Restore 这一对,解决的是「历史怎么留」。

归档不等于删除。任务从当前视野里退出去,记录还在,需要的时候能恢复。这对长项目很重要:3 周前为什么改了那个配置,答案可能就在一条已归档的任务里。

坑在哪?在委派的边界。

委派一段活,如果只说了「你把这部分做了」,没说清输入和验收标准,接收方会自己补全假设。补全的假设和你的预期不一致,返工就是必然。

我的写法是三句话:给什么、要什么、怎么算完。第三句最容易被省,也最容易被反咬。

另一个坑是频道里的信息密度。

频道是所有 Agent 都能看到的地方,适合放「公共事实」:约定、接口、当前 owner。不适合放过程性思考——那些东西会污染其他人的上下文,让它们把别人的草稿当成前提。

所以我把频道当公告板,不当聊天室。公告板上只贴已经确定的东西。

还有一层是人的位置。频道里人也是参与者,可以停、可以换人。这意味着责任最终还在人这边——Agent 不会替你决定优先级,它只会按你说的做。

Reassign 用得多,往往说明前面的范围没划清。这是个信号,值得回头看。

OpenBot 共享频道示意:一个任务一个 owner,支持 Stop、Resume、Reassign、Archive、Restore 五个操作

单一 owner 到底防什么

「一个任务只能有一个 owner」这条规则听起来像常识,但它是多 Agent 协作里最容易出事的地方。

出事的方式不是两个 Agent 抢着干,而是两个 Agent 都以为对方在干。你 @ 了 A 和 B,两个都看到了,A 想「B 会处理」,B 想「A 会处理」,任务静静躺着,直到你来问进度。

显式委派解决的正是这个。它要求一个任务有且只有一个 owner,其他参与者要么不接,要么得先把 owner 转过去。这样责任链是清晰的,出问题能追到人。

配套的四个控制也各有分工,值得分开看:

Stop 是暂停当前执行,任务还在,owner 还是那个人。用于「先别动了,我要看看」。

Resume 是从暂停点继续。它和 Stop 是一对,保证暂停不等于放弃。

Reassign 是换人。这才是真正处理「owner 干不了」的按钮——不删任务、不重开任务,只把责任转移。

Archive 是把任务收起来。它不同于删除,归档的任务还在,只是不在当前视图里。

Restore 是 Archive 的反向操作。

五个动作凑齐,一个任务才算能完整地走完「进行—暂停—换人—归档—恢复」这条生命周期的每一段。少一个,你就会在某个具体场景里卡住。比如没有 Reassign 时,owner 的 Agent 挂了,你只能删任务重开,前面的上下文全丢。

七、浏览器:Agent 的眼睛要留着

很多任务是「去网页上看一眼」。查文档、看控制台、打开一个预览页。终端 Agent 撞上这类活,通常只能求人。

OpenBot 里内嵌了一个持久浏览器。这里的「持久」有两个意思:窗口常驻,登录状态也跟着留下。

为什么要嵌入,不用本机已有的浏览器?

因为在外面开一个浏览器,Agent 看不到它。它能调工具打开页面,但读不到页面上渲染出来的东西——那些靠 JavaScript 拼出来的内容,抓页面源码是拿不到的。

嵌进工作台,情况就变了:页面渲染在 Agent 能读的地方,滚动、点击、取值都在同一个上下文里。

第二个原因是登录态。真实任务经常需要登录:看后台的一个数字,确认一条数据是否落库。每次都要人重新登录,Agent 的活就断在这里。

持久浏览器把会话留着,第一次登录由人完成,后面 Agent 用同一份状态。这个设计把「人的一次介入」和「Agent 的多次使用」分开了。

第三个原因是可见。界面就在旁边,Agent 在页面上做什么,你看得见。这个可见性对信任很关键——把一个会点按钮的 Agent 放到看不见的浏览器里,多数人不敢用。

再说 Computer Use,也就是让 Agent 直接操作图形界面。

它是可选的,不是默认开的。这个默认值选得对。

Computer Use 的原理是看屏幕、动鼠标。它能做的事很广,代价是每步都可能出错:点错了位置、识别错了按钮、把弹窗当成了正文。而且它的错误很难复现,同一串动作第二次跑可能就点偏了。

所以我的建议是把 Computer Use 当备选,不是主力。

有 API 的走 API,有命令行工具的走命令行,都没有了,再让人去点一次。Computer Use 只在这一串都走不通的时候才值得开。

那它什么时候值得开?我总结了两类。

一类是必须看渲染结果的活,比如判断一个样式有没有错位、一张图有没有加载出来。这类没法用接口验证。

另一类是低频的一次性操作,写脚本不划算,手动点又不放心。这种交给 Computer Use,省下的是写脚本的时间。

边界要说清:它不保证可重复。

同一个操作跑两次,结果可能不一样。所以凡是要跑很多次的活,别交给它;凡是出错代价高的活,也别交给它。它适合「做一次、看一眼、错了重来」的场景。

坑还有一个:权限。

浏览器能访问的网站,等于 Agent 能访问的数据。把工作浏览器和私人浏览器分开,是个务实的做法。工作台上那个窗口,只登工作账号。

登录态持久这件事,反过来也是风险。状态留着方便,也意味着别人用这台机器的时候,能直接进到那些页面。机器要不要锁屏,这是个现实问题。

值不值?如果你的任务里网页占比超过三成,内嵌浏览器省下的时间很实在。

如果只是偶尔查一次文档,用不着为它多开一层。

OpenBot 沙箱隔离示意:内嵌浏览器运行在独立沙箱会话中,碰不到本地文件与桌面接口

那个「清楚」的边界具体是什么

架构那一节要讲的不是分层本身,是这条边界为什么重要。

src/main、src/backend、src/preload、src/renderer 四个目录。前三层的职责是:主进程管生命周期与安全,后城管适配器、持久化、调度、传输、浏览器,preload 只暴露一个带类型的 window.openbot。

这个划分的关键在 preload。它是一道收窄的接口:渲染层能用什么,全由这个文件定义。渲染层不能直接 require,不能直接拿 Node API,不能直接访问文件系统。

Electron 项目里这条线容易糊。图省事直接用 nodeIntegration: true 的项目,渲染层等于一个完整的 Node 进程,一个 XSS 就等于本地代码执行。OpenBot 选的是一条更费事但更正确的路:所有能力都得在 preload 里显式声明,渲染层只能调声明过的东西。

再叠一层:内嵌浏览器用的是独立的沙箱会话,它访问不到 window.openbot,也访问不到管理的本地附件。这一条很重要——Agent 打开的网页和它自己的运行环境必须隔离,否则一个恶意页面就能顺着浏览器摸到 Agent 的文件。

边界划清楚的实际收益是「可审计」。你能一眼看出渲染层能做什么,因为清单在一个文件里。这比散落在几百个组件里的直接调用要好查得多。

八、架构:日志、投影与一条清楚的边界

前面七节讲的都是功能,最后说骨架。

OpenBot 分三层。

主进程是 Electron。它管窗口、文件系统、子进程、系统权限。所有「碰系统」的动作都经过它,渲染层不直接碰文件。

渲染层是 SolidJS。它只做界面,把状态画出来,把操作发下去。细粒度响应式的意思是改一个值只重画用到它的地方,不是整页刷新。

状态层是本地 SQLite,文件名前面提过,openbot.db。里面是命令日志加读投影。

再加一层在云上:账号 API 跑在 Cloudflare Workers。

这条边界最关键:什么在本机,什么在云上。

本机的是工作区、会话、队列、传输清单、日志、投影。云上的只有账号相关的那一小段。所以断网时,本地协作照常跑;登录、身份这类事情才需要网络。

为什么要这么切?因为 Agent 处理的东西,多数不适合上传。

代码、配置、日志、截图,这些内容一旦离开本机就没有回头路。把功能尽量压在本机,是 local-first 的实际含义。它不是一句「我们很注重隐私」,而是把数据流画出来之后,绝大部分箭头都在本机内部。

分层带来的另一个好处是崩溃可控。

渲染层崩了,重开窗口,主进程和子进程还在,队列还排着。主进程崩了,队列在库里,重开之后能接上。这个「分层降级」的能力,比任何单点的稳定性优化都实在。

代价是复杂度。

三层之间要有协议,要有错误传递,要有状态同步。一个操作从点击到落盘,经过渲染层、主进程、子进程,任何一环慢或者错,你看到的表现都是「点了没反应」。排查时得按层往下走:先看界面状态变没变,再看主进程有没有收到,最后看子进程是不是活着。

日志加投影这套结构,也是同样的取舍。

日志是事实,投影是视图。查询走投影很快,重建走日志很稳。代价是两边要对齐,写路径一多,漏更新就是 bug。

我的经验是:看界面上的进度卡住,先怀疑投影;看 Agent 完全不动,先怀疑子进程;看子进程活着但没输出,先怀疑 Provider 连接。三个方向,对应三层,别混着猜。

值不值?这套架构对一个桌面应用来说是偏重的。

单独的 Electron 加一个渲染层就够了,加 SQLite 日志和云上账号 API,都是为「多 Agent 常驻」这个前提付的成本——没有持久化,队列活不下来;没有账号,多设备就无从谈起。

反过来说,如果你要的只是一个本地对话框,这套东西全是负担。

这就是我在开头说的:它的定位不是「更好的终端」,是「多个 Agent 的工位系统」。判断它值不值,先判断你有没有那么多 Agent。

OpenBot 三层架构图:渲染层、IPC 桥、主进程(适配器、调度、传输、浏览器)与 SQLite 命令日志加读投影,云上只留账号 API

结语

回到最开始那句:1 个 Agent 能干一段活,3 个 Agent 一起干就容易乱。

OpenBot 的答案不复杂。给每个 Agent 一个工位,给它一条队列,给它一套传东西的规矩,再给一个只能有一个 owner 的任务板。剩下的,交给人判断。

这套设计里有几处我觉得值得抄:

  • 顺序显式化。FIFO 不是技术炫耀,是让「先后」这件事有据可查。
  • 权责单一化。一个任务一个 owner,把模糊地带砍掉。
  • 边界写死。什么在本机、什么在云上,画得出来才守得住。
  • 分层降级。崩一层不至于全崩,这是长跑的必备。

也有几处要有心理准备:配置面变宽、日志会增长、多入口意味着多套脾气、Computer Use 不适合重复劳动。

它不是给所有人的。它是给那些已经在一个屏幕上开了 3 个 Agent,并且开始记不住谁在做什么的人的。

如果你正处在这个阶段,试试把终端换成工位。不是为了好看,是为了让「还剩什么没干」这件事,有一个地方能查到。

如果你已经在多 Agent 协作里踩过坑,也欢迎来云栈社区聊聊你的工位规划和队列安排。

参考

  1. OpenBot 仓库:https://github.com/nightly-labs/openbot — 源码、README 与协议说明,本文技术细节的主要来源
  2. OpenBot 官网:https://openbot.run/ — 产品定位与下载入口,含商业授权联系方式
  3. Agent Client Protocol:https://agentclientprotocol.com/ — ACP 的协议定义,理解多 CLI 接入的公共基础
  4. PolyForm Noncommercial 1.0.0:https://polyformproject.org/licenses/noncommercial/1.0.0/ — 非商业许可原文,商用前需确认授权范围



上一篇:二维码被遮三分之一为何还能扫?马赛克不可逆与AI乱画真相
下一篇:全球法律塞进一个 Git 仓库:查旧法条可精确到某年某月某日
您需要登录后才可以回帖 登录 | 立即注册

关闭

站长推荐上一条 /1 下一条

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-11-2 07:21 , Processed in 0.070055 second(s), 39 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表