chore(desktop): remove dead active-learning frontend code
Zero imports across codebase — never wired to any UI or Tauri command. Rust Growth crate handles memory/summary generation instead.
This commit is contained in:
@@ -1,369 +0,0 @@
|
|||||||
/**
|
|
||||||
* 主动学习引擎 - 从用户交互中学习并改进 Agent 行为
|
|
||||||
*
|
|
||||||
* 提供学习事件记录、模式提取和建议生成功能。
|
|
||||||
* Phase 1: 内存存储,Zustand 持久化
|
|
||||||
* Phase 2: SQLite + 向量化存储
|
|
||||||
*/
|
|
||||||
|
|
||||||
import {
|
|
||||||
type LearningEvent,
|
|
||||||
type LearningPattern,
|
|
||||||
type LearningSuggestion,
|
|
||||||
type LearningEventType,
|
|
||||||
type FeedbackSentiment,
|
|
||||||
} from '../types/active-learning';
|
|
||||||
import { generateRandomString } from './crypto-utils';
|
|
||||||
|
|
||||||
// === 常量 ===
|
|
||||||
|
|
||||||
const MAX_EVENTS = 1000;
|
|
||||||
const PATTERN_CONFIDENCE_THRESHOLD = 0.7;
|
|
||||||
const SUGGESTION_COOLDOWN_HOURS = 2;
|
|
||||||
|
|
||||||
// === 生成 ID ===
|
|
||||||
|
|
||||||
function generateEventId(): string {
|
|
||||||
return `le-${Date.now()}-${generateRandomString(8)}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
// === 分析反馈情感 ===
|
|
||||||
|
|
||||||
export function analyzeSentiment(text: string): FeedbackSentiment {
|
|
||||||
const positive = ['好的', '很棒', '谢谢', '完美', 'excellent', '喜欢', '爱了', 'good', 'great', 'nice', '满意'];
|
|
||||||
const negative = ['不好', '差', '糟糕', '错误', 'wrong', 'bad', '不喜欢', '讨厌', '问题', '失败', 'fail', 'error'];
|
|
||||||
|
|
||||||
const lowerText = text.toLowerCase();
|
|
||||||
|
|
||||||
if (positive.some(w => lowerText.includes(w.toLowerCase()))) return 'positive';
|
|
||||||
if (negative.some(w => lowerText.includes(w.toLowerCase()))) return 'negative';
|
|
||||||
return 'neutral';
|
|
||||||
}
|
|
||||||
|
|
||||||
// === 分析学习类型 ===
|
|
||||||
|
|
||||||
export function analyzeEventType(text: string): LearningEventType {
|
|
||||||
const lowerText = text.toLowerCase();
|
|
||||||
|
|
||||||
if (lowerText.includes('纠正') || lowerText.includes('不对') || lowerText.includes('修改')) {
|
|
||||||
return 'correction';
|
|
||||||
}
|
|
||||||
if (lowerText.includes('喜欢') || lowerText.includes('偏好') || lowerText.includes('风格')) {
|
|
||||||
return 'preference';
|
|
||||||
}
|
|
||||||
if (lowerText.includes('场景') || lowerText.includes('上下文') || lowerText.includes('情况')) {
|
|
||||||
return 'context';
|
|
||||||
}
|
|
||||||
if (lowerText.includes('总是') || lowerText.includes('经常') || lowerText.includes('习惯')) {
|
|
||||||
return 'behavior';
|
|
||||||
}
|
|
||||||
return 'implicit';
|
|
||||||
}
|
|
||||||
|
|
||||||
// === 推断偏好 ===
|
|
||||||
|
|
||||||
export function inferPreference(feedback: string, sentiment: FeedbackSentiment): string {
|
|
||||||
if (sentiment === 'positive') {
|
|
||||||
if (feedback.includes('简洁')) return '用户偏好简洁的回复';
|
|
||||||
if (feedback.includes('详细')) return '用户偏好详细的回复';
|
|
||||||
if (feedback.includes('快速')) return '用户偏好快速响应';
|
|
||||||
return '用户对当前回复风格满意';
|
|
||||||
}
|
|
||||||
if (sentiment === 'negative') {
|
|
||||||
if (feedback.includes('太长')) return '用户偏好更短的回复';
|
|
||||||
if (feedback.includes('太短')) return '用户偏好更详细的回复';
|
|
||||||
if (feedback.includes('不准确')) return '用户偏好更准确的信息';
|
|
||||||
return '用户对当前回复风格不满意';
|
|
||||||
}
|
|
||||||
return '用户反馈中性';
|
|
||||||
}
|
|
||||||
|
|
||||||
// === 学习引擎类 ===
|
|
||||||
|
|
||||||
export class ActiveLearningEngine {
|
|
||||||
private events: LearningEvent[] = [];
|
|
||||||
private patterns: LearningPattern[] = [];
|
|
||||||
// Reserved for future learning suggestions feature
|
|
||||||
private suggestions: LearningSuggestion[] = [];
|
|
||||||
private initialized: boolean = false;
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
this.initialized = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Get current suggestions (reserved for future use) */
|
|
||||||
getSuggestions(): LearningSuggestion[] {
|
|
||||||
return this.suggestions;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Check if engine is initialized */
|
|
||||||
isInitialized(): boolean {
|
|
||||||
return this.initialized;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 记录学习事件
|
|
||||||
*/
|
|
||||||
recordEvent(
|
|
||||||
event: Omit<LearningEvent, 'id' | 'timestamp' | 'acknowledged' | 'appliedCount'>
|
|
||||||
): LearningEvent {
|
|
||||||
// 检查重复事件
|
|
||||||
const existing = this.events.find(e =>
|
|
||||||
e.agentId === event.agentId &&
|
|
||||||
e.messageId === event.messageId &&
|
|
||||||
e.type === event.type
|
|
||||||
);
|
|
||||||
|
|
||||||
if (existing) {
|
|
||||||
// 更新现有事件
|
|
||||||
existing.observation += ' | ' + event.observation;
|
|
||||||
existing.confidence = (existing.confidence + event.confidence) / 2;
|
|
||||||
existing.appliedCount++;
|
|
||||||
return existing;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 创建新事件
|
|
||||||
const newEvent: LearningEvent = {
|
|
||||||
...event,
|
|
||||||
id: generateEventId(),
|
|
||||||
timestamp: Date.now(),
|
|
||||||
acknowledged: false,
|
|
||||||
appliedCount: 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
this.events.push(newEvent);
|
|
||||||
this.extractPatterns(newEvent);
|
|
||||||
|
|
||||||
// 保持事件数量限制
|
|
||||||
if (this.events.length > MAX_EVENTS) {
|
|
||||||
this.events = this.events.slice(-MAX_EVENTS);
|
|
||||||
}
|
|
||||||
|
|
||||||
return newEvent;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 从反馈中学习
|
|
||||||
*/
|
|
||||||
learnFromFeedback(
|
|
||||||
agentId: string,
|
|
||||||
messageId: string,
|
|
||||||
feedback: string,
|
|
||||||
context?: string
|
|
||||||
): LearningEvent {
|
|
||||||
const sentiment = analyzeSentiment(feedback);
|
|
||||||
const type = analyzeEventType(feedback);
|
|
||||||
|
|
||||||
return this.recordEvent({
|
|
||||||
type,
|
|
||||||
agentId,
|
|
||||||
messageId,
|
|
||||||
trigger: context || 'User feedback',
|
|
||||||
observation: feedback,
|
|
||||||
context,
|
|
||||||
inferredPreference: inferPreference(feedback, sentiment),
|
|
||||||
confidence: sentiment === 'positive' ? 0.8 : sentiment === 'negative' ? 0.5 : 0.3,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 提取学习模式
|
|
||||||
*/
|
|
||||||
private extractPatterns(event: LearningEvent): void {
|
|
||||||
// 1. 正面反馈 -> 偏好正面回复
|
|
||||||
if (event.observation.includes('谢谢') || event.observation.includes('好的')) {
|
|
||||||
this.addPattern({
|
|
||||||
id: `pat-${Date.now()}-${generateRandomString(8)}`,
|
|
||||||
type: 'preference',
|
|
||||||
pattern: 'positive_response_preference',
|
|
||||||
description: '用户偏好正面回复风格',
|
|
||||||
examples: [event.observation],
|
|
||||||
confidence: 0.8,
|
|
||||||
agentId: event.agentId,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. 纠正 -> 需要更精确
|
|
||||||
if (event.type === 'correction') {
|
|
||||||
this.addPattern({
|
|
||||||
id: `pat-${Date.now()}-${generateRandomString(8)}`,
|
|
||||||
type: 'preference',
|
|
||||||
pattern: 'precision_preference',
|
|
||||||
description: '用户对精确性有更高要求',
|
|
||||||
examples: [event.observation],
|
|
||||||
confidence: 0.9,
|
|
||||||
agentId: event.agentId,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. 上下文相关 -> 场景偏好
|
|
||||||
if (event.context) {
|
|
||||||
this.addPattern({
|
|
||||||
id: `pat-${Date.now()}-${generateRandomString(8)}`,
|
|
||||||
type: 'context',
|
|
||||||
pattern: 'context_aware',
|
|
||||||
description: 'Agent 需要关注上下文',
|
|
||||||
examples: [event.context],
|
|
||||||
confidence: 0.6,
|
|
||||||
agentId: event.agentId,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 添加学习模式
|
|
||||||
*/
|
|
||||||
private addPattern(pattern: Omit<LearningPattern, 'updatedAt'>): void {
|
|
||||||
const existing = this.patterns.find(p =>
|
|
||||||
p.type === pattern.type &&
|
|
||||||
p.pattern === pattern.pattern &&
|
|
||||||
p.agentId === pattern.agentId
|
|
||||||
);
|
|
||||||
|
|
||||||
if (existing) {
|
|
||||||
// 增强置信度
|
|
||||||
existing.confidence = Math.min(1, existing.confidence + pattern.confidence * 0.1);
|
|
||||||
existing.examples.push(pattern.examples[0]);
|
|
||||||
existing.updatedAt = Date.now();
|
|
||||||
} else {
|
|
||||||
this.patterns.push({
|
|
||||||
...pattern,
|
|
||||||
updatedAt: Date.now(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 生成学习建议
|
|
||||||
*/
|
|
||||||
generateSuggestions(agentId: string): LearningSuggestion[] {
|
|
||||||
const suggestions: LearningSuggestion[] = [];
|
|
||||||
const now = Date.now();
|
|
||||||
|
|
||||||
// 获取该 Agent 的模式
|
|
||||||
const agentPatterns = this.patterns.filter(p => p.agentId === agentId);
|
|
||||||
|
|
||||||
for (const pattern of agentPatterns) {
|
|
||||||
// 检查冷却时间
|
|
||||||
const hoursSinceUpdate = (now - (pattern.updatedAt || now)) / (1000 * 60 * 60);
|
|
||||||
if (hoursSinceUpdate < SUGGESTION_COOLDOWN_HOURS) continue;
|
|
||||||
|
|
||||||
// 检查置信度阈值
|
|
||||||
if (pattern.confidence < PATTERN_CONFIDENCE_THRESHOLD) continue;
|
|
||||||
|
|
||||||
// 生成建议
|
|
||||||
suggestions.push({
|
|
||||||
id: `sug-${Date.now()}-${generateRandomString(8)}`,
|
|
||||||
agentId,
|
|
||||||
type: pattern.type,
|
|
||||||
pattern: pattern.pattern,
|
|
||||||
suggestion: this.generateSuggestionContent(pattern),
|
|
||||||
confidence: pattern.confidence,
|
|
||||||
createdAt: now,
|
|
||||||
expiresAt: new Date(now + 7 * 24 * 60 * 60 * 1000),
|
|
||||||
dismissed: false,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return suggestions;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 生成建议内容
|
|
||||||
*/
|
|
||||||
private generateSuggestionContent(pattern: LearningPattern): string {
|
|
||||||
const templates: Record<string, string> = {
|
|
||||||
positive_response_preference:
|
|
||||||
'用户似乎偏好正面回复。建议在回复时保持积极和确认的语气。',
|
|
||||||
precision_preference:
|
|
||||||
'用户对精确性有更高要求。建议在提供信息时更加详细和准确。',
|
|
||||||
context_aware:
|
|
||||||
'Agent 需要关注上下文。建议在回复时考虑对话的背景和历史。',
|
|
||||||
};
|
|
||||||
|
|
||||||
return templates[pattern.pattern] || `观察到模式: ${pattern.pattern}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取统计信息
|
|
||||||
*/
|
|
||||||
getStats(agentId: string) {
|
|
||||||
const agentEvents = this.events.filter(e => e.agentId === agentId);
|
|
||||||
const agentPatterns = this.patterns.filter(p => p.agentId === agentId);
|
|
||||||
|
|
||||||
const eventsByType: Record<LearningEventType, number> = {
|
|
||||||
preference: 0,
|
|
||||||
correction: 0,
|
|
||||||
context: 0,
|
|
||||||
feedback: 0,
|
|
||||||
behavior: 0,
|
|
||||||
implicit: 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
for (const event of agentEvents) {
|
|
||||||
eventsByType[event.type]++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
totalEvents: agentEvents.length,
|
|
||||||
eventsByType,
|
|
||||||
totalPatterns: agentPatterns.length,
|
|
||||||
avgConfidence: agentPatterns.length > 0
|
|
||||||
? agentPatterns.reduce((sum, p) => sum + p.confidence, 0) / agentPatterns.length
|
|
||||||
: 0,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取所有事件
|
|
||||||
*/
|
|
||||||
getEvents(agentId?: string): LearningEvent[] {
|
|
||||||
if (agentId) {
|
|
||||||
return this.events.filter(e => e.agentId === agentId);
|
|
||||||
}
|
|
||||||
return [...this.events];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取所有模式
|
|
||||||
*/
|
|
||||||
getPatterns(agentId?: string): LearningPattern[] {
|
|
||||||
if (agentId) {
|
|
||||||
return this.patterns.filter(p => p.agentId === agentId);
|
|
||||||
}
|
|
||||||
return [...this.patterns];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 确认事件
|
|
||||||
*/
|
|
||||||
acknowledgeEvent(eventId: string): void {
|
|
||||||
const event = this.events.find(e => e.id === eventId);
|
|
||||||
if (event) {
|
|
||||||
event.acknowledged = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 清除事件
|
|
||||||
*/
|
|
||||||
clearEvents(agentId: string): void {
|
|
||||||
this.events = this.events.filter(e => e.agentId !== agentId);
|
|
||||||
this.patterns = this.patterns.filter(p => p.agentId !== agentId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// === 单例实例 ===
|
|
||||||
|
|
||||||
let engineInstance: ActiveLearningEngine | null = null;
|
|
||||||
|
|
||||||
export function getActiveLearningEngine(): ActiveLearningEngine {
|
|
||||||
if (!engineInstance) {
|
|
||||||
engineInstance = new ActiveLearningEngine();
|
|
||||||
}
|
|
||||||
return engineInstance;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function resetActiveLearningEngine(): void {
|
|
||||||
engineInstance = null;
|
|
||||||
}
|
|
||||||
@@ -1,59 +0,0 @@
|
|||||||
/**
|
|
||||||
* 主动学习引擎类型定义
|
|
||||||
*/
|
|
||||||
|
|
||||||
export type LearningEventType =
|
|
||||||
| 'preference'
|
|
||||||
| 'correction'
|
|
||||||
| 'context'
|
|
||||||
| 'feedback'
|
|
||||||
| 'behavior'
|
|
||||||
| 'implicit';
|
|
||||||
|
|
||||||
export type FeedbackSentiment = 'positive' | 'negative' | 'neutral';
|
|
||||||
|
|
||||||
export interface LearningEvent {
|
|
||||||
id: string;
|
|
||||||
type: LearningEventType;
|
|
||||||
agentId: string;
|
|
||||||
messageId: string;
|
|
||||||
timestamp: number;
|
|
||||||
trigger: string;
|
|
||||||
observation: string;
|
|
||||||
context?: string;
|
|
||||||
inferredPreference?: string;
|
|
||||||
confidence: number;
|
|
||||||
acknowledged: boolean;
|
|
||||||
appliedCount: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface LearningPattern {
|
|
||||||
id: string;
|
|
||||||
type: LearningEventType;
|
|
||||||
pattern: string;
|
|
||||||
description: string;
|
|
||||||
examples: string[];
|
|
||||||
confidence: number;
|
|
||||||
agentId: string;
|
|
||||||
updatedAt: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface LearningSuggestion {
|
|
||||||
id: string;
|
|
||||||
agentId: string;
|
|
||||||
type: LearningEventType;
|
|
||||||
pattern: string;
|
|
||||||
suggestion: string;
|
|
||||||
confidence: number;
|
|
||||||
createdAt: number;
|
|
||||||
expiresAt?: Date;
|
|
||||||
dismissed: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ActiveLearningState {
|
|
||||||
events: LearningEvent[];
|
|
||||||
patterns: LearningPattern[];
|
|
||||||
suggestions: LearningSuggestion[];
|
|
||||||
isEnabled: boolean;
|
|
||||||
lastProcessed: number;
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user