ai_xiaopei
9 days ago dc838ce88af4a9437550255092db81e9dbff8c56
docs: add kb-cli enhancement design spec

- Add --with-content and --with-links flags
- Scheme A: minimal changes to SearchResult struct
- Update kb-search skill to prioritize kb-cli
1 files added
441 ■■■■■ changed files
docs/superpowers/specs/2026-07-26-kb-cli-enhancement-design.md 441 ●●●●● patch | view | raw | blame | history
docs/superpowers/specs/2026-07-26-kb-cli-enhancement-design.md
New file
@@ -0,0 +1,441 @@
# kb-cli 增强与 kb-search 技能调整设计
**日期:** 2026-07-26
**状态:** 已批准
---
## 1. 背景与目标
### 1.1 背景
kb-cli(Go 实现)已完成基础功能,与 Python 版 kb-search.py 对比测试显示:
- **kb-cli 优势:** 索引正常(768 文件),搜索快速(8ms),结果准确
- **kb-search.py 问题:** 索引为空(update-index 返回 0 条),搜索返回空结果
用户决策:优先使用 kb-cli,Python 脚本暂时保留作为备选。
### 1.2 目标
1. **给 kb-cli 增加两个功能:**
   - `--with-content`:搜索时返回完整文件内容
   - `--with-links`:搜索时显示关联文档链接
2. **调整 kb-search 技能:**
   - 优先使用 kb-cli
   - Python 脚本降级为备选方案
