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。

它和「把聊天框接到 Shell」的本质区别在于:模型只提出 Tool Call,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 和私钥在边界拒绝。
  • 文件 Tool 只能访问配置的 Workspace/允许根;symlink、路径逃逸、二进制和超限内容 fail closed。
  • run_command 只接受程序与参数数组,shell=False,最小环境、固定 cwd、超时和输出上限。
  • http_get 只允许经过 URL、DNS、端口和重绑定检查的 HTTPS 目标。
  • Approval 绑定 Tool 名、规范化参数 hash、Owner、TTL 和可用决策;篡改、重放和跨 Owner 使用都会拒绝。
  • Memory、Skill 和外部内容只能提供上下文,不能扩大 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 分开标注,短期看起来"没那么完成",长期看省下了所有解释成本。

参考