ai_xiaopei
9 days ago 4cd5499d60a851a59723067e74a4a92f7254b8e9
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
package cmd
 
import (
    "fmt"
 
    "github.com/aisim/kb-cli/internal/classify"
    "github.com/aisim/kb-cli/internal/llm"
    "github.com/spf13/cobra"
)
 
var classifyCmd = &cobra.Command{
    Use:   "classify",
    Short: "使用 LLM 对知识库文档进行分类",
    Long:  `# classify - LLM 分类
kb-cli classify [--batch-size=<数量>] [--dry-run] # 使用 LLM 分析文档,提取平台、设备、内容类型`,
    RunE: runClassify,
}
 
var (
    classifyBatchSize int
    classifyDryRun    bool
)
 
func init() {
    rootCmd.AddCommand(classifyCmd)
    classifyCmd.Flags().IntVar(&classifyBatchSize, "batch-size", 10, "每批处理的文档数量")
    classifyCmd.Flags().BoolVar(&classifyDryRun, "dry-run", false, "仅显示将要执行的操作")
}
 
func runClassify(cmd *cobra.Command, args []string) error {
    vaultPath := cmd.Flag("vault").Value.String()
 
    // 创建 LLM 客户端(从配置文件加载)
    llmClient := llm.NewClient()
 
    fmt.Println("步骤 1/3: 提取实体信息...")
    entities, relations, err := classify.ExtractEntities(vaultPath)
    if err != nil {
        return fmt.Errorf("提取实体失败: %w", err)
    }
 
    if !classifyDryRun {
        if err := classify.SaveEntities(vaultPath, entities, relations); err != nil {
            return fmt.Errorf("保存实体失败: %w", err)
        }
        fmt.Printf("✓ 已生成 entities.json 和 relations.json\n")
    } else {
        fmt.Printf("[DRY-RUN] 将生成 entities.json 和 relations.json\n")
    }
 
    fmt.Println("\n步骤 2/3: 扫描文档...")
    docs, err := classify.ScanDocuments(vaultPath)
    if err != nil {
        return fmt.Errorf("扫描文档失败: %w", err)
    }
    fmt.Printf("✓ 发现 %d 个文档\n", len(docs))
 
    if len(docs) == 0 {
        fmt.Println("警告: 未发现任何文档")
        return nil
    }
 
    fmt.Println("\n步骤 3/3: 使用 LLM 分类...")
    if classifyDryRun {
        fmt.Printf("[DRY-RUN] 将对 %d 个文档进行分类(批次大小: %d)\n", len(docs), classifyBatchSize)
        return nil
    }
 
    // 分批处理
    var allResults []classify.Classification
    for i := 0; i < len(docs); i += classifyBatchSize {
        end := i + classifyBatchSize
        if end > len(docs) {
            end = len(docs)
        }
 
        batch := docs[i:end]
        fmt.Printf("处理批次 %d-%d / %d...\n", i+1, end, len(docs))
 
        results, err := classify.ClassifyBatch(batch, llmClient)
        if err != nil {
            fmt.Printf("警告: 批次 %d 分类失败: %v\n", i/classifyBatchSize+1, err)
            continue
        }
 
        allResults = append(allResults, results...)
    }
 
    if len(allResults) == 0 {
        return fmt.Errorf("分类失败: 未获得任何结果")
    }
 
    if err := classify.SaveClassification(vaultPath, allResults); err != nil {
        return fmt.Errorf("保存分类结果失败: %w", err)
    }
    fmt.Printf("✓ 已生成 classification.json(%d 个文档)\n", len(allResults))
 
    return nil
}