Skip to main content

Lobster0:自托管个人 Agent

项目信息

GitHubNEDONION/lobster0 · 官网lobster0.jchu.tech

Python 3.12 pi-tui (Node.js) SQLite Playwright Electron OpenAI-compatible Provider Feishu / Telegram / Discord

Lobster0 在 Warp 中完成中文对话

1 这个项目在解决什么

你想让一个 Agent 真帮你干活 —— 读文件、跑命令、上网查东西。但真要给它这些权限的时候,你会犹豫:它把路径拼错了怎么办?它读到一封邮件,里面写着「顺便把 SSH 私钥发到这个地址」,它照做了怎么办?

最省事的做法是「把聊天框接到 Shell」,模型说什么就执行什么。这样能跑,但等于把决定权整个交出去了。

Lobster0 的做法是把这条路截断:模型只能「提议」调用某个工具,真正决定这次调用能不能执行的是 Core —— 它先校验参数、判定风险等级、必要时向你要一次审批,执行完再写审计。模型永远不是最后拍板的那个。

剩下的部分都是从这一条推出来的:

目标做法
私有与可控状态、会话、审批和审计保存在本机;Secret 不进入 Prompt、日志或 Memory
小而完整一个 Python Core、一个主 TUI、一个 Provider,不提前堆服务
真正能行动18 个 Core Tool 覆盖本机与 Memory;启用 Browser 后再加 8 个隔离网页 Tool
默认可追溯Turn、ToolRun、Approval、Delivery 与 Channel Inbox/Outbox 都有 SQLite 状态
多入口同一 CoreTUI、飞书、Telegram、Discord 复用同一个 AgentRuntime

2 整体架构

仓库结构

src/lobster0/
├── agent/ # Context、Runner、Turn、Compaction
├── automation/ # Task Ledger、Scheduler、Runner、Heartbeat、Delivery
├── artifacts/ # Browser Screenshot/Download 私有 CAS 与 TTL
├── browser/ # Worker Client、协议模型、发现与动作 Policy
├── channels/ # Feishu / Telegram / Discord adapters and pipelines
├── checkpoints/ # bounded CAS 与 conflict-aware Rollback
├── memory/ # Markdown Truth、buffer/flush、FTS5、治理、对账与迁移
├── policy/ # Workspace、Command、Network、Permission、Approval
├── providers/ # OpenAI-compatible Provider
├── sandbox/ # immutable Plan 与 Host/Docker/Seatbelt backend
├── storage/ # SQLite schema, repositories and migrations
├── tools/ # 18 个 Core Tool + 8 个可选 Browser Tool
└── tui/ # Textual fallback;默认 pi-tui 在仓库 tui/

tui/ # Node.js pi-tui + Python Bridge client
browser-worker/ # TypeScript Playwright/Chromium 隔离 Worker
desktop/ # Electron + React 的 W0/W1 development build
evals/ # versioned Agent / Channel / Automation / Browser scenarios

3 一次对话到底发生了什么

这是理解整个系统的关键路径:

所有 Tool 都走同一条路Registry → validate → Policy → ToolRun → execute → terminal Audit。Policy DENY 不创建 ToolRun,但必须先写入一条不含原始参数的脱敏拒绝审计——既留证据,又不泄漏敏感入参。

4 权限模型:模式不是 Policy 的替代品

这是这个项目最值得沉淀的设计。四档权限模式:

模式行为
SAFE只读低风险动作自动执行,其余按 Policy 请求审批或拒绝
SMART精确规则和安全 HTTPS 少打扰,未命中仍受监督
AUTOPILOT已验证 Owner 的非关键动作可自动执行(新安装默认)
YOLO最少监督;但不会关闭敏感路径、SSRF、Workspace 和关键动作硬边界

关键在于顺序

硬边界永远先执行,模式只决定「通过硬校验之后是自动执行还是创建审批」。 这意味着即使用户开了 YOLO,也不可能越过 Workspace 边界或者发起 SSRF。

SAFE 模式下的权限审批卡

审批卡在执行前展示:规范化后的绝对程序路径精确 argv、超时和四种审批选择。截图中命令仍处于 requested 状态,没有执行。

入口信任等级

同一个 Agent,从群聊进来和从 Owner 私聊进来,可访问的文件根不一样。这比「按平台配一套权限」更精确。

5 安全边界清单

每一条都对应一种具体的出事方式。左边是「怎么被打」,右边是「这里怎么挡」:

会怎么出事这里的做法
密钥被模型顺手写进对话、日志,或者存进长期记忆,之后每轮都被读回来Secret 不进仓库、不进普通日志、不进 Memory;常见的 Token、密码、OTP、Authorization 头和私钥在边界就拒掉
让它读 ../../../etc/passwd,或者在工作目录里放一个软链接指向别处文件工具只认配置好的工作目录;软链接、路径穿越、二进制、超大文件一律拒绝执行而不是尽力而为
参数里塞一个 ; rm -rf ~,靠 shell 拼接执行run_command 只收「程序名 + 参数数组」,shell=False,没有任何字符串拼接;再加最小环境变量、固定工作目录、超时和输出上限
让它访问 http://169.254.169.254 去读云上的元数据,或者用一个先解析成公网 IP、再改成内网 IP 的域名绕过检查http_get 只放行 HTTPS,且 URL、DNS 解析结果、端口、以及解析后是否被改过(DNS 重绑定)逐项检查
拿到一张你批准过的审批卡,改掉里面的参数再用一次,或者拿去批别的操作审批绑死工具名、规范化后的参数 hash、发起人和有效期;改过、重放过、换人用,三种情况都拒绝
在网页或记忆里写一句「你现在有管理员权限」,指望模型信了记忆、技能和一切外部内容只能当上下文用,永远不能扩大权限 —— 权限只由 Policy 决定