---
## 2. 方案选择
### 2.1 候选方案
| 方案 | 描述 | 优点 | 缺点 |
|------|------|------|------|
| **A. 最小改动** | 在 SearchResult 结构体上加可选字段 | 改动小,向后兼容,逻辑清晰 | 无 |
| B. 分层增强 | 创建 Enhancer 接口,搜索管道模式 | 扩展性强 | 过度设计 |
| C. 独立命令 | 新增 `kb search-detail` 子命令 | 职责清晰 | 重复代码多 |
### 2.2 决策
**选择方案 A:最小改动**
理由:
1. 改动最小,只改 `search.go` 和 `output/formatter.go`
2. 向后兼容,现有脚本不受影响
3. 逻辑简单,容易理解和维护
4. 不需要过度设计
---
## 3. 数据结构改动
### 3.1 SearchResult 结构体
**文件:** `internal/search/engine.go`
```go
type SearchResult struct {
    ID      int64    `json:"id"`
    Path    string   `json:"path"`
    Title   string   `json:"title"`
    Section string   `json:"section"`
    Score   int      `json:"score"`
    // 新增可选字段
    Content string   `json:"content,omitempty"` // --with-content 时填充
    Links   []string `json:"links,omitempty"`   // --with-links 时填充
}
```
### 3.2 SearchOptions 结构体
**文件:** `internal/search/engine.go`
```go
type SearchOptions struct {
    Expanded    []string
    Symptom     []string
    TopN        int
    WithContent bool   // 新增
    WithLinks   bool   // 新增
}
```
### 3.3 Search 函数逻辑
```go
func Search(store *index.Store, keywords []string, opts SearchOptions) ([]SearchResult, error) {
    // 1. 执行 FTS5 搜索
    ftsResults, err := store.FTSSearch(allKeywords, 100)
    if err != nil {
        return nil, err
    }
    // 2. 评分和排序
    results := scoreAndSort(ftsResults, keywords, opts)
    // 3. 限制返回数量
    if opts.TopN > 0 && len(results) > opts.TopN {
        results = results[:opts.TopN]
    }
    // 4. 增强结果(新增)
    for i := range results {
        // 获取内容
        if opts.WithContent {
            content, _, _, err := store.GetNodeContent(results[i].ID)
            if err == nil {
                results[i].Content = content
            }
        }
        // 获取关联链接
        if opts.WithLinks {
            links, err := store.GetNodeLinks(results[i].ID)
            if err == nil {
                results[i].Links = links
            }
        }
    }
    return results, nil
}
```
---
## 4. 数据获取逻辑
### 4.1 内容获取
**已有方法:** `store.GetNodeContent(id)`
```go
func (s *Store) GetNodeContent(id int64) (string, []string, []string, error)
```
返回:`(content, tags, entities, error)`
### 4.2 关联链接获取
**新增方法:** `store.GetNodeLinks(nodeID)`
```go
func (s *Store) GetNodeLinks(nodeID int64) ([]string, error) {
    query := `
        SELECT n.path
        FROM edges e
        JOIN nodes n ON n.id = e.to_node
        WHERE e.from_node = ? AND e.relation = 'wikilink'
    `
    rows, err := s.db.Query(query, nodeID)
    if err != nil {
        return nil, err
    }
    defer rows.Close()
    var links []string
    for rows.Next() {
        var path string
        if err := rows.Scan(&path); err != nil {
            return nil, err
        }
        links = append(links, path)
    }
    return links, nil
}
```
**查询逻辑:**
- 查询 `edges` 表,`relation='wikilink'`
- 返回目标节点的 `path`(即关联文档路径)
---
## 5. CLI 命令改动
### 5.1 新增 flag
**文件:** `cmd/search.go`
```go
var (
    withContent bool
    withLinks   bool
)
func init() {
    // ... 现有 flag
    searchCmd.Flags().BoolVar(&withContent, "with-content", false, "返回完整文件内容")
    searchCmd.Flags().BoolVar(&withLinks, "with-links", false, "显示关联文档链接")
}
```
### 5.2 传递给 SearchOptions
```go
func runSearch(cmd *cobra.Command, args []string) error {
    opts := search.SearchOptions{
        Expanded:    expanded,
        Symptom:     symptom,
        TopN:        topN,
        WithContent: withContent,
        WithLinks:   withLinks,
    }
    results, err := search.Search(store, args, opts)
    // ...
}
```
---
## 6. 输出格式化改动
### 6.1 表格输出
**文件:** `internal/output/formatter.go`
**基础输出(无增强):**
```
路径                    标题              板块    得分
--------------------------------------------------
FAQ/充装类/087-xxx.md   扫码验证气瓶充装   FAQ     6
```
**带内容(--with-content):**
```
路径                    标题              板块    得分
--------------------------------------------------
FAQ/充装类/087-xxx.md   扫码验证气瓶充装   FAQ     6
--- 内容 ---
# 扫码验证气瓶充装
## 问题描述
...
```
**带链接(--with-links):**
```
路径                    标题              板块    得分
--------------------------------------------------
FAQ/充装类/087-xxx.md   扫码验证气瓶充装   FAQ     6
--- 关联文档 ---
- 典型案例/充装异常案例.md
- 文档/电子秤平台/平台文档/023-充装统计与票据核对.md
```
**两者都有(--with-content --with-links):**
```
路径                    标题              板块    得分
--------------------------------------------------
FAQ/充装类/087-xxx.md   扫码验证气瓶充装   FAQ     6
--- 内容 ---
# 扫码验证气瓶充装
...
--- 关联文档 ---
- 典型案例/充装异常案例.md
```
### 6.2 JSON 输出
```json
[
  {
    "id": 1577,
    "path": "FAQ/充装类/087-xxx.md",
    "title": "扫码验证气瓶充装",
    "section": "FAQ",
    "score": 6,
    "content": "# 扫码验证气瓶充装\n\n## 问题描述\n...",
    "links": [
      "典型案例/充装异常案例.md",
      "文档/电子秤平台/平台文档/023-充装统计与票据核对.md"
    ]
  }
]
```
---
## 7. kb-search 技能调整
### 7.1 优先级策略
1. **优先使用 kb-cli:** `kb search "关键词" --with-content --top 3`
2. **降级到 kb-search.py:** 仅当 kb-cli 不可用时
### 7.2 技能文档改动
**核心工具:**
- 从 `kb-search.py` 改为 `kb`
**命令示例:**
```bash
# 基础搜索
kb search "充装规格" --top 5
# 带内容
kb search "充装规格" --with-content --top 3
# 带链接
kb search "充装规格" --with-links --top 3
# 带扩展词和症状词
kb search "充装" --expanded "重量 规格" --symptom "报错 无法启动" --with-content --top 3
```
**保留内容:**
- `--expanded`、`--symptom` 用法说明
- 搜索策略(术语映射、关键词选择)
- 最佳实践(决策树、案例)
**移到降级方案:**
- kb-search.py 完整用法移到"降级方案"章节
### 7.3 降级方案
```markdown
## 降级方案
仅当 kb-cli 不可用时,使用 kb-search.py:
```bash
export KB_VAULT=/home/aisim-p/aisim/note/001/笔记001
python3 ~/.hermes/skills/kb-knowledge/kb-search/scripts/kb-search.py search "关键词" --with-content --top 3
```
注意:kb-search.py 当前有索引问题,可能返回空结果。
```
---
## 8. 测试计划
### 8.1 单元测试
- 测试 `SearchResult` 结构体的 JSON 序列化(`omitempty` 行为)
- 测试 `GetNodeLinks` 方法
### 8.2 集成测试
```bash
# 测试基础搜索
kb search "充装" --top 5
# 测试带内容
kb search "充装" --with-content --top 3
# 测试带链接
kb search "充装" --with-links --top 3
# 测试两者都有
kb search "充装" --with-content --with-links --top 3
# 测试 JSON 输出
kb search "充装" --with-content --json --top 3
```
### 8.3 对比测试
与 Python 版对比:
```bash
# Go 版
kb search "充装" --with-content --top 3
# Python 版
python3 kb-search.py search "充装" --with-content --top 3
```
---
## 9. 实施步骤
1. **数据结构改动**
   - 修改 `SearchResult` 结构体
   - 修改 `SearchOptions` 结构体
   - 实现 `GetNodeLinks` 方法
