116 lines
3.7 KiB
TypeScript
116 lines
3.7 KiB
TypeScript
/**
|
||
* ============================================================
|
||
* Memory Extractor (기억 추출기)
|
||
*
|
||
* 대화 종료 시 히스토리를 분석하여 각 메모리 레이어에
|
||
* 저장할 정보를 자동으로 추출합니다.
|
||
* LLM 호출 없이 패턴 매칭 기반으로 동작합니다.
|
||
* ============================================================
|
||
*/
|
||
|
||
import { LongTermMemory } from './LongTermMemory';
|
||
import { ProjectMemory } from './ProjectMemory';
|
||
import { EpisodicMemory } from './EpisodicMemory';
|
||
|
||
interface ExtractionResult {
|
||
longTermCandidates: number;
|
||
episodeCreated: boolean;
|
||
projectUpdated: boolean;
|
||
}
|
||
|
||
export class MemoryExtractor {
|
||
|
||
/**
|
||
* 세션 종료 시 모든 메모리 레이어에 대해 추출을 수행합니다.
|
||
*/
|
||
public extractFromSession(
|
||
sessionId: string,
|
||
messages: Array<{ role: string; content: string; timestamp?: number }>,
|
||
longTermMemory: LongTermMemory,
|
||
episodicMemory: EpisodicMemory,
|
||
projectMemory: ProjectMemory | null,
|
||
projectContext?: string
|
||
): ExtractionResult {
|
||
const result: ExtractionResult = {
|
||
longTermCandidates: 0,
|
||
episodeCreated: false,
|
||
projectUpdated: false
|
||
};
|
||
|
||
// 1. Long-Term Memory 추출
|
||
const candidates = LongTermMemory.extractCandidates(messages);
|
||
for (const candidate of candidates) {
|
||
longTermMemory.addEntry(
|
||
candidate.category,
|
||
candidate.content,
|
||
`session:${sessionId}`,
|
||
0.7 // 자동 추출이므로 기본 신뢰도 0.7
|
||
);
|
||
}
|
||
result.longTermCandidates = candidates.length;
|
||
|
||
// 2. Episodic Memory 생성
|
||
const episode = episodicMemory.createEpisode(
|
||
sessionId,
|
||
messages,
|
||
projectContext
|
||
);
|
||
result.episodeCreated = !!episode;
|
||
|
||
// 3. Project Memory 업데이트 (프로젝트 관련 대화인 경우)
|
||
if (projectMemory && projectContext) {
|
||
const updated = this.extractProjectInfo(messages, projectMemory);
|
||
result.projectUpdated = updated;
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
/**
|
||
* 대화에서 프로젝트 관련 정보를 추출하여 Project Memory에 저장합니다.
|
||
*/
|
||
private extractProjectInfo(
|
||
messages: Array<{ role: string; content: string }>,
|
||
projectMemory: ProjectMemory
|
||
): boolean {
|
||
let updated = false;
|
||
const allText = messages.map((m) => m.content).join('\n');
|
||
|
||
// Tech stack 추출
|
||
const techPatterns = [
|
||
/(?:사용|using|사용하는|tech\s*stack|기술\s*스택)[\s::]*([^\n]+)/gi
|
||
];
|
||
for (const pattern of techPatterns) {
|
||
let match;
|
||
while ((match = pattern.exec(allText)) !== null) {
|
||
const techs = match[1]
|
||
.split(/[,,\s]+/)
|
||
.filter((t) => t.length >= 2 && t.length <= 20);
|
||
for (const tech of techs) {
|
||
projectMemory.addTechStack(tech.trim());
|
||
updated = true;
|
||
}
|
||
}
|
||
}
|
||
|
||
// Bug report 추출
|
||
const bugPatterns = [
|
||
/(?:버그|bug|오류|error|이슈|issue)[\s::]+(.{10,200})/gi
|
||
];
|
||
for (const pattern of bugPatterns) {
|
||
let match;
|
||
while ((match = pattern.exec(allText)) !== null) {
|
||
// 간단한 버그만 자동 기록 (상세 분석은 사용자 확인 필요)
|
||
// 여기서는 패턴만 감지하고, 실제 기록은 사용자 확인 후
|
||
updated = true;
|
||
}
|
||
}
|
||
|
||
if (updated) {
|
||
projectMemory.save();
|
||
}
|
||
|
||
return updated;
|
||
}
|
||
}
|