fix: platform_silent记忆提取 + 群聊上下文整合 + 多QQ实例支持
- platform_silent模式接入Orchestrator记忆提取:被动观察群聊时提取值得记住的信息到对应命名空间 - post_chat后台思考注入平台观察:对话后思考也能看到群聊摘要 - QQ适配器:OneBot v11 self_id动态捕获、CQ图片URL提取、视觉+OCR并行处理 - Router解耦:ConfigName/PlatformName分离,支持多QQ实例独立连接 - 黑白名单功能:后端API + Ethend代理 + UI面板 - \n\n双换行断句:AI回复按双换行分割为多条消息按间隔发送 - @提及修复:bot自感知UID进行@检测 - 群聊上下文共享:channel-based userID避免记忆碎片化 - 消息日志显示处理后内容而非原始SSE数据 - platform-bridge Dockerfile + docker-compose.yml更新 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -34,16 +34,28 @@ func (e *Extractor) ExtractAndStore(ctx context.Context, userID, sessionID, user
|
||||
logger.Printf("[memory] 记忆提取失败: %v", err)
|
||||
return
|
||||
}
|
||||
e.storeMemories(ctx, userID, sessionID, memories)
|
||||
}
|
||||
|
||||
// ExtractObservations 从观察到的单条消息中提取记忆(无语境回复)。
|
||||
// 用于 platform_silent 模式:昔涟被动观察群聊,提取值得记住的信息。
|
||||
func (e *Extractor) ExtractObservations(ctx context.Context, userID, sessionID, message string) {
|
||||
memories, err := e.extractObservations(ctx, message)
|
||||
if err != nil {
|
||||
logger.Printf("[memory] 观察记忆提取失败: %v", err)
|
||||
return
|
||||
}
|
||||
e.storeMemories(ctx, userID, sessionID, memories)
|
||||
}
|
||||
|
||||
func (e *Extractor) storeMemories(ctx context.Context, userID, sessionID string, memories []model.MemoryEntry) {
|
||||
for _, mem := range memories {
|
||||
mem.UserID = userID
|
||||
mem.SessionID = sessionID
|
||||
mem.Source = "conversation"
|
||||
|
||||
// 去重检查:查询用户已有的相关记忆
|
||||
existing, err := e.findSimilar(ctx, userID, &mem)
|
||||
if err == nil && existing != nil {
|
||||
// 相似度 > 80%,更新现有记忆
|
||||
e.mergeMemory(ctx, existing, &mem)
|
||||
continue
|
||||
}
|
||||
@@ -56,6 +68,58 @@ func (e *Extractor) ExtractAndStore(ctx context.Context, userID, sessionID, user
|
||||
}
|
||||
}
|
||||
|
||||
// extractObservations 从观察到的消息中提取记忆(无助手回复)
|
||||
func (e *Extractor) extractObservations(ctx context.Context, message string) ([]model.MemoryEntry, error) {
|
||||
if e.llmChat != nil {
|
||||
return e.extractObservationsWithLLM(ctx, message)
|
||||
}
|
||||
return e.extractWithRules(message, ""), nil
|
||||
}
|
||||
|
||||
// extractObservationsWithLLM 使用LLM从观察到的消息中提取值得记住的信息
|
||||
func (e *Extractor) extractObservationsWithLLM(ctx context.Context, message string) ([]model.MemoryEntry, error) {
|
||||
prompt := fmt.Sprintf(`分析以下在聊天平台观察到的消息,提取值得记住的信息作为记忆。
|
||||
|
||||
观察到的消息: %s
|
||||
|
||||
请以JSON格式返回提取的记忆。这条消息来自群聊/频道,昔涟只是旁观者。
|
||||
提取角度:这条消息中包含了什么关于聊天参与者、讨论主题、事件或氛围的信息?
|
||||
|
||||
每条记忆需要包含以下字段:
|
||||
- content: 完整的记忆内容(一句话描述,客观准确)
|
||||
- summary: 简短摘要(10字以内)
|
||||
- category: 记忆分类,必须是以下之一:
|
||||
* conversation: 对话主题/讨论摘要
|
||||
* event: 事件记录(发生了什么)
|
||||
* personal_info: 参与者的个人信息
|
||||
* knowledge: 知识性信息
|
||||
* user_preference: 某人的偏好
|
||||
* task: 提及的计划/任务
|
||||
- priority: 优先级 (0=临时, 1=普通, 2=重要, 3=核心)
|
||||
- importance: 重要程度 1-10
|
||||
* 1-3: 日常闲聊,不太重要
|
||||
* 4-6: 一般有用的信息
|
||||
* 7-8: 重要信息,值得长期记住
|
||||
* 9-10: 核心信息
|
||||
- keywords: 关键词标签数组(3-5个词)
|
||||
|
||||
只提取有意义的信息。如果消息只是日常寒暄或无实质内容,返回空数组。
|
||||
|
||||
输出格式:
|
||||
{"memories": [{"content": "...", "summary": "...", "category": "...", "priority": 1, "importance": 6, "keywords": ["词1", "词2"]}]}
|
||||
`, message)
|
||||
|
||||
resp, err := e.llmChat(ctx, []model.LLMMessage{
|
||||
{Role: "system", Content: "你是一个聊天观察记录助手。你只输出JSON格式的结果。你的任务是从观察到的聊天消息中提取值得记住的信息。"},
|
||||
{Role: "user", Content: prompt},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("LLM提取观察记忆失败: %w", err)
|
||||
}
|
||||
|
||||
return e.parseExtractionResult(resp.Content)
|
||||
}
|
||||
|
||||
// extract 从对话中提取记忆
|
||||
func (e *Extractor) extract(ctx context.Context, userMessage, assistantResponse string) ([]model.MemoryEntry, error) {
|
||||
// 如果有LLM,使用LLM提取
|
||||
@@ -128,11 +192,18 @@ func (e *Extractor) extractWithLLM(ctx context.Context, userMessage, assistantRe
|
||||
return nil, fmt.Errorf("LLM提取记忆失败: %w", err)
|
||||
}
|
||||
|
||||
// 解析JSON
|
||||
entries, err := e.parseExtractionResult(resp.Content)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return entries, nil
|
||||
}
|
||||
|
||||
// parseExtractionResult 解析LLM返回的记忆提取JSON结果
|
||||
func (e *Extractor) parseExtractionResult(text string) ([]model.MemoryEntry, error) {
|
||||
result := MemoryExtractionResult{}
|
||||
content := extractJSON(resp.Content)
|
||||
content := extractJSON(text)
|
||||
if err := json.Unmarshal([]byte(content), &result); err != nil {
|
||||
// 尝试作为数组解析(兼容旧格式)
|
||||
var arrResult []ExtractedMemory
|
||||
if err2 := json.Unmarshal([]byte(content), &arrResult); err2 != nil {
|
||||
return nil, fmt.Errorf("解析记忆JSON失败: %w (原始: %s)", err, content[:minint(len(content), 100)])
|
||||
|
||||
Reference in New Issue
Block a user