AI 同事各有电脑

OpenBot 给每个 AI 同事一台独立电脑:浏览器、文件系统、终端全隔离。听起来很美好,但沙箱怎么搭、权限边界怎么收、任务中断怎么续——这篇拆 4 个最常踩的坑,附可直接 npm install 跑起来的代码。

源仓库: CopilotKit/OpenBot

项目是什么,为什么现在火

OpenBot 是 CopilotKit 开源的「AI 同事」框架,核心理念只有一句话:每个 agent 不再共享主机的浏览器和文件系统,而是被关进一台独立电脑——独立的 Chromium profile、独立的 workspace 目录、独立的 shell 会会话。8 月 22 日它冲上 GitHub Trending 主榜,⭐2079,背后是 agent 从「会聊天」走到「能干活」的最后一公里:谁来管环境、谁来收权限、谁来兜底崩溃。

问题一:为什么每个 agent 必须独占一台电脑?

现象:多 agent 共用一个 Playwright 实例时,Cookie / LocalStorage / 下载目录会互相串台,A agent 刚登录的 SaaS 账号被 B agent 直接顶掉。

根因:Chromium profile 不是线程安全的,登录态、历史记录、IndexedDB 都落在磁盘上的同一份 userDataDir,谁先启动谁说了算。

解法:每个 agent 用独立的 userDataDir,本质就是「一台电脑一个浏览器」。

import { chromium } from 'playwright';
import path from 'path';
import os from 'os';

async function spawnAgentBrowser(agentId) {
  const profileDir = path.join(os.tmpdir(), `openbot-${agentId}`);
  const ctx = await chromium.launchPersistentContext(profileDir, {
    headless: true,
    viewport: { width: 1280, height: 720 },
  });
  return ctx;
}

跑起来 npm i playwright && node bot.js,每个 agentId 会拿到一个完全干净的 Chromium,Cookie互不污染。

问题二:文件系统怎么隔离?

现象:agent 写文件到 /tmp,结果把别的 agent 的中间产物覆盖了,第二天 debug 时根本对不上哪一步出的错。

根因/tmp 是共享目录,POSIX 文件锁只对单进程有效,多 agent 同一时刻写同名文件就会撞车。

解法:每个 agent 独立 workspace 目录,最轻量做法就是一个 mkdir;要做硬隔离,可以挂 chroot / Docker / bubblewrap。下面给最轻量但够用的版本:

from pathlib import Path

def workspace(agent_id: str) -> Path:
    base = Path("/var/lib/openbot/workspaces")
    ws = base / agent_id
    (ws / "downloads").mkdir(parents=True, exist_ok=True)
    (ws / "uploads").mkdir(exist_ok=True)
    return ws

OpenBot 默认就这套结构,所以 README 里强调「不要把 workspaces/ 软链到 /tmp」。

问题三:任务跑到一半 agent 崩溃怎么办?

现象:长流程跑 30 分钟后 LLM 超时或者浏览器 OOM 挂掉,前功尽弃,重新跑又得重新登录、重新填表单。

根因:状态只活在内存里,没有 checkpoint,重启等于从零开始。

解法:周期性把任务状态、当前 URL、cookie 列表快照到磁盘,重启时读 checkpoint 续跑:

import json, time
from pathlib import Path

CKPT_DIR = Path("/var/lib/openbot/checkpoints")

def checkpoint(agent_id, state: dict):
    CKPT_DIR.mkdir(parents=True, exist_ok=True)
    p = CKPT_DIR / f"{agent_id}.json"
    p.write_text(json.dumps({"ts": time.time(), "state": state}))

def resume(agent_id):
    p = CKPT_DIR / f"{agent_id}.json"
    return json.loads(p.read_text())["state"] if p.exists() else None

社区里 r/ClaudeAI 上有人提「能不能把 checkpoint 也存进 git」,答案是可以但没必要——单机 JSON 足够,恢复速度比 git checkout 快一个数量级。

问题四:权限边界怎么收?

现象:agent 在 shell 里跑出 rm -rf,把宿主目录清空,第二天整个服务起不来。

根因:agent 进程默认继承宿主用户权限,LLM 一次幻觉就能把你送走。

解法:三道闸门——白名单命令、路径前缀、syscall filter(seccomp / bubblewrap)。最常用的是前两道,写起来也最快:

const ALLOW = new Set(["ls", "cat", "grep", "curl", "npm", "git"]);

function safeExec(cmd, args, cwd) {
  if (!ALLOW.has(cmd)) throw new Error(`blocked: ${cmd}`);
  if (!cwd.startsWith("/var/lib/openbot/workspaces/")) {
    throw new Error(`bad cwd: ${cwd}`);
  }
  return { cmd, args, cwd };
}

OpenBot 在 issue #142 里把这套抽象成 Capability 接口,扩展时只要注册新的白名单项,不用改 executor 本身。

Sources

  • GitHub Trending 2026-08-22 主榜
  • CopilotKit/OpenBot README 与 examples/ 目录
  • r/ClaudeAI 「AI coworker sandbox」讨论串
  • HN 「Show HN: OpenBot」评论