feat: integrate unified RAG pipeline and bump version to 2.60.0

This commit is contained in:
g1nation
2026-05-04 11:00:01 +09:00
parent 0515dd625d
commit 445d530b63
16 changed files with 2178 additions and 112 deletions
+64 -75
View File
@@ -37,6 +37,8 @@ import {
renderSecondBrainTraceMarkdown,
SecondBrainTrace
} from './features/secondBrainTrace';
import { MemoryManager } from './memory';
import { RetrievalOrchestrator } from './retrieval';
export interface ChatMessage {
role: 'user' | 'assistant' | 'system';
@@ -80,6 +82,8 @@ export class AgentExecutor {
private transactionManager: TransactionManager;
private sessionManager: SessionManager;
private statusBarManager: StatusBarManager;
private memoryManager: MemoryManager;
private retrievalOrchestrator: RetrievalOrchestrator;
private currentTaskId: string = 'default_session';
constructor(
@@ -88,6 +92,17 @@ export class AgentExecutor {
this.transactionManager = new TransactionManager();
this.sessionManager = new SessionManager(this.context);
this.statusBarManager = new StatusBarManager();
// Initialize 5-Layer Cognitive Memory System
const activeBrain = getActiveBrainProfile();
this.memoryManager = new MemoryManager(activeBrain.localBrainPath, {
enabled: getConfig().memoryEnabled,
shortTermLimit: getConfig().memoryShortTermMessages,
});
// Initialize RAG Pipeline Orchestrator
this.retrievalOrchestrator = new RetrievalOrchestrator();
this.restoreLastSession();
}
@@ -142,6 +157,10 @@ export class AgentExecutor {
}
public clearHistory() {
// Extract memories before clearing
if (this.chatHistory.length > 2) {
this.onSessionEnd();
}
this.chatHistory = [];
this.emitHistoryChanged();
}
@@ -156,6 +175,10 @@ export class AgentExecutor {
public resetConversation() {
this.stop();
// Extract memories before resetting
if (this.chatHistory.length > 2) {
this.onSessionEnd();
}
this.chatHistory = [];
this.emitHistoryChanged();
}
@@ -1692,84 +1715,30 @@ export class AgentExecutor {
const config = getConfig();
if (!config.memoryEnabled) return '';
// Update memory manager config in case settings changed
this.memoryManager.updateConfig({
enabled: config.memoryEnabled,
shortTermLimit: config.memoryShortTermMessages,
});
const visibleHistory = this.chatHistory.filter((message) => !message.internal);
const shortTerm = visibleHistory
.slice(-config.memoryShortTermMessages)
.map((message) => `- ${message.role}: ${summarizeText(message.content, 260)}`)
.join('\n');
const workspaceFolders = vscode.workspace.workspaceFolders;
const workspacePath = workspaceFolders ? workspaceFolders[0].uri.fsPath : undefined;
const savedSessions = this.context.globalState.get<any[]>('chat_sessions', []) || [];
const mediumTerm = savedSessions
.slice(0, config.memoryMediumTermSessions)
.map((session: any) => {
const title = summarizeText(String(session?.title || 'Untitled session'), 120);
const lastMessage = Array.isArray(session?.history)
? session.history[session.history.length - 1]?.content || ''
: '';
return `- ${title}: ${summarizeText(String(lastMessage), 220)}`;
})
.join('\n');
// Use the Unified RAG Pipeline
const result = this.retrievalOrchestrator.retrieve(currentPrompt, {
brain: activeBrain,
memoryManager: this.memoryManager,
workspacePath,
chatHistory: visibleHistory,
contextBudget: {
totalBudget: 8000,
retrievalRatio: 0.4
},
brainFileLimit: config.memoryLongTermFiles
});
const longTerm = this.findRelevantBrainMemory(currentPrompt, config.memoryLongTermFiles, activeBrain);
const sections = [
shortTerm ? `### Short-Term Memory\n${shortTerm}` : '',
mediumTerm ? `### Medium-Term Memory\n${mediumTerm}` : '',
longTerm ? `### Long-Term Memory\n${longTerm}` : ''
].filter(Boolean).join('\n\n');
if (!sections) return '';
return [
'',
'[MEMORY CONTEXT]',
'Review this layered memory before preparing the answer. Use it only when relevant, and prefer the current user request when there is conflict.',
sections
].join('\n');
}
private findRelevantBrainMemory(currentPrompt: string, limit: number, activeBrain: BrainProfile): string {
if (limit <= 0) return '';
try {
const files = findBrainFiles(activeBrain.localBrainPath);
const terms = currentPrompt
.toLowerCase()
.split(/[^a-z0-9가-힣_]+/g)
.filter((term) => term.length >= 2)
.slice(0, 24);
const scored = files.map((file) => {
let score = 0;
const basename = path.basename(file).toLowerCase();
for (const term of terms) {
if (basename.includes(term)) score += 4;
}
let preview = '';
try {
const content = fs.readFileSync(file, 'utf8');
const lower = content.toLowerCase();
for (const term of terms) {
if (lower.includes(term)) score += 1;
}
preview = summarizeText(content, 360);
} catch {
preview = '';
}
const stat = fs.existsSync(file) ? fs.statSync(file) : undefined;
return { file, score, preview, mtime: stat?.mtimeMs || 0 };
});
return scored
.sort((a, b) => (b.score - a.score) || (b.mtime - a.mtime))
.slice(0, limit)
.map((entry) => `- ${path.relative(activeBrain.localBrainPath, entry.file)}: ${entry.preview}`)
.join('\n');
} catch (error: any) {
logError('Failed to build long-term memory context.', { error: error?.message || String(error) });
return '';
}
return this.retrievalOrchestrator.buildContextString(result);
}
private emitHistoryChanged() {
@@ -1787,6 +1756,26 @@ export class AgentExecutor {
});
}
/**
* 세션 종료 시 5-Layer Memory에 자동 추출을 수행합니다.
* 새 채팅 시작 또는 Extension 비활성화 시 호출됩니다.
*/
public onSessionEnd(): void {
try {
const workspaceFolders = vscode.workspace.workspaceFolders;
const workspacePath = workspaceFolders ? workspaceFolders[0].uri.fsPath : undefined;
this.memoryManager.onSessionEnd(
this.currentTaskId,
this.chatHistory.filter((m) => !m.internal),
workspacePath
);
logInfo('Memory extraction completed for session end.', { taskId: this.currentTaskId });
} catch (error: any) {
logError('Memory extraction failed on session end.', { error: error?.message || String(error) });
}
}
private async createStreamingRequest(params: {
baseUrl: string;
modelName: string;