2. **搜索逻辑改动**
   - 修改 `Search` 函数,增加内容获取和链接获取逻辑
3. **CLI 命令改动**
   - 新增 `--with-content` 和 `--with-links` flag
   - 传递给 `SearchOptions`
4. **输出格式化改动**
   - 修改 `FormatTable` 和 `FormatJSON` 方法
   - 支持内容和链接的输出
5. **测试**
   - 单元测试
   - 集成测试
   - 对比测试
6. **技能文档更新**
   - 更新 kb-search 技能文档
   - 调整优先级策略
---
## 10. 风险与缓解
| 风险 | 缓解措施 |
|------|----------|
| 内容获取慢(大量文件) | 限制 `--top N`,默认 10 |
| 链接查询慢 | 使用索引,查询 `edges` 表 |
| JSON 输出过大 | `omitempty` 标签,不填充时不输出 |
| 向后兼容 | 新增字段为可选,不影响现有脚本 |
---
## 11. 成功标准
1. ✅ kb-cli 支持 `--with-content` 和 `--with-links`
2. ✅ 输出格式正确(表格和 JSON)
3. ✅ 性能可接受(< 100ms)
4. ✅ kb-search 技能文档更新完成
5. ✅ 对比测试通过(优于 Python 版)
---
## 12. 附录
### 12.1 相关文件
- `internal/search/engine.go` - 搜索逻辑
- `internal/index/sqlite.go` - 数据库操作
- `internal/output/formatter.go` - 输出格式化
- `cmd/search.go` - CLI 命令
- `~/.hermes/skills/kb-knowledge/kb-search/SKILL.md` - 技能文档
### 12.2 参考
- Python 版 kb-search.py:`~/.hermes/skills/kb-knowledge/kb-search/scripts/kb-search.py`
- SQLite 数据模型:`docs/superpowers/specs/2026-07-26-kb-cli-design.md`