Skip to content

Latest commit

 

History

History
410 lines (300 loc) · 37.4 KB

File metadata and controls

410 lines (300 loc) · 37.4 KB

Bubbles 产品规格说明(SPEC)

本文档地位:本文档是 Bubbles 产品层的唯一事实源(Single Source of Truth, SSoT)

  • 所有"产品定义、用户体验、能力边界、行为契约"层面的变更,必须先修改本文档,再落地代码。
  • README.md / SECURITY.md / 命令帮助文本 / 配置 schema 注释 / Skills 文档与本文档冲突时,以本文档为准,并提交修复 PR。
  • 实现细节(数据结构、类名、行号、依赖版本)不进本文档;那是代码层的事实,由代码本身陈述。本文档只关心"产品对用户承诺了什么"。
  • 任何 PR 若引入用户可见的新能力 / 改了交互契约 / 改了配置字段语义 / 改了支持的渠道或 provider 列表,必须包含本文档的同步修改,否则视为不完整。

1. 产品定位

Bubbles 是一个本地优先、以 coding agent 为内核的个人 AI 助手框架。

它不是一个聊天机器人,而是一个长期运行、可被多渠道触达、有持久记忆与工作空间、能够主动执行任务的个人助理。一句话区分:

不是
一个 ChatBot 模板 一个把"个人助理"做成本地服务的框架
一个只能问答的 LLM 包装 一个能读写文件、跑命令、上网、定时唤醒、跨渠道发消息的执行体
一个云上 SaaS 一个跑在用户自己机器或 NAS / VPS 上的进程
一个产品化的终端用户应用 一个面向"懂技术的个人用户"的可编程框架

1.1 核心价值主张

  1. 统一入口:在用户已有的通讯工具里(Telegram / 飞书 / 微信 / 邮箱 ...),用同一个助手,跨渠道共享上下文。
  2. 可执行:助手不是只会回复文字,它有工具——读写文件、跑 shell、抓网页、搜索、定时、跨会话派生子 agent。
  3. 可记忆:每个会话是一个独立的工作目录,有自己的人设(SOUL)、长期记忆(MEMORY)、文件、技能(Skills)。
  4. 本地与隐私:所有会话数据存放在用户本地(~/.bubbles/),用户自己掌握 LLM API Key,没有强制云依赖。
  5. 可被定时唤醒:助手不只是被动响应,可以基于 cron 在指定时间主动唤起一次会话并把结果投递回某个渠道。

1.2 目标用户

懂技术、对自己时间和工作流有控制欲、愿意为"更好的助手"付出本地配置成本的个人用户:开发者、研究者、知识工作者、创作者。

非目标用户:纯小白用户、企业团队协作场景、需要 SLA 的客服机器人。


2. 设计原则

以下原则用于裁决产品决策上的分歧。当出现"该不该加这个功能"、"该不该这样做"的争论时,按顺序对照:

  1. Coding agent first:助手的推理与执行能力,由当前 SOTA 的 coding agent 范式提供(tool calling 循环 + 文件/Shell/Web 工具)。任何降级到"硬编码工作流"的方案需要强理由。
  2. 本地优先:默认所有状态、密钥、消息历史在用户本地。任何"必须上云"的功能要么不做,要么提供等价的本地路径。
  3. 渠道是入口,不是产品:渠道(Telegram/飞书/微信...)只负责把消息搬进搬出,不承载产品逻辑。同一个 agent 内核必须能挂接任意渠道。
  4. 会话即工作空间:每个会话是一个目录,不是一段聊天记录。这意味着助手有"地方"放它的笔记、产物、技能。
  5. 配置即契约:用户可见的产品行为由 ~/.bubbles/config.json 决定。新增一个用户可感知的开关 = 新增一个配置字段 + 修改本文档。
  6. 优雅胜过完备:宁可少支持一个渠道,也不要把渠道实现做得乱。宁可少一个 provider,也不要把 provider 路由做成猜谜。
  7. Alpha 阶段允许破坏性变更:版本未到 1.0 之前,可以为了长期正确放弃短期兼容。改名、删命令、调配置 schema 都允许,但必须同步改本文档与 README。

3. 用户场景

