feat(auto-reply): 优化自动回复逻辑和知识库功能

- 将默认回复详细程度从"detailed"调整为"medium",前后端保持一致
- 新增话题切换检测逻辑,当用户主动要求换话题时提供引导回复
- 优化上下文处理机制,仅在指代型追问时注入历史对话,避免模型复读旧内容
- 改进知识库检索逻辑,区分自包含问题和指代型问题的上下文需求
- 完善知识库完整性指令,确保回复详细程度与知识展开程度一致
- 重构知识库重建逻辑,支持递归扫描子目录中的文件,修复索引为空的问题
- 增强素材匹配算法,引入强信号检测机制,避免仅凭模糊匹配误发素材
- 新增素材开场白AI生成功能,支持图片、视频、文档等类型智能描述
- 改进知识库重建通知,显示具体的文件数、分片数及失败统计信息
This commit is contained in:
2026-06-26 14:25:35 +08:00
parent 1517be2a25
commit 849090a627
12 changed files with 809 additions and 40 deletions

View File

@@ -11,6 +11,7 @@ import (
"fmt"
"html"
"io"
"io/fs"
"math"
"os"
"os/exec"
@@ -111,19 +112,20 @@ func (e *AutoReplyEngine) rebuildKnowledgeIndex() (*KnowledgeIndex, error) {
if err := os.MkdirAll(root, 0755); err != nil {
return nil, err
}
entries, err := os.ReadDir(root)
if err != nil {
return nil, err
}
for _, entry := range entries {
if entry.IsDir() {
continue
// 递归遍历子目录filepath.WalkDir知识库常按分类分文件夹组织
// (如 01_产品与设备/、03_售后支持/01_故障排查/),与素材扫描保持一致。
// 仅扫根目录会漏掉所有子目录文件,导致索引为空、向量召回失败。
walkErr := filepath.WalkDir(root, func(path string, d fs.DirEntry, err error) error {
if err != nil {
return nil // 单个条目出错跳过,不中断整体重建
}
ext := strings.ToLower(filepath.Ext(entry.Name()))
if !isRootKnowledgeFile(entry.Name(), ext, allowed, cfg.Knowledge.IndexPath, cfg.Retrieval.EmbeddingIndexPath) {
continue
if d.IsDir() {
return nil
}
ext := strings.ToLower(filepath.Ext(d.Name()))
if !isRootKnowledgeFile(d.Name(), ext, allowed, cfg.Knowledge.IndexPath, cfg.Retrieval.EmbeddingIndexPath) {
return nil
}
path := filepath.Join(root, entry.Name())
chunks, err := parseKnowledgeFile(path, root)
if err != nil {
var warning knowledgeParseWarning
@@ -133,15 +135,19 @@ func (e *AutoReplyEngine) rebuildKnowledgeIndex() (*KnowledgeIndex, error) {
}
} else {
idx.FailedFiles = append(idx.FailedFiles, fmt.Sprintf("%s: %v", path, err))
continue
return nil
}
}
if len(chunks) == 0 {
idx.FailedFiles = append(idx.FailedFiles, fmt.Sprintf("%s: 未读取到可索引内容", path))
continue
return nil
}
idx.FileCount++
idx.Chunks = append(idx.Chunks, chunks...)
return nil
})
if walkErr != nil {
return nil, walkErr
}
idx.LastIndexedAt = time.Now().Unix()
indexPath := resolveAutoReplyPath(cfg.Knowledge.IndexPath)