跳转到内容

使用数据库存储会话历史

Bub 默认采用本地 JSONL 文件持久化会话 Tape。

通过安装 bub-tapestore-sqlalchemy 插件,可以让应用改用 SQLite、PostgreSQL 或任何支持 SQLAlchemy 的关系型数据库,同时保持 ,tape.info、,tape.search、会话分支及 Sidecar 等行为完全一致。

  • 已安装 Bub 应用,并正确配置模型。
  • (可选)安装 sqlite3 命令行工具,用于直接查询和检验数据表。
  • 确认当前 Bub 主目录路径:
    printf '%s\n' "${BUB_HOME:-$HOME/.bub}"

在运行 Bub 的环境中安装存储插件:

bub install bub-tapestore-sqlalchemy@main
bub hooks

执行 bub hooks 检查扩展点挂载情况:

provide_tape_store: tapestore-sqlalchemy, builtin

列表中高优先级的实现在前。出现 tapestore-sqlalchemy 表示插件已生效,并将覆盖内置的文件存储。

插件默认使用 <BUB_HOME>/tapes.db。为了便于演示与验证,本指南将其指向当前工作目录:

export BUB_TAPESTORE_SQLALCHEMY_URL="sqlite+pysqlite:///$PWD/bub-tapes.db"
  • sqlite+pysqlite 使用 Python 标准库内置的 SQLite 驱动,无额外依赖。
  • URL 需符合标准 SQLAlchemy 连接串规范;这里使用 $PWD 拼接为绝对路径。
环境变量 默认值 说明
BUB_TAPESTORE_SQLALCHEMY_ECHO false 设为 true 可打印执行的 SQL 语句,用于开发调试。
BUB_TAPESTORE_SQLALCHEMY_CONNECT_ARGS {} 传给 create_engine(connect_args=...) 的 JSON 对象(如超时设置)。

如果需要持久生效,可将变量写入工作区根目录的 .env 文件。插件在首次连接时会自动初始化所需表结构。

运行一次会话,再通过内置指令检查 Tape 是否写入数据库:

bub run "用一句话打个招呼,表明这是通过数据库存储的一轮会话。"
bub run ",tape.info"

输出应包含本次会话的 Tape 名称、条目数量与 anchor 状态:

name: 86774b31b96845a4__0b871d5e50e7c192
entries: 9
anchors: 1
last_anchor: session/start
entries_since_last_anchor: 8

使用 sqlite3 检查数据库中的物理记录:

# 查询 Tape 会话列表
sqlite3 "$PWD/bub-tapes.db" \
  "SELECT name, last_entry_id FROM tapes ORDER BY name;"
# 查询anchor记录
sqlite3 "$PWD/bub-tapes.db" \
  "SELECT entry_id, anchor_name, entry_date FROM tape_entries WHERE kind = 'anchor';"

tapes 表维护所有会话的元数据;tape_entries 表按条目记录每一次 Turn、模型回答与工具调用的具体内容。

该插件基于 SQLAlchemy,支持连接 PostgreSQL、MySQL 等关系型数据库:

  1. 安装驱动(在 bub 运行环境中安装对应数据库驱动):
    # 以 PostgreSQL 为例
    uv add "psycopg[binary]"
  2. 配置连接串:
    export BUB_TAPESTORE_SQLALCHEMY_URL="postgresql+psycopg://user:YOUR_DB_PASSWORD@localhost:5432/bub_db"

生产环境中建议提前测试并发写入与连接池超时配置,保证服务稳定。

core.Agent 与内置应用只依赖存储协议,实现协议即可把它换成任意后端。需要区分「Tape 保存历史」与「TapeContext 投影上下文」时,见 Tape 与模型上下文。

协议 使用位置
bub.core.store.TapeStore 同步实现,传给 Agent(store=...)
bub.core.store.AsyncTapeStore 异步实现,推荐用于数据库与远程存储
provide_tape_store 应用层替换,内置应用默认返回 FuzzyFileTapeStore

完整方法签名见 Python API。实现时至少要保证:按 Tape 名称隔离条目、保持追加顺序、支持 reset(),并能让 TapeQuery 的 anchor 过滤生效。分支由 ForkTapeStore 在现有存储之上构建,后端不需要自行实现。

内置应用通过 provide_tape_store 提供 FuzzyFileTapeStore,默认目录为 ~/.bub/tapes/。Tape 名称由工作区路径与会话 ID 共同生成,因此不同工作区中的同名会话不会共享历史。

BubAgent 按以下顺序选择存储:

  1. 显式传入的 tape_store;
  2. 创建 Agent 时 framework.get_tape_store() 返回的存储;
  3. 两者都没有时,使用内存存储。

需要使用 framework.running() 提供的存储时,在该作用域内创建 BubAgent。替换存储使用 provide_tape_store,替换投影策略使用 build_tape_context。

内置应用会从 Tape 恢复模型、推理力度和 代码模式 等会话设置,其他应用状态需要显式持久化。

常见问题 可能原因 解决办法
bub hooks 中无 tapestore-sqlalchemy 插件装到了其他 Python 环境 检查 command -v bub,确保在当前使用的虚拟环境中执行 bub install。
提示 SQLite 数据库被锁(Database is locked) 多进程并发写入超过 SQLite 承受能力 配置 busy timeout 参数,或切换到 PostgreSQL 等更适合高并发的关系型数据库。
连接 PostgreSQL 提示 ModuleNotFoundError 未安装对应数据库驱动 在 Bub 环境中执行 uv add "psycopg[binary]" 安装驱动。
无法创建数据表或权限拒绝 目标路径或远程数据库用户无建表权限 确认文件目录对当前用户可写,或授予数据库用户 CREATE TABLE 权限。

如需还原为 Bub 默认的文件存储模式,清除环境变量并删除测试数据库即可:

unset BUB_TAPESTORE_SQLALCHEMY_URL BUB_TAPESTORE_SQLALCHEMY_ECHO BUB_TAPESTORE_SQLALCHEMY_CONNECT_ARGS
rm -f "$PWD/bub-tapes.db"