以下是 Bubbles 必须支撑的核心场景。任何与这些场景冲突的设计需要重新评估:

场景 描述
CLI 私人副驾 在终端 bubbles agent,进入交互模式,绑定一个会话,聊任何事,让助手读写当前项目文件。
多渠道触达 启动 bubbles gateway,从 Telegram / 飞书 / 微信任何一处发消息给助手,得到响应。
群里 @ 我才回 在群里只有被 @ 时才回应,其他时候安静;私聊默认回应。
定时主动 设置一个 cron 任务:"每天早上 9 点总结今日日程并发到我 Telegram"。
跨渠道一致 在 Telegram 跟助手聊到一半,切到飞书继续聊(取决于 session_mode 配置)。
Skill 复用 让助手做一份 PPT / PDF / Excel / 海报,调用预置 Skill 完成,不用用户每次现场教。
私人工作空间 助手在自己的会话目录里写代码、存草稿、维护 MEMORY,下次回来还在。
本地大模型 用户用 Ollama / vLLM / 任意 OpenAI 兼容端点替代云 API。

4. 产品形态

Bubbles 以**单一二进制 CLI(bubbles)**对外暴露能力,没有 GUI、没有 Web 控制台。

4.1 运行模式

模式 命令 说明
交互式 CLI bubbles agent 终端聊天,最贴近开发自用场景,支持 /session /config /compact 等斜杠命令。
单次调用 bubbles agent -m "…" -s <session> 适合脚本化与定时调用,发完拿到响应即退出。
网关常驻 bubbles gateway 启动所有已 enable 的渠道 + cron 调度器,长时间运行,是"个人助理"的主形态。
运维命令 bubbles status / channels status / cron … / provider login … / sync-skills 配置、登录、同步、巡检。

bubbles onboard 是唯一的初始化命令,幂等可重入;任何"重置"操作不会自动删除已有会话数据。

bubbles status 的产品契约:展示装机级基线(默认 model、已配置 provider、启用渠道)。会话维度只在该会话与默认不同时才出现——例如 model override、绑定了 cron job、开了心跳。沿用默认的会话在 status 里保持安静,避免把命令撑成会话审计。一直没有 session 维度的子命令;所有会话级细节通过此差异视图与交互模式的 /config / /prompt 暴露。

4.2 部署形态

  • 本地直跑pip install -e . + bubbles onboard + bubbles gateway
  • Docker:基于 Dockerfile / docker-compose.yml 构建镜像,挂载 ~/.bubbles 作为持久卷。
  • 网桥进程:WhatsApp 渠道依赖一个独立的 Node.js 网桥(项目内 bridge/ 目录),由 bubbles channels login 引导构建与登录。其他渠道全部在 Python 主进程内。

5. 核心能力

5.1 Agent 内核

契约

  • 助手以"tool calling 迭代循环"方式工作:LLM 决定调哪个工具 → 工具执行 → 结果回填 → LLM 继续,直到产出最终回复或达到迭代上限。
  • 用户在 agents.defaults 配置默认模型、温度、上下文上限、最大迭代次数、记忆窗口、API 重试次数与超时、并发上限。
  • 历史超过上下文限制时,助手会自动 compact(摘要旧消息、保留最近一段原文)。保留窗口按 token 预算选取而非固定条数:长工具输出可能只留两三轮,短对话能留几十轮。用户也可在交互模式下手动 /compact
  • 多会话并发:不同会话可同时处理,并发上限默认按 CPU 核数推算(可配)。同一个会话内部始终串行——同一份历史不会被两个回合同时写。
  • 回合进行中仍可插话:助手正在跑工具时收到的新消息,会在它的下一次工具调用之前插入当前回合,而不是排队等整个回合结束。助手自行判断这是补充信息(继续原任务并一并交代结果)还是改变方向(放弃当前路径并说明放弃了什么)。斜杠命令不走此路径,仍按命令处理。
  • 助手在每次调用时构建系统提示词 = 全局基底 + 会话 SOUL + 会话 MEMORY + 会话 Skills 索引 + 当前会话/渠道上下文。
  • 工具调用过程支持流式回显"进度"(文字片段)与"工具提示"(如 read_file("…"));两者由 channels.send_progress / channels.send_tool_hints 控制是否回传到渠道。

