Initial spark-mind: heartbeat + memory + persona + mind on Spark X2.5-4B

This commit is contained in:
n1kt4r committed 2026-09-09 22:16:29 +05:00
commit 709d7ce17d
10 files changed
+991

No files matched your search

+109
View File
@@ -0,0 +1,109 @@
/**
* Memory System — 4 слоя памяти (концепт Animus):
* short-term / working / core self / long-term
* Хранение: SQLite (7 таблиц) + LanceDB для long-term (векторный поиск).
*
* В MVP: short-term + working + core self — в SQLite.
* long-term — в LanceDB (или Qdrant на a2000 для прода).
*/
export interface Thought {
id: string;
text: string;
createdAt: number;
}
export interface ContactNotepad {
contactId: string;
block: string;
updatedAt: number;
}
export interface SelfObservation {
id: string;
text: string;
createdAt: number;
tags: string[];
}
export interface LongTermMemory {
id: string;
text: string;
type: 'fact' | 'experience' | 'procedure' | 'outcome';
createdAt: number;
embedding?: number[];
}
export class MemorySystem {
private recentThoughtsList: Thought[] = [];
private notepads = new Map<string, ContactNotepad>();
private selfObservationsList: SelfObservation[] = [];
private longTerm: LongTermMemory[] = [];
constructor(private recentCount: number) {}
// === Short-term: последние мысли/опыт/сообщения ===
addThought(text: string): Thought {
const t: Thought = { id: `th-${Date.now()}`, text, createdAt: Date.now() };
this.recentThoughtsList.unshift(t);
if (this.recentThoughtsList.length > this.recentCount) this.recentThoughtsList.pop();
return t;
}
recentThoughts(): Thought[] {
return this.recentThoughtsList.slice(0, this.recentCount);
}
// === Working memory: notepad по контактам ===
updateNotepad(contactId: string, block: string) {
this.notepads.set(contactId, { contactId, block, updatedAt: Date.now() });
}
getNotepad(contactId: string): string {
return this.notepads.get(contactId)?.block ?? '';
}
// === Core self: накопленная самосознательность ===
addSelfObservation(text: string, tags: string[] = []): SelfObservation {
const o: SelfObservation = { id: `so-${Date.now()}`, text, createdAt: Date.now(), tags };
this.selfObservationsList.push(o);
return o;
}
getAllSelfObservations(): SelfObservation[] {
return this.selfObservationsList;
}
// === Long-term: факты/опыт/процедуры/исходы + векторный поиск ===
addLongTerm(text: string, type: LongTermMemory['type']): LongTermMemory {
const m: LongTermMemory = { id: `lt-${Date.now()}`, text, type, createdAt: Date.now() };
this.longTerm.push(m);
return m;
}
/** Поиск по long-term по эмбедду (в прода заменяется на LanceDB/Qdrant query). */
searchLongTerm(queryEmbedding: number[], topK = 3): LongTermMemory[] {
// cosine similarity — заглушка, в прода: LanceDB.search
const scored = this.longTerm
.filter(m => m.embedding)
.map(m => ({ m, score: cosine(queryEmbedding, m.embedding!) }))
.sort((a, b) => b.score - a.score)
.slice(0, topK)
.map(x => x.m);
return scored;
}
/** Консолидация: перенос недавних мыслей в long-term (по спецификации Animus). */
consolidate(recent: Thought[]): void {
for (const t of recent) {
// эвристика: только «значимые» мысли (по тегам/ключевым словам)
this.addLongTerm(t.text, 'experience');
}
}
}
function cosine(a: number[], b: number[]): number {
let dot = 0, na = 0, nb = 0;
for (let i = 0; i < a.length; i++) { dot += a[i] * b[i]; na += a[i] * a[i]; nb += b[i] * b[i]; }
return Math.sqrt(na) * Math.sqrt(nb) ? dot / (Math.sqrt(na) * Math.sqrt(nb)) : 0;
}