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