用户可感知的能力

工具类 用户可感知的能力
文件 在会话工作目录里读、写、改、列出文件。
Shell 在受控超时内跑 shell 命令,输出会回到对话。
Web 搜索 / 抓取 用 Tavily 搜索;用 trafilatura 抓取并提取网页正文。
跨渠道发消息 助手主动给某个渠道某个会话发消息(不一定是当前对话渠道)。
派生子 agent 启动一个独立子任务,不阻塞主对话,完成后异步通知。
定时任务 在对话里说"明天早上提醒我 X",助手把它落到 cron。
Task 列表 维护"待办任务",跨轮次保留。
MCP 协议工具 通过 MCP server 配置接入第三方工具集(stdio 或 streamable HTTP)。

契约边界:助手不会突破会话工作目录访问任意文件系统路径;shell 工具有超时;网络工具有超时与输出截断。

错误反馈策略(强默认,不提供开关):

  • 处理消息时抛出的任何未捕获异常都进入日志(logger.exception),不向用户暴露异常类型、堆栈、内部路径。
  • 判据是"这一轮是否由用户主动触发",不是群聊 / 私聊:
    • 用户触发(私聊、群里 @ 了机器人、CLI):回一条简短提示。有人在等回应,静默才是坏体验。
    • 非用户触发(cron / 心跳等系统触发轮次,以及群里没 @ 机器人的旁听消息):完全静默。错误只进日志,不回任何文字、不发表情反应、不留痕迹。理由:没人在等的消息,"机器人在叫"的成本远高于"错过一次失败信号"。
  • 模型接口失败会说明类别:重试仍失败时,回一条点明类别的提示(限流 / 接口暂时不可用 / 上下文超限 / 认证失败 / 请求被拒),并说明重试了几次。给类别是为了让用户能判断"等一下再试"还是"去检查 API key";类别之外不暴露任何异常类型、堆栈、内部路径。其他异常仍回与具体错误无关的固定文案。
  • 节流:同一会话(session_key)在 60 秒窗口内只发送一次错误反馈;窗口内的后续错误一律静默落日志。
  • CLI 例外:CLI 是开发与调试主入口;节流命中时仍 publish 一条空 outbound,保证交互模式的"等待回合结束"信号能正常解锁,不让用户的 prompt 永久卡住。其他渠道无此特殊处理。
  • 达到迭代上限不算错误:当助手用完 max_tool_iterations 仍未结束,会回一条说明性的提示(非异常路径),不受上述策略约束。

5.2 对话渠道

已支持的渠道(按字母序,这是产品对外承诺的列表):

渠道 私聊 群聊行为 鉴权方式 备注
CLI 内置交互模式,等价于一个本地"渠道"。
DingTalk @ 触发 ClientID + ClientSecret Stream 模式。
Discord @ 触发 Bot Token 走 Gateway WebSocket。
Email IMAP + SMTP 凭据 周期性轮询新邮件,自动回信开关可控。
Feishu @ 触发 App ID + App Secret WebSocket 长连接接入飞书开放平台。
Matrix group_policy 决定 Access Token 可选依赖(pip install bubbles[matrix]),支持 E2EE。
Mochat mention.require_in_groups 决定 claw_token 内部协作工具集成。
QQ AppID + Secret 基于官方 botpy。
Slack DM 策略可控 group_policy 决定 Bot Token + App Token Socket Mode。
Telegram @ 触发 Bot Token 可走代理。
WeChat @ 触发 wcferry(仅 Windows) 受限于 wcferry 的平台支持。
WhatsApp 群聊 @ 触发 扫码(经 Node.js 网桥) 唯一一个需要外部进程协作的渠道。

