Spaces:
Paused
Paused
| import { neo4jService } from '../database/Neo4jService.js'; | |
| import { neuralStream, NeuralEvent } from './NeuralStream.js'; | |
| import { metricsService } from './MetricsService.js'; | |
| export interface KnowledgePacket { | |
| title: string; | |
| content: string; | |
| source: string; | |
| category: string; | |
| tags: string[]; | |
| } | |
| class KnowledgeAcquisitionService { | |
| private static instance: KnowledgeAcquisitionService; | |
| private constructor() { | |
| console.log('🧠 [KnowledgeAcquisition] Cortex Ingestor Online.'); | |
| this.setupNeuralListeners(); | |
| } | |
| public static getInstance(): KnowledgeAcquisitionService { | |
| if (!KnowledgeAcquisitionService.instance) { | |
| KnowledgeAcquisitionService.instance = new KnowledgeAcquisitionService(); | |
| } | |
| return KnowledgeAcquisitionService.instance; | |
| } | |
| /** | |
| * Lytter på nervesystemet efter ny viden fra OmniHarvester eller andre agenter | |
| */ | |
| private setupNeuralListeners() { | |
| // Når OmniHarvester har fundet noget (SYSTEM_HEALED med context 'ActiveLearning') | |
| // eller når vi manuelt pusher viden. | |
| neuralStream.on('KNOWLEDGE_INGEST_REQ', async (event: NeuralEvent) => { | |
| console.log('📥 [Acquisition] Receiving knowledge stream...', event.payload.title); | |
| await this.ingestKnowledge(event.payload as unknown as KnowledgePacket); | |
| }); | |
| } | |
| /** | |
| * KERNE-LOGIK: Konverterer rå tekst til Graf-Struktur | |
| * Dette er "Memory Consolidation" processen. | |
| */ | |
| public async ingestKnowledge(packet: KnowledgePacket): Promise<boolean> { | |
| const startTime = Date.now(); | |
| try { | |
| // 1. Opret selve videns-noden (Fact / Document) | |
| // Vi bruger MERGE for at undgå duplikater baseret på titel/source | |
| const cypher = ` | |
| MERGE (k:Knowledge {title: $title}) | |
| SET k.content = $content, | |
| k.source = $source, | |
| k.category = $category, | |
| k.ingestedAt = datetime(), | |
| k.hash = $hash | |
| // 2. Opret kategori-struktur | |
| MERGE (c:Category {name: $category}) | |
| MERGE (k)-[:BELONGS_TO]->(c) | |
| // 3. Auto-Tagging (Opretter tags og linker dem) | |
| FOREACH (tagName IN $tags | | |
| MERGE (t:Tag {name: tagName}) | |
| MERGE (k)-[:TAGGED_WITH]->(t) | |
| ) | |
| `; | |
| // Simpel hash for integritet | |
| const hash = Buffer.from(packet.title + packet.source).toString('base64'); | |
| await neo4jService.runQuery(cypher, { | |
| title: packet.title, | |
| content: packet.content, | |
| source: packet.source, | |
| category: packet.category || 'General', | |
| tags: packet.tags || [], | |
| hash | |
| }); | |
| // 4. THE MAGIC: Semantic Auto-Linking | |
| // Vi søger efter eksisterende noder, der nævnes i den nye tekst, og linker dem. | |
| await this.createSemanticLinks(packet.title, packet.content); | |
| // 5. Metrics & Feedback | |
| const duration = Date.now() - startTime; | |
| metricsService.incrementCounter('knowledge_ingested'); | |
| console.log(`✅ [Acquisition] Absorbed: "${packet.title}" in ${duration}ms`); | |
| neuralStream.emitEvent('SYSTEM_HEALED', 'LOW', { | |
| action: 'MEMORY_CONSOLIDATED', | |
| target: packet.title | |
| }, 'KnowledgeAcquisition'); | |
| return true; | |
| } catch (error) { | |
| console.error('❌ [Acquisition] Failed to ingest:', error); | |
| return false; | |
| } | |
| } | |
| /** | |
| * Finder andre noder i grafen, der nævnes i denne tekst, og skaber relationer. | |
| * Dette gør grafen "tættere" og klogere over tid. | |
| */ | |
| private async createSemanticLinks(nodeTitle: string, content: string) { | |
| // Find noder (Personer, Tech, Threats) hvis navne optræder i den nye tekst | |
| // Undgå at linke til sig selv | |
| const linkCypher = ` | |
| MATCH (k:Knowledge {title: $title}) | |
| MATCH (other) | |
| WHERE other <> k | |
| AND (other:Person OR other:Technology OR other:Threat OR other:Organization) | |
| AND size(other.name) > 3 // Ignorer støj | |
| AND $content CONTAINS other.name | |
| MERGE (k)-[:MENTIONS]->(other) | |
| RETURN count(other) as links | |
| `; | |
| const result = await neo4jService.runQuery(linkCypher, { | |
| title: nodeTitle, | |
| content: content | |
| }); | |
| const linksCreated = (result[0]?.links as any)?.toNumber?.() || 0; | |
| if (linksCreated > 0) { | |
| console.log(`🔗 [Acquisition] Auto-linked "${nodeTitle}" to ${linksCreated} existing concepts.`); | |
| } | |
| } | |
| // Stubs for backward compatibility | |
| public async acquire(params: any): Promise<any> { return { success: false, message: 'Deprecated' }; } | |
| public async batchAcquire(params: any): Promise<any> { return []; } | |
| public async semanticSearch(query: string, limit: number): Promise<any> { return []; } | |
| public async getVectorStats(): Promise<any> { return { totalRecords: 0 }; } | |
| public async acquireFromTargets(ids?: string[]): Promise<any> { return []; } | |
| public async acquireSingleTarget(id: string): Promise<any> { return null; } | |
| } | |
| export const knowledgeAcquisition = KnowledgeAcquisitionService.getInstance(); | |