ai_xiaopei
6 days ago 1196f409d86bc61e7596eb274840244a62ce84ba
internal/llm/client.go
@@ -73,16 +73,17 @@
   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
   // 配置文件路径
@@ -193,17 +194,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 +222,7 @@
{
  "recommendation": {
    "action": "merge|new|split",
    "target": "目标路径(如果 action=merge)",
    "target": "目标路径(如果 action=merge,必须是上述目录结构中存在的目录)",
    "reason": "判断理由",
    "confidence": "high|medium|low"
  },
@@ -228,11 +237,16 @@
判断标准:
- action=merge: 草稿内容与某个候选文档高度相关,应该合并
- action=new: 草稿内容是全新的,应该新建文档
- action=new: 草稿内容是全新的,应该新建文档(target 填写建议的目录路径)
- action=split: 草稿内容包含多个独立主题,应该拆分
- confidence=high: 判断很确定
- confidence=medium: 判断比较确定
- confidence=low: 判断不太确定
分类原则:
- 平台专属内容放对应平台目录下(如电子秤平台/智能枪/)
- 多平台共用的硬件知识放 共有硬件/ 下
- 跨平台通用知识放 通用/通用/
只输出 JSON,不要其他内容。`
@@ -249,6 +263,37 @@
   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)