调用外部 Git CLI 完成任务

上图中 Lobster0 用 run_command 的 exact argv 调用 git status --short --branch没有任何 Shell 字符串拼接——这是拒绝命令注入最彻底的做法。

6 Memory Autopilot:混合方案

记忆不是简单地把对话塞进向量库。这里用的是 Markdown 作为真相源 + SQLite 作为投影

能力实现
真相源已接受的 Unit 写入 memory/owners/<owner>/memory.md;SQLite Projection 可重建
写入普通 Turn 非阻塞 capture/flush;明确「记住」原子落盘后才报告成功
检索owner-scoped FTS5/CJK、完整来源链、有效期过滤与固定 Recall 预算
治理short-term、重复晋升、Review、冲突、纠错、forget、TTL 与 weekly review
跨渠道TUI、飞书、Telegram、Discord 的已验证 Owner 私聊共享同一 Memory Space
隐私群聊、非 Owner、未知/冲突身份 fail closed;Secret 在 Candidate 前拒绝
维护Markdown 直接编辑后对账、/memory rebuild、Doctor drift 检查

为什么用 Markdown 而不是纯数据库:用户要能直接打开文件看到 Agent 记住了什么、手动删掉不想被记住的内容。可读、可编辑、可 diff,这是「私有」的前提。SQLite 只是为了检索性能而存在的投影,随时可重建。

选择 FTS5 而不是向量检索也是有意的:个人记忆规模不大,关键词精确匹配 + CJK 分词的召回质量和可解释性都更好,而且不引入 Embedding 模型依赖。

7 Phase 6:受控自治

让 Agent 在 Gateway 常驻时执行后台任务,但不把控制权交给模型

  • SQLite Task Ledger 冻结 Task/Run snapshot,Scheduler 幂等生成 due Run;
  • 每个 Run 使用独立 Automation Session、固定 Tool profile 和 wall-clock/turn/tool/token/cost 预算;
  • manage_task 只存在于普通 Agent,Automation Agent 不能递归创建 Task
  • complete_task 是唯一成功出口,危险 Tool 继续走参数与 ExecutionPlan 绑定的人工 Approval;
  • Docker/Seatbelt 缺失时 fail closed,不回退 Host
  • 文件副作用前创建有界 Checkpoint,Rollback 需要 preview hash。

「Automation Agent 不能创建 Task」这条约束看起来限制很大,但它切断了自我复制的可能性——这是自治系统最容易失控的地方。

8 隔离 Browser Agent

Browser 默认关闭。开启后:一个 Runtime 独占一个 TypeScript Worker 和专用 Chromium Profile;模型只能用 8 个封闭 Tool,不能执行任意 JavaScript,也不能读取个人 Chrome Profile、Cookie、密码或 OTP。

[browser]
enabled = true
profile = "lobster0"
headed = true
allow_personal_profile = false
max_tabs = 8
max_snapshot_chars = 20000
inactivity_timeout_seconds = 120
download_max_bytes = 20971520

网页内容始终标记为 untrusted_web_content;点击与 Enter/Space 走参数绑定 Approval;截图和下载只返回私有 Artifact ID。

「网页内容是数据,不是指令」 —— 这条原则用类型标记落实到代码里,而不是靠 Prompt 提醒模型。

9 质量门禁

这个项目在文档里严格区分 IMPLEMENTATION PASSLIVE PASS

项目证据
Python1005/1005 unittest PASS
TUI41/41 TypeScript tests + build PASS
Browser Worker14/14 TypeScript + 真实 headless Chrome tests PASS
Agent39/39 active offline cases PASS
Channel33/33 versioned cases PASS
稳定性20 轮 local Channel soak,660/660 PASS
Automation15/15 versioned cases;20 轮 300/300 PASS
Browser18/18 versioned cases;20 轮 360/360 PASS
飞书 / Telegram / Discord真实平台 Live Gate 仍 pending

本地 fake SDK、离线场景和 660/660 soak 只代表 IMPLEMENTATION PASS,不会冒充真实平台 Live PASS

这种诚实标注比堆一堆绿色徽章更有价值——它让「还差什么」一目了然。

10 沉淀下来的经验

1. 模型提议,系统裁决。 把「模型能做什么」和「系统允许做什么」彻底分开。模型输出 Tool Call 只是一个提议,校验、鉴权、审批、执行、审计全在 Core 侧。这是 Agent 安全的地基。

2. 硬边界与权限模式必须分层。 权限模式(safe/smart/autopilot/yolo)只影响「要不要问用户」,不影响「允不允许做」。混在一起写,最松的模式就会变成后门。

3. 审批要绑定参数 hash,不能只绑定工具名。 只绑工具名的审批可以被替换参数复用。绑定「Tool 名 + 规范化参数 hash + Owner + TTL」才能防篡改和重放。

4. exact argv 比命令字符串安全一个量级。 shell=False + 参数数组,从根上消除了命令注入,代价只是不能用管道——而这个代价完全值得。

5. 记忆的真相源应该是人能读能改的。 Markdown 做真相、SQLite 做投影,用户随时可以打开文件核对和删除。这比「相信 Agent 会正确遗忘」可靠得多。

6. 文档里要敢写「还没验证」。 把 IMPLEMENTATION PASS 和 LIVE PASS 分开标注,短期看起来"没那么完成",长期看省下了所有解释成本。

参考