所有渠道的统一契约

  1. 白名单:每个渠道支持 allow_from(用户/账号粒度)作为最小权限模型;空列表的语义由各渠道的实现决定,但产品层规则是"默认应保守,公开访问需用户显式同意"。
  2. 群聊触发策略:群聊默认"被 @ 才回";私聊默认全回;具体策略字段(如 group_policy / mention.require_in_groups)属配置层细节,由 Channels Config 字段定义。
  3. 群聊标注:每条入站消息必须在 metadata 里标注是否来自群聊(标准字段 is_group: bool,缺省视为 false / 私聊)。这是错误反馈策略与触发策略统一处理的依据,新增渠道必须实现。
  4. 媒体支持:渠道实现负责把图片 / 文件下载到会话工作目录的 data/ 下,作为路径传入对话上下文。
  5. 回执:助手回复可附带"进度文字"与"工具调用提示",是否启用由 channels.send_progresschannels.send_tool_hints 控制。

会话归属(channels.session_mode

  • channel(默认):每个 (渠道, chat_id) 一个会话,互不串。
  • user:同一用户跨渠道共享会话。
  • global:全局单一会话。

@ 提及(跨渠道统一)

  • 模型在文本中嵌入 <@id> 标记表示 @ 某人;id 是该渠道的原生用户 ID(微信 wxid、飞书 open_id 等)。
  • 入站:渠道收到原生 @ 时(如微信 @昵称 、飞书 @_user_1 占位符),统一转换为 <@id> 后交给模型。
  • 出站:发送时渠道把 <@id> 翻译回原生格式(微信填 aters 数组、飞书构造卡片 <at> 元素等)。
  • 模型可通过 find_person(query) 工具按姓名搜当前群成员,拿到对应的 <@id> 标记后嵌入回复文本中。仅群聊有效,私聊/CLI 返回"无成员"。

新增渠道的产品检查清单:必须支持白名单、必须明确群聊触发策略、必须在 metadata 标注 is_group、必须能把媒体落到会话工作目录、必须能回流进度。若渠道支持群 @,必须实现 inbound/outbound 的 <@id> 转译及 get_group_members

5.3 LLM Providers

Bubbles 不绑定任何单一模型厂商。Provider 列表是产品对用户的承诺:用户切到任意一个支持的 provider,最小变更应只是改一行 model + 加一个 API key。

支持类别

类别 代表 provider 鉴权
直连标准模型厂商 Anthropic / OpenAI / Google Gemini / DeepSeek / Groq / Moonshot / Minimax / Zhipu / Dashscope(通义) API Key
网关聚合 OpenRouter / AiHubMix / SiliconFlow / VolcEngine API Key
自托管 / 兼容端点 Custom(任意 OpenAI 兼容地址)/ Ollama / vLLM 可选 API Key
OAuth 授权 OpenAI Codex / GitHub Copilot bubbles provider login <name>

Provider 选择契约

  1. 用户在 agents.defaults.provider 显式指定,或留空让系统按 model 字符串自动匹配。
  2. 自动匹配在歧义场景下要可解释——"为什么我配了 model X 走了 provider Y"必须是用户能从 model 字符串前缀或关键词读出来的,不是黑盒。
  3. OAuth provider 不会被当作 fallback 使用,必须用户显式选择。
  4. Custom(任意 OpenAI 兼容端点)走独立 client,不经过 LiteLLM,以保证用户对请求格式的完全控制。
  5. 所有非 OAuth provider 都通过 LiteLLM 统一适配,新增 provider 的成本应仅是登记一行注册条目 + 改本文档。

失败与重试契约(对所有 provider 一致,不因换 provider 而变):

  • 值得重试的失败会自动重试:限流、服务端故障、超时、连接失败。默认重试 2 次,指数退避;服务端给了 Retry-After 就听它。
  • 上下文超限会先压缩历史再重试一次,而不是直接把失败抛给用户。
  • 重试无意义的失败立即放弃:认证失败、请求非法、内容策略拒绝。
  • 重试次数与单次请求超时由用户配置。底层 SDK 的隐式重试一律关闭——否则各厂商默认值不同(有的 2 次、有的 0 次),实际请求数会变成两层相乘,既不可控也不可解释。
  • 传输层失败不会被伪装成模型的回复:它不进对话历史、不会被当成摘要内容。用户可见部分见 §5.1 错误反馈策略。

对外产品承诺:用户在配置文件里看到的"支持哪些 provider 字段",等于本文档列出的清单,等于代码 providers 注册表。三者保持同步。

5.4 会话与工作空间

会话(Session)是 Bubbles 的核心抽象。一个会话 ≠ 一段对话历史,而是一个完整的工作单元:

~/.bubbles/sessions/<session_key>/
├── session.jsonl    # 对话历史(含元数据头与压缩点)
├── SOUL.md          # 该会话的人设
├── MEMORY.md        # 该会话的长期记忆
├── config.json      # 该会话对默认配置的覆盖(model / system_prompt 等)
├── data/            # 助手工作区与临时媒体缓冲区(无长期保留承诺,见下)
└── skills/          # 该会话可用的技能(从模板同步)

契约

  1. 会话目录是助手的"工作目录",所有文件工具默认在此根下操作。
  2. 会话 key 由"渠道 + chat_id"按 session_mode 派生,或由用户在 CLI 里 /session <name> 显式指定。
  3. 会话级配置(config.json)覆盖全局默认;用户在交互模式下 /config <key> <value> 也写入此文件。
  4. 历史压缩(compaction)会在 session.jsonl 里留下显式标记,可被读出但不会假装"消息没发生过"。
  5. bubbles sync-skills 会把模板里的 skills 同步到所有会话;用户在自己会话里改过的 skills 不会被覆盖(同步策略详见 sync-skills 命令的实际实现,但产品契约是"用户编辑不丢")。
  6. data/ 是工作区与临时缓冲区,无长期保留承诺:服务会按 mtime 自动清理 3 天未访问的文件。需要长留的事实写进 MEMORY.md;需要长留的产物分发后即视为可清。session.jsonl / SOUL.md / MEMORY.md / config.json / skills/ 不受清理影响。
  7. 执行沙箱(每会话可选):Shell 与文件工具的实际执行环境由该会话绑定的沙箱后端提供,全局默认在 tools.sandbox 配置,会话可用 /config sandbox <backend> 覆盖。默认后端 local 与历史行为完全一致(共享宿主环境)。local_isolated 给每个会话一份独立的 $HOME(及 XDG / Windows APPDATA 等),让会话内的 CLI 工具(gh / aws / gcloud / kubectl / git 等)各自存取按会话隔离的凭证——即"每会话独立 CLI 身份"。该私有 home 落在会话工作目录之外(与 sessions/ 平级的 session_homes/<key>/),因此不受 data/ 的 3 天清理影响,也不被模型的文件工具读到(只有 exec 子进程可见)。沙箱抽象为后续容器 / 远程后端预留,不改变工具的对外契约。

沙箱的隔离边界local_isolated 只隔离"环境变量导向的凭证查找",不隔离整个文件系统——用绝对路径直读宿主文件仍可达。要"绝不可能跨会话/越出工作区"的硬隔离,需容器 / OS 级后端或部署侧隔离,见 §7 与 SECURITY.md §4.1。

5.5 Skills

Skills 是助手的"长期能力",预装在会话模板里,新会话首次创建时复制过去。

当前预装的 Skills 清单(产品承诺):

Skill 能力
pdf 生成 / 读取 PDF。
docx 生成 / 读取 Word 文档。
xlsx 生成 / 读取 Excel。
pptx 生成 PPT。
canvas-design 在画布上做视觉设计(含字体库)。
summarize 长文本摘要工作流。
tmux 终端多路复用辅助。
file-share 把文件分享给用户的标准化流程。

契约

  • 每个 Skill 是一个目录,包含一个 SKILL.md(描述何时使用、能力清单、调用规范)以及它依赖的脚本/资源。
  • 助手在系统提示词里看得到所有 Skills 的索引;具体内容按需加载。
  • 用户可以在自己的会话里增删改 Skills,不影响全局模板。
  • 增删默认 Skills 是产品级变更,必须改本文档。

5.6 Cron(定时与主动唤醒)

契约

  • 用户可以通过 bubbles cron add / 助手工具调用 / 自然语言"明天早上提醒我 X"三种方式登记定时任务。
  • 调度类型支持:at(一次性绝对时间)、every(间隔)、cron(标准 5 段表达式);时区可指定。
  • 每个 job 包含一条要发给助手的消息。触发时助手会以这条消息为输入跑一次完整的 agent 循环。
  • 任务可绑定一个会话 key,触发时复用该会话的上下文与历史。
  • 任务可声明"投递目标"(渠道 + chat_id):助手响应除了写回会话历史,还会主动发到目标渠道。
  • 任务有启用/禁用开关;禁用任务不会自动执行,但可被 cron run -f 手动触发。
  • 任务列表持久化在 ~/.bubbles/cron/jobs.json,与会话目录平级。
  • 会话隔离(仅 agent 工具):模型可调用的 cron 工具的 list / remove 严格限定在当前会话——只看得到、只删得掉本会话创建的 job。其他会话的 job 既不出现在 list 返回中,对 remove 也一律返回与"不存在"完全相同的错误文案(不通过错误信息泄露其他会话的 job id)。CLI(bubbles cron …)保持全局视角不受此限,是跨会话运维入口。
  • 系统触发 turn 不可用 cron 工具:cron job 与心跳触发的 agent turn(即"系统触发 turn",与 stay_silent 同款判断)期间,模型完全看不到 cron 工具——无论 add / list / remove 都不可用。防止"一次触发 → 注册更多 job → 雪崩"的嵌套;triggered turn 想表达"完成后自删"等语义请走声明式字段(如 delete_after_run),不要走运行时再调度。
  • 沉默信号 stay_silent:cron job 触发的 agent turn(以及任何系统触发的周期/事件 turn,如群聊心跳)可调 stay_silent 工具表达"本轮判断无需动作"——调后不向 channel 发出任何 outbound,也不写回会话历史。这是"看一眼条件、不满足就别说话"模式的标准实现方式。该工具仅在系统触发的 turn 里注册,用户直接发问的 turn 看不到。
  • 崩溃安全:服务按 next_run_at_ms 锚点对齐持久化(每个 every job 在创建时锁定 anchor,重启后按原节奏继续不漂移);执行前 pre-advance + 写盘,崩溃也不会重发。
  • 失败退避:连续失败按 30s / 1m / 5m / 15m / 60m 指数退避,避开 API rate-limit 风暴;成功后自动清零。一次性 at 任务失败不进退避(一次性,下次也不会跑)。

5.7 心跳(手动启用的主动唤醒)

契约

  • 每个 session 可独立开启一个心跳;通过群内/CLI 内 /heartbeat <interval>(如 /heartbeat 30m/heartbeat 2h/heartbeat 30 默认分钟)开启,/heartbeat off 关闭,/heartbeat 无参看状态。
  • 默认关闭仅用户能开关,模型没有对应工具(防止 AI 给自己装上自动闹钟)。
  • 间隔范围 1 分钟 – 24 小时。开启后底层会注册一个 every 类型的 cron job,复用 §5.6 的全部基础设施(持久化、崩溃安全、退避)。
  • 开启时若 <work_dir>/HEARTBEATS.md 不存在则写入模板;存在则沿用用户编辑过的版本。
  • 心跳触发的 turn 里,模型会看到 HEARTBEATS.md 加载在 context 中、并收到 [心跳触发] 提示消息。模型按 HEARTBEATS.md 决策;无事可做就调 stay_silent默认偏向沉默
  • 当心跳处于开启状态时,系统 prompt 会出现 ## Heartbeat: ON 块告诉模型当前节奏;关闭时模型仍知道这是个用户可启用的功能(INSTRUCTIONS 里有静态说明),但不会以为自己当下在心跳态。

5.8 工具集(用户视角)

用户感知到的"助手能做什么",由以下工具集合定义:

类别 用户感知
文件 读、写、改、列目录(在会话绑定的沙箱内执行)。
Shell 跑命令、看输出(受时间与输出长度限制;在会话绑定的沙箱内执行,可选每会话独立 $HOME / CLI 凭证隔离)。
Web 搜索、抓网页正文。
消息 主动给指定渠道指定会话发消息(必须显式声明目标)。
Spawn 派生独立子任务,主对话不阻塞。
Cron 登记 / 修改 / 删除定时任务。
Task 维护 Claude Code 风格的待办列表,跨对话轮次保留。
FindPerson 按姓名搜当前群成员,拿到 <@id> 标记可嵌入回复实现 @ 提及。
MCP 接入用户在 tools.mcp_servers 里配置的任意 MCP server 提供的工具。

新增工具的产品检查清单:必须给用户带来一个可清晰命名的能力(一行话),必须有合理的副作用边界与超时,必须在本文档登记。

工具结果长度上限:任何工具(含 MCP)的单次结果都有 token 上限,超出时保留头尾、中间省略并明确告知助手已截断。理由:单条结果若超过 compact 的保留预算,任何压缩策略都守不住上下文上限。


6. 用户旅程

6.1 首次上手

  1. pip install -e .
  2. bubbles onboard:建立 ~/.bubbles/config.json 与 sessions 目录。
  3. 编辑 ~/.bubbles/config.json:至少配置一个 provider 的 API key 与默认模型。
  4. bubbles agent:进入交互模式 → /session my → 开始对话。
  5. (可选)配置一个或多个渠道 → bubbles gateway → 长期常驻。

6.2 关键交互(CLI 交互模式)

输入 行为
/session <id> 绑定到指定会话;不存在则创建。
/config 显示当前会话配置。
/config <k> <v> 设置会话级配置项(model / system_prompt / sandbox)。
/prompt 打印发给模型的完整系统提示词(调试用)。
/compact 手动压缩当前会话历史。
exit / quit 退出。
其他文本 作为用户消息发给助手。

6.3 配置事实源

~/.bubbles/config.json 是用户配置的唯一文件,它的 schema 决定了产品的可配置面:

顶级字段 用户面对的产品决定
agents 默认模型 / 温度 / 上下文 / 迭代上限 / 记忆窗口 / 压缩保留预算 / API 重试与超时 / 会话并发上限。
channels 启用哪些渠道、每个渠道的鉴权与白名单、会话归属模式、是否流式回显进度。
providers 每个 provider 的 API key、自定义 base URL、自定义 header。
gateway 网关进程绑定的 host 与 port。
tools Web 搜索 key、Shell 工具超时与 PATH、执行沙箱后端(sandbox,含每会话独立 HOME 开关)、MCP servers 列表。

契约

  • 所有顶级字段同时支持 camelCase 与 snake_case 两种 key 命名。
  • 环境变量以 BUBBLES_ 前缀,嵌套字段以 __ 分隔(如 BUBBLES_AGENTS__DEFAULTS__MODEL),可覆盖配置文件。
  • bubbles onboard 在已有配置文件时支持"覆盖默认"或"刷新(保留旧值,补全新字段)"两个分支,绝不静默丢用户配置。

SPEC 与 Schema 的分工:本文档只承诺顶级字段的产品语义、可配置的能力面、以及关键开关的产品行为。具体字段名、类型、嵌套结构、默认值、校验规则由配置 schema(项目里的 pydantic 模型)定义,schema 是配置层的事实源。两者保持一致:本文档新增产品决定 → schema 实现;schema 改了字段名/默认值 → 检查本文档的产品语义是否仍然成立。

新增配置字段的产品检查清单:是否给用户带来可被一句话描述的新能力 / 新选择?是否有合理默认值?是否会破坏旧配置?这三点要在本文档先回答,再写代码。


7. 安全与隐私模型

核心信念:用户的数据在用户机器上;助手是用户雇的私人助理,不是云服务的客户端。

承诺

  1. 数据本地:会话历史、人设、记忆、产物、密钥全部存放在 ~/.bubbles/,不主动上传到 Bubbles 任何托管服务(项目本身没有这种服务)。
  2. 明确出站:助手向外发出的网络请求只有三类——LLM provider API、用户配置的渠道平台 API、用户/助手主动调用的 Web 工具。新增任何"默认开启的对外网络行为"是产品级决策,必须改本文档。
  3. 白名单:所有渠道支持基于发件人/账号的 allow_from 白名单。这是个人助理与公开机器人的边界。
  4. 危险命令防护:Shell 工具拦截一组明显有破坏性的模式(参考 SECURITY.md)。这是底线,不是充分防护——用户对自己的会话目录与 shell 行为负责。
  5. 路径沙盒:文件工具(read/write/edit/list)和 Shell 工具的 working_dir 强制限制在会话工作目录内,绝对路径 / .. / symlink 越界一律被拦下。Shell 命令本身的越界尝试(绝对路径、cd ..、变量间接引用、命令替换等)也做 best-effort 拦截。框架承诺到这一步为止——shell 是图灵完备的,静态分析不可能拦下全部 trick(base64 解码、runtime 构造路径等)。需要"绝不可能跨会话访问"的硬隔离,由部署侧或容器 / OS 级沙箱后端承担:每会话独立 OS 用户 / bubblewrap / firejail / sandbox-exec / 容器边界等,参考 SECURITY.md §4.1。
  6. 凭证隔离(可选,每会话):默认所有会话共享宿主环境(含 $HOME、已登录的 CLI 凭证)。开启 local_isolated 沙箱后端后,每个会话获得独立的 $HOME,会话内 CLI 工具的凭证按会话隔离、彼此不可见,且存放在会话工作目录之外(模型的文件工具读不到)。边界:这只隔离环境变量导向的凭证查找,不隔离整个文件系统;硬隔离仍需容器 / OS 级手段(见 SECURITY.md §4.1)。
  7. 密钥:API key 以明文存储在 ~/.bubbles/config.json;配置文件权限收紧到用户私有。生产场景推荐用环境变量或 OS keyring 替代明文(详见 SECURITY.md)。
  8. 审计:助手的每一步工具调用在日志中可见;用户可以用 --logs / --debug 追到模型实际收到的系统提示词与每一次工具调用的入参与结果。

当前明确不做的(与未来不做承诺,但当前如此):

  • 速率限制(依赖底层 provider 的额度)。
  • 自动会话过期。
  • 端到端审计日志。
  • 多用户隔离(这是个人助理框架,不是多租户系统)。

8. 产品边界(明确不做什么)

为避免边界蔓延,以下是 Bubbles 当前明确不做的事情:

  1. 不做多用户 SaaS。一份 ~/.bubbles/ 服务一个人。
  2. 不做企业 IM 网关。渠道集成的目的是让"我自己"在 IM 里用助手,不是给团队架起客服或群聊机器人。
  3. 不做 GUI。CLI 是唯一的官方界面。
  4. 不做模型训练 / 微调。Bubbles 是模型的消费者。
  5. 不做向量库 / RAG 平台。如果某个 Skill 需要 RAG,它走自己的 MCP server 或 Skill 内部实现,不进核心。
  6. 不做插件市场。Skills 与 MCP servers 是用户自己配置的,不存在中心化分发。
  7. 不与某个特定渠道深度绑定。任何只服务单一渠道的特性都属于 Skill / 工具层面,不进核心。

9. 当前阶段与状态

  • 版本:Alpha(< 1.0)。
  • 稳定性承诺:本阶段允许破坏性变更,但任何破坏性变更必须先改本文档。
  • 公测渠道:项目以 GitHub 开源仓库形式分发,用户自行 clone / pip install。
  • 支持平台:macOS / Linux 全功能;Windows 仅在使用 WeChat 渠道时是必需的,其他渠道在 Windows 上能跑但不是主要测试目标。

10. 维护规则(致未来的修改者)

  1. 不要把实现搬进本文档。代码是实现的事实源,本文档是产品的事实源。本文档不应该出现 Python 类名、函数名、行号、依赖版本号。
  2. 新增能力 → 先改本文档。一个用户可见的新能力 = 一段本文档新文字 + 一份 PR 描述对应到这段文字 + 必要时改 README。
  3. 删除能力 → 先改本文档。删一个渠道 / 删一个工具 / 删一个 provider,先在本文档里删掉对应承诺,再在代码里删。
  4. 冲突时以本文档为准。如果 README、命令帮助文本、配置 schema 注释、Skills 文档与本文档不一致,请改它们而不是改本文档(除非是本文档错了——那就改本文档)。
  5. 保持本文档可被一口气读完。当一个章节膨胀到无法被一次读完时,要么压缩,要么拆出独立子文档并在此处保留一段不超过半页的概要。
  6. 每个产品级变更的 PR 描述必须回答三件事:变更了哪段文字、变更前后的产品承诺差异、用户感知层面是 break / non-break。