| New file |
| | |
| | | # 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` |