From bf3dc0f7ad9207c6ae941e049ab8aa7aafe4d1c0 Mon Sep 17 00:00:00 2001
From: ax_rd <ax_rd@aisim.cn>
Date: Thu, 03 Sep 2026 08:34:55 +0800
Subject: [PATCH] docs: 对标CodeGraph增强实施计划(10任务)+ go.env构建参数固化

---
 docs/superpowers/specs/2026-09-03-kb-cli-codegraph-alignment-design.md |  236 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 236 insertions(+), 0 deletions(-)

diff --git a/docs/superpowers/specs/2026-09-03-kb-cli-codegraph-alignment-design.md b/docs/superpowers/specs/2026-09-03-kb-cli-codegraph-alignment-design.md
new file mode 100644
index 0000000..b3f06da
--- /dev/null
+++ b/docs/superpowers/specs/2026-09-03-kb-cli-codegraph-alignment-design.md
@@ -0,0 +1,236 @@
+# kb-cli 对标 CodeGraph 增强设计
+
+**日期**: 2026-09-03
+**状态**: 已批准(Lexi 确认方案 C 全量对标)
+**版本**: v1.0.0
+
+## 背景
+
+kb-cli 是 Go 实现的知识库索引与检索 CLI(SQLite + FTS5 + 知识图谱),服务 LPG 知识库(`~/rag-lpg-obsidian`,约 768 个 .md 文件)。
+
+CodeGraph(colbymchenry/codegraph)是代码知识图谱工具,其核心机制经源码分析确认:
+
+1. **双信号排序**:FTS5/bm25 文本信号 + Random-Walk-with-Restart(个性化 PageRank,α=0.25)图结构信号。纯文本命中但调用图不连通的符号自然沉底
+2. **悬空引用留痕**:`unresolved_refs` 表记录解析失败的引用(failed + name_tail),下次同步有新符号出现时自动重试
+3. **三层新鲜度**:文件事件 + (size, mtime, content-hash) 对账 + 连接时追平;增量同步成本只与改动量成正比
+4. **输出预算控制**:explore 一次调用返回相关源码原文(带字节预算)+ 调用路径 + 影响面,明确标注"原文直出,agent 无需再读文件"
+5. **边可信度**:边带 `provenance` 字段(静态解析 vs 启发式合成),消费方一眼区分
+6. **低价值内容降权**:generated 文件、测试文件在排序中降权垫底
+
+kb-cli 现状痛点(源码分析确认):
+
+| 痛点 | 位置 |
+|------|------|
+| 排序纯文本加权,无图结构信号 | `internal/search/engine.go` |
+| aliases 已解析但搜索完全未用 | `internal/vault/parser.go` / `internal/index/fts.go` |
+| 索引新鲜度靠 git commit hash 比对,未 commit 的编辑不可见 | `internal/index/cache.go` |
+| 索引变更即全量重建,无增量 | `cmd/index.go` / `cmd/rebuild.go` |
+| wikilink 模糊匹配(去编号前缀/标题部分匹配)可能产生假边,无可信度标注 | `internal/graph/builder.go` |
+| 悬空 wikilink 直接丢弃,无留痕无自动补全 | `internal/graph/builder.go` |
+| 搜索结果全量 dump 内容,无预算控制 | `internal/search/engine.go` WithContent |
+
+## 目标
+
+1. 搜索排序引入图结构信号,售后场景(症状词查询)召回质量显著提升
+2. 悬空双链自动补全:新文档入库后历史文档指向它的链接自动解析
+3. 增量同步:未 commit 的编辑在下次 search/index 时可见,变更只重解析变更文件
+4. `explore` 命令供 Hermes agent 一次调用拿到完整上下文(原文 + 关联 + 预算控制)
+5. 边带可信度标注,排序与展示区分精确边和启发式边
+
+## 非目标
+
+- 不引入文件事件 watcher(知识库写入频率低,命令触发式对账足够;codegraph 的 watcher 面向高频编辑的代码库)
+- 不引入 embedding / LLM 参与排序(RWR 是确定性算法,零 API 成本,保持可复现)
+- 不改变 vault 目录结构和编号规则
+- 不改 draft/classify/review 等 LLM 录入链路
+
+## 设计
+
+### 1. 搜索质量层
+
+#### 1.1 RWR 图结构排序
+
+新增 `internal/graph/rwr.go`:
+
+- **种子**:FTS5 命中的节点 ID 集合(去重,上限 20 个)
+- **邻接**:无向图。边类型纳入排序:wikilink(全权重)、entity(全权重)、tag(半权重——tag 虚拟节点扇出大,区分度低)
+- **算法**:power iteration,restart 概率 α=0.25(种子均匀分布),收敛阈值 1e-6,上限 50 次迭代。768 节点规模下单次计算 <1ms
+- **输出**:`map[nodeID]float64`(游走质量),归一化到 [0,1]
+
+最终排序分(`internal/search/engine.go` 修改):
+
+```
+finalScore = norm(textScore) * textWeight + rwrMass * (1 - textWeight)
+```
+
+- `norm(textScore)`:本批次内 min-max 归一化到 [0,1](textScore 沿用现有位置加权:path/title/tag/content + 通用词/实体词降权;批次只有 1 条结果时 norm=1)
+- `rwrMass`:已归一化 [0,1]
+- textWeight 可配置(config.yaml `search.text_weight`,默认 0.5),两信号量纲对齐后直接加权
+
+#### 1.2 aliases 进 FTS
+
+- `nodes` 表加列 `aliases TEXT`(JSON 数组),parser 已解析 frontmatter aliases,直接落库
+- FTS5 虚拟表改为 external-content 模式(见 §3.2),aliases 列纳入
+- bm25 列权重:`aliases 4.0, title 3.0, content 1.0`(aliases 是 FAQ 症状词主战场,最高权重)
+- `FTSSearch` 对 aliases 列的命中在 textScore 中按 title 档计权
+
+#### 1.3 status 降权
+
+- `nodes` 表加列 `status TEXT`(parser 从 frontmatter 提取,缺省视为"已解决")
+- 排序系数:status ∈ {草稿, 待确认, 跟进中} 或 section = `待审阅` 时 finalScore × 0.6
+- 结果展示中标注 status(`--json` 输出含 status 字段)
+
+### 2. 图谱质量层
+
+#### 2.1 边可信度(provenance)
+
+`edges` 表加列 `provenance TEXT`:
+
+| 值 | 含义 |
+|----|------|
+| `exact` | wikilink 文本 == 目标标题,或 == 文件名(去 .md 和编号前缀)精确匹配 |
+| `fuzzy` | 标题包含关系等模糊匹配(现有 `matchesWikilink` 的非精确分支) |
+| `tag` / `entity` | tag/entity 虚拟边(非 wikilink,不适用可信度概念,但统一存值) |
+
+消费规则:RWR 邻接中 fuzzy 边权重 ×0.5;`graph query` / `graph related` 输出标注 provenance。
+
+#### 2.2 悬空链接留痕 + 自动补全
+
+新增表:
+
+```sql
+CREATE TABLE IF NOT EXISTS unresolved_links (
+    id INTEGER PRIMARY KEY AUTOINCREMENT,
+    from_node INTEGER NOT NULL,
+    link_text TEXT NOT NULL,
+    name_tail TEXT NOT NULL,       -- link_text 去掉 [[ 内的锚点/标题修饰后的尾部
+    status TEXT NOT NULL DEFAULT 'pending',  -- pending | failed
+    created_at TEXT DEFAULT (datetime('now')),
+    last_attempt TEXT
+);
+CREATE INDEX idx_unresolved_tail ON unresolved_links(name_tail) WHERE status='failed';
+```
+
+- **索引时**:wikilink 未匹配到任何节点 → 插入行(status=pending);匹配到但为 fuzzy → 建边(provenance=fuzzy)
+- **重试时机**:每次增量同步(§3.1)后执行 `retryUnresolved`:
+  1. 对本批新增/变更节点,以其 title 和文件名(去编号前缀)查 `unresolved_links` 的 name_tail 精确命中
+  2. 命中且满足 exact 规则 → 建边(provenance=exact),删行
+  3. 命中仅 fuzzy 规则 → 建边(provenance=fuzzy),删行;同时输出提示"建议将双链改为精确标题"
+  4. 全量重建(`--force`)时清空表并重新解析
+- **效果**:新文档入库 → 历史文档中指向它的悬空双链自动补全,无需人工回改
+
+### 3. 增量同步层
+
+#### 3.1 对账机制(替代 commit hash 全量重建)
+
+`internal/index/cache.go` 重写为 `reconcile.go`:
+
+1. 扫描 vault 得到文件清单 `{path: (size, mtime, sha256)}`(sha256 仅对 size/mtime 变化的文件计算,避免全库哈希开销)
+2. 与 nodes 表记录比对,分四类:
+   - **新增**:vault 有、库中没有 → 解析、建节点、建边
+   - **修改**:size/mtime 变化且 sha256 不同 → 重解析该文件,更新节点行,删该文件相关边后重建(tag/entity 边 + 该文件作为 from 的 wikilink 边);该文件作为 to 的边不受影响
+   - **删除**:库中有、vault 没有 → 删节点,ON DELETE CASCADE 删相关边;unresolved_links 中 from_node 指向它的行一并删除
+   - **未变**:跳过
+3. 执行 `retryUnresolved`(§2.2)
+4. `meta.git_commit` 仍记录(供 status 展示),但**不再作为重建判据**
+
+`index build` 默认走增量对账路径;`index build --force` 保留全量重建(ClearData + 重建 + 清空 unresolved_links)。
+
+`NeedsRebuild` 逻辑废弃:`search` 命令的 pre-flight 改为"快速对账"——只比对 size/mtime(不读内容不哈希),有差异才触发完整对账。768 文件 stat 开销 <50ms。
+
+#### 3.2 FTS external-content + 触发器
+
+```sql
+CREATE VIRTUAL TABLE nodes_fts USING fts5(
+    title, content, tags, aliases,
+    content='nodes', content_rowid='id',
+    tokenize='unicode61'
+);
+-- 触发器:nodes 的 INSERT/DELETE/UPDATE 同步维护 nodes_fts
+```
+
+- 删除 `PopulateFTS` 全量重建路径,增量维护
+- `--force` 全量重建时执行 `INSERT INTO nodes_fts(nodes_fts) VALUES('rebuild')` 一次性重灌
+
+#### 3.3 schema 迁移
+
+新增 `schema_versions` 表(同 codegraph)。`Open` 时按版本顺序应用迁移:
+
+- v1 → 现有 schema(老库兼容)
+- v2:nodes 加 aliases/status 列;edges 加 provenance 列;unresolved_links 建表;FTS 改 external-content(rebuild 重灌)
+
+迁移幂等(列已存在则跳过),老库自动升级,无需手动重建。
+
+### 4. explore 命令
+
+`cmd/explore.go`(新):
+
+```
+kb-cli explore <问题> [--budget 16000] [--top 5] [--json]
+```
+
+流程:
+
+1. 查询词按空白切分为关键词,走 §1 的 FTS + RWR 双信号排序
+2. 取 top N(默认 5)文档,按 `--budget`(默认 16000 字节,硬上限 32000)分配预算:按 finalScore 降序依次分配,每文档至少 800 字节,超预算的文档截断到整段边界
+3. **段落级截取**:文档按 markdown 标题(`#`~`####`)切段,只输出命中查询词的段落(整段不截半句);文档总长 < 分配预算时整篇输出
+4. 关联清单:每个入选文档的 wikilink 目标(exact 边优先,fuzzy 边标注)
+5. 悬空链接提示:入选文档中未解析的 wikilink 列表
+6. 尾部标注:`以上为文档原文直出(含行号),agent 无需再读文件`;`--json` 输出结构化
+
+预算诊断:`KB_EXPLORE_DEBUG=1` 时 stderr 输出每文档分配/实际/占比表(对标 codegraph 的 explore 诊断,默认关闭且不影响输出字节)。
+
+### 5. 配置
+
+config.yaml 新增:
+
+```yaml
+search:
+  text_weight: 0.5      # 文本分权重(图分权重 = 1 - text_weight)
+explore:
+  default_budget: 16000 # 默认字节预算
+  hard_budget: 32000    # 硬上限
+  top_n: 5
+```
+
+## 数据流
+
+```
+vault 扫描(stat对账) → 变更文件解析(parser) → 节点/边增量写入(SQLite+FTS触发器)
+                                        ↘ 悬空链接入 unresolved_links
+查询: 关键词 → FTS5(bm25) → 种子 → RWR(图质量) → 双信号排序 → [search 列表 | explore 原文+关联]
+```
+
+## 测试计划
+
+**单元测试**(Go test):
+- `rwr_test.go`:收敛性、种子外孤立节点质量≈0、tag 半权重生效
+- `reconcile_test.go`:新增/修改/删除/未变四场景,边重建正确性
+- `unresolved_test.go`:悬空入表 → 新节点入库 → 自动解析建边删行
+- `migration_test.go`:v1 老库 → v2 迁移幂等,FTS 重灌后查询正确
+- `explore_test.go`:预算分配、段落截取不截半句、硬上限
+
+**端到端验证**(`~/rag-lpg-obsidian` 768 文件实库):
+1. 旧版索引 vs 新版索引,同一组售后查询(含症状词,如"红绿闪""充气失败")排序对比:目标文档 rank 提升
+2. 增量同步耗时 vs 全量重建耗时
+3. 未 commit 编辑:修改一个文档后直接 `kb-cli search`,新内容可见
+4. 悬空补全:新建一个文档标题匹配既有悬空链接,确认双链自动建边
+5. `explore` 输出在预算内且原文与文件一致(diff 校验)
+
+## 实施顺序
+
+1. schema v2 迁移 + FTS external-content(基础设施,其他全部依赖它)
+2. 增量对账(reconcile)
+3. RWR + 双信号排序 + aliases + status 降权
+4. provenance + unresolved_links 自动补全
+5. explore 命令
+6. 端到端验证 + 文档(README 命令更新)
+
+## 风险与缓解
+
+| 风险 | 缓解 |
+|------|------|
+| RWR 权重调不当导致排序退化 | text_weight 可配置;端到端用固定查询集回归对比,退化即回调 |
+| 增量对账边界 case(git mv、文件重命名) | 重命名 = 删+增,正确性不受影响;mtime 未变但内容变的极端 case 由 sha256 二次确认兜底 |
+| 老库迁移破坏现有索引 | 迁移前自动备份 .db 文件(cp 到 .db.bak);迁移失败可回滚 |
+| explore 段落截取对无标题结构的文档失效 | 无标题文档整篇输出(受预算约束) |

--
Gitblit v1.10.0