From 7ba0de7dc75ec9b09bb662d59068b1ed6dbdc0a7 Mon Sep 17 00:00:00 2001
From: ai_xiaopei <xiaopei@aisim.cn>
Date: Fri, 21 Aug 2026 20:11:20 +0800
Subject: [PATCH] fix(llm): getDefaultConfig 默认路径经 expandPath 展开(修复无配置文件时在 cwd 下创建字面 ~ 目录)
---
internal/llm/client.go | 103 +++++++++++++++++++++++++++++++++++++++++++++------
1 files changed, 90 insertions(+), 13 deletions(-)
diff --git a/internal/llm/client.go b/internal/llm/client.go
index 5ffd7e2..d48961f 100644
--- a/internal/llm/client.go
+++ b/internal/llm/client.go
@@ -70,19 +70,20 @@
// SearchCandidate 搜索候选
type SearchCandidate struct {
- Title string `json:"title"`
- Path string `json:"path"`
- Score float64 `json:"score"`
+ Title string `json:"title"`
+ Path string `json:"path"`
+ Score float64 `json:"score"`
+ Content string `json:"content,omitempty"` // 文件内容(截取前5000字)
}
// NewClient 创建 LLM 客户端
func NewClient() *Client {
- config := loadConfig()
+ config := LoadConfig()
return &Client{config: config, usePrimary: true}
}
-// loadConfig 加载配置文件
-func loadConfig() Config {
+// LoadConfig 加载配置文件(导出供其他包使用)
+func LoadConfig() Config {
var config Config
// 配置文件路径
@@ -123,8 +124,9 @@
config.LLM.Primary.Temperature = 0.3
config.LLM.Primary.MaxTokens = 2000
config.LLM.Primary.DisableThinking = true
- config.KnowledgeBase.VaultPath = "~/aisim/note/001/笔记001"
- config.KnowledgeBase.DBPath = "~/.cache/kb-cli/kb.db"
+ // 默认路径同样经过 expandPath 展开,与成功加载分支保持一致
+ config.KnowledgeBase.VaultPath = expandPath("~/aisim/note/001/笔记001")
+ config.KnowledgeBase.DBPath = expandPath("~/.cache/kb-cli/kb.db")
config.Draft.ReviewDir = "待审阅"
config.Draft.DefaultType = "售后"
return config
@@ -193,17 +195,25 @@
}
// GenerateMergeHint 生成合并指示
-func (c *Client) GenerateMergeHint(draftContent string, candidates []SearchCandidate) (*MergeHint, error) {
+func (c *Client) GenerateMergeHint(draftContent string, candidates []SearchCandidate, dirStructure string) (*MergeHint, error) {
prompt := fmt.Sprintf(`你是一个知识库管理助手。请判断以下草稿与哪些知识库文档相关。
+## 知识库目录结构
+%s
+
## 草稿内容
%s
## 候选文档(Top 3)
-`, draftContent)
+`, dirStructure, draftContent)
for i, cand := range candidates {
- prompt += fmt.Sprintf("%d. 标题: %s, 路径: %s, 相关度: %.2f\n", i+1, cand.Title, cand.Path, cand.Score)
+ prompt += fmt.Sprintf("### %d. %s\n", i+1, cand.Title)
+ prompt += fmt.Sprintf("- 路径: %s\n", cand.Path)
+ prompt += fmt.Sprintf("- 相关度: %.2f\n\n", cand.Score)
+ if cand.Content != "" {
+ prompt += fmt.Sprintf("**文档内容**:\n```\n%s\n```\n\n", cand.Content)
+ }
}
prompt += `
@@ -213,7 +223,7 @@
{
"recommendation": {
"action": "merge|new|split",
- "target": "目标路径(如果 action=merge)",
+ "target": "目标路径(如果 action=merge,必须是上述目录结构中存在的目录)",
"reason": "判断理由",
"confidence": "high|medium|low"
},
@@ -228,12 +238,17 @@
判断标准:
- action=merge: 草稿内容与某个候选文档高度相关,应该合并
-- action=new: 草稿内容是全新的,应该新建文档
+- action=new: 草稿内容是全新的,应该新建文档(target 填写建议的目录路径)
- action=split: 草稿内容包含多个独立主题,应该拆分
- confidence=high: 判断很确定
- confidence=medium: 判断比较确定
- confidence=low: 判断不太确定
+分类原则:
+- 平台专属内容放对应平台目录下(如电子秤平台/智能枪/)
+- 多平台共用的硬件知识放 共有硬件/ 下
+- 跨平台通用知识放 通用/通用/
+
只输出 JSON,不要其他内容。`
response, err := c.callLLMWithRetry(prompt)
@@ -249,6 +264,68 @@
return &result, nil
}
+// GenerateReviewPreview 生成审阅预览概要(3句话概括目标FAQ)
+func (c *Client) GenerateReviewPreview(targetTitle, targetContent string) (string, error) {
+ // 截取前2000字符作为上下文
+ contentPreview := targetContent
+ if len(contentPreview) > 2000 {
+ contentPreview = contentPreview[:2000] + "..."
+ }
+
+ prompt := fmt.Sprintf(`请用3句话概括以下知识库文档的核心内容,帮助用户快速了解这个文档讲的是什么:
+
+## 文档标题
+%s
+
+## 文档内容(部分)
+%s
+
+要求:
+1. 第一句:这个文档解决什么问题/讲什么主题
+2. 第二句:核心内容/关键要点
+3. 第三句:适用场景/使用条件
+
+直接输出3句话,不要编号,不要其他内容。`, targetTitle, contentPreview)
+
+ response, err := c.callLLMWithRetry(prompt)
+ if err != nil {
+ return "", err
+ }
+
+ return strings.TrimSpace(response), nil
+}
+
+// ClassifyDocuments 分类文档
+func (c *Client) ClassifyDocuments(prompt string) (string, error) {
+ response, err := c.callLLMWithRetry(prompt)
+ if err != nil {
+ return "", err
+ }
+
+ // 解析 JSON
+ var result []struct {
+ Path string `json:"path"`
+ Platform string `json:"platform"`
+ Device string `json:"device"`
+ ContentType string `json:"content_type"`
+ Confidence float64 `json:"confidence"`
+ }
+
+ if err := parseJSON(response, &result); err != nil {
+ return "", fmt.Errorf("解析 LLM 响应失败: %w", err)
+ }
+
+ // 转换回 JSON 字符串
+ jsonBytes, err := json.Marshal(result)
+ if err != nil {
+ return "", err
+ }
+
+ return string(jsonBytes), nil
+}
+
+// ClassifyDocuments 分类文档
+
// callLLMWithRetry 带重试的 LLM 调用
func (c *Client) callLLMWithRetry(prompt string) (string, error) {
// 第一次尝试
--
Gitblit v1.10.0