Beads 数据库瘦身实战:bd compact压缩已关闭 issue 的完整指南
【免费下载链接】beadsBeads - A memory upgrade for your coding agent项目地址: https://gitcode.com/GitHub_Trending/beads1/beads
导读
Beads(一个为编码 Agent 提供"记忆升级"的开源项目)在长期运行中,issue 库会随着每条记录不断写入 Dolt 版本历史而持续膨胀,直接拖慢查询、占用磁盘。本文以 compact.md 为核心骨架,系统讲解bd admin compact命令的分层压缩(Tier)机制、三种执行模式、全部命令行参数与适用场景,并结合仓库源码深入剖析候选筛选、资格校验、AI 语义摘要、快照归档与回滚的完整实现链路。读完本文,你将掌握在不丢失关键技术决策的前提下,为长期项目安全地控制 issue 数据库体积的完整方案。
1. 为什么需要压缩:长期运行的记忆膨胀问题
Beads 使用 Dolt(带版本控制的 SQL 数据库)作为存储后端,bd admin compact正是为了控制这种膨胀而设计的。它的作用在命令帮助文本中定义得非常明确(见 cmd/bd/compact.go):
"Compaction reduces database size by summarizing closed issues that are no longer actively referenced. This is permanent graceful decay - original content is discarded."
即:通过语义摘要压缩那些已关闭、且不再被活跃引用的 issue,将原始正文丢弃,只保留浓缩后的技术要点。
bd admin compact是admin命令组下的一个子命令(注册位置见 cmd/bd/admin.go),其最简用法为:
bd admin compact --auto --all1.1 为什么必须压缩:Dolt 的提交历史机制
理解压缩的必要性,需要先理解 Beads 的存储模型。从命令帮助文本可以看出一个关键事实(cmd/bd/compact.go):
"With auto-commit per mutation, Dolt commit history grows over time."
Beads 对每次数据变更(issue 创建、更新、关闭)都会产生一次 Dolt 提交。这意味着:
- 每一次 issue 编辑都永久留在提交历史里;
- 长期项目中,issue 数量与迭代次数同步增长,数据库体积线性膨胀;
- 关闭的 issue 通常只承担"历史查询"价值,却占用与活跃 issue 相同的存储成本。
压缩机制的价值在于:把"长尾"的已关闭 issue 从"全文保留"降级为"结构化摘要保留",从而将存储成本集中到真正活跃的数据上。
2. 压缩分层(Compaction Tiers)机制
bd admin compact采用分层压缩策略,原文档定义了两种 Tier:
| Tier | 触发条件 | 压缩强度 | 实现状态 |
|---|---|---|---|
| Tier 1 | 已关闭 30+ 天 | 语义压缩,约 70% 体积缩减 | ✅ 已实现 |
| Tier 2 | 已关闭 90+ 天(且已 Tier 1 压缩) | 超压缩(Ultra compression) | ⏳ 规划中,尚未实现 |
2.1 源码中的 Tier 1 判定细节
Tier 1 的完整资格判定逻辑位于 internal/storage/issueops/compaction.go,其校验条件严格且顺序明确:
- issue 必须存在,否则返回 "issue not found";
- 状态必须为 closed(
types.StatusClosed),未关闭的 issue 直接拒绝; - 必须有
closed_at时间戳,无时间戳无法计算关闭时长; - 未被更高层级压缩过:Tier 1 要求
compaction_level < 1,Tier 2 要求compaction_level < 2且>= 1(即必须先完成 Tier 1); - 关闭时长必须达标:
time.Since(closedAt) >= threshold,threshold 默认 Tier 1 为 30 天、Tier 2 为 90 天。
2.2 时间阈值可配置:compact_tier1_days/compact_tier2_days
阈值并非写死的常量。从 internal/storage/issueops/compaction.go 可以看到,阈值实际从 Beads 配置表的compact_tier1_days与compact_tier2_days键读取:
- 若键缺失、为空或解析失败(非正整数),回退到默认值(30 / 90);
- 这意味你可以通过 Beads 配置(
bd config set等途径)按项目实际情况调整压缩时延。
2.3 候选筛选的 SQL 依据
--all批量模式与--stats统计模式都依赖候选查询。Tier 1 候选的 SQL 定义在 internal/storage/issueops/compaction.go:
SELECT i.id, i.closed_at, CHAR_LENGTH(i.description) + CHAR_LENGTH(i.design) + CHAR_LENGTH(i.notes) + CHAR_LENGTH(i.acceptance_criteria) AS original_size, COALESCE((SELECT COUNT(*) FROM dependencies d WHERE d.depends_on_issue_id = i.id AND d.type = 'blocks'), 0) AS dependent_count FROM issues i WHERE i.status = 'closed' AND i.closed_at IS NOT NULL AND i.closed_at <= (now - 30 days) AND (i.compaction_level = 0 OR i.compaction_level IS NULL) ORDER BY i.closed_at ASC;几个值得注意的实现细节:
original_size是四个文本字段的字符长度之和:Description、Design、Notes、AcceptanceCriteria,这正是压缩前后对比、计算节省字节数的基准;- 候选查询同时统计
dependent_count(被多少个blocks类型依赖引用)——从源码结构看,这为将来"有依赖的 issue 优先跳过"提供了数据基础,但目前资格判定尚未使用该字段; - Tier 2 候选的查询(compaction.go)与 Tier 1 唯一的差异是要求
compaction_level = 1(必须先经过 Tier 1 压缩)且关闭时长满足 90 天阈值。
3. 命令用法与参数全解
3.1 原文档的基础用法
| 场景 | 命令 |
|---|---|
| 预览候选 issue(不执行) | bd admin compact --dry-run |
| 压缩所有符合条件的 issue | bd admin compact --all |
| 压缩指定 issue | bd admin compact --id bd-42 |
| 强制压缩指定 issue(跳过资格校验) | bd admin compact --id bd-42 --force |
| 查看压缩统计 | bd admin compact --stats |
注意:在 Beads 较新的 CLI 结构中,
compact命令挂在admin命令组下,因此完整调用是bd admin compact ...。文档 frontmatter 中的argument-hint: "[--all] [--id issue-id] [--dry-run]"也印证了这一参数形态。
3.2 核心参数速查表
以下参数全部来自 cmd/bd/compact.go 的 flag 注册代码,语义与默认值以源码为准:
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
--tier | int | 1 | 压缩层级,仅 1 已实现,传 2 会直接报错拒绝 |
--workers | int | 5 | 并行 worker 数(batch 模式的并发上限) |
--batch-size | int | 10 | 每批处理的 issue 数 |
--dry-run | bool | false | 只预览不实际压缩 |
--all | bool | false | 处理全部候选 issue |
--id | string | "" | 指定单个 issue ID(如bd-42) |
--force | bool | false | 强制压缩,绕过资格校验(必须配合--id) |
--stats | bool | false | 输出压缩统计信息 |
--json | bool | false | 以 JSON 格式输出结果 |
--analyze | bool | false | 分析模式:导出候选供 Agent 审阅(无需 API Key) |
--apply | bool | false | 应用模式:接受 Agent 提供的摘要(无需 API Key) |
--auto | bool | false | 自动模式:AI 驱动压缩(需要 API Key) |
--summary | string | "" | 摘要文件路径,-表示从 stdin 读取(配合--apply) |
--actor | string | "agent" | 审计日志中的操作者名称 |
--limit | int | 0 | 候选数量上限,0 表示不限制(配合--analyze) |
--dolt | bool | false | Dolt 模式:对.beads/dolt目录执行垃圾回收 |
3.3 参数组合的合法性约束
源码中对参数组合有严格的互斥与前置校验(cmd/bd/compact.go):
--analyze、--apply、--auto三种模式互斥,且必须且只能指定其一,否则报错;--id与--all不能同时使用;--force必须配合--id使用;--apply必须同时提供--id与--summary;--auto模式下若不提供--id、--all或--dry-run三者之一,会因缺少目标而报错;- 指定
--tier 2会被前置拦截:"Tier 2 compaction is not yet implemented; only --tier 1 is available"——这是刻意为之的"早失败"设计,避免在模式内部深层才暴露错误。
4. 三种工作模式详解
除了原文档描述的--auto(AI 自动模式),当前实现还提供了另外两种无需 API Key 的模式。三种模式统一由一个入口分发(cmd/bd/compact.go)。
4.1--analyze:导出候选供 Agent 审阅(无 API Key)
适用场景:你(或你的 Agent)希望人工/智能体审阅每个候选 issue 后再决定如何压缩,全程不调用外部 AI 服务。
# 导出全部候选(含完整正文),JSON 格式便于 Agent 处理 bd admin compact --analyze --json # 只看单个 issue 的完整内容 bd admin compact --analyze --id bd-42 --json # 限制候选数量 bd admin compact --analyze --limit 20 --json从实现看(cmd/bd/compact.go),--analyze输出的每个候选包含:id、title、description、design、notes、acceptance_criteria(全文)、size_bytes、age_days、tier、compacted(是否已压缩)。它要求直接数据库访问(ensureDirectMode),不能走代理服务器模式。
4.2--apply:应用 Agent 提供的摘要(无 API Key)
这是与--analyze配套的落地步骤:Agent 审阅候选后自行撰写摘要,写入文件或通过 stdin 传入,--apply负责校验并写入数据库。
# 从文件读取摘要 bd admin compact --apply --id bd-42 --summary summary.txt # 从 stdin 读取摘要(管道/脚本友好) bd admin compact --apply --id bd-42 --summary - < summary.txt--apply的关键校验逻辑(cmd/bd/compact.go):
- 资格校验:除非
--force,否则复用与--auto相同的CheckEligibility逻辑; - 尺寸校验(防膨胀保护):除非
--force,否则摘要长度必须严格小于原文长度,否则报错并提示use --force to bypass size validation; - 写入:将摘要写入
description,清空design/notes/acceptance_criteria,记录compaction_level、compacted_at、compacted_at_commit、original_size等元数据; - 审计:以
--actor(默认agent)身份在 issue 上追加一条评论,记录压缩前后的字节数与节省比例。
4.3--auto:AI 驱动压缩(需要 API Key)
原文档的主推模式。适合完全无人值守地批量压缩:
# 预览(不消耗 API 调用,仅列出候选) bd admin compact --auto --dry-run # 压缩所有符合条件的历史 issue bd admin compact --auto --all # 压缩单个 issue bd admin compact --auto --id bd-42--auto需要 AI API Key,解析优先级由 internal/config/config.go 定义:
ANTHROPIC_API_KEY环境变量MINIMAX_API_KEY环境变量- 配置项
ai.api_key - 显式传入的 key
若四种来源都为空,命令会直接报错提示配置缺失(--dry-run除外,因为它不需要真正的 AI 调用)。
5. 底层实现原理:AI 摘要如何工作
5.1 摘要引擎:Haiku 客户端
--auto模式的 AI 摘要由internal/compact包承担。核心组件是haikuClient(internal/compact/haiku.go),它封装 Anthropic 兼容 API 完成 issue 摘要。摘要模型的选择逻辑(internal/config/config.go):
- 配置了
ai.model时优先使用该模型; - 使用
MINIMAX_API_KEY且未配置模型时,默认路由到 MiniMax 提供的 Anthropic 兼容模型(MiniMax-M2),可用MINIMAX_MODEL环境变量覆盖; - 使用
ANTHROPIC_API_KEY时走 Anthropic 默认模型。
API Base URL 同样智能路由(internal/config/config.go):ai.base_url或BD_AI_BASE_URL优先,其次MINIMAX_BASE_URL,最后回落 MiniMax 默认端点(https://api.minimax.io/anthropic)。
5.2 Tier 1 摘要提示词:结构化三要素
摘要不是自由发挥,而是由固定的提示词模板(tier1PromptTemplate,见 internal/compact/haiku.go)驱动,要求模型输出严格的三段式结构:
**Summary:** [2-3 句简明概括:做了什么、为什么] **Key Decisions:** [最重要的技术决策要点列表] **Resolution:** [一句话说明最终结果与持久影响]提示词强制要求:"Your summary must be shorter than the original. Be concise and eliminate redundancy."——这从提示词层面保证了压缩率。
5.3 可靠性设计:重试、遥测与审计
从源码可以看到多层可靠性保障(internal/compact/haiku.go):
- 指数退避重试:最多 3 次重试,初始退避 1 秒,仅对可重试错误(网络超时、429 限流、5xx)生效;
- OTel 遥测:记录输入/输出 token 数与请求耗时(
bd.ai.input_tokens、bd.ai.output_tokens、bd.ai.request.duration); - LLM 调用审计:若启用审计(
AuditEnabled),每次 LLM 调用都会写入llm_call审计条目,且审计失败不会导致压缩失败(best-effort 设计)。
5.4 压缩主流程(CompactTier1)
单个 issue 的 Tier 1 压缩在 internal/compact/compactor.go 中按以下顺序执行:
1. 资格预检(CheckEligibility,快速失败) 2. 读取 issue,计算原始大小(四字段字节之和) 3. 调用 AI 生成摘要 4. 尺寸保护:摘要 >= 原文则跳过并记录警告评论 5. SnapshotIssue:先把原文归档到 compaction_snapshots 表 6. UpdateIssue:用摘要覆盖 description,清空其余三字段 7. ApplyCompaction:写入压缩元数据(含 git commit hash) 8. AddComment:在 issue 上记录压缩事件其中第 5 步是可回滚性的关键——先归档、后覆盖,顺序不可颠倒。若归档失败,整个压缩中止且原文保持原样。
5.5 批量压缩的并发模型
CompactTier1Batch(internal/compact/compactor.go)采用信号量限流的 goroutine 池:并发上限由--workers(默认 5)控制,逐个 issue 独立执行CompactTier1,结果按索引收集到BatchResult数组,最后统一汇总成功数、失败数与节省字节数。CLI 层会渲染一个基于文本的进度条(progressBar,见 cmd/bd/compact.go)。
6. 数据安全与回滚:bd restore
6.1 "优雅的永久衰减"意味着什么
原文档特别强调(compact.md):
"This ispermanent graceful decay- original content is discarded. Use
bd restore <id>to view full history from git if needed."
翻译过来:压缩后 issue 的 description/design/notes/acceptance_criteria 原文不再以正文形式存在。但并非不可恢复——存在两条恢复路径。
6.2 快照归档机制(首选恢复路径)
当前实现比文档描述更进一步:压缩前会先执行SnapshotIssue(internal/storage/issueops/compaction.go),把完整原文 JSON 序列化后写入compaction_snapshots表。该快照按"压缩目标层级"打标签,是bd restore的权威恢复源。
bd restore <issue-id>命令(cmd/bd/restore.go)行为如下:
- 默认只读:展示归档的原文内容,不修改数据库;
--apply写回:将快照内容恢复到 issue,并把compaction_level降回(完全恢复时清零,并清空compacted_at、compacted_at_commit、original_size等簿记字段,见 compaction.go);- 快照行保留:恢复后快照记录不删除,作为审计痕迹留存。
6.3 从 Dolt 历史恢复(兜底路径)
对于在快照归档机制引入之前就被压缩的旧 issue(没有快照),bd restore会退化为从 Dolt 版本历史尽力重建原文——但该路径只能展示,不能写回(restore.go帮助文本明确:"can only be displayed, not applied")。
6.4 真实回滚场景
# 查看某 issue 压缩前的原文(只读) bd restore bd-42 # 将压缩前的原文写回 issue bd restore bd-42 --apply7. Dolt 垃圾回收:bd compact --dolt
语义压缩解决的是"内容冗余",而--dolt模式解决的是"存储冗余"。
7.1 为什么需要 Dolt GC
如前所述,Beads 每次变更都会产生 Dolt 提交,历史提交不断累积。Dolt 的垃圾回收可以移除不可达的提交并压缩存储。bd compact --dolt即对.beads/dolt目录执行此操作。
# 预览 GC 效果(不真正执行) bd admin compact --dolt --dry-run # 执行 Dolt 垃圾回收 bd admin compact --dolt7.2 实现要点
从 cmd/bd/compact.go 可以看到:
- 命令先定位 Beads 目录(
beads.FindBeadsDir()),检查.beads/dolt是否存在——该模式仅适用于 Dolt 后端仓库,非 Dolt 仓库会得到明确提示; --dry-run会报告当前目录大小与可回收前提,但不执行;- 实际执行调用外部
dolt gc --archive-level 0命令(--archive-level 0写入经典 Snappy 表文件而非 zstd 归档,与进程内 GC 路径保持一致); - 做了外部 Dolt 版本兼容处理:若外部
dolt二进制不认识--archive-level参数(旧版本),会自动降级为普通dolt gc并给出提示,而不是直接失败——且仅在明确识别出"未知参数"类错误时才降级,真正的 GC 失败不会被吞掉; - 执行前后对比目录大小,输出释放的空间字节数(如
1.2 GB → 850.0 MB (freed 374.0 MB)); - 需要
dolt命令存在于 PATH。
8. 统计与验证:bd admin compact --stats
执行大规模压缩前,先用统计模式评估收益:
bd admin compact --stats # JSON 输出,便于脚本解析 bd admin compact --stats --json输出(cmd/bd/compact.go):
Compaction Statistics Tier 1 (30+ days closed): Candidates: 42 Total size: 153600 bytes Estimated savings: 107520 bytes (70%) Tier 2 (90+ days closed, Tier 1 compacted): not yet implemented Candidates: 0 Total size: 0 bytes统计逻辑会查询 Tier 1 与 Tier 2 各自的候选集合,累加original_size(四字段长度之和),并按 70% 给出预估节省量。JSON 模式额外携带"implemented": false标记明确 Tier 2 未实现的状态。注意:这是基于EstimatedSize = OriginalSize * 3 / 10(compaction.go)的估算,实际节省以压缩后报告的字节数为准。
9. 运行前提与限制
综合原文档与源码,使用bd admin compact前需要了解以下前提:
- 模式互斥:
--analyze/--apply/--auto三选一,不能混用; - API Key 依赖:只有
--auto需要 AI Key(ANTHROPIC_API_KEY、MINIMAX_API_KEY或配置ai.api_key);--analyze与--apply全程离线; - 服务模式要求:除
--stats、--analyze、--dry-run等只读路径外,变更型操作要求以服务模式(server mode)运行,且--analyze、--apply要求直接数据库访问(不能走代理服务器模式); - 只读仓库防护:非只读路径会触发
CheckReadonly("compact")防护; - Tier 2 尚未实现:指定
--tier 2会直接报错,请勿在生产中依赖该参数; - 外部 Dolt:
--dolt模式需要 PATH 中存在dolt可执行文件; - 恢复兜底:快照归档之前压缩的 issue 无法通过
--apply写回原文(只能展示 Dolt 历史重建版本)。
10. 推荐工作流总结
针对不同场景,推荐以下组合:
场景 A:无人值守、全自动维护(适合长期 CI/定时任务)
# 1. 先看有多少可压缩的历史 bd admin compact --stats # 2. 预览候选 bd admin compact --auto --dry-run # 3. 全量压缩(需要 API Key) bd admin compact --auto --all场景 B:Agent 人工审阅(适合重要 issue 或不确定的批量操作)
# 1. 导出候选全文供审阅 bd admin compact --analyze --json --limit 20 # 2. 为每个 issue 撰写摘要并应用 bd admin compact --apply --id bd-42 --summary - < summary.txt场景 C:配套的存储回收
# 语义压缩完成后,回收 Dolt 提交历史占用的空间 bd admin compact --dolt场景 D:误操作回滚
# 查看原文(只读) bd restore bd-42 # 确认后写回 bd restore bd-42 --apply延伸阅读
- 命令入口与参数定义:cmd/bd/compact.go
- 压缩器核心逻辑:internal/compact/compactor.go
- AI 摘要客户端与提示词:internal/compact/haiku.go
- 存储层资格判定与快照/恢复 SQL:internal/storage/issueops/compaction.go
- Dolt 后端存储适配:internal/storage/dolt/compact.go
- 压缩功能测试:internal/storage/dolt/compact_test.go
- 回滚命令实现:cmd/bd/restore.go
- AI Key 与模型解析:internal/config/config.go
【免费下载链接】beadsBeads - A memory upgrade for your coding agent项目地址: https://gitcode.com/GitHub_Trending/beads1/beads
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考