This repository has been archived on 2026-08-12. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
Cyrene/backend/ai-core/internal/subsession/knowledge_provider.go
T
AskaEth fd44b15d81 feat: 全链路优化 — 死锁修复、MD3主题、上下文持久化、群聊自然化、打字状态、知识库
**死锁根因修复**
- periodicThinkLoop:1015 orphaned lock → 删除(字段已原子化)
- RecordUserMessage 隔离为 recordMu
- atomic.Int64 替换 lastUserMessage/lastThinkTime 等

**MD3 / Android 17 主题**
- 毛玻璃卡片 (backdrop-filter)
- MD3 色彩令牌 (pink primary #f472b6)
- icons.js 独立矢量图标库 + 运行时 emoji 替换
- 无边框卡片、圆角按钮、阴影层次

**上下文持久化**
- AddMessage → saveToDB 异步写 PostgreSQL
- LoadFromDB 恢复 (admin-session-main + 懒加载)
- LLMMessage.Timestamp 字段

**群聊与适配器**
- group_ambient 模式: 非@消息让 LLM 自己判断是否插话
- 戳一戳动作消息总是回复
- NapCat 打字状态 (set_input_status, 最小3秒显示)
- HTTP API 配置 (http_url/http_token)

**知识库 & 防编造**
- knowledge.CanHandle 对 chat 意图也触发
- 关键词预筛选避免无关 embedding 调用
- persona + synthesizer 三重诚实规则
- 工具结果持久化到会话历史

**平台桥接器**
- detached:true Go进程独立存活
- ethend 重启自动接管已运行服务
- stop() 接管模式 taskkill/F/ PID
- Windows netstat 替代 fuser 获取 PID
- 重复适配器种子逻辑修复
- 失败转发日志 Direction: error

**崩溃诊断**
- crashlog 包 (Recover + WrapHTTP + LLMCall)
- /api/v1/debug/goroutines 端点
- thinker 操作日志 + 30s stats
- 日志写入 logs/ 目录持久化

Co-Authored-By: Claude <noreply@anthropic.com>
2026-06-28 11:29:29 +08:00

112 lines
3.2 KiB
Go

package subsession
import (
"context"
"fmt"
"strings"
"time"
"git.yeij.top/AskaEth/Cyrene/ai-core/internal/model"
"git.yeij.top/AskaEth/Cyrene/ai-core/internal/rag"
"git.yeij.top/AskaEth/Cyrene/pkg/logger"
)
// KnowledgeProvider searches the knowledge base for relevant information.
type KnowledgeProvider struct {
retriever *rag.Retriever
}
// NewKnowledgeProvider creates a knowledge subsession provider.
func NewKnowledgeProvider(retriever *rag.Retriever) *KnowledgeProvider {
return &KnowledgeProvider{retriever: retriever}
}
func (p *KnowledgeProvider) Type() model.SubSessionType {
return model.SubSessionKnowledge
}
// knowledgeKeywords are trigger words from _index.md. Only run expensive embedding search if message matches.
var knowledgeKeywords = []string{
"翁法罗斯", "泰坦", "城邦", "黑潮", "帝皇权杖", "黄金裔",
"白厄", "阿格莱雅", "缇宝", "万敌", "那刻夏", "遐蝶", "风堇", "赛飞儿", "海瑟音", "刻律德菈",
"哀丽秘榭", "昔涟", "星神", "浮黎", "轮回", "始源命途", "无漏净子",
"剧情", "结局", "逐火", "盗火", "火种", "奥赫玛", "来古士",
"世界观", "设定", "哲学", "浪漫", "哀怜", "有瑕",
}
func (p *KnowledgeProvider) CanHandle(_ context.Context, intent *model.IntentResult, userMessage string) bool {
if intent == nil {
return true
}
switch intent.Primary {
case "knowledge", "technical", "how_to", "factual", "research":
return true
case "chat":
// 仅当消息包含知识库相关关键词时才触发检索,避免每次聊天都跑 embedding
msg := strings.ToLower(userMessage)
for _, kw := range knowledgeKeywords {
if strings.Contains(msg, strings.ToLower(kw)) {
return true
}
}
return false
}
return true
}
func (p *KnowledgeProvider) Priority() int {
return 3
}
func (p *KnowledgeProvider) Timeout() time.Duration {
return 15 * time.Second
}
func (p *KnowledgeProvider) CreateContext(ctx context.Context, params CreateContextParams) ([]model.LLMMessage, error) {
return []model.LLMMessage{
{Role: model.RoleSystem, Content: "知识库检索子会话"},
{Role: model.RoleUser, Content: params.UserMessage},
}, nil
}
func (p *KnowledgeProvider) Execute(ctx context.Context, subCtx []model.LLMMessage) (*model.SubSessionResult, error) {
userMessage := ""
for i := len(subCtx) - 1; i >= 0; i-- {
if subCtx[i].Role == model.RoleUser {
userMessage = subCtx[i].Content
break
}
}
if userMessage == "" {
return nil, fmt.Errorf("无法提取用户消息")
}
result := &model.SubSessionResult{
Type: model.SubSessionKnowledge,
Confidence: 0,
}
if p.retriever == nil {
result.Summary = "(知识库未就绪)"
return result, nil
}
retrieval, err := p.retriever.Retrieve(ctx, userMessage, 3)
if err != nil {
logger.Printf("[knowledge-subsession] 知识检索失败: %v", err)
result.Error = fmt.Sprintf("检索失败: %v", err)
result.Summary = "(知识库检索失败)"
return result, nil
}
if len(retrieval.Results) == 0 {
result.Summary = "(未找到相关知识)"
return result, nil
}
result.Summary = retrieval.Summary
result.Confidence = 0.6
logger.Printf("[knowledge-subsession] 完成: 找到 %d 条知识", len(retrieval.Results))
return result, nil
}