079project 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/GroupStarter.cjs +647 -0
  2. package/LICENSE +165 -0
  3. package/PropagateSignalUseJsWorker.js +92 -0
  4. package/README.md +102 -0
  5. package/Redis-8.0.3-Windows-x64-cygwin-with-Service/README.md +52 -0
  6. package/Redis-8.0.3-Windows-x64-cygwin-with-Service/README.zh_CN.md +59 -0
  7. package/Redis-8.0.3-Windows-x64-cygwin-with-Service/RedisService.exe +0 -0
  8. package/Redis-8.0.3-Windows-x64-cygwin-with-Service/cygcrypto-3.dll +0 -0
  9. package/Redis-8.0.3-Windows-x64-cygwin-with-Service/cyggcc_s-seh-1.dll +0 -0
  10. package/Redis-8.0.3-Windows-x64-cygwin-with-Service/cygssl-3.dll +0 -0
  11. package/Redis-8.0.3-Windows-x64-cygwin-with-Service/cygstdc++-6.dll +0 -0
  12. package/Redis-8.0.3-Windows-x64-cygwin-with-Service/cygwin1.dll +0 -0
  13. package/Redis-8.0.3-Windows-x64-cygwin-with-Service/cygz.dll +0 -0
  14. package/Redis-8.0.3-Windows-x64-cygwin-with-Service/dump.rdb +0 -0
  15. package/Redis-8.0.3-Windows-x64-cygwin-with-Service/install_redis_service.bat +100 -0
  16. package/Redis-8.0.3-Windows-x64-cygwin-with-Service/redis-benchmark.exe +0 -0
  17. package/Redis-8.0.3-Windows-x64-cygwin-with-Service/redis-check-aof.exe +0 -0
  18. package/Redis-8.0.3-Windows-x64-cygwin-with-Service/redis-check-rdb.exe +0 -0
  19. package/Redis-8.0.3-Windows-x64-cygwin-with-Service/redis-cli.exe +0 -0
  20. package/Redis-8.0.3-Windows-x64-cygwin-with-Service/redis-full.conf +376 -0
  21. package/Redis-8.0.3-Windows-x64-cygwin-with-Service/redis-sentinel.exe +0 -0
  22. package/Redis-8.0.3-Windows-x64-cygwin-with-Service/redis-server.exe +0 -0
  23. package/Redis-8.0.3-Windows-x64-cygwin-with-Service/redis.conf +2348 -0
  24. package/Redis-8.0.3-Windows-x64-cygwin-with-Service/sentinel.conf +361 -0
  25. package/Redis-8.0.3-Windows-x64-cygwin-with-Service/start.bat +4 -0
  26. package/Redis-8.0.3-Windows-x64-cygwin-with-Service/uninstall_redis_service.bat +30 -0
  27. package/boot.py +51 -0
  28. package/chat_Client.js +29 -0
  29. package/controller.cjs +118 -0
  30. package/enhancedForwarder.js +378 -0
  31. package/forwarder.js +1456 -0
  32. package/groupmanager.cjs +143 -0
  33. package/howToStart.txt +8 -0
  34. package/lemma.csv +210 -0
  35. package/load.py +35 -0
  36. package/mainManager.cjs +81 -0
  37. package/mainStarter.cjs +535 -0
  38. package/main_Serve.cjs +2745 -0
  39. package/main_Study.cjs +3230 -0
  40. package/memeMergeWorker.cjs +55 -0
  41. package/model_RNN.py +117 -0
  42. package/note.txt +5 -0
  43. package/notebook.txt +8 -0
  44. package/npminstall-debug.log +206 -0
  45. package/package.json +48 -0
  46. package/public/chat_straight.html +90 -0
  47. package/public/index.html +247 -0
  48. package/public/indexmain.html +136 -0
  49. package/public/monitor.html +194 -0
  50. package/robots/wikitext-something.txt +25 -0
  51. package/runtime.proto +24 -0
  52. package/runtime_data.json +766294 -0
  53. package/serializer_seq2seq.h5 +0 -0
  54. package/start.js +46 -0
  55. package/tests/test_FIrststep1.txt +1224 -0
  56. package/tests/test_FIrststep2.txt +2956 -0
  57. package/tests/test_FIrststep3.txt +1224 -0
  58. package/tests/test_FIrststep4.txt +1396 -0
  59. package/tests/test_FIrststep5.txt +2852 -0
  60. package/tests/test_FIrststep6.txt +1516 -0
  61. package/tests/test_FirstStep7.txt +1748 -0
  62. package/tests/test_Firstsetp8.txt +2672 -0
  63. package/tokenizer.json +1 -0
  64. package/vocabularySplitter.js +253 -0
  65. package/wikitext/.gitattributes +27 -0
  66. package/wikitext/README.md +344 -0
  67. package/wikitext/describtion.txt +1 -0
package/main_Study.cjs ADDED
@@ -0,0 +1,3230 @@
1
+ //this is the study version of the AI, it is used to study and learn from the data
2
+ // it will send the pre-trained model to the server version by redis
3
+ const axios = require('axios');
4
+ const { exec } = require('child_process');
5
+ const express = require('express');
6
+ const redis = require('redis');
7
+ const fs = require('fs');
8
+ const path = require('path');
9
+ const tf = require('@tensorflow/tfjs'); // 依然用这个
10
+ const net = require('net');
11
+ const http = require('http');
12
+ const cluster = require('cluster');
13
+ const protobuf = require('protobufjs');
14
+ const runtimeProtoPath = path.join(__dirname, 'runtime.proto');
15
+ let RuntimeMessage = null;
16
+ const worker = require('worker_threads');
17
+ const workerpool = require('workerpool');
18
+ let redisClient = null; // 新增:模块级引用
19
+ let isShuttingDown = false; // 新增:退出标志
20
+ const crossTimers = []; // 新增:记录定时器,便于退出时清理
21
+ function registerInterval(fn, ms) { const t = setInterval(fn, ms); crossTimers.push(t); return t; }
22
+ const os = require('os');
23
+ const csvParse = require('csv-parse/sync'); // 需要安装 csv-parse
24
+ const MAX_WORKERS = os.cpus().length;
25
+ const pool = workerpool.pool(path.join(__dirname, 'memeMergeWorker.cjs'), {
26
+ minWorkers: 1,
27
+ maxWorkers: MAX_WORKERS,
28
+ workerType: 'process'
29
+ });
30
+ const natural = require('natural');
31
+ const STOP_WORDS = natural.stopwords; // 英文停用词
32
+
33
+ console.log(`[WORKERS] 工作池已创建,最大工作进程数: ${MAX_WORKERS}`);
34
+ protobuf.load(runtimeProtoPath, (err, root) => {
35
+ if (err) throw err;
36
+ RuntimeMessage = root.lookupType('Runtime');
37
+ });
38
+ // 全局配置
39
+ global.config = {
40
+ masterPortOfMain: process.argv[2],
41
+ emitExitport: process.argv[3] || 8641
42
+ };
43
+ const modelDefaults = {
44
+ decayFactor: 0.5,
45
+ maxMemeWords: 100,
46
+ minOverlapThreshold: 2,
47
+ maliciousThreshold: 0.7,
48
+ learningIterations: 3,
49
+ iteration: 5,
50
+ threshold: 3,
51
+ decay: 1,
52
+ decayK: 1,
53
+ maxLen: 16,
54
+ edgeWeight: 1
55
+ };
56
+ const currentModelParams = { ...modelDefaults };
57
+ async function batchAddPoints(graph, pointsArr, batchSize = 500) {
58
+ if (!graph || !pointsArr || !Array.isArray(pointsArr)) {
59
+ console.error('[BATCH] 无效的参数:', {
60
+ hasGraph: !!graph,
61
+ hasPointsArr: !!pointsArr,
62
+ isArray: Array.isArray(pointsArr)
63
+ });
64
+ return;
65
+ }
66
+
67
+ for (let i = 0; i < pointsArr.length; i += batchSize) {
68
+ const batch = pointsArr.slice(i, i + batchSize);
69
+ for (const item of batch) {
70
+ if (item && item.key && item.value && item.value.pointID) {
71
+ try {
72
+ const connects = Array.isArray(item.value.connect) ? item.value.connect : [];
73
+ graph.addPoint(item.value.pointID, connects);
74
+ } catch (err) {
75
+ console.error('[BATCH] 添加点失败:', item.value.pointID, err.message);
76
+ }
77
+ }
78
+ }
79
+ await new Promise(resolve => setImmediate(resolve));
80
+ }
81
+ }
82
+ // 反触发机制
83
+ async function antiTrigger(onContinue, onExit) {
84
+ const PORT = global.config.emitExitport || 8641;
85
+ let server;
86
+ let shouldExit = false;
87
+ let lastRequestTime = Date.now();
88
+
89
+ // 1. 尝试监听端口
90
+ const tryListen = () => new Promise(resolve => {
91
+ server = net.createServer().listen(PORT, () => resolve(true));
92
+ server.on('error', () => resolve(false));
93
+ });
94
+
95
+ const canListen = await tryListen();
96
+
97
+ if (canListen) {
98
+ // 监听成功,10秒内检测是否有连接
99
+ console.log(`[ANTI-TRIGGER] 端口${PORT}监听成功,等待10秒...`);
100
+ let timer = setTimeout(() => {
101
+ server.close();
102
+ onContinue();
103
+ }, 10000);
104
+
105
+ server.on('connection', socket => {
106
+ // 有连接,说明有外部监控,进入等待模式
107
+ clearTimeout(timer);
108
+ socket.destroy();
109
+ server.close();
110
+ console.log(`[ANTI-TRIGGER] 检测到外部请求,等待20秒后启动主逻辑...`);
111
+ setTimeout(onContinue, 20000);
112
+ });
113
+ } else {
114
+ // 监听失败,说明端口被占用,定时发送请求
115
+ console.log(`[ANTI-TRIGGER] 端口${PORT}被占用,进入监控模式...`);
116
+ let interval = setInterval(() => {
117
+ const req = http.request({ port: PORT, timeout: 2000 }, res => {
118
+ lastRequestTime = Date.now();
119
+ res.on('data', () => { });
120
+ });
121
+ req.on('error', () => { });
122
+ req.end();
123
+ }, 2000);
124
+
125
+ // 检查请求是否消失
126
+ let checkInterval = setInterval(() => {
127
+ if (Date.now() - lastRequestTime > 4000) {
128
+ // 4秒内没有收到响应,认为监控消失
129
+ clearInterval(interval);
130
+ clearInterval(checkInterval);
131
+ console.log(`[ANTI-TRIGGER] 监控请求消失,保存数据并退出...`);
132
+ shouldExit = true;
133
+ if (onExit) onExit();
134
+ process.exit(0);
135
+ }
136
+ }, 1000);
137
+
138
+ // 10秒后继续主逻辑
139
+ setTimeout(() => {
140
+ if (!shouldExit) {
141
+ clearInterval(interval);
142
+ clearInterval(checkInterval);
143
+ onContinue();
144
+ }
145
+ }, 10000);
146
+ }
147
+ }
148
+ function verifySystemConsistency(sourceRuntime, targetRuntime) {
149
+ console.log('[VERIFY] 开始验证系统一致性...');
150
+ console.log(`[VERIFY] 源词表大小: ${sourceRuntime.vocabManager.vocab.length}, 目标: ${targetRuntime.vocabManager.vocab.length}`);
151
+ console.log(`[VERIFY] 源KVM条目: ${sourceRuntime.kvm.memory.size}, 目标: ${targetRuntime.kvm.memory.size}`);
152
+ console.log(`[VERIFY] 源Spider: ${!!sourceRuntime.spider}, 目标: ${!!targetRuntime.spider}`);
153
+ // 检查部分词是否能在目标系统找到
154
+ const sample = sourceRuntime.vocabManager.vocab.slice(0, 5);
155
+ for (const word of sample) {
156
+ const inTarget = targetRuntime.vocabManager.word2idx.has(word);
157
+ console.log(`[VERIFY] 词 "${word}" 在目标系统: ${inTarget}`);
158
+ }
159
+ }
160
+ const app = express();
161
+ const bodyParser = require('body-parser');
162
+ const readline = require('readline');
163
+
164
+ app.use(bodyParser.json());
165
+ app.use(bodyParser.urlencoded({ extended: true }));
166
+ app.use(express.static(path.join(__dirname, 'public')));
167
+
168
+ // 持久化路径
169
+ const SAVE_PATH = path.join(__dirname, 'runtime_data.json');
170
+
171
+
172
+
173
+
174
+ class SnapshotManager {
175
+ constructor(runtime, snapshotDir = path.join(__dirname, 'snapshots')) {
176
+ this.runtime = runtime;
177
+ this.snapshotDir = snapshotDir;
178
+ this.snapshotList = [];
179
+ this.isCreatingSnapshot = false;
180
+ this.autoSnapshotTimer = null;
181
+
182
+ // 创建快照目录
183
+ if (!fs.existsSync(this.snapshotDir)) {
184
+ fs.mkdirSync(this.snapshotDir, { recursive: true });
185
+ }
186
+
187
+ // 加载现有快照列表
188
+ this.loadSnapshotList();
189
+ }
190
+
191
+ loadSnapshotList() {
192
+ try {
193
+ const files = fs.readdirSync(this.snapshotDir).filter(f => f.endsWith('.snapshot'));
194
+ this.snapshotList = files.map(f => ({
195
+ id: f.replace('.snapshot', ''),
196
+ timestamp: parseInt(f.split('_')[0]),
197
+ name: f.replace(/^\d+_/, '').replace('.snapshot', ''),
198
+ path: path.join(this.snapshotDir, f)
199
+ })).sort((a, b) => b.timestamp - a.timestamp); // 按时间降序排序
200
+
201
+ console.log(`[SNAPSHOT] 加载了${this.snapshotList.length}个快照`);
202
+ } catch (e) {
203
+ console.error('[SNAPSHOT] 加载快照列表失败:', e);
204
+ this.snapshotList = [];
205
+ }
206
+ }
207
+
208
+ async createSnapshot(name = 'auto') {
209
+ // 防止并发创建
210
+ if (this.isCreatingSnapshot) {
211
+ console.log('[SNAPSHOT] 另一个快照正在创建中,跳过');
212
+ return null;
213
+ }
214
+
215
+ this.isCreatingSnapshot = true;
216
+ console.time('snapshotCreation');
217
+
218
+ try {
219
+ const timestamp = Date.now();
220
+ const snapshotId = `${timestamp}_${name}`;
221
+ const filePath = path.join(this.snapshotDir, `${snapshotId}.snapshot`);
222
+
223
+ console.log(`[SNAPSHOT] 开始创建快照: ${snapshotId}`);
224
+
225
+ // 准备快照数据,分批处理以优化性能
226
+ const snapshotData = {
227
+ id: snapshotId,
228
+ timestamp,
229
+ name,
230
+ createDate: new Date().toISOString(),
231
+ memes: this.runtime.graph.getAllPoints(),
232
+ wordGraph: Array.from(this.runtime.wordGraph.points.values()),
233
+ kvm: Array.from(this.runtime.kvm.memory.entries()),
234
+ vocab: this.runtime.vocabManager.vocab,
235
+ wordAccessLog: Array.from(this.runtime.wordAccessLog ? this.runtime.wordAccessLog.entries() : [])
236
+ };
237
+
238
+ // 写入临时文件,然后原子重命名以确保数据完整性
239
+ const tempPath = `${filePath}.temp`;
240
+ await fs.promises.writeFile(tempPath, JSON.stringify(snapshotData), 'utf-8');
241
+ await fs.promises.rename(tempPath, filePath);
242
+
243
+ // 更新快照列表
244
+ const snapshotInfo = {
245
+ id: snapshotId,
246
+ timestamp,
247
+ name,
248
+ path: filePath
249
+ };
250
+ this.snapshotList.unshift(snapshotInfo);
251
+
252
+ console.timeEnd('snapshotCreation');
253
+ console.log(`[SNAPSHOT] 快照创建成功: ${snapshotId}`);
254
+ return snapshotInfo;
255
+ } catch (e) {
256
+ console.error('[SNAPSHOT] 创建快照失败:', e);
257
+ return null;
258
+ } finally {
259
+ this.isCreatingSnapshot = false;
260
+ }
261
+ }
262
+
263
+ async restoreSnapshot(snapshotId) {
264
+ console.log(`[SNAPSHOT] 开始从快照恢复: ${snapshotId}`);
265
+ console.time('snapshotRestore');
266
+
267
+ // 查找快照
268
+ const snapshot = this.snapshotList.find(s => s.id === snapshotId);
269
+ if (!snapshot) {
270
+ console.error(`[SNAPSHOT] 快照不存在: ${snapshotId}`);
271
+ return false;
272
+ }
273
+
274
+ try {
275
+ // 读取快照文件
276
+ console.log(`[SNAPSHOT] 从文件读取数据: ${snapshot.path}`);
277
+ const dataStr = await fs.promises.readFile(snapshot.path, 'utf-8');
278
+ const data = JSON.parse(dataStr);
279
+
280
+ // 在恢复前创建自动备份
281
+ await this.createSnapshot(`auto_before_restore_${snapshotId}`);
282
+
283
+ // 清空当前运行时
284
+ console.log('[SNAPSHOT] 清空当前运行时...');
285
+ this.runtime.graph = new GraphDB();
286
+ this.runtime.wordGraph = new GraphDB();
287
+ this.runtime.kvm = new KVM();
288
+ this.runtime.wordAccessLog = new Map();
289
+
290
+ // 恢复图结构
291
+ console.log('[SNAPSHOT] 恢复模因网络...');
292
+ if (data.memes) {
293
+ const BATCH_SIZE = 500;
294
+ for (let i = 0; i < data.memes.length; i += BATCH_SIZE) {
295
+ const batch = data.memes.slice(i, i + BATCH_SIZE);
296
+ for (const point of batch) {
297
+ this.runtime.graph.addPoint(point.pointID, point.connect);
298
+ }
299
+ // 让事件循环有机会处理其他事件
300
+ await new Promise(resolve => setImmediate(resolve));
301
+ }
302
+ }
303
+
304
+ // 恢复词图
305
+ console.log('[SNAPSHOT] 恢复词语网络...');
306
+ if (data.wordGraph) {
307
+ const BATCH_SIZE = 1000;
308
+ for (let i = 0; i < data.wordGraph.length; i += BATCH_SIZE) {
309
+ const batch = data.wordGraph.slice(i, i + BATCH_SIZE);
310
+ for (const point of batch) {
311
+ this.runtime.wordGraph.addPoint(point.pointID, point.connect);
312
+ }
313
+ await new Promise(resolve => setImmediate(resolve));
314
+ }
315
+ }
316
+
317
+ // 恢复KVM
318
+ console.log('[SNAPSHOT] 恢复键值存储...');
319
+ if (data.kvm) {
320
+ const BATCH_SIZE = 1000;
321
+ for (let i = 0; i < data.kvm.length; i += BATCH_SIZE) {
322
+ const batch = data.kvm.slice(i, i + BATCH_SIZE);
323
+ for (const [k, v] of batch) {
324
+ this.runtime.kvm.set(k, v);
325
+ }
326
+ await new Promise(resolve => setImmediate(resolve));
327
+ }
328
+ }
329
+
330
+ // 恢复词表
331
+ console.log('[SNAPSHOT] 恢复词表...');
332
+ if (data.vocab) {
333
+ this.runtime.vocabManager.vocab = data.vocab;
334
+ this.runtime.vocabManager.updateMappings();
335
+ }
336
+
337
+ // 恢复词访问日志
338
+ console.log('[SNAPSHOT] 恢复词访问日志...');
339
+ if (data.wordAccessLog) {
340
+ this.runtime.wordAccessLog = new Map(data.wordAccessLog);
341
+ }
342
+
343
+ console.timeEnd('snapshotRestore');
344
+ console.log(`[SNAPSHOT] 成功从快照恢复: ${snapshotId}`);
345
+ return true;
346
+ } catch (e) {
347
+ console.error('[SNAPSHOT] 恢复快照失败:', e);
348
+ return false;
349
+ }
350
+ }
351
+
352
+ getSnapshotList() {
353
+ return this.snapshotList.map(({ id, timestamp, name }) => ({
354
+ id,
355
+ timestamp,
356
+ name,
357
+ date: new Date(timestamp).toLocaleString()
358
+ }));
359
+ }
360
+
361
+
362
+
363
+ setupAutoSnapshot(interval = 30 * 60 * 1000) { // 默认30分钟
364
+ // 清除可能存在的旧定时器
365
+ if (this.autoSnapshotTimer) {
366
+ clearInterval(this.autoSnapshotTimer);
367
+ }
368
+
369
+ // 设置新定时器
370
+ this.autoSnapshotTimer = setInterval(async () => {
371
+ // 检查系统是否空闲
372
+ if (global.ctrlA && !global.ctrlA.isLearning && !global.ctrlA.isMainLoopRunning) {
373
+ console.log('[SNAPSHOT] 系统空闲,开始自动创建快照');
374
+ await this.createSnapshot(`auto_${new Date().toLocaleDateString().replace(/\//g, '-')}`);
375
+
376
+ // 清理旧的自动快照,只保留最近10个
377
+ const autoSnapshots = this.snapshotList
378
+ .filter(s => s.name.startsWith('auto_'))
379
+ .sort((a, b) => b.timestamp - a.timestamp);
380
+
381
+ if (autoSnapshots.length > 10) {
382
+ console.log('[SNAPSHOT] 清理旧的自动快照...');
383
+ for (const s of autoSnapshots.slice(10)) {
384
+ this.deleteSnapshot(s.id);
385
+ }
386
+ }
387
+ } else {
388
+ console.log('[SNAPSHOT] 系统忙碌,跳过自动创建快照');
389
+ }
390
+ }, interval);
391
+
392
+ console.log(`[SNAPSHOT] 已设置自动快照,每${interval / 60000}分钟检查一次`);
393
+ return true;
394
+ }
395
+
396
+ stopAutoSnapshot() {
397
+ if (this.autoSnapshotTimer) {
398
+ clearInterval(this.autoSnapshotTimer);
399
+ this.autoSnapshotTimer = null;
400
+ console.log('[SNAPSHOT] 已停止自动快照');
401
+ return true;
402
+ }
403
+ return false;
404
+ }
405
+ }
406
+
407
+
408
+ class VocabManager {
409
+ constructor(spider) {
410
+ this.vocab = ['<PAD>', '<BOS>', '<EOS>', '<UNK>'];
411
+ this.word2idx = new Map();
412
+ this.spider = spider; // 持有 spider 实例
413
+ this.updateMappings();
414
+ }
415
+
416
+ // 优化 VocabManager.buildFromData 方法
417
+ async buildFromData(texts) {
418
+ console.time('vocabBuild');
419
+ const wordCounts = new Map();
420
+ for (let i = 0; i < texts.length; i++) {
421
+ // 先分词再归一化
422
+ let words = texts[i]
423
+ .toLowerCase()
424
+ .replace(/[^a-zA-Z\s]/g, ' ')
425
+ .split(/\s+/)
426
+ .filter(w => w.length > 0);
427
+ words = this.spider.lemmatizeWords(words);
428
+ for (const word of words) {
429
+ wordCounts.set(word, (wordCounts.get(word) || 0) + 1);
430
+ }
431
+ if (i % 1000 === 0 && i > 0) await new Promise(r => setImmediate(r));
432
+ }
433
+ const MAX_VOCAB_SIZE = 100000;
434
+ const sortedWords = Array.from(wordCounts.entries())
435
+ .sort((a, b) => b[1] - a[1])
436
+ .slice(0, MAX_VOCAB_SIZE)
437
+ .map(([word]) => word);
438
+ const existingWords = new Set(this.vocab);
439
+ const newWords = sortedWords.filter(word => !existingWords.has(word));
440
+ this.vocab.push(...newWords);
441
+ //这里,在模因网络建立之前实现词归一化
442
+ //this.vocab.forEach(word => this.spider.lemmatizeWords(word))
443
+ this.updateMappings();
444
+ console.log(`Built vocab with ${this.vocab.length} words`);
445
+ console.timeEnd('vocabBuild');
446
+ }
447
+
448
+ // 从Spider加载并构建词表
449
+ buildVocabFromSpider(spider) {
450
+ if (!spider) {
451
+ console.warn('Spider not provided to buildVocabFromSpider');
452
+ return [];
453
+ }
454
+ const articles = spider.fetchArticles();
455
+ this.buildFromData(articles);
456
+ return articles;
457
+ }
458
+
459
+ // 添加新词
460
+ addWord(word) {
461
+ if (!this.word2idx.has(word)) {
462
+ this.vocab.push(word);
463
+ this.word2idx.set(word, this.vocab.length - 1);
464
+ }
465
+ }
466
+
467
+ // 更新映射
468
+ updateMappings() {
469
+ this.word2idx.clear();
470
+ this.vocab.forEach((word, idx) => {
471
+ this.word2idx.set(word, idx);
472
+ });
473
+ }
474
+
475
+ // 词转索引
476
+ wordsToIndices(words) {
477
+ return words.map(w => this.word2idx.get(w) || this.word2idx.get('<UNK>'));
478
+ }
479
+
480
+ // 索引转词
481
+ indicesToWords(indices) {
482
+ return indices.map(i => this.vocab[i] || '<UNK>');
483
+ }
484
+
485
+ // 获取词表大小
486
+ getsize() {
487
+ return this.vocab.length;
488
+ }
489
+ }
490
+
491
+ // 创建全局词表管理器
492
+ global.vocabmanager = new VocabManager();
493
+
494
+ class GraphDB {
495
+ // 点格式:{pointID, connect: [[lineweight, pointID, 0|1|2], ...]}
496
+ // 0: 双向,1: 指向自己,2: 指向对方
497
+ constructor() {
498
+ this.points = new Map();
499
+ }
500
+ // 确保 GraphDB 类有完整的单向连接支持
501
+ /**
502
+ * 添加专门的单向边
503
+ * @param {string} fromID - 起点ID
504
+ * @param {string} toID - 终点ID
505
+ * @param {number} weight - 边权重
506
+ * @param {number} specificDirection - 具体方向类型(1=指向自己,2=指向对方)
507
+ */
508
+ addBidirectionalEdge(id1, id2, weight = 1) {
509
+ this.addEdge(id1, id2, weight, 0);
510
+ }
511
+ // 优化 addDirectionalEdge 方法
512
+ addDirectionalEdge(fromID, toID, weight = 1, specificDirection = 2) {
513
+ if (specificDirection !== 1 && specificDirection !== 2) {
514
+ specificDirection = 2; // 默认值而不是抛出错误
515
+ }
516
+
517
+ // 确保点存在
518
+ if (!this.points.has(fromID)) {
519
+ this.points.set(fromID, { pointID: fromID, connect: [] });
520
+ }
521
+ if (!this.points.has(toID)) {
522
+ this.points.set(toID, { pointID: toID, connect: [] });
523
+ }
524
+
525
+ const fromPoint = this.points.get(fromID);
526
+ const safeWeight = (typeof weight === 'number' && isFinite(weight)) ? weight : 1;
527
+
528
+ // 使用更高效的查找
529
+ let edgeExists = false;
530
+ for (let i = 0; i < fromPoint.connect.length; i++) {
531
+ if (fromPoint.connect[i][1] === toID && fromPoint.connect[i][2] === specificDirection) {
532
+ edgeExists = true;
533
+ break;
534
+ }
535
+ }
536
+
537
+ if (!edgeExists) {
538
+ fromPoint.connect.push([safeWeight, toID, specificDirection]);
539
+ // 移除冗余日志
540
+ // console.log(`[GRAPH] 添加单向边: ${fromID} --(${specificDirection})--> ${toID}, 权重: ${safeWeight}`);
541
+ }
542
+ }
543
+ // 添加一个点,如果connect中有未记录的点,也要自动补充
544
+ addPoint(pointID, connect = []) {
545
+ if (!this.points.has(pointID)) {
546
+ this.points.set(pointID, { pointID, connect: [] });
547
+ }
548
+ const point = this.points.get(pointID);
549
+ for (const [weight, neighborID, direction] of connect) {
550
+ // 修复:确保权重为数字
551
+ const safeWeight = (typeof weight === 'number' && isFinite(weight)) ? weight : 1;
552
+ // 检查是否已存在这条边,避免重复
553
+ if (!point.connect.some(([w, id, d]) => w === safeWeight && id === neighborID && d === direction)) {
554
+ point.connect.push([safeWeight, neighborID, direction]);
555
+ }
556
+ // 自动补全邻居点
557
+ if (!this.points.has(neighborID)) {
558
+ this.points.set(neighborID, { pointID: neighborID, connect: [] });
559
+ }
560
+ // 如果是双向(0),补充对方的connect
561
+ if (direction === 0) {
562
+ const neighbor = this.points.get(neighborID);
563
+ if (!neighbor.connect.some(([w, id, d]) => w === safeWeight && id === pointID && d === 0)) {
564
+ neighbor.connect.push([safeWeight, pointID, 0]);
565
+ }
566
+ }
567
+ }
568
+ }
569
+
570
+ // 添加一条边,支持方向
571
+ addEdge(fromID, toID, weight = 1, direction = 0) {
572
+ this.addPoint(fromID);
573
+ this.addPoint(toID);
574
+ // 修复:确保权重为数字
575
+ const safeWeight = (typeof weight === 'number' && isFinite(weight)) ? weight : 1;
576
+ const fromPoint = this.points.get(fromID);
577
+ if (!fromPoint.connect.some(([w, id, d]) => w === safeWeight && id === toID && d === direction)) {
578
+ fromPoint.connect.push([safeWeight, toID, direction]);
579
+ }
580
+ // 如果是双向,自动补全反向
581
+ if (direction === 0) {
582
+ const toPoint = this.points.get(toID);
583
+ if (!toPoint.connect.some(([w, id, d]) => w === safeWeight && id === fromID && d === 0)) {
584
+ toPoint.connect.push([safeWeight, fromID, 0]);
585
+ }
586
+ }
587
+ }
588
+
589
+ // 更新边的权重
590
+ updateEdge(fromID, toID, newWeight, direction = 0) {
591
+ if (this.points.has(fromID)) {
592
+ const fromPoint = this.points.get(fromID);
593
+ const edgeIndex = fromPoint.connect.findIndex(([weight, id, d]) => id === toID && d === direction);
594
+ if (edgeIndex !== -1) {
595
+ fromPoint.connect[edgeIndex][0] = newWeight;
596
+ } else {
597
+ // 如果边不存在,创建新边
598
+ this.addEdge(fromID, toID, newWeight, direction);
599
+ }
600
+ }
601
+ }
602
+
603
+ // A*算法查找最短路径
604
+ selectPath(fromID, toID) {
605
+ if (!this.points.has(fromID) || !this.points.has(toID)) return null;
606
+ if (fromID === toID) return [fromID];
607
+
608
+ const openSet = new Set([fromID]);
609
+ const cameFrom = new Map();
610
+ const gScore = new Map([[fromID, 0]]);
611
+ const fScore = new Map([[fromID, this.heuristic(fromID, toID)]]);
612
+ const closedSet = new Set();
613
+
614
+ while (openSet.size > 0) {
615
+ // 找到fScore最小的点
616
+ let current = null;
617
+ let minFScore = Infinity;
618
+ for (const pointID of openSet) {
619
+ const score = fScore.get(pointID) ?? Infinity;
620
+ if (score < minFScore) {
621
+ current = pointID;
622
+ minFScore = score;
623
+ }
624
+ }
625
+ if (current === null) break;
626
+
627
+ openSet.delete(current);
628
+ closedSet.add(current);
629
+
630
+ if (current === toID) {
631
+ // reconstruct the path
632
+ const path = [];
633
+ let temp = current;
634
+ while (cameFrom.has(temp)) {
635
+ path.push(temp);
636
+ temp = cameFrom.get(temp);
637
+ }
638
+ path.push(fromID);
639
+ return path.reverse();
640
+ }
641
+
642
+ for (const [weight, neighborID, direction] of this.points.get(current).connect) {
643
+ // 只考虑允许的方向
644
+ if (direction === 1 && neighborID !== current) continue; // 只指向自己
645
+ if (direction === 2 && current !== fromID) continue; // 只指向对方
646
+ if (closedSet.has(neighborID)) continue;
647
+
648
+ const tentativeGScore = (gScore.get(current) || Infinity) + weight;
649
+ if (!openSet.has(neighborID)) {
650
+ openSet.add(neighborID);
651
+ } else if (tentativeGScore >= (gScore.get(neighborID) || Infinity)) {
652
+ continue;
653
+ }
654
+
655
+ cameFrom.set(neighborID, current);
656
+ gScore.set(neighborID, tentativeGScore);
657
+ fScore.set(neighborID, tentativeGScore + this.heuristic(neighborID, toID));
658
+ }
659
+ }
660
+ return null;
661
+ }
662
+
663
+ // 获取所有点
664
+ getAllPoints() {
665
+ return Array.from(this.points.values());
666
+ }
667
+
668
+ // 启发式函数:简单的常数启发式
669
+ heuristic(pointID, toID) {
670
+ return 1; // 简化的启发式函数
671
+ }
672
+ existEdge(pointID, neighborID) {
673
+ const point = this.points.get(pointID);
674
+ if (!point) {
675
+ return { exist: false, weight: undefined, type: undefined };
676
+ }
677
+ const connectArr = point.connect || [];
678
+ const found = connectArr.find(([_, id]) => id === neighborID);
679
+ return {
680
+ exist: connectArr.some(([_, id]) => id === neighborID),
681
+ weight: found ? found[0] : undefined,
682
+ type: found ? found[2] : undefined
683
+ };
684
+ }
685
+ existPoint(pointID) {
686
+ return { exist: this.points.has(pointID), connect: this.points.get(pointID)?.connect || [] };
687
+ }
688
+ deleteEdge(pointID, neighborID) {
689
+ if (this.existEdge(pointID, neighborID).exist) {
690
+ this.points.get(pointID).connect = this.points.get(pointID).connect.filter(([_, id]) => id !== neighborID);
691
+ this.points.get(neighborID).connect = this.points.get(neighborID).connect.filter(([_, id]) => id !== pointID);
692
+ }
693
+ }
694
+ deletePoint(pointID) {
695
+ if (this.existPoint(pointID).exist) {
696
+ this.points.delete(pointID);
697
+ }
698
+ }
699
+ }
700
+
701
+ class KVM {
702
+ constructor() {
703
+ this.memory = new Map();
704
+ }
705
+ get(key) {
706
+ return this.memory.get(key);
707
+ }
708
+ set(key, value) {
709
+ // 强制保证 value 为数组
710
+ if (!Array.isArray(value)) {
711
+ value = value == null ? [] : [value];
712
+ }
713
+ this.memory.set(key, value);
714
+ }
715
+ }
716
+
717
+
718
+
719
+ class Spider {
720
+ constructor(dir = path.join(__dirname, 'robots'), lemmaCsvPath = path.join(__dirname, 'lemma.csv')) {
721
+ this.dir = dir;
722
+ this.lemmaCsvPath = lemmaCsvPath;
723
+ this.lemmaMap = this.loadLemmaMap();
724
+ }
725
+
726
+ // 加载词形归一化映射表
727
+ loadLemmaMap() {
728
+ const lemmaMap = new Map();
729
+ if (!fs.existsSync(this.lemmaCsvPath)) {
730
+ console.warn(`[Spider] 未找到词形归一化文件: ${this.lemmaCsvPath}`);
731
+ return lemmaMap;
732
+ }
733
+ const csvContent = fs.readFileSync(this.lemmaCsvPath, 'utf-8');
734
+ const records = csvParse.parse(csvContent, { skip_empty_lines: true, relax_column_count: true });
735
+ for (const row of records) {
736
+ if (row.length < 2) continue;
737
+ const base = row[0].trim().toLowerCase();
738
+ for (const form of row) {
739
+ lemmaMap.set(form.trim().toLowerCase(), base);
740
+ }
741
+ }
742
+ return lemmaMap;
743
+ }
744
+
745
+ // 单词归一化
746
+ lemmatize(word) {
747
+ return this.lemmaMap.get(word.toLowerCase()) || word.toLowerCase();
748
+ }
749
+
750
+ // 归一化一组词
751
+ lemmatizeWords(words) {
752
+ return words.map(w => this.lemmatize(w));
753
+ }
754
+
755
+ // 优化 Spider.fetchArticles 方法
756
+ // ...existing code...
757
+
758
+ fetchArticles() {
759
+ console.time('articlesLoad');
760
+ const articles = [];
761
+ if (!fs.existsSync(this.dir)) {
762
+ console.warn(`Spider: 目录不存在: ${this.dir}`);
763
+ return articles;
764
+ }
765
+ const files = fs.readdirSync(this.dir).filter(f => f.endsWith('.txt'));
766
+ const MAX_ARTICLE_SIZE = 5000000;
767
+ for (const file of files) {
768
+ const filePath = path.join(this.dir, file);
769
+ try {
770
+ let content = fs.readFileSync(filePath, { encoding: 'utf8', flag: 'r' });
771
+ if (content.length > MAX_ARTICLE_SIZE) {
772
+ content = content.substring(0, MAX_ARTICLE_SIZE);
773
+ }
774
+ // 按段落或行分割
775
+ const rawArticles = content.split(/\r?\n\s*\r?\n/); // 按空行分段
776
+ for (let article of rawArticles) {
777
+ article = article.trim();
778
+ if (article.length < 10) continue;
779
+ // 不要全局正则替换,分词时再处理
780
+ articles.push(article);
781
+ }
782
+ } catch (e) {
783
+ console.warn(`Spider: 读取失败: ${filePath}, ${e.message}`);
784
+ }
785
+ }
786
+ console.timeEnd('articlesLoad');
787
+ return articles;
788
+ }
789
+
790
+ }
791
+ const globalSpider = new Spider();
792
+ global.vocabmanager = new VocabManager(globalSpider);
793
+ class Runtime {
794
+ static cloneRegistry = new Set();
795
+ // 运行时负责AI核心的调度、模因转换、信号传递与主流程控制
796
+ constructor(config = {}) {
797
+ this.config = config;
798
+ this.graph = new GraphDB();
799
+ this.wordGraph = new GraphDB();
800
+ this.kvm = new KVM();
801
+
802
+ this.transformer = null;
803
+ this.vocabManager = global.vocabmanager;
804
+ this.spider = new Spider();
805
+ this.wordAccessLog = new Map();
806
+ this.initWordGraph();
807
+ this.forgetTimer = setInterval(() => this.forgetWords(), 350 * 1000); // 3. 判断是否合并到已有模因
808
+ this.MAX_MEME_WORDS = 100; // 单个模因最大词数
809
+ this.MIN_OVERLAP = 2; // 至少有2个词重叠才允许合并
810
+ // Runtime类内新增
811
+ this.activationStats = new Map(); // 记录激活关系
812
+ this.isclone = false;
813
+ // 添加系统资源监控
814
+ this.systemLoad = {
815
+ lastCpuUsage: process.cpuUsage(),
816
+ lastCheckTime: Date.now(),
817
+ currentLoad: 0,
818
+ batchSizeMultiplier: 1
819
+ };
820
+ this.memeBarrier = new memeBarrier(this);
821
+ }
822
+ // 添加到Runtime类内部
823
+ filterStopWords(words) {
824
+ return words.filter(word => !STOP_WORDS.includes(word.toLowerCase()));
825
+ }
826
+
827
+ // 新增资源监控方法
828
+ monitorSystemLoad() {
829
+ const now = Date.now();
830
+ const cpuUsage = process.cpuUsage();
831
+
832
+ // 计算CPU使用率
833
+ const userDiff = cpuUsage.user - this.systemLoad.lastCpuUsage.user;
834
+ const sysDiff = cpuUsage.system - this.systemLoad.lastCpuUsage.system;
835
+ const elapsedMs = now - this.systemLoad.lastCheckTime;
836
+
837
+ if (elapsedMs > 0) {
838
+ // 转换为百分比 (0-100)
839
+ const cpuPercent = (userDiff + sysDiff) / (elapsedMs * 10); // 粗略计算
840
+ this.systemLoad.currentLoad = cpuPercent;
841
+
842
+ // 动态调整批处理大小
843
+ if (cpuPercent > 80) {
844
+ this.systemLoad.batchSizeMultiplier = 0.5; // 负载高,减小批量
845
+ console.log(`[LOAD] 系统负载高 (${cpuPercent.toFixed(1)}%),减小批处理大小`);
846
+ } else if (cpuPercent < 30) {
847
+ this.systemLoad.batchSizeMultiplier = 2.0; // 负载低,增加批量
848
+ console.log(`[LOAD] 系统负载低 (${cpuPercent.toFixed(1)}%),增加批处理大小`);
849
+ } else {
850
+ this.systemLoad.batchSizeMultiplier = 1.0; // 负载适中,保持默认
851
+ }
852
+
853
+ // 更新基准值
854
+ this.systemLoad.lastCpuUsage = cpuUsage;
855
+ this.systemLoad.lastCheckTime = now;
856
+ }
857
+
858
+ return this.systemLoad.batchSizeMultiplier;
859
+ }
860
+ // 清理定时器
861
+ cleanup() {
862
+ if (this.forgetTimer) {
863
+ clearInterval(this.forgetTimer);
864
+ }
865
+ if (this.memeBarrier) {
866
+ this.memeBarrier.stop();
867
+ }
868
+ }
869
+ // Runtime类中添加监控函数
870
+ monitorMemeSize() {
871
+ const memes = this.graph.getAllPoints();
872
+ for (const meme of memes) {
873
+ const words = this.kvm.get(meme.pointID) || [];
874
+ if (words.length > this.MAX_MEME_WORDS * 0.8) { // 如果接近最大限制
875
+ // console.log(`[MONITOR] 检测到大模因: ${meme.pointID}, 词数: ${words.length}`);
876
+ this.splitMemeIfNeeded(meme.pointID); // 尝试分裂
877
+ }
878
+ }
879
+ }
880
+ // 启动自主学习
881
+ async startSelfLearning(iterations) {
882
+ // 优先用 config
883
+ if (iterations === undefined) {
884
+ iterations = this.config.iteration !== undefined ? this.config.iteration : 5;
885
+ }
886
+ console.log('[LEARN] 启动自主学习模块...');
887
+ const learner = new ReinforcementLearner(this);
888
+ return await learner.learn(iterations);
889
+ }
890
+ // 在每轮信号扩散后,记录激活链
891
+ recordActivationPath(activatedNodes) {
892
+ // activatedNodes: 按顺序的被激活节点数组
893
+ for (let i = 0; i < activatedNodes.length - 1; i++) {
894
+ const from = activatedNodes[i];
895
+ const to = activatedNodes[i + 1];
896
+ const key = `${from}->${to}`;
897
+ this.activationStats.set(key, (this.activationStats.get(key) || 0) + 1);
898
+ }
899
+ }
900
+
901
+
902
+ // 在主循环或REPL后,动态调整模因网络
903
+ updateAttentionLinks(threshold, decay) {
904
+ threshold = threshold !== undefined ? threshold : (this.config.threshold !== undefined ? this.config.threshold : 3);
905
+ decay = decay !== undefined ? decay : (this.config.decay !== undefined ? this.config.decay : 1);
906
+ for (const [key, count] of this.activationStats.entries()) {
907
+ const [from, to] = key.split('->');
908
+ if (count >= threshold) {
909
+ // 在模因网络中建立/强化 from->to 的边
910
+ if (!this.graph.existEdge(from, to).exist) {
911
+ this.graph.addEdge(from, to, this.graph.existEdge(from, to).weight * 0.8, this.graph.existEdge(from, to).type);
912
+ } else {
913
+ this.graph.updateEdge(from, to, 1, 0);
914
+ }
915
+ // 可选:重置计数
916
+ this.activationStats.set(key, 0);
917
+ } else if (count <= -decay) {
918
+ if (this.graph.existEdge(from, to).weight > 5) {
919
+ this.graph.deleteEdge(from, to);
920
+ this.graph.deleteEdge(to, from);
921
+ } else {
922
+ this.graph.updateEdge(from, to, this.graph.existEdge(from, to).weight * 1.2, this.graph.existEdge(from, to).type);
923
+ }
924
+ }
925
+ }
926
+ }
927
+
928
+ // 记录词语点被访问
929
+ logWordAccess(word) {
930
+ const now = Date.now();
931
+ if (!this.wordAccessLog.has(word)) {
932
+ this.wordAccessLog.set(word, []);
933
+ }
934
+ this.wordAccessLog.get(word).push(now);
935
+ }
936
+ registerClone() {
937
+ Runtime.cloneRegistry.add(this);
938
+ this.isClone = true;
939
+ // 不自动销毁
940
+ }
941
+
942
+ dispose() {
943
+ this.graph.points.clear();
944
+ this.wordGraph.points.clear();
945
+ this.kvm.memory.clear();
946
+ if (this.wordAccessLog) this.wordAccessLog.clear();
947
+ if (this.forgetTimer) clearInterval(this.forgetTimer);
948
+ // 其它属性也可清空
949
+ }
950
+ forgetWords() {
951
+ const now = Date.now();
952
+ const windowMs = 350 * 1000;
953
+
954
+ // 只保留窗口内访问
955
+ for (const [key, times] of this.wordAccessLog.entries()) {
956
+ const recent = times.filter(t => now - t <= windowMs);
957
+ this.wordAccessLog.set(key, recent);
958
+ }
959
+
960
+ // 保护:收集所有被KVM引用的词
961
+ const protectedWords = new Set();
962
+ for (const [, words] of this.kvm.memory.entries()) {
963
+ if (Array.isArray(words)) {
964
+ for (const w of words) protectedWords.add(w);
965
+ }
966
+ }
967
+
968
+ // 若词表过小,直接跳过遗忘,避免崩塌
969
+ const vocabSize = this.vocabManager.vocab.length;
970
+ if (vocabSize < 1000) {
971
+ console.log('[FORGET] 词表过小,跳过本轮遗忘');
972
+ return;
973
+ }
974
+
975
+ // 仅对“存在于词图且不在保护集”的词做统计
976
+ const stats = [];
977
+ for (const [word, recent] of this.wordAccessLog.entries()) {
978
+ if (!this.wordGraph.points.has(word)) continue;
979
+ if (!this.vocabManager.word2idx.has(word)) continue;
980
+ if (protectedWords.has(word)) continue;
981
+ stats.push({ word, count: recent.length });
982
+ }
983
+ if (stats.length === 0) return;
984
+
985
+ stats.sort((a, b) => a.count - b.count);
986
+
987
+ // 限制每轮上限,避免快速抽干
988
+ const maxForgetRate = 0.001; // 改为每轮最多0.1%
989
+ const n = Math.max(1, Math.floor(stats.length * maxForgetRate));
990
+
991
+ const toForget = stats.slice(0, n).map(s => s.word);
992
+ for (const word of toForget) {
993
+ this.wordGraph.points.delete(word);
994
+ this.vocabManager.vocab = this.vocabManager.vocab.filter(w => w !== word);
995
+ this.vocabManager.updateMappings();
996
+ this.wordAccessLog.delete(word);
997
+ console.log(`[FORGET] 淘汰词语点: ${word}`);
998
+
999
+ // 同步清理所有模因节点的词表
1000
+ for (const [memeID, words] of this.kvm.memory.entries()) {
1001
+ if (!Array.isArray(words)) continue;
1002
+ const newWords = words.filter(w => w !== word);
1003
+ if (newWords.length !== words.length) {
1004
+ this.kvm.set(memeID, newWords);
1005
+ if (newWords.length === 0) {
1006
+ this.graph.points.delete(memeID);
1007
+ this.kvm.memory.delete(memeID);
1008
+ console.log(`[FORGET] 删除空模因节点: ${memeID}`);
1009
+ }
1010
+ }
1011
+ }
1012
+ }
1013
+
1014
+ // 不在这里调用 runMainLoop,避免循环内触发再次遗忘
1015
+ // this.runMainLoop();
1016
+ }
1017
+ // 修改 initWordGraph 方法
1018
+ initWordGraph() {
1019
+ // 添加一些基本的词语关系,优先使用有方向的连接
1020
+ const relations = [
1021
+ // [词1, 词2, 权重, 方向]
1022
+ // 方向:0=双向, 1=指向自己, 2=指向对方
1023
+
1024
+ // 代词流向关系
1025
+ ['I', 'am', 1, 2],
1026
+ ['you', 'are', 1, 2],
1027
+ ['he', 'is', 1, 2],
1028
+ ['she', 'is', 1, 2],
1029
+ ['they', 'are', 1, 2],
1030
+ ['we', 'are', 1, 2],
1031
+
1032
+ // 动词流向关系
1033
+ ['am', 'a', 1, 2],
1034
+ ['is', 'a', 1, 2],
1035
+ ['are', 'a', 1, 2],
1036
+ ['like', 'to', 1, 2],
1037
+ ['want', 'to', 1, 2],
1038
+
1039
+ // 形容词关系(可以保留部分双向)
1040
+ ['good', 'happy', 1, 0],
1041
+ ['bad', 'sad', 1, 0],
1042
+
1043
+ // 名词关系(类别上的)
1044
+ ['apple', 'fruit', 1, 2],
1045
+ ['orange', 'fruit', 1, 2]
1046
+ ];
1047
+
1048
+ for (const [word1, word2, weight, direction] of relations) {
1049
+ // 根据方向值选择边类型
1050
+ if (direction === 0) {
1051
+ this.wordGraph.addBidirectionalEdge(word1, word2, weight);
1052
+ } else {
1053
+ this.wordGraph.addDirectionalEdge(word1, word2, weight, direction);
1054
+ }
1055
+ }
1056
+ }
1057
+
1058
+ // 载入/保存模因网络
1059
+ loadGraph(data) {
1060
+ data.forEach(point => this.graph.addPoint(point.pointID, point.connect));
1061
+ }
1062
+
1063
+ saveGraph() {
1064
+ return this.graph.getAllPoints();
1065
+ }
1066
+
1067
+ // 信号在模因网络中的传递
1068
+ propagateSignal(startID, strength, decayK = 1) {
1069
+ let active = [{ id: startID, value: strength }];
1070
+ const visited = new Set();
1071
+ const activatedOrder = [];
1072
+ const MAX_VISITS = 5000;
1073
+ let visitCount = 0;
1074
+
1075
+ while (active.length > 0 && visitCount < MAX_VISITS) {
1076
+ const next = [];
1077
+ for (const { id, value } of active) {
1078
+ if (value <= 0 || visited.has(id)) continue;
1079
+ visited.add(id);
1080
+ visitCount++;
1081
+ activatedOrder.push(id);
1082
+
1083
+ // 仅在是“词”时记录访问,避免把模因ID写入词访问日志
1084
+ if (this.wordGraph.points.has(id)) {
1085
+ this.logWordAccess(id);
1086
+ }
1087
+
1088
+ const point = this.graph.points.get(id);
1089
+ if (point) {
1090
+ const MAX_NEIGHBORS = 50;
1091
+ const neighbors = point.connect.slice(0, MAX_NEIGHBORS);
1092
+ for (const [weight, neighborID] of neighbors) {
1093
+ if (!visited.has(neighborID)) {
1094
+ next.push({ id: neighborID, value: value - decayK * weight });
1095
+ }
1096
+ }
1097
+ }
1098
+ }
1099
+ active = next;
1100
+ }
1101
+ if (visitCount >= MAX_VISITS) {
1102
+ console.log(`[WARNING] propagateSignal 达到节点访问上限(${MAX_VISITS}),可能未完全传播`);
1103
+ }
1104
+ return activatedOrder;
1105
+ }
1106
+ async generateResponseWithMemes(inputWords, maxLen) {
1107
+ maxLen = maxLen !== undefined ? maxLen : (this.config.maxLen !== undefined ? this.config.maxLen : 16);
1108
+ // 1. 计算每个模因的覆盖率,选出所有被激活的模因
1109
+ const memeStats = [];
1110
+ for (const meme of this.graph.getAllPoints()) {
1111
+ const memeWords = this.kvm.get(meme.pointID) || [];
1112
+ let overlap = 0;
1113
+ for (const word of inputWords) {
1114
+ if (memeWords.includes(word)) overlap++;
1115
+ }
1116
+ let coverage = memeWords.length > 0 ? overlap / memeWords.length : 0;
1117
+ memeStats.push({ meme, coverage });
1118
+ }
1119
+ // 只选覆盖率>0的模因
1120
+ const activated = memeStats.filter(m => m.coverage > 0);
1121
+ if (activated.length === 0) return '(没有相关记忆)';
1122
+
1123
+ // 2. 多源扩散,信号强度=覆盖率
1124
+ const startIDs = activated.map(m => m.meme.pointID);
1125
+ const strengths = activated.map(m => m.coverage);
1126
+ const signalMap = this.propagateSignalMultiSource(startIDs, strengths, 0.5, 2);
1127
+
1128
+ // 3. 按信号强度排序,收集词表
1129
+ const sortedMemes = Array.from(signalMap.entries())
1130
+ .sort((a, b) => b[1] - a[1])
1131
+ .map(([id]) => id);
1132
+
1133
+ let contextWords = [];
1134
+ for (const memeID of sortedMemes) {
1135
+ const words = this.kvm.get(memeID) || [];
1136
+ contextWords.push(...words);
1137
+ if (contextWords.length >= maxLen) break;
1138
+ }
1139
+ contextWords = Array.from(new Set(contextWords)).slice(0, maxLen);
1140
+
1141
+ if (contextWords.length === 0) return '(没有相关记忆)';
1142
+ return contextWords.join(' ');
1143
+ }
1144
+
1145
+ // 处理自然语言输入(wordsArr为分好词的数组)
1146
+ // 修改 processInput 函数,创建单向连接而非双向连接
1147
+ processInput(wordsArr, { addNewWords = true } = {}) {
1148
+ wordsArr = this.filterStopWords(wordsArr);
1149
+ if (wordsArr.length === 0) { console.log('[FILTER] 输入全为停用词,已全部过滤'); return; }
1150
+ // console.log('Processing input:', wordsArr);
1151
+ // 批量处理新词添加
1152
+ if (addNewWords) {
1153
+ // 一次性检查哪些词不在词表中
1154
+ const newWords = wordsArr.filter(word => !this.wordGraph.points.has(word));
1155
+
1156
+ // 批量添加新词到词表
1157
+ for (const word of newWords) {
1158
+ this.vocabManager.addWord(word);
1159
+ this.wordGraph.addPoint(word, []);
1160
+ }
1161
+
1162
+ // 批量添加词语访问记录
1163
+ const now = Date.now();
1164
+ for (const word of wordsArr) {
1165
+ if (!this.wordAccessLog.has(word)) {
1166
+ this.wordAccessLog.set(word, []);
1167
+ }
1168
+ this.wordAccessLog.get(word).push(now);
1169
+ }
1170
+
1171
+ // 批量创建词语之间的有向连接
1172
+ if (wordsArr.length > 1) {
1173
+ // 优化 addDirectionalEdge 调用
1174
+ this._batchAddDirectionalEdges(wordsArr);
1175
+ }
1176
+ }
1177
+ // 添加新词到词表和词图
1178
+ for (const word of wordsArr) {
1179
+ if (addNewWords) {
1180
+ this.vocabManager.addWord(word);
1181
+ if (!this.wordGraph.points.has(word)) {
1182
+ this.wordGraph.addPoint(word, []);
1183
+ }
1184
+ }
1185
+ this.logWordAccess(word); // 记录访问
1186
+ }
1187
+
1188
+ // 创建词语之间的有向连接 (按顺序连接)
1189
+ if (addNewWords && wordsArr.length > 1) {
1190
+ for (let i = 0; i < wordsArr.length - 1; i++) {
1191
+ // 从前一个词指向后一个词 (方向:2表示指向对方)
1192
+ this.wordGraph.addDirectionalEdge(wordsArr[i], wordsArr[i + 1], 1, 2);
1193
+ }
1194
+ }
1195
+
1196
+ // 寻找最近的模因
1197
+ let minDistance = Infinity;
1198
+ let minMemeID = null;
1199
+
1200
+ for (const meme of this.graph.getAllPoints()) {
1201
+ const memeWords = this.kvm.get(meme.pointID) || [];
1202
+ let totalDist = 0;
1203
+ let count = 0;
1204
+
1205
+ for (const word of wordsArr) {
1206
+ let wordDist = Infinity;
1207
+ for (const memeWord of memeWords) {
1208
+ if (word === memeWord) {
1209
+ wordDist = 0;
1210
+ break;
1211
+ }
1212
+ const path = this.wordGraph.selectPath(word, memeWord);
1213
+ if (path) {
1214
+ wordDist = Math.min(wordDist, path.length - 1);
1215
+ }
1216
+ }
1217
+ if (wordDist < Infinity) {
1218
+ totalDist += wordDist;
1219
+ count++;
1220
+ }
1221
+ }
1222
+
1223
+ let avgDist = count > 0 ? totalDist / count : Infinity;
1224
+ if (avgDist < minDistance) {
1225
+ minDistance = avgDist;
1226
+ minMemeID = meme.pointID;
1227
+ }
1228
+ }
1229
+
1230
+ if (minDistance < Infinity && minDistance <= 1) {
1231
+ // 合并到现有模因或创建新模因...
1232
+ const memeWords = this.kvm.get(minMemeID) || [];
1233
+ const overlap = wordsArr.filter(w => memeWords.includes(w)).length;
1234
+ if (overlap >= this.MIN_OVERLAP && memeWords.length + wordsArr.length <= this.MAX_MEME_WORDS) {
1235
+ this.kvm.set(minMemeID, Array.from(new Set([...memeWords, ...wordsArr])));
1236
+ /// console.log(`Merged to existing meme: ${minMemeID}`);
1237
+ } else {
1238
+ // 创建新模因,使用有向连接
1239
+ const newID = 'meme_' + Date.now();
1240
+ this.graph.addPoint(newID, []);
1241
+ this.kvm.set(newID, wordsArr);
1242
+
1243
+ // 单向连接到最近的模因 (方向:2表示指向对方)
1244
+ if (minMemeID) {
1245
+ this.graph.addDirectionalEdge(newID, minMemeID, minDistance, 2);
1246
+ // console.log(`[LINK] 新模因 ${newID} 单向连接到最近模因 ${minMemeID}`);
1247
+ }
1248
+ // console.log(`Created new meme: ${newID}`);
1249
+ }
1250
+ } else {
1251
+ // 创建新模因
1252
+ const newID = 'meme_' + Date.now();
1253
+ this.graph.addPoint(newID, []);
1254
+ this.kvm.set(newID, wordsArr);
1255
+
1256
+ // 如果有较近的模因,仍然创建单向连接
1257
+ if (minMemeID) {
1258
+ this.graph.addDirectionalEdge(newID, minMemeID, Math.min(minDistance, 5), 2);
1259
+ // console.log(`[LINK] 新模因 ${newID} 单向连接到最近模因 ${minMemeID}`);
1260
+ }
1261
+ // console.log(`Created new meme: ${newID}`);
1262
+ }
1263
+ }
1264
+ // 新增批量添加边的辅助方法
1265
+ _batchAddDirectionalEdges(words) {
1266
+ // 预先确保所有点都存在
1267
+ for (const word of words) {
1268
+ if (!this.wordGraph.points.has(word)) {
1269
+ this.wordGraph.addPoint(word, []);
1270
+ }
1271
+ }
1272
+
1273
+ // 批量添加边
1274
+ for (let i = 0; i < words.length - 1; i++) {
1275
+ const fromWord = words[i];
1276
+ const toWord = words[i + 1];
1277
+ const fromPoint = this.wordGraph.points.get(fromWord);
1278
+
1279
+ // 检查是否已存在这条边
1280
+ if (!fromPoint.connect.some(([_, id, d]) => id === toWord && d === 2)) {
1281
+ fromPoint.connect.push([1, toWord, 2]);
1282
+ }
1283
+ }
1284
+ }
1285
+ // 从Spider加载并构建词表
1286
+ buildVocabFromSpider() {
1287
+ return this.vocabManager.buildVocabFromSpider(this.spider);
1288
+ }
1289
+ // Runtime类内
1290
+ // ...existing code...
1291
+ /**
1292
+ * 多源信号扩散(优化版,无防环,充分利用单向路线,信号可多次叠加)
1293
+ * 支持信号融合、动态权重、阻塞式高并发保护、可追踪激活路径
1294
+ * @param {Array<string>} startIDs - 多个起点ID
1295
+ * @param {Array<number>} strengths - 各起点信号强度
1296
+ * @param {number} decayK - 衰减系数
1297
+ * @param {number} maxStep - 最大扩散步数
1298
+ * @param {Object} options - 可选参数
1299
+ * options.maxActiveNodes: 每步最大活跃节点数
1300
+ * options.minSignal: 信号强度低于此值不再传播
1301
+ * options.trackPath: 是否记录激活路径
1302
+ * @returns {Object|Map} { signalMap, activationPaths } 或 signalMap
1303
+ */
1304
+ propagateSignalMultiSource(startIDs, strengths, decayK, maxStep, options = {}) {
1305
+ decayK = decayK !== undefined ? decayK : (this.config.decayK !== undefined ? this.config.decayK : 1);
1306
+ maxStep = maxStep !== undefined ? maxStep : (this.config.maxStep !== undefined ? this.config.maxStep : 10);
1307
+ const maxActiveNodes = options.maxActiveNodes || 5000;
1308
+ const minSignal = options.minSignal || 0.01;
1309
+ const trackPath = options.trackPath || false;
1310
+
1311
+ // 节点信号累加表
1312
+ const signalMap = new Map();
1313
+ // 路径追踪表(可选)
1314
+ const activationPaths = trackPath ? new Map() : null;
1315
+
1316
+ // 初始化活跃队列,每个元素{id, value, from}
1317
+ let active = startIDs.map((id, i) => ({
1318
+ id,
1319
+ value: strengths[i],
1320
+ from: null // 起点无前驱
1321
+ }));
1322
+
1323
+ let step = 0;
1324
+
1325
+ while (active.length > 0 && step < maxStep) {
1326
+ // 1. 限制活跃节点数,优先保留信号强的
1327
+ if (active.length > maxActiveNodes) {
1328
+ active.sort((a, b) => b.value - a.value);
1329
+ active = active.slice(0, maxActiveNodes);
1330
+ console.log(`[LIMIT] 多源扩散活跃节点数已限制为 ${maxActiveNodes}`);
1331
+ }
1332
+
1333
+ const next = [];
1334
+ for (const { id, value, from } of active) {
1335
+ if (value < minSignal) continue;
1336
+
1337
+ // 信号融合:累加到signalMap
1338
+ signalMap.set(id, (signalMap.get(id) || 0) + value);
1339
+
1340
+ // 路径追踪
1341
+ if (trackPath) {
1342
+ if (!activationPaths.has(id)) activationPaths.set(id, []);
1343
+ if (from !== null) activationPaths.get(id).push(from);
1344
+ }
1345
+
1346
+ // 传播到邻居(不防环,允许信号多次叠加)
1347
+ const point = this.graph.points.get(id);
1348
+ if (point) {
1349
+ // 限制每个节点最多处理的邻居数量
1350
+ const MAX_NEIGHBORS = 30;
1351
+ const neighbors = point.connect.slice(0, MAX_NEIGHBORS);
1352
+
1353
+ for (const [weight, neighborID] of neighbors) {
1354
+ // 信号可按边权重调整(如weight越小越容易传播)
1355
+ const nextValue = value - decayK * weight;
1356
+ if (nextValue >= minSignal) {
1357
+ next.push({
1358
+ id: neighborID,
1359
+ value: nextValue,
1360
+ from: id
1361
+ });
1362
+ }
1363
+ }
1364
+ }
1365
+ }
1366
+ active = next;
1367
+ step++;
1368
+ }
1369
+
1370
+ if (trackPath) {
1371
+ return { signalMap, activationPaths };
1372
+ }
1373
+ return signalMap;
1374
+ }
1375
+ // ...existing code...
1376
+
1377
+ // 主循环:持续归纳和信号传递
1378
+ // Runtime类内
1379
+ // Runtime类内
1380
+ // Runtime类内
1381
+ // 添加到Runtime类
1382
+ checkMemoryUsage() {
1383
+ const memUsage = process.memoryUsage();
1384
+ const usedHeapPct = memUsage.heapUsed / memUsage.heapTotal * 100;
1385
+ const usedRssPct = memUsage.rss / (1024 * 1024 * 1024) * 100; // 占用物理内存百分比(假设8GB)
1386
+
1387
+ console.log(`[MEMORY] Heap: ${(memUsage.heapUsed / 1024 / 1024).toFixed(1)}MB/${(memUsage.heapTotal / 1024 / 1024).toFixed(1)}MB (${usedHeapPct.toFixed(1)}%), RSS: ${(memUsage.rss / 1024 / 1024).toFixed(1)}MB`);
1388
+
1389
+ // 如果堆内存或RSS过高,执行紧急GC
1390
+ if (usedHeapPct > 85 || usedRssPct > 80) {
1391
+ console.log('[MEMORY] 内存使用率过高,执行紧急清理');
1392
+ this.emergencyMemoryCleanup();
1393
+ return false; // 表示内存紧张
1394
+ }
1395
+ return true; // 内存正常
1396
+ }
1397
+
1398
+ // 紧急内存清理
1399
+ emergencyMemoryCleanup() {
1400
+ // 清理可丢弃的缓存
1401
+ this.wordAccessLog = new Map([...this.wordAccessLog.entries()]
1402
+ .slice(-1000)); // 只保留最近1000条
1403
+ // 清理模因阻断系统的历史记录
1404
+ if (this.memeBarrier && this.memeBarrier.suspiciousMemesHistory) {
1405
+ // 只保留最近的记录
1406
+ for (const [memeID, history] of this.memeBarrier.suspiciousMemesHistory.entries()) {
1407
+ if (history.length > 10) {
1408
+ this.memeBarrier.suspiciousMemesHistory.set(memeID, history.slice(-5));
1409
+ }
1410
+ }
1411
+ }
1412
+ // 强制GC (如果环境支持)
1413
+ if (global.gc) {
1414
+ global.gc();
1415
+ console.log('[MEMORY] 已触发垃圾回收');
1416
+ } else {
1417
+ console.log('[MEMORY] 垃圾回收不可用,请使用 --expose-gc 启动');
1418
+ }
1419
+ }
1420
+ async runMainLoop() {
1421
+ this.memeBarrier.start();
1422
+ let running = true;
1423
+ let tick = 0;
1424
+ console.log('Starting main loop...');
1425
+
1426
+ while (running) {
1427
+ // 1. 内存与系统负载监控
1428
+ const memoryOk = this.checkMemoryUsage();
1429
+ if (!memoryOk) {
1430
+ console.log('[LOOP] 内存压力大,本轮循环减少处理量');
1431
+ // 可以跳过此轮或减少处理量
1432
+ }
1433
+ // 动态调整批处理大小
1434
+ const loadMultiplier = this.monitorSystemLoad();
1435
+ const BATCH_SIZE = Math.max(50, Math.floor(100 * loadMultiplier));
1436
+
1437
+ // 2. 随机激活一个模因节点并信号扩散
1438
+ const allMemes = this.graph.getAllPoints();
1439
+ if (allMemes.length === 0) {
1440
+ console.log('No memes found, breaking loop');
1441
+ break;
1442
+ }
1443
+ const startMeme = allMemes[Math.floor(Math.random() * allMemes.length)];
1444
+ let activatedNodes = this.propagateSignal(startMeme.pointID, 10, 1) || [];
1445
+ this.recordActivationPath(activatedNodes);
1446
+
1447
+ // 3. 自动归纳/合并模因(多线程并行批处理)
1448
+ let memesToDelete = new Set();
1449
+ const results = [];
1450
+
1451
+ for (let i = 0; i < allMemes.length; i++) {
1452
+ const memeA = allMemes[i];
1453
+ if (memesToDelete.has(memeA.pointID)) continue;
1454
+ const wordsA = this.kvm.get(memeA.pointID) || [];
1455
+
1456
+ // 分批处理j
1457
+ for (let jStart = i + 1; jStart < allMemes.length; jStart += BATCH_SIZE) {
1458
+ const batchEnd = Math.min(jStart + BATCH_SIZE, allMemes.length);
1459
+ const batchTasks = [];
1460
+
1461
+ for (let j = jStart; j < batchEnd; j++) {
1462
+ const memeB = allMemes[j];
1463
+ if (memesToDelete.has(memeB.pointID)) continue;
1464
+ const wordsB = this.kvm.get(memeB.pointID) || [];
1465
+ batchTasks.push(
1466
+ safePoolExec('calcMemeDistance', [{ wordsA, wordsB }], { avgDist: Infinity, overlap: 0 })
1467
+ .then(({ avgDist, overlap }) => ({ i, j, avgDist, overlap }))
1468
+ );
1469
+ }
1470
+
1471
+ // 等待当前批次完成
1472
+ const batchResults = await Promise.all(batchTasks);
1473
+ results.push(...batchResults);
1474
+
1475
+ // 处理结果
1476
+ for (const { i, j, avgDist, overlap } of batchResults) {
1477
+ this._processMemeComparisonResult(
1478
+ allMemes[i], allMemes[j], avgDist, overlap, memesToDelete
1479
+ );
1480
+ }
1481
+ }
1482
+ }
1483
+
1484
+ // 4. 其它AI流程
1485
+ const activeMemes = this.graph.getAllPoints().length;
1486
+ console.log(`Tick ${tick}: ${activeMemes} memes active`);
1487
+
1488
+ // 5. 集成注意力机制与模因监控
1489
+ this.updateAttentionLinks(3, 1);
1490
+ this.monitorMemeSize();
1491
+
1492
+ // 6. 控制循环退出条件
1493
+ tick++;
1494
+ if (tick >= 10) running = false;
1495
+ }
1496
+
1497
+ console.log('Main loop ended');
1498
+ }
1499
+
1500
+ // 新增辅助函数,处理模因比较结果
1501
+ // Runtime类内
1502
+ _processMemeComparisonResult(memeA, memeB, avgDist, overlap, memesToDelete) {
1503
+ if (!memeA || !memeB) return;
1504
+ if (memesToDelete.has(memeA.pointID) || memesToDelete.has(memeB.pointID)) return;
1505
+
1506
+ const wordsA = this.kvm.get(memeA.pointID) || [];
1507
+ const wordsB = this.kvm.get(memeB.pointID) || [];
1508
+
1509
+ // 距离小于等于1则合并
1510
+ if (
1511
+ avgDist <= 1 &&
1512
+ wordsA.length + wordsB.length <= this.MAX_MEME_WORDS &&
1513
+ overlap >= this.MIN_OVERLAP &&
1514
+ (wordsA.length < this.MAX_MEME_WORDS * 0.8 && wordsB.length < this.MAX_MEME_WORDS * 0.8) &&
1515
+ (overlap / Math.min(wordsA.length, wordsB.length) >= 0.3)
1516
+ ) {
1517
+ // 合并词汇
1518
+ this.kvm.set(memeA.pointID, Array.from(new Set([...wordsA, ...wordsB])));
1519
+
1520
+ // --- 合并边 ---
1521
+ // 1. 将所有指向 memeB 的边重定向到 memeA
1522
+ for (const point of this.graph.getAllPoints()) {
1523
+ for (let edge of point.connect) {
1524
+ if (edge[1] === memeB.pointID) {
1525
+ edge[1] = memeA.pointID;
1526
+ }
1527
+ }
1528
+ }
1529
+ // 2. 将 memeB 的出边合并到 memeA(避免重复)
1530
+ const memeBPoint = this.graph.points.get(memeB.pointID);
1531
+ const memeAPoint = this.graph.points.get(memeA.pointID);
1532
+ if (memeBPoint && memeAPoint) {
1533
+ for (const edge of memeBPoint.connect) {
1534
+ if (!memeAPoint.connect.some(e => e[1] === edge[1] && e[2] === edge[2])) {
1535
+ memeAPoint.connect.push([...edge]);
1536
+ }
1537
+ }
1538
+ }
1539
+
1540
+ // 3. 删除 memeB 节点
1541
+ this.graph.points.delete(memeB.pointID);
1542
+ this.kvm.memory.delete(memeB.pointID);
1543
+ memesToDelete.add(memeB.pointID);
1544
+
1545
+ // console.log(`Merged memes: ${memeA.pointID} <- ${memeB.pointID}`);
1546
+ // 合并后立即尝试分裂
1547
+ this.splitMemeIfNeeded(memeA.pointID);
1548
+ } else {
1549
+ // 不合并,但有重合单词,且距离大于1且小于Infinity
1550
+ if (
1551
+ overlap > 0 &&
1552
+ avgDist > 1 &&
1553
+ avgDist < Infinity
1554
+ ) {
1555
+ // 检查是否已有单向边
1556
+ const existAtoB = this.graph.existEdge(memeA.pointID, memeB.pointID);
1557
+ const existBtoA = this.graph.existEdge(memeB.pointID, memeA.pointID);
1558
+
1559
+ // 如果没有双向边,则添加双向边
1560
+ if (!(existAtoB.exist && existAtoB.type === 0) && !(existBtoA.exist && existBtoA.type === 0)) {
1561
+ this.graph.addBidirectionalEdge(memeA.pointID, memeB.pointID, avgDist);
1562
+ // console.log(`[LINK] 添加双向边: ${memeA.pointID} <-> ${memeB.pointID} (avgDist=${avgDist})`);
1563
+ }
1564
+ }
1565
+ }
1566
+
1567
+ // 更新模因之间的边权重为距离
1568
+ this.graph.updateEdge(memeA.pointID, memeB.pointID, avgDist, 0);
1569
+ }
1570
+ // Runtime类内
1571
+ splitMemeIfNeeded(memeID) {
1572
+ const words = this.kvm.get(memeID) || [];
1573
+ if (words.length <= 5) return; // 太小不分裂
1574
+
1575
+ // 强制分裂:超过阈值直接分裂
1576
+ if (words.length > this.MAX_MEME_WORDS * 0.8) {
1577
+ // 优化分裂算法,减少循环复杂度
1578
+ const chunkSize = Math.ceil(this.MAX_MEME_WORDS / 2.5);
1579
+ const chunksCount = Math.ceil(words.length / chunkSize);
1580
+
1581
+ // 预分配ID,减少循环中的时间戳计算
1582
+ const newIDs = Array(chunksCount).fill().map(() =>
1583
+ 'meme_' + Date.now() + '_' + Math.floor(Math.random() * 10000)
1584
+ );
1585
+
1586
+ // 批量创建新模因
1587
+ for (let i = 0; i < chunksCount; i++) {
1588
+ const chunk = words.slice(i * chunkSize, (i + 1) * chunkSize);
1589
+ if (chunk.length > 1) {
1590
+ const newID = newIDs[i];
1591
+ this.graph.addPoint(newID, []);
1592
+ this.kvm.set(newID, chunk);
1593
+ // console.log(`[SPLIT-FORCE] 新建模因: ${newID} 词数: ${chunk.length}`);
1594
+ }
1595
+ }
1596
+
1597
+ // 删除原模因
1598
+ this.graph.points.delete(memeID);
1599
+ this.kvm.memory.delete(memeID);
1600
+ // console.log(`[SPLIT-FORCE] 删除原模因: ${memeID}`);
1601
+ return;
1602
+ }
1603
+
1604
+ // 构建词语子图
1605
+ const subgraph = new Map();
1606
+ for (const word of words) {
1607
+ const connects = [];
1608
+ const point = this.wordGraph.points.get(word);
1609
+ if (point) {
1610
+ for (const [, neighbor] of point.connect) {
1611
+ if (words.includes(neighbor)) {
1612
+ connects.push(neighbor);
1613
+ }
1614
+ }
1615
+ }
1616
+ subgraph.set(word, connects);
1617
+ }
1618
+
1619
+ // 寻找连通分量,使用BFS算法而非递归,避免栈溢出
1620
+ const visited = new Set();
1621
+ const components = [];
1622
+ for (const word of words) {
1623
+ if (visited.has(word)) continue;
1624
+ const queue = [word];
1625
+ const comp = [];
1626
+ visited.add(word);
1627
+ while (queue.length > 0) {
1628
+ const w = queue.shift();
1629
+ comp.push(w);
1630
+ for (const n of subgraph.get(w) || []) {
1631
+ if (!visited.has(n)) {
1632
+ visited.add(n);
1633
+ queue.push(n);
1634
+ }
1635
+ }
1636
+ }
1637
+ if (comp.length > 0) components.push(comp);
1638
+ }
1639
+
1640
+ // 判断是否需要分裂
1641
+ if (components.length > 1 && components.some(c => c.length >= 5)) {
1642
+ // 分裂
1643
+ for (const comp of components) {
1644
+ if (comp.length < 2) continue; // 太小不建新节点
1645
+ const newID = 'meme_' + Date.now() + '_' + Math.floor(Math.random() * 10000);
1646
+ this.graph.addPoint(newID, []);
1647
+ this.kvm.set(newID, comp);
1648
+ // console.log(`[SPLIT] 新建模因: ${newID} 词数: ${comp.length}`);
1649
+ }
1650
+ // 删除原节点
1651
+ this.graph.points.delete(memeID);
1652
+ this.kvm.memory.delete(memeID);
1653
+ // console.log(`[SPLIT] 删除原模因: ${memeID}`);
1654
+ }
1655
+ }
1656
+ }
1657
+ function safePoolExec(method, args, fallback) {
1658
+ if (isShuttingDown) return Promise.resolve(fallback);
1659
+ try {
1660
+ return pool.exec(method, args);
1661
+ } catch (e) {
1662
+ if (String(e && e.message).toLowerCase().includes('terminated')) {
1663
+ return Promise.resolve(fallback);
1664
+ }
1665
+ throw e;
1666
+ }
1667
+ }
1668
+ // 关联层:分析模因层边的关系并形成抽象模式
1669
+ class AssociationLayer {
1670
+ constructor(runtime) {
1671
+ this.runtime = runtime;
1672
+ this.patterns = []; // 存储发现的模因边关系模式
1673
+ }
1674
+
1675
+ // 识别模因层的边关系模式
1676
+ identifyPatterns() {
1677
+ const memes = this.runtime.graph.getAllPoints();
1678
+ // 边关系模式:记录权重、连接度分布等
1679
+ const edgePatterns = new Map();
1680
+ const connectivityPatterns = new Map(); // 模因连接度分布
1681
+
1682
+ // 分析边权重模式
1683
+ for (const meme of memes) {
1684
+ // 记录此模因的连接度
1685
+ const connectivity = meme.connect.length;
1686
+ connectivityPatterns.set(
1687
+ connectivity,
1688
+ (connectivityPatterns.get(connectivity) || 0) + 1
1689
+ );
1690
+
1691
+ // 记录边权重模式
1692
+ for (const [weight, neighborID] of meme.connect) {
1693
+ const key = `${weight.toFixed(2)}`;
1694
+ edgePatterns.set(key, (edgePatterns.get(key) || 0) + 1);
1695
+
1696
+ // 获取连接两端模因的词汇量
1697
+ const sourceWords = this.runtime.kvm.get(meme.pointID) || [];
1698
+ const targetWords = this.runtime.kvm.get(neighborID) || [];
1699
+
1700
+ // 记录"词汇量比例-边权重"关系
1701
+ const sizeRatio = Math.max(sourceWords.length, targetWords.length) /
1702
+ (Math.min(sourceWords.length, targetWords.length) || 1);
1703
+ const ratioKey = `ratio:${sizeRatio.toFixed(1)}-weight:${weight.toFixed(2)}`;
1704
+ edgePatterns.set(ratioKey, (edgePatterns.get(ratioKey) || 0) + 1);
1705
+ }
1706
+ }
1707
+
1708
+ // 提取最常见模式
1709
+ const commonEdgePatterns = Array.from(edgePatterns.entries())
1710
+ .sort((a, b) => b[1] - a[1])
1711
+ .slice(0, 10); // 取前10种最常见边模式
1712
+
1713
+ const connectivityDistribution = Array.from(connectivityPatterns.entries())
1714
+ .sort((a, b) => a[0] - b[0]); // 按连接度排序
1715
+
1716
+ this.patterns = {
1717
+ edgeWeights: commonEdgePatterns,
1718
+ connectivity: connectivityDistribution
1719
+ };
1720
+
1721
+ console.log(`[ASSOCIATION] 识别出${commonEdgePatterns.length}种边模式`);
1722
+ return this.patterns;
1723
+ }
1724
+ // 继续AssociationLayer类
1725
+
1726
+ // ...前面的代码...
1727
+
1728
+ // 创建系统副本
1729
+ async cloneSystem() {
1730
+ console.log('[CLONE] 创建系统副本');
1731
+ const clone = new Runtime();
1732
+ clone.registerClone();
1733
+
1734
+ try {
1735
+ // 1. 确保Spider实例共享
1736
+ clone.spider = this.runtime.spider;
1737
+
1738
+ // 2. 复制词表(确保顺序一致)
1739
+ clone.vocabManager.vocab = [...this.runtime.vocabManager.vocab];
1740
+ clone.vocabManager.updateMappings();
1741
+
1742
+ // 3. 复制词语网络
1743
+ console.log('[CLONE] 开始复制词语网络...');
1744
+ for (const [key, value] of this.runtime.wordGraph.points.entries()) {
1745
+ clone.wordGraph.addPoint(key, Array.isArray(value.connect) ?
1746
+ value.connect.map(conn => [...conn]) : []);
1747
+ }
1748
+
1749
+ // 4. 复制模因网络
1750
+ console.log('[CLONE] 开始复制模因网络...');
1751
+ for (const [key, value] of this.runtime.graph.points.entries()) {
1752
+ clone.graph.addPoint(key, Array.isArray(value.connect) ?
1753
+ value.connect.map(conn => [...conn]) : []);
1754
+ }
1755
+
1756
+ // 5. 复制KVM - 确保一致性和类型
1757
+ console.log('[CLONE] 开始复制KVM...');
1758
+ let nonEmptyCount = 0;
1759
+ let totalWordCount = 0;
1760
+
1761
+ for (const [key, value] of this.runtime.kvm.memory.entries()) {
1762
+ // 严格规范化所有词汇
1763
+ if (Array.isArray(value)) {
1764
+ // 确保数组中每个元素都是字符串且归一化
1765
+ const normalizedWords = value.map(word =>
1766
+ typeof word === 'string' ? word.toLowerCase().trim() : String(word)
1767
+ );
1768
+
1769
+ // 应用词形归一化
1770
+ const lemmatizedWords = clone.spider.lemmatizeWords(normalizedWords);
1771
+ clone.kvm.set(key, lemmatizedWords);
1772
+
1773
+ if (lemmatizedWords.length > 0) {
1774
+ nonEmptyCount++;
1775
+ totalWordCount += lemmatizedWords.length;
1776
+ }
1777
+ } else if (value != null) {
1778
+ // 非数组值转换为单元素数组
1779
+ const singleWord = String(value).toLowerCase().trim();
1780
+ const lemmatizedWord = clone.spider.lemmatize(singleWord);
1781
+ clone.kvm.set(key, [lemmatizedWord]);
1782
+
1783
+ nonEmptyCount++;
1784
+ totalWordCount++;
1785
+ } else {
1786
+ // null或undefined情况,设为空数组
1787
+ clone.kvm.set(key, []);
1788
+ }
1789
+ }
1790
+
1791
+ console.log(`[CLONE] KVM复制完成: ${nonEmptyCount}个非空模因,${totalWordCount}个词语`);
1792
+
1793
+ // 6. 复制其他配置和参数
1794
+ console.log('[CLONE] 开始复制词表和其他属性...');
1795
+ clone.MAX_MEME_WORDS = this.runtime.MAX_MEME_WORDS;
1796
+ clone.MIN_OVERLAP = this.runtime.MIN_OVERLAP;
1797
+ clone.config = { ...this.runtime.config };
1798
+
1799
+ console.log('[CLONE] 系统副本创建完成');
1800
+ return clone;
1801
+ } catch (error) {
1802
+ console.error('[CLONE ERROR]', error);
1803
+ return clone;
1804
+ }
1805
+ } // 将模式应用到系统副本
1806
+
1807
+ applyPatternsToClone(systemClone) {
1808
+ console.log('[CLONE] 应用关系模式到副本');
1809
+
1810
+ // 在开始修改前进行KVM内容检查
1811
+ let wordCount = 0;
1812
+ let nonEmptyMemes = 0;
1813
+ for (const [memeID, words] of systemClone.kvm.memory.entries()) {
1814
+ if (Array.isArray(words) && words.length > 0) {
1815
+ wordCount += words.length;
1816
+ nonEmptyMemes++;
1817
+ }
1818
+ }
1819
+ console.log(`[CLONE] 克隆系统KVM检查: ${nonEmptyMemes}个非空模因, ${wordCount}个词语`);
1820
+
1821
+ // 如果词汇数为0,说明克隆过程有问题,尝试重新复制KVM
1822
+ if (wordCount === 0) {
1823
+ console.log('[CLONE] 克隆系统KVM为空,重新复制KVM数据');
1824
+ for (const [key, value] of this.runtime.kvm.memory.entries()) {
1825
+ systemClone.kvm.set(key, Array.isArray(value) ? [...value] : [String(value)]);
1826
+ }
1827
+ }
1828
+
1829
+ const cloneMemes = systemClone.graph.getAllPoints();
1830
+ if (cloneMemes.length === 0) return systemClone;
1831
+ // 防御性处理
1832
+ if (!this.patterns || !Array.isArray(this.patterns.edgeWeights)) {
1833
+ console.warn('[CLONE] 未检测到有效的边模式,跳过关系模式应用');
1834
+ return systemClone;
1835
+ }
1836
+ // 限制修改范围在10%以内
1837
+ const modifyLimit = Math.max(1, Math.floor(cloneMemes.length * 0.1));
1838
+ let modified = 0;
1839
+
1840
+ for (const [patternKey, frequency] of this.patterns.edgeWeights) {
1841
+ if (modified >= modifyLimit) break;
1842
+
1843
+ if (!patternKey.includes('ratio:')) {
1844
+ const weight = parseFloat(patternKey);
1845
+ if (isNaN(weight)) continue;
1846
+
1847
+ const targetCount = Math.ceil(frequency / 10);
1848
+ for (let i = 0; i < targetCount && modified < modifyLimit; i++) {
1849
+ const randomIdx = Math.floor(Math.random() * cloneMemes.length);
1850
+ const meme = cloneMemes[randomIdx];
1851
+
1852
+ if (meme.connect.length > 0) {
1853
+ const connIdx = Math.floor(Math.random() * meme.connect.length);
1854
+ const oldWeight = meme.connect[connIdx][0];
1855
+ const direction = meme.connect[connIdx][2];
1856
+
1857
+ const newWeight = Math.min(5, Math.max(0.1, isFinite(weight) ?
1858
+ weight + (Math.random() - 0.5) * 0.5 : Math.random() * 2 + 0.5));
1859
+ meme.connect[connIdx][0] = newWeight;
1860
+ meme.connect[connIdx][2] = direction;
1861
+
1862
+ // console.log(`[CLONE] 修改边权重: ${meme.pointID}->${meme.connect[connIdx][1]}, ${oldWeight}->${newWeight.toFixed(2)}, 保留方向: ${direction}`);
1863
+ modified++;
1864
+ }
1865
+ }
1866
+ }
1867
+ }
1868
+
1869
+ return systemClone;
1870
+ }
1871
+ }
1872
+ // 强化学习模块
1873
+ class ReinforcementLearner {
1874
+ constructor(runtime, testArticlesPath = path.join(__dirname, 'tests')) {
1875
+ this.runtime = runtime;
1876
+ this.testArticlesPath = testArticlesPath;
1877
+ this.associationLayer = new AssociationLayer(runtime);
1878
+ }
1879
+ updateRuntime(newRuntime) {
1880
+ if (this.runtime) {
1881
+ this.runtime.cleanup();
1882
+ }
1883
+
1884
+ this.runtime = newRuntime;
1885
+
1886
+ // 确保词表映射是最新的
1887
+ this.runtime.vocabManager.updateMappings();
1888
+
1889
+ // 确保Spider实例一致
1890
+ if (global.ctrlA && global.ctrlA.runtime && global.ctrlA.runtime.spider) {
1891
+ this.runtime.spider = global.ctrlA.runtime.spider;
1892
+ }
1893
+
1894
+ // 进行词汇一致性检查
1895
+ this._validateVocabulary();
1896
+
1897
+ console.log(`[CONTROLLER] Runtime已更新,词汇表大小: ${this.runtime.vocabManager.vocab.length}`);
1898
+ }
1899
+ // 添加辅助方法:词汇一致性检查
1900
+ _validateVocabulary() {
1901
+ // 抽查KVM中的词是否在词表中
1902
+ let missingWords = 0;
1903
+ const sampleSize = Math.min(100, this.runtime.kvm.memory.size);
1904
+ const sampleMemes = Array.from(this.runtime.kvm.memory.keys()).slice(0, sampleSize);
1905
+
1906
+ for (const memeID of sampleMemes) {
1907
+ const words = this.runtime.kvm.get(memeID) || [];
1908
+ if (!Array.isArray(words)) {
1909
+ console.warn(`[CONTROLLER] 模因${memeID}的KVM值不是数组`);
1910
+ this.runtime.kvm.set(memeID, []);
1911
+ continue;
1912
+ }
1913
+
1914
+ for (const word of words) {
1915
+ if (!this.runtime.vocabManager.word2idx.has(word)) {
1916
+ this.runtime.vocabManager.addWord(word);
1917
+ missingWords++;
1918
+ }
1919
+ }
1920
+ }
1921
+
1922
+ if (missingWords > 0) {
1923
+ console.log(`[CONTROLLER] 已将${missingWords}个缺失词添加到词表`);
1924
+ this.runtime.vocabManager.updateMappings();
1925
+ }
1926
+ }
1927
+ // 加载测试文章
1928
+ loadTestArticles() {
1929
+ const articles = [];
1930
+ if (!fs.existsSync(this.testArticlesPath)) {
1931
+ console.warn(`[RL] 测试目录不存在: ${this.testArticlesPath}`);
1932
+ return articles;
1933
+ }
1934
+
1935
+ const files = fs.readdirSync(this.testArticlesPath).filter(f => f.endsWith('.txt'));
1936
+ for (const file of files) {
1937
+ try {
1938
+ const content = fs.readFileSync(path.join(this.testArticlesPath, file), 'utf-8');
1939
+ articles.push(content);
1940
+ } catch (e) {
1941
+ console.warn(`[RL] 读取失败: ${file}`);
1942
+ }
1943
+ }
1944
+ return articles;
1945
+ }
1946
+
1947
+ // 评估系统
1948
+ // ReinforcementLearner类内
1949
+ evaluateSystem(system, article) {
1950
+ // 1. 标准化处理输入文章
1951
+ const cleaned = article.toLowerCase().replace(/[^a-z\s]/g, ' ');
1952
+ const allWords = cleaned.split(/\s+/).filter(w => w.length > 0);
1953
+
1954
+ // 关键:使用Spider进行词形还原
1955
+ const lemmatizedWords = system.spider ? system.spider.lemmatizeWords(allWords) : allWords;
1956
+ const uniqueWords = Array.from(new Set(lemmatizedWords));
1957
+
1958
+ if (uniqueWords.length === 0) return 0;
1959
+
1960
+ // 屏蔽高频词
1961
+ const stopWords = ['be', 'for', 'the', 'and', 'a', 'an', 'of', 'in', 'to', 'that', 'it', 'with', 'is', 'was', 'as', 'on'];
1962
+ const filteredWords = uniqueWords.filter(word => !stopWords.includes(word));
1963
+
1964
+ if (filteredWords.length === 0) return 0; // 全是停用词,直接返回0
1965
+
1966
+ // 取10%的词作为输入(至少1个)
1967
+ const inputCount = Math.max(1, Math.floor(filteredWords.length * 0.1));
1968
+ const shuffled = filteredWords.slice().sort(() => Math.random() - 0.5);
1969
+ const inputWords = shuffled.slice(0, inputCount);
1970
+
1971
+ // 2. 诊断KVM中的模因数量
1972
+ let nonEmptyMemeCount = 0;
1973
+ let totalWordCount = 0;
1974
+
1975
+ for (const [memeID, words] of system.kvm.memory.entries()) {
1976
+ if (Array.isArray(words) && words.length > 0) {
1977
+ nonEmptyMemeCount++;
1978
+ totalWordCount += words.length;
1979
+ }
1980
+ }
1981
+
1982
+ if (nonEmptyMemeCount === 0) {
1983
+ console.warn(`[RL-WARN] 系统KVM中无有效模因,无法评估! 总模因数: ${system.graph.getAllPoints().length}`);
1984
+ return 0;
1985
+ }
1986
+
1987
+ // 3. 计算每个模因和输入词的匹配度
1988
+ const memeStats = [];
1989
+ let foundMatch = false;
1990
+
1991
+ for (const meme of system.graph.getAllPoints()) {
1992
+ // 确保所有KVM值为数组且标准化
1993
+ let memeWords = system.kvm.get(meme.pointID);
1994
+ if (!Array.isArray(memeWords)) {
1995
+ memeWords = memeWords ? [String(memeWords)] : [];
1996
+ system.kvm.set(meme.pointID, memeWords);
1997
+ }
1998
+
1999
+ // 规范化词汇(确保小写+归一化)
2000
+ const normalizedMemeWords = memeWords.map(word =>
2001
+ typeof word === 'string' ? word.toLowerCase().trim() : String(word)
2002
+ );
2003
+
2004
+ let overlap = 0;
2005
+ for (const word of inputWords) {
2006
+ if (normalizedMemeWords.includes(word)) {
2007
+ overlap++;
2008
+ foundMatch = true;
2009
+ }
2010
+ }
2011
+
2012
+ const coverage = normalizedMemeWords.length > 0 ? overlap / normalizedMemeWords.length : 0;
2013
+ if (coverage > 0) {
2014
+ memeStats.push({ meme, coverage });
2015
+ }
2016
+ }
2017
+
2018
+ // 4. 如果没找到匹配,记录调试信息
2019
+ if (!foundMatch) {
2020
+ console.log(`[RL-DEBUG] ${system.isClone ? '克隆' : '原'}系统没有匹配: 输入词=${inputWords.slice(0, 3).join(',')}`);
2021
+
2022
+ // 取样几个模因检查它们的词汇
2023
+ const sampleMemes = system.graph.getAllPoints().slice(0, 3);
2024
+ for (const meme of sampleMemes) {
2025
+ const words = system.kvm.get(meme.pointID) || [];
2026
+ console.log(`[RL-DEBUG] 模因${meme.pointID}词汇样本: ${words.slice(0, 5).join(',')}`);
2027
+ }
2028
+
2029
+ return 0;
2030
+ }
2031
+
2032
+ // 5. 基于匹配的模因进行多源信号扩散
2033
+ const activated = memeStats.filter(m => m.coverage > 0);
2034
+ const startIDs = activated.map(m => m.meme.pointID);
2035
+ const strengths = activated.map(m => m.coverage);
2036
+ const signalMap = system.propagateSignalMultiSource(startIDs, strengths, 0.5, 2);
2037
+
2038
+ // 6. 收集激活模因的词汇作为输出
2039
+ let outputWords = [];
2040
+ for (const memeID of Array.from(signalMap.keys())) {
2041
+ const words = system.kvm.get(memeID) || [];
2042
+ outputWords.push(...words);
2043
+ if (outputWords.length >= uniqueWords.length) break;
2044
+ }
2045
+
2046
+ outputWords = Array.from(new Set(outputWords));
2047
+
2048
+ // 7. 计算覆盖率
2049
+ let covered = 0;
2050
+ for (const word of uniqueWords) {
2051
+ if (outputWords.includes(word)) covered++;
2052
+ }
2053
+
2054
+ const coverageRatio = uniqueWords.length > 0 ? covered / uniqueWords.length : 0;
2055
+ return coverageRatio;
2056
+ }
2057
+ // 学习过程
2058
+ // ReinforcementLearner类内
2059
+ // ReinforcementLearner类内
2060
+ async learn(iterations = 5) {
2061
+ console.log(`[RL] 开始自主学习,计划${iterations}轮迭代`);
2062
+
2063
+ // 用当前runtime作为每轮的基础
2064
+ let currentRuntime = this.runtime;
2065
+
2066
+ for (let i = 0; i < iterations; i++) {
2067
+ console.log(`\n[RL] 第${i + 1}/${iterations}轮学习开始...`);
2068
+
2069
+ // 1. 用当前系统识别模因边关系
2070
+ const associationLayer = new AssociationLayer(currentRuntime);
2071
+ const patterns = associationLayer.identifyPatterns();
2072
+
2073
+ // 2. 克隆系统
2074
+ const systemClone = await associationLayer.cloneSystem();
2075
+ // 这里要加上
2076
+ associationLayer.patterns = associationLayer.identifyPatterns();
2077
+ // 3. 应用模式到克隆系统
2078
+ associationLayer.applyPatternsToClone(systemClone);
2079
+
2080
+ // 4. 评估原系统和克隆系统
2081
+ const testArticles = this.loadTestArticles();
2082
+ console.log(`[RL] 加载了${testArticles.length}篇测试文章`);
2083
+
2084
+ let origCoverages = [];
2085
+ let cloneCoverages = [];
2086
+
2087
+ for (let j = 0; j < testArticles.length; j++) {
2088
+ const article = testArticles[j];
2089
+ const origCoverage = this.evaluateSystem(currentRuntime, article);
2090
+ const cloneCoverage = this.evaluateSystem(systemClone, article);
2091
+ origCoverages.push(origCoverage);
2092
+ cloneCoverages.push(cloneCoverage);
2093
+ console.log(`[RL] 文章#${j + 1}: 原系统覆盖率=${(origCoverage * 100).toFixed(1)}%, 克隆系统覆盖率=${(cloneCoverage * 100).toFixed(1)}%`);
2094
+ }
2095
+
2096
+ // 计算总和减方差
2097
+ const sum = arr => arr.reduce((a, b) => a + b, 0);
2098
+ const mean = arr => arr.length ? sum(arr) / arr.length : 0;
2099
+ const variance = arr => {
2100
+ const m = mean(arr);
2101
+ return arr.length ? arr.reduce((a, b) => a + (b - m) ** 2, 0) / arr.length : 0;
2102
+ };
2103
+ const origScore = sum(origCoverages) - Math.sqrt(variance(origCoverages));
2104
+ const cloneScore = sum(cloneCoverages) - Math.sqrt(variance(cloneCoverages));
2105
+
2106
+ console.log(`[RL] 评估结果: 原系统指标=${origScore.toFixed(4)}, 克隆系统指标=${cloneScore.toFixed(4)}`);
2107
+
2108
+ // 5. 选择更好的系统
2109
+ if (cloneScore > origScore) {
2110
+ console.log(`[RL] 克隆系统表现更好! 替换原系统`);
2111
+ this.replaceSystem(systemClone);
2112
+ currentRuntime = systemClone;
2113
+ // 修复:同步 controller 的 runtime
2114
+ if (global.ctrl) global.ctrl.updateRuntime(systemClone);
2115
+ } else {
2116
+ console.log(`[RL] 原系统表现更好或相同,保留原系统`);
2117
+ // 保持 currentRuntime 不变
2118
+ }
2119
+ }
2120
+
2121
+ console.log(`[RL] 自主学习完成`);
2122
+ }
2123
+
2124
+ replaceSystem(newSystem) {
2125
+ // 保存旧的 runtime 引用
2126
+ const oldRuntime = this.runtime;
2127
+
2128
+ // 更新自己的 runtime
2129
+ this.runtime = newSystem;
2130
+
2131
+ // 查找并更新对应的控制器
2132
+ if (global.ctrlA && global.ctrlA.runtime === oldRuntime) {
2133
+ console.log(`[RL] 更新控制器A的runtime`);
2134
+ global.ctrlA.updateRuntime(newSystem);
2135
+ }
2136
+ if (global.ctrlB && global.ctrlB.runtime === oldRuntime) {
2137
+ console.log(`[RL] 更新控制器B的runtime`);
2138
+ global.ctrlB.updateRuntime(newSystem);
2139
+ }
2140
+ if (global.ctrlC && global.ctrlC.runtime === oldRuntime) {
2141
+ console.log(`[RL] 更新控制器C的runtime`);
2142
+ global.ctrlC.updateRuntime(newSystem);
2143
+ }
2144
+
2145
+ // 同时更新全局 ctrl (如果存在)
2146
+ if (global.ctrl && global.ctrl.runtime === oldRuntime) {
2147
+ global.ctrl.updateRuntime(newSystem);
2148
+ }
2149
+ }
2150
+ }
2151
+ /**
2152
+ * 模因阻断模块 - 识别并隔离恶性模因
2153
+ * 恶性模因特征:
2154
+ * 1. 自行传播倾向
2155
+ * 2. 不可被自我观察
2156
+ */
2157
+ class memeBarrier {
2158
+ constructor(runtime) {
2159
+ this.runtime = runtime;
2160
+ this.suspiciousMemesHistory = new Map(); // 记录可疑模因历史
2161
+ this.maliciousThreshold = 2.0; // 初始恶性阈值
2162
+ this.learningRate = 0.05; // 学习率
2163
+ this.scanInterval = null; // 保存定时器引用
2164
+ this.metrics = {
2165
+ totalScans: 0,
2166
+ totalBlocked: 0,
2167
+ falsePositives: 0,
2168
+ falseNegatives: 0
2169
+ };
2170
+ // 特征权重:[传播率, 自指率, 连接异常率]
2171
+ this.features = [0.5, 0.3, 0.2];
2172
+
2173
+ // 记录网络连接历史快照,用于对比检测异常增长
2174
+ this.networkSnapshots = [];
2175
+ }
2176
+
2177
+ // 启动模因阻断扫描
2178
+ start() {
2179
+ if (this.scanInterval) return;
2180
+ this.scanInterval = setInterval(() => this.scanNetwork(), 1000 * 1000);
2181
+ console.log('[BARRIER] 模因阻断系统已启动,每1000秒扫描一次');
2182
+
2183
+ // 创建初始网络快照
2184
+ this.takeNetworkSnapshot();
2185
+ }
2186
+
2187
+ // 停止扫描
2188
+ stop() {
2189
+ if (this.scanInterval) {
2190
+ clearInterval(this.scanInterval);
2191
+ this.scanInterval = null;
2192
+ console.log('[BARRIER] 模因阻断系统已停止');
2193
+ }
2194
+ }
2195
+
2196
+ // 创建网络结构快照,用于后续分析
2197
+ takeNetworkSnapshot() {
2198
+ const memes = this.runtime.graph.getAllPoints();
2199
+ const snapshot = {
2200
+ timestamp: Date.now(),
2201
+ memeStats: memes.map(meme => ({
2202
+ memeID: meme.pointID,
2203
+ connections: meme.connect.length,
2204
+ words: (this.runtime.kvm.get(meme.pointID) || []).length,
2205
+ outDegree: meme.connect.filter(([_, __, dir]) => dir === 2).length,
2206
+ inDegree: meme.connect.filter(([_, __, dir]) => dir === 1).length,
2207
+ biDegree: meme.connect.filter(([_, __, dir]) => dir === 0).length
2208
+ }))
2209
+ };
2210
+
2211
+ this.networkSnapshots.push(snapshot);
2212
+
2213
+ // 只保留最近5个快照
2214
+ if (this.networkSnapshots.length > 5) {
2215
+ this.networkSnapshots.shift();
2216
+ }
2217
+ }
2218
+
2219
+ // 扫描整个网络
2220
+ scanNetwork() {
2221
+ console.log('[BARRIER] 开始扫描模因网络...');
2222
+ this.metrics.totalScans++;
2223
+
2224
+ this.takeNetworkSnapshot();
2225
+
2226
+ if (this.networkSnapshots.length < 2) {
2227
+ console.log('[BARRIER] 快照数据不足,等待下次扫描');
2228
+ return;
2229
+ }
2230
+
2231
+ const memes = this.runtime.graph.getAllPoints();
2232
+ let maxScore = -Infinity;
2233
+ let topSuspicious = [];
2234
+
2235
+ for (const meme of memes) {
2236
+ const maliciousScore = this.evaluateMaliciousness(meme);
2237
+ if (maliciousScore > maxScore) {
2238
+ maxScore = maliciousScore;
2239
+ topSuspicious = [{ memeID: meme.pointID, score: maliciousScore, reason: this.generateReason(meme, maliciousScore) }];
2240
+ } else if (maliciousScore === maxScore) {
2241
+ topSuspicious.push({ memeID: meme.pointID, score: maliciousScore, reason: this.generateReason(meme, maliciousScore) });
2242
+ }
2243
+ }
2244
+
2245
+ // 只隔离评分最高的(可并列)
2246
+ if (maxScore >= this.maliciousThreshold) {
2247
+ for (const { memeID, score, reason } of topSuspicious) {
2248
+ this.isolateMeme(memeID, score, reason);
2249
+ this.metrics.totalBlocked++;
2250
+ }
2251
+ console.log(`[BARRIER] 本轮隔离${topSuspicious.length}个评分最高的模因`);
2252
+ } else {
2253
+ console.log(`[BARRIER] 本轮无可疑模因达到阈值`);
2254
+ }
2255
+
2256
+ if (this.metrics.totalScans % 3 === 0) {
2257
+ this.updateEvaluationFunction();
2258
+ }
2259
+ }
2260
+ // 生成可疑原因描述
2261
+ generateReason(meme, score) {
2262
+ const reasons = [];
2263
+
2264
+ // 计算各个指标
2265
+ const outgoingConnections = meme.connect.length;
2266
+ const avgConnections = this.getAverageConnections();
2267
+ const propagationRate = outgoingConnections / (avgConnections || 1);
2268
+
2269
+ const selfReferences = meme.connect.filter(([_, pointID]) => pointID === meme.pointID).length;
2270
+ const selfReferenceRate = 1 - (selfReferences / (outgoingConnections || 1));
2271
+
2272
+ const connectionAbnormality = this.detectConnectionAbnormality(meme);
2273
+
2274
+ // 分析增长率
2275
+ const growthRate = this.calculateGrowthRate(meme.pointID);
2276
+
2277
+ // 根据各指标贡献确定原因
2278
+ if (propagationRate > 1.5) reasons.push(`传播率过高(${propagationRate.toFixed(2)})`);
2279
+ if (selfReferenceRate > 0.9) reasons.push(`自指率过低(${selfReferenceRate.toFixed(2)})`);
2280
+ if (connectionAbnormality > 0.7) reasons.push(`连接模式异常(${connectionAbnormality.toFixed(2)})`);
2281
+ if (growthRate > 1.5) reasons.push(`连接增长过快(${growthRate.toFixed(2)}x)`);
2282
+
2283
+ return reasons.join(', ');
2284
+ }
2285
+
2286
+ evaluateMaliciousness(meme) {
2287
+ // 1. 计算传播率
2288
+ const outgoingConnections = meme.connect.length;
2289
+ const avgConnections = this.getAverageConnections();
2290
+ const propagationRate = outgoingConnections / (avgConnections || 1);
2291
+
2292
+ // 2. 计算自指率 - 恶性模因自指率高
2293
+ const selfReferences = meme.connect.filter(([_, pointID]) => pointID === meme.pointID).length;
2294
+ const selfReferenceRate = (selfReferences / (outgoingConnections || 1)); // ← 只改这一行
2295
+
2296
+ // 3. 连接异常率
2297
+ const connectionAbnormality = this.detectConnectionAbnormality(meme);
2298
+
2299
+ // 4. 增长率
2300
+ const growthRate = this.calculateGrowthRate(meme.pointID);
2301
+ const growthFactor = Math.min(1, (growthRate - 1) / 2);
2302
+
2303
+ // 综合评分
2304
+ return this.features[0] * propagationRate +
2305
+ this.features[1] * selfReferenceRate + // ← 只改这里
2306
+ this.features[2] * connectionAbnormality +
2307
+ 0.3 * growthFactor;
2308
+ }
2309
+
2310
+ // 计算模因的增长率
2311
+ calculateGrowthRate(memeID) {
2312
+ if (this.networkSnapshots.length < 2) return 1.0;
2313
+
2314
+ const current = this.networkSnapshots[this.networkSnapshots.length - 1];
2315
+ const previous = this.networkSnapshots[this.networkSnapshots.length - 2];
2316
+
2317
+ const currentStats = current.memeStats.find(m => m.memeID === memeID);
2318
+ const previousStats = previous.memeStats.find(m => m.memeID === memeID);
2319
+
2320
+ if (!currentStats || !previousStats) return 1.0;
2321
+
2322
+ // 计算连接数增长率
2323
+ return previousStats.connections > 0 ?
2324
+ currentStats.connections / previousStats.connections :
2325
+ (currentStats.connections > 0 ? 2.0 : 1.0);
2326
+ }
2327
+
2328
+ // 计算平均连接数
2329
+ getAverageConnections() {
2330
+ const memes = this.runtime.graph.getAllPoints();
2331
+ if (memes.length === 0) return 0;
2332
+
2333
+ const totalConnections = memes.reduce((sum, meme) => sum + meme.connect.length, 0);
2334
+ return totalConnections / memes.length;
2335
+ }
2336
+
2337
+ // 检测连接异常
2338
+ detectConnectionAbnormality(meme) {
2339
+ // 分析连接模式是否异常
2340
+ const connections = meme.connect;
2341
+ if (connections.length === 0) return 0;
2342
+
2343
+ // 1. 检查权重分布
2344
+ const weights = connections.map(([weight]) => weight);
2345
+ const avgWeight = weights.reduce((sum, w) => sum + w, 0) / weights.length;
2346
+ const stdDev = Math.sqrt(
2347
+ weights.reduce((sum, w) => sum + Math.pow(w - avgWeight, 2), 0) / weights.length
2348
+ );
2349
+
2350
+ // 权重分布异常性
2351
+ const weightAbnormality = stdDev / (avgWeight || 1);
2352
+
2353
+ // 2. 检查方向分布 - 恶性模因倾向于单向输出
2354
+ const directionCounts = {
2355
+ bidirectional: connections.filter(([_, __, dir]) => dir === 0).length,
2356
+ inward: connections.filter(([_, __, dir]) => dir === 1).length,
2357
+ outward: connections.filter(([_, __, dir]) => dir === 2).length
2358
+ };
2359
+
2360
+ // 如果几乎都是向外的连接,这是可疑的
2361
+ const directionBalance = directionCounts.outward /
2362
+ (directionCounts.bidirectional + directionCounts.inward + 1);
2363
+
2364
+ // 3. 检查邻居多样性
2365
+ const uniqueNeighbors = new Set(connections.map(([_, pointID]) => pointID)).size;
2366
+ const neighborDiversity = uniqueNeighbors / connections.length;
2367
+
2368
+ return (weightAbnormality * 0.3 + directionBalance * 0.5 + neighborDiversity * 0.2);
2369
+ }
2370
+
2371
+ // 隔离可疑模因
2372
+ isolateMeme(memeID, maliciousScore, reason) {
2373
+ console.log(`[BARRIER] 隔离可疑模因: ${memeID}, 恶性评分: ${maliciousScore.toFixed(4)}`);
2374
+ console.log(`[BARRIER] 原因: ${reason}`);
2375
+
2376
+ // 记录历史
2377
+ if (!this.suspiciousMemesHistory.has(memeID)) {
2378
+ this.suspiciousMemesHistory.set(memeID, []);
2379
+ }
2380
+
2381
+ const meme = this.runtime.graph.points.get(memeID);
2382
+ if (!meme) return;
2383
+
2384
+ const connectionCount = meme.connect.length;
2385
+ this.suspiciousMemesHistory.get(memeID).push({
2386
+ timestamp: Date.now(),
2387
+ score: maliciousScore,
2388
+ reason,
2389
+ connectionCount
2390
+ });
2391
+
2392
+ // 增加该模因与其他节点的距离
2393
+ for (const [index, [weight, neighborID, direction]] of meme.connect.entries()) {
2394
+ // 增加边权重 (增加距离)
2395
+ const increaseFactor = 1 + maliciousScore;
2396
+ const newWeight = Math.min(10, weight * increaseFactor); // 限制最大权重
2397
+
2398
+ // 更新边权重
2399
+ meme.connect[index][0] = newWeight;
2400
+
2401
+ // 如果是双向连接,同时增加另一端的权重
2402
+ if (direction === 0) {
2403
+ const neighbor = this.runtime.graph.points.get(neighborID);
2404
+ if (neighbor) {
2405
+ const reverseEdgeIndex = neighbor.connect.findIndex(
2406
+ ([_, id, dir]) => id === memeID && dir === 0
2407
+ );
2408
+ if (reverseEdgeIndex !== -1) {
2409
+ neighbor.connect[reverseEdgeIndex][0] = newWeight;
2410
+ }
2411
+ }
2412
+ }
2413
+ }
2414
+
2415
+ // 极端情况:如果评分非常高,考虑完全隔离
2416
+ if (maliciousScore > 0.9) {
2417
+ console.log(`[BARRIER] 模因 ${memeID} 评分极高,标记为恶性`);
2418
+ // 标记为恶性模因
2419
+ JSON.stringify({
2420
+ timestamp: Date.now(),
2421
+ score: maliciousScore,
2422
+ reason
2423
+ });
2424
+ }
2425
+ }
2426
+
2427
+ // 强化学习更新评估函数
2428
+ updateEvaluationFunction() {
2429
+ console.log('[BARRIER] 更新恶性模因评估函数...');
2430
+
2431
+ // 1. 分析历史数据,找出可能的错误判断
2432
+ let falsePositives = 0;
2433
+ let falseNegatives = 0;
2434
+
2435
+ // 分析可能的误判
2436
+ for (const [memeID, history] of this.suspiciousMemesHistory.entries()) {
2437
+ // 至少需要2条历史记录才能分析
2438
+ if (history.length < 2) continue;
2439
+
2440
+ // 检查隔离后的行为变化
2441
+ const behaviorChange = this.analyzeMemeEvolution(memeID, history);
2442
+
2443
+ // 如果模因在隔离后仍然增长,可能是误判
2444
+ if (behaviorChange > 0.2) {
2445
+ falsePositives++;
2446
+ }
2447
+ }
2448
+
2449
+ // 寻找漏判的恶性模因
2450
+ const missedMalicious = this.detectMissedMaliciousMemes();
2451
+ falseNegatives = missedMalicious.length;
2452
+
2453
+ // 2. 根据错误类型调整阈值和特征权重
2454
+ if (falsePositives > falseNegatives * 2) {
2455
+ // 太多误判,提高阈值,减少灵敏度
2456
+ this.maliciousThreshold = Math.min(0.95, this.maliciousThreshold + this.learningRate);
2457
+ // 调整特征权重
2458
+ this.features[2] = Math.max(0.1, this.features[2] - this.learningRate); // 减少连接异常权重
2459
+ this.features[0] = Math.min(0.7, this.features[0] + this.learningRate); // 增加传播率权重
2460
+ } else if (falseNegatives > falsePositives * 2) {
2461
+ // 太多漏判,降低阈值,提高灵敏度
2462
+ this.maliciousThreshold = Math.max(0.5, this.maliciousThreshold - this.learningRate);
2463
+ // 调整特征权重
2464
+ this.features[0] = Math.max(0.2, this.features[0] - this.learningRate); // 减少传播率权重
2465
+ this.features[2] = Math.min(0.6, this.features[2] + this.learningRate); // 增加连接异常权重
2466
+ }
2467
+
2468
+ // 归一化特征权重
2469
+ const sum = this.features.reduce((a, b) => a + b, 0);
2470
+ this.features = this.features.map(f => f / sum);
2471
+
2472
+ // 更新指标
2473
+ this.metrics.falsePositives += falsePositives;
2474
+ this.metrics.falseNegatives += falseNegatives;
2475
+
2476
+ console.log(`[BARRIER] 评估函数已更新: 阈值=${this.maliciousThreshold.toFixed(3)}, 权重=[${this.features.map(f => f.toFixed(2)).join(', ')}]`);
2477
+ console.log(`[BARRIER] 本轮检测到: 误判=${falsePositives}, 漏判=${falseNegatives}`);
2478
+ }
2479
+
2480
+ // 分析模因的演化
2481
+ analyzeMemeEvolution(memeID, history) {
2482
+ const meme = this.runtime.graph.points.get(memeID);
2483
+ if (!meme) return 0;
2484
+
2485
+ // 获取第一次和最后一次隔离记录
2486
+ const firstRecord = history[0];
2487
+ const lastRecord = history[history.length - 1];
2488
+
2489
+ // 计算连接数变化
2490
+ const initialConnections = firstRecord.connectionCount || 0;
2491
+ const currentConnections = meme.connect.length;
2492
+
2493
+ // 计算隔离后的相对增长率
2494
+ // 如果是真正的恶性模因,隔离应该有效阻止其增长
2495
+ const timeDiff = (lastRecord.timestamp - firstRecord.timestamp) / (1000 * 60 * 60 * 24); // 天数
2496
+ const growthRate = (currentConnections - initialConnections) / (initialConnections || 1);
2497
+ const normalizedGrowth = growthRate / (timeDiff || 1); // 归一化为每天增长率
2498
+
2499
+ return normalizedGrowth;
2500
+ }
2501
+
2502
+ // 检测可能漏判的恶性模因
2503
+ detectMissedMaliciousMemes() {
2504
+ if (this.networkSnapshots.length < 2) return [];
2505
+
2506
+ const currentSnapshot = this.networkSnapshots[this.networkSnapshots.length - 1];
2507
+ const previousSnapshot = this.networkSnapshots[this.networkSnapshots.length - 2];
2508
+
2509
+ const missedMalicious = [];
2510
+
2511
+ // 检查所有模因的异常增长
2512
+ for (const currentStat of currentSnapshot.memeStats) {
2513
+ // 跳过已识别的可疑模因
2514
+ if (this.suspiciousMemesHistory.has(currentStat.memeID)) continue;
2515
+
2516
+ const previousStat = previousSnapshot.memeStats.find(
2517
+ m => m.memeID === currentStat.memeID
2518
+ );
2519
+
2520
+ if (!previousStat) continue; // 新模因,无法比较
2521
+
2522
+ // 检查连接数异常增长
2523
+ const connectionGrowth = previousStat.connections > 0 ?
2524
+ currentStat.connections / previousStat.connections :
2525
+ (currentStat.connections > 0 ? 2.0 : 1.0);
2526
+
2527
+ // 检查单向外连接比例异常
2528
+ const outwardRatio = currentStat.outDegree /
2529
+ (currentStat.connections || 1);
2530
+
2531
+ // 如果增长率异常高且主要是向外的连接
2532
+ if (connectionGrowth > 2.0 && outwardRatio > 0.7) {
2533
+ missedMalicious.push({
2534
+ memeID: currentStat.memeID,
2535
+ growthRate: connectionGrowth,
2536
+ outwardRatio
2537
+ });
2538
+ }
2539
+ }
2540
+
2541
+ return missedMalicious;
2542
+ }
2543
+
2544
+ // 获取统计数据
2545
+ getStats() {
2546
+ return {
2547
+ ...this.metrics,
2548
+ threshold: this.maliciousThreshold,
2549
+ features: this.features,
2550
+ suspiciousCount: this.suspiciousMemesHistory.size
2551
+ };
2552
+ }
2553
+ }
2554
+
2555
+ class controller {
2556
+ constructor() {
2557
+ this.runtime = new Runtime();
2558
+ // 启动时自动用Spider构建词表
2559
+ this.articles = this.runtime.buildVocabFromSpider();
2560
+ this.isLearning = false;
2561
+ this.isMainLoopRunning = false;
2562
+
2563
+ // 初始化快照管理器 (仅为ctrlA实例初始化)
2564
+ if (this === global.ctrlA || !global.ctrlA) {
2565
+ this.snapshotManager = new SnapshotManager(this.runtime);
2566
+ }
2567
+ }
2568
+ updateRuntime(newRuntime) {
2569
+ if (this.runtime) this.runtime.cleanup();
2570
+ this.runtime = newRuntime;
2571
+ }
2572
+ // 处理用户输入
2573
+ async handleInput(text) {
2574
+ const words = text.toLowerCase().split(' ').filter(w => w.length > 0);
2575
+ this.runtime.processInput(words, { addNewWords: false });
2576
+ // 用模因网络参与推理
2577
+ //console.log('[DEBUG] 当前所有模因节点:', this.runtime.kvm.memory);
2578
+ return await this.runtime.generateResponseWithMemes(words);
2579
+ }
2580
+ // 启动自主学习
2581
+ async startSelfLearning(iterations) {
2582
+ if (iterations === undefined) {
2583
+ iterations = this.config.learningIterations !== undefined ? this.config.learningIterations : 5;
2584
+ }
2585
+ if (this.isLearning) return;
2586
+ this.isLearning = true;
2587
+ await this.runtime.startSelfLearning(iterations);
2588
+ this.isLearning = false;
2589
+ }
2590
+ // 在controller类内
2591
+ startMainLoop() {
2592
+ if (this.isMainLoopRunning) return;
2593
+ this.isMainLoopRunning = true;
2594
+ this.runtime.runMainLoop().then(() => {
2595
+ this.isMainLoopRunning = false;
2596
+ });
2597
+ }
2598
+ // 添加快照相关方法
2599
+ async createSnapshot(name) {
2600
+ if (!this.snapshotManager) {
2601
+ console.error('[SNAPSHOT] 当前控制器未初始化快照管理器');
2602
+ return null;
2603
+ }
2604
+
2605
+ if (this.isLearning || this.isMainLoopRunning) {
2606
+ console.warn('[SNAPSHOT] 系统忙碌,无法创建快照');
2607
+ return null;
2608
+ }
2609
+
2610
+ return await this.snapshotManager.createSnapshot(name || 'manual');
2611
+ }
2612
+
2613
+ async restoreFromSnapshot(snapshotId) {
2614
+ if (!this.snapshotManager) {
2615
+ console.error('[SNAPSHOT] 当前控制器未初始化快照管理器');
2616
+ return false;
2617
+ }
2618
+
2619
+ if (this.isLearning || this.isMainLoopRunning) {
2620
+ console.warn('[SNAPSHOT] 系统忙碌,无法恢复快照');
2621
+ return false;
2622
+ }
2623
+
2624
+ return await this.snapshotManager.restoreSnapshot(snapshotId);
2625
+ }
2626
+
2627
+ getSnapshotList() {
2628
+ if (!this.snapshotManager) {
2629
+ console.error('[SNAPSHOT] 当前控制器未初始化快照管理器');
2630
+ return [];
2631
+ }
2632
+
2633
+ return this.snapshotManager.getSnapshotList();
2634
+ }
2635
+
2636
+ deleteSnapshot(snapshotId) {
2637
+ if (!this.snapshotManager) {
2638
+ console.error('[SNAPSHOT] 当前控制器未初始化快照管理器');
2639
+ return false;
2640
+ }
2641
+
2642
+ return this.snapshotManager.deleteSnapshot(snapshotId);
2643
+ }
2644
+ }
2645
+ let latestRuntimeData = null;
2646
+ let saveQueued = false;
2647
+
2648
+
2649
+ // 保存所有点、图、词表等到硬盘
2650
+ function saveAll(runtime) {
2651
+ // 只更新内存缓存
2652
+ latestRuntimeData = {
2653
+ memes: runtime.graph.getAllPoints(),
2654
+ wordGraph: Array.from(runtime.wordGraph.points.values()),
2655
+ kvm: Array.from(runtime.kvm.memory.entries()),
2656
+ vocab: runtime.vocabManager.vocab,
2657
+ wordAccessLog: Array.from(runtime.wordAccessLog ? runtime.wordAccessLog.entries() : [])
2658
+ };
2659
+ // 标记有保存请求
2660
+ saveQueued = true;
2661
+ }
2662
+ // 定时真正写入硬盘,只保存最新的
2663
+ setInterval(() => {
2664
+ if (saveQueued && latestRuntimeData) {
2665
+ fs.writeFile(SAVE_PATH, JSON.stringify(latestRuntimeData, null, 2), 'utf-8', () => {
2666
+ console.log(`[SAVE] 系统状态已保存到 ${SAVE_PATH}`);
2667
+ });
2668
+ saveQueued = false;
2669
+ }
2670
+ }, 10000); // 每10秒最多写盘一次
2671
+ // 从硬盘恢复
2672
+ function loadAll(runtime) {
2673
+ if (!fs.existsSync(SAVE_PATH)) return;
2674
+ const data = JSON.parse(fs.readFileSync(SAVE_PATH, 'utf-8'));
2675
+ if (data.memes) runtime.loadGraph(data.memes);
2676
+ if (data.wordGraph) {
2677
+ for (const point of data.wordGraph) {
2678
+ runtime.wordGraph.addPoint(point.pointID, point.connect);
2679
+ }
2680
+ }
2681
+ // 3. 修改 loadAll 中的数据恢复
2682
+ if (data.kvm) {
2683
+ for (const [k, v] of data.kvm) {
2684
+ runtime.kvm.set(k, Array.isArray(v) ? v : [String(v)]);
2685
+ // 确保一定是数组
2686
+ }
2687
+ }
2688
+ if (data.vocab) {
2689
+ runtime.vocabManager.vocab = data.vocab;
2690
+ runtime.vocabManager.updateMappings();
2691
+ }
2692
+ if (data.wordAccessLog && runtime.wordAccessLog) {
2693
+ runtime.wordAccessLog = new Map(data.wordAccessLog);
2694
+ }
2695
+ console.log(`[LOAD] 系统状态已从 ${SAVE_PATH} 恢复`);
2696
+ }
2697
+
2698
+
2699
+
2700
+ function scheduleCrossLearning() {
2701
+ const cycle = 1800 * 1000; // 每轮周期(毫秒)
2702
+ const learnTime = 200 * 1000; // 每轮学习后等待时间
2703
+
2704
+ // A -> B
2705
+ registerInterval(() => {
2706
+ if (global.ctrlA.isLearning || global.ctrlB.isLearning || global.ctrlC.isLearning || isShuttingDown) return;
2707
+ console.log('[CROSS] A开始学习');
2708
+ global.ctrlA.startSelfLearning(3).then(() => {
2709
+ setTimeout(async () => {
2710
+ console.log('[CROSS] A将成果传递给B');
2711
+ const associationLayer = new AssociationLayer(global.ctrlA.runtime);
2712
+
2713
+ // 归一化一致性测试
2714
+ const testWord = "testing";
2715
+ const normalizedWord = global.ctrlA.runtime.spider.lemmatize(testWord);
2716
+ console.log(`[CROSS-CHECK] 词归一化测试: "${testWord}" -> "${normalizedWord}"`);
2717
+ //const associationLayer = new AssociationLayer(global.ctrlA.runtime);
2718
+ // 这里要加上
2719
+ associationLayer.patterns = associationLayer.identifyPatterns();
2720
+ // 克隆
2721
+ const systemClone = await associationLayer.cloneSystem();
2722
+ systemClone.spider = global.ctrlA.runtime.spider;
2723
+ associationLayer.applyPatternsToClone(systemClone);
2724
+
2725
+ verifySystemConsistency(global.ctrlA.runtime, systemClone);
2726
+
2727
+ // 预热克隆系统
2728
+ console.log('[CROSS] 预热克隆系统...');
2729
+ const sampleWords = Array.from(
2730
+ new Set([...systemClone.vocabManager.vocab].slice(4, 20))
2731
+ ).filter(w => w.length > 1);
2732
+ await systemClone.processInput(sampleWords, { addNewWords: false });
2733
+
2734
+ await systemClone.startSelfLearning(3);
2735
+ global.ctrlB.updateRuntime(systemClone);
2736
+
2737
+ verifySystemConsistency(global.ctrlA.runtime, global.ctrlB.runtime);
2738
+
2739
+ console.log('[CROSS] 已将A的系统更新到B');
2740
+ testCloneMatching(global.ctrlB.runtime);
2741
+ }, learnTime);
2742
+ });
2743
+ }, cycle);
2744
+
2745
+ // B -> C
2746
+ registerInterval(() => {
2747
+ if (global.ctrlA.isLearning || global.ctrlB.isLearning || global.ctrlC.isLearning || isShuttingDown) return;
2748
+ global.ctrlB.startSelfLearning(3).then(() => {
2749
+ setTimeout(async () => {
2750
+ console.log('[CROSS] B将成果传递给C');
2751
+ const associationLayer = new AssociationLayer(global.ctrlB.runtime);
2752
+
2753
+ const testWord = "testing";
2754
+ const normalizedWord = global.ctrlB.runtime.spider.lemmatize(testWord);
2755
+ console.log(`[CROSS-CHECK] 词归一化测试: "${testWord}" -> "${normalizedWord}"`);
2756
+ // 这里要加上
2757
+ associationLayer.patterns = associationLayer.identifyPatterns();
2758
+ const systemClone = await associationLayer.cloneSystem();
2759
+ systemClone.spider = global.ctrlB.runtime.spider;
2760
+ associationLayer.applyPatternsToClone(systemClone);
2761
+
2762
+ verifySystemConsistency(global.ctrlB.runtime, systemClone);
2763
+
2764
+ console.log('[CROSS] 预热克隆系统...');
2765
+ const sampleWords = Array.from(
2766
+ new Set([...systemClone.vocabManager.vocab].slice(4, 20))
2767
+ ).filter(w => w.length > 1);
2768
+ await systemClone.processInput(sampleWords, { addNewWords: false });
2769
+
2770
+ await systemClone.startSelfLearning(3);
2771
+ global.ctrlC.updateRuntime(systemClone);
2772
+
2773
+ verifySystemConsistency(global.ctrlB.runtime, global.ctrlC.runtime);
2774
+
2775
+ console.log('[CROSS] 已将B的系统更新到C');
2776
+ testCloneMatching(global.ctrlC.runtime);
2777
+ }, learnTime);
2778
+ });
2779
+ }, cycle);
2780
+
2781
+ // C -> A
2782
+ registerInterval(() => {
2783
+ if (global.ctrlA.isLearning || global.ctrlB.isLearning || global.ctrlC.isLearning || isShuttingDown) return;
2784
+ global.ctrlC.startSelfLearning(3).then(() => {
2785
+ setTimeout(async () => {
2786
+ console.log('[CROSS] C将成果传递给A');
2787
+ const associationLayer = new AssociationLayer(global.ctrlC.runtime);
2788
+
2789
+ const testWord = "testing";
2790
+ const normalizedWord = global.ctrlC.runtime.spider.lemmatize(testWord);
2791
+ console.log(`[CROSS-CHECK] 词归一化测试: "${testWord}" -> "${normalizedWord}"`);
2792
+ // 这里要加上
2793
+ associationLayer.patterns = associationLayer.identifyPatterns();
2794
+ const systemClone = await associationLayer.cloneSystem();
2795
+ systemClone.spider = global.ctrlC.runtime.spider;
2796
+ associationLayer.applyPatternsToClone(systemClone);
2797
+
2798
+ verifySystemConsistency(global.ctrlC.runtime, systemClone);
2799
+
2800
+ console.log('[CROSS] 预热克隆系统...');
2801
+ const sampleWords = Array.from(
2802
+ new Set([...systemClone.vocabManager.vocab].slice(4, 20))
2803
+ ).filter(w => w.length > 1);
2804
+ await systemClone.processInput(sampleWords, { addNewWords: false });
2805
+
2806
+ await systemClone.startSelfLearning(3);
2807
+ global.ctrlA.updateRuntime(systemClone);
2808
+
2809
+ verifySystemConsistency(global.ctrlC.runtime, global.ctrlA.runtime);
2810
+
2811
+ console.log('[CROSS] 已将C的系统更新到A');
2812
+ testCloneMatching(global.ctrlA.runtime);
2813
+ }, learnTime);
2814
+ });
2815
+ console.log('Publishing runtime state to Redis...');
2816
+ if (!RuntimeMessage) return; // protobuf未加载完成
2817
+ if (!redisClient || !redisClient.isOpen) {
2818
+ console.warn('[REDIS] 客户端未连接,跳过发布');
2819
+ return;
2820
+ }
2821
+ if (!RuntimeMessage) return;
2822
+ if (global.ctrlA.runtime.isLearning || global.ctrlA.runtime.isMainLoopRunning) return;
2823
+ const plainObj = runtimeToPlain(global.ctrlA.runtime);
2824
+ const errMsg = RuntimeMessage.verify(plainObj);
2825
+ if (errMsg) throw Error(errMsg);
2826
+ const message = RuntimeMessage.create(plainObj);
2827
+ const buffer = RuntimeMessage.encode(message).finish();
2828
+ redisClient.publish(`AI-model-${__dirname}`, buffer);
2829
+ console.log('已发布运行时状态到Redis');
2830
+ }, cycle);
2831
+ }
2832
+
2833
+ // 新增:测试克隆系统的词汇匹配能力
2834
+ function testCloneMatching(runtime) {
2835
+ // 从词表中随机选取10个词
2836
+ const sampleSize = Math.min(10, runtime.vocabManager.vocab.length - 4);
2837
+ const sampleWords = runtime.vocabManager.vocab
2838
+ .slice(4, 4 + sampleSize) // 跳过前4个特殊词
2839
+ .filter(w => w && w.length > 2);
2840
+
2841
+ if (sampleWords.length === 0) {
2842
+ console.log('[CROSS-TEST] 没有足够的词汇进行测试');
2843
+ return;
2844
+ }
2845
+
2846
+ console.log(`[CROSS-TEST] 使用${sampleWords.length}个词测试匹配能力: ${sampleWords.slice(0, 3).join(',')}`);
2847
+
2848
+ // 检查这些词是否能匹配模因
2849
+ let matchFound = false;
2850
+ let checkedMemes = 0;
2851
+
2852
+ for (const meme of runtime.graph.getAllPoints()) {
2853
+ if (checkedMemes > 100) break; // 限制检查数量
2854
+
2855
+ const memeWords = runtime.kvm.get(meme.pointID) || [];
2856
+ for (const testWord of sampleWords) {
2857
+ if (memeWords.includes(testWord)) {
2858
+ console.log(`[CROSS-TEST] 匹配成功! 词"${testWord}"在模因${meme.pointID}中`);
2859
+ matchFound = true;
2860
+ break;
2861
+ }
2862
+ }
2863
+
2864
+ if (matchFound) break;
2865
+ checkedMemes++;
2866
+ }
2867
+
2868
+ if (!matchFound) {
2869
+ console.log('[CROSS-TEST] 警告: 无法找到任何词汇匹配!');
2870
+
2871
+ // 检查词表和KVM中的词汇格式
2872
+ const sampleKVM = Array.from(runtime.kvm.memory.entries()).slice(0, 3);
2873
+ for (const [memeID, words] of sampleKVM) {
2874
+ console.log(`[CROSS-TEST] 模因${memeID}词汇样本: ${Array.isArray(words) ? words.slice(0, 5).join(',') : '非数组'}`);
2875
+ }
2876
+ }
2877
+ }
2878
+
2879
+ function runtimeToPlain(runtime) {
2880
+ return {
2881
+ memes: runtime.graph.getAllPoints().map(meme => ({
2882
+ pointID: meme.pointID,
2883
+ connect: meme.connect.map(([weight, pointID, direction]) => ({
2884
+ weight,
2885
+ pointID,
2886
+ direction
2887
+ }))
2888
+ })),
2889
+ wordGraph: Array.from(runtime.wordGraph.points.values()).map(point => ({
2890
+ pointID: point.pointID,
2891
+ connect: point.connect.map(([weight, pointID, direction]) => ({
2892
+ weight,
2893
+ pointID,
2894
+ direction
2895
+ }))
2896
+ })),
2897
+ kvm: Array.from(runtime.kvm.memory.entries()).map(([key, value]) => ({
2898
+ key,
2899
+ value: Array.isArray(value) ? value : (value == null ? [] : [value]) // 保证为数组
2900
+ })),
2901
+ vocab: runtime.vocabManager.vocab
2902
+ };
2903
+ }
2904
+ function safePoolExec(method, args, fallback) {
2905
+ if (isShuttingDown) return Promise.resolve(fallback);
2906
+ try {
2907
+ return pool.exec(method, args);
2908
+ } catch (e) {
2909
+ if (String(e && e.message).toLowerCase().includes('terminated')) {
2910
+ return Promise.resolve(fallback);
2911
+ }
2912
+ throw e;
2913
+ }
2914
+ }
2915
+ function setupExitHandler() {
2916
+ let exitingOnce = false;
2917
+ let isExiting = false;
2918
+
2919
+ const exitHandler = async () => {
2920
+ if (isExiting) return; // 防止重复执行
2921
+ isExiting = true;
2922
+ if (exitingOnce) {
2923
+ return;
2924
+ }
2925
+ exitingOnce = true;
2926
+ isShuttingDown = true;
2927
+ console.log('正在保存数据并关闭系统...');
2928
+ try {
2929
+ // 尝试创建退出快照
2930
+ if (global.ctrlA && global.ctrlA.snapshotManager) {
2931
+ console.log('[EXIT] 创建退出快照...');
2932
+ await global.ctrlA.snapshotManager.createSnapshot('exit_snapshot');
2933
+ }
2934
+
2935
+ // 原有保存逻辑
2936
+ if (global.ctrlA && global.ctrlA.runtime) saveAll(global.ctrlA.runtime);
2937
+ //if (global.ctrlB && global.ctrlB.runtime) saveAll(global.ctrlB.runtime);
2938
+ //if (global.ctrlC && global.ctrlC.runtime) saveAll(global.ctrlC.runtime);
2939
+ for (const t of crossTimers) { try { clearInterval(t); } catch (_) { } }
2940
+ // ...existing code...
2941
+ if (pool) {
2942
+ try { await pool.terminate(true); } catch (err) { console.error('关闭工作池时出错:', err.message); }
2943
+ }
2944
+ } catch (err) {
2945
+ console.error('关闭过程中出错:', err);
2946
+ }
2947
+ // 给异步操作一点时间完成
2948
+ setTimeout(() => process.exit(0), 2000);
2949
+ };
2950
+
2951
+ process.on('SIGINT', exitHandler);
2952
+ process.on('SIGTERM', exitHandler);
2953
+ process.on('uncaughtException', (err) => {
2954
+ console.error('未捕获的异常:', err);
2955
+ exitHandler();
2956
+ });
2957
+ }
2958
+ // 添加定期垃圾回收帮助函数
2959
+ function optimizeMemory() {
2960
+ const memUsage = process.memoryUsage();
2961
+ console.log(`Memory usage before cleanup: RSS=${Math.round(memUsage.rss / 1024 / 1024)}MB, Heap=${Math.round(memUsage.heapUsed / 1024 / 1024)}MB/${Math.round(memUsage.heapTotal / 1024 / 1024)}MB`);
2962
+
2963
+ // 如果支持手动GC,则触发
2964
+ if (global.gc) {
2965
+ global.gc();
2966
+ const afterUsage = process.memoryUsage();
2967
+ console.log(`Memory usage after cleanup: RSS=${Math.round(afterUsage.rss / 1024 / 1024)}MB, Heap=${Math.round(afterUsage.heapUsed / 1024 / 1024)}MB/${Math.round(afterUsage.heapTotal / 1024 / 1024)}MB`);
2968
+ }
2969
+ }
2970
+ async function main() {
2971
+ console.log('Starting AI system...');
2972
+ redisClient = redis.createClient();
2973
+ // 创建三个全局控制器副本
2974
+ const ctrlA = new controller();
2975
+ const ctrlB = new controller();
2976
+ const ctrlC = new controller();
2977
+ global.ctrlA = ctrlA;
2978
+ global.ctrlB = ctrlB;
2979
+ global.ctrlC = ctrlC;
2980
+ global.ctrl = ctrlA;
2981
+
2982
+ // 恢复各自持久化数据
2983
+ loadAll(ctrlA.runtime);
2984
+ loadAll(ctrlB.runtime);
2985
+ loadAll(ctrlC.runtime);
2986
+
2987
+ // 用A副本初始化语料和模因
2988
+ console.time('articleProcessing');
2989
+ const articles = ctrlA.runtime.buildVocabFromSpider();
2990
+ console.log(`Spider: 加载文章数: ${articles.length}`);
2991
+
2992
+ // 修复:在首次使用前定义 lemmaCsvPath
2993
+ const BATCH_SIZE = 20;
2994
+ const lemmaCsvPath = path.join(__dirname, 'lemma.csv');
2995
+
2996
+ for (let i = 0; i < articles.length; i += BATCH_SIZE) {
2997
+ const batch = articles.slice(i, i + BATCH_SIZE);
2998
+ // 修复:allSentences 作用域在循环内声明并初始化
2999
+ let allSentences = [];
3000
+ for (const article of batch) {
3001
+ let sentences = article.split(/[.!?]+/);
3002
+ const MAX_SENTENCE_WORDS = 40;
3003
+ for (const sentence of sentences) {
3004
+ const cleaned = sentence.toLowerCase().replace(/[^a-z\s]/g, ' ');
3005
+ const words = cleaned.split(/\s+/).filter(w => w.length > 0);
3006
+ if (words.length === 0) continue;
3007
+ if (words.length > MAX_SENTENCE_WORDS) {
3008
+ for (let j = 0; j < words.length; j += MAX_SENTENCE_WORDS) {
3009
+ allSentences.push(words.slice(j, j + MAX_SENTENCE_WORDS));
3010
+ }
3011
+ } else {
3012
+ allSentences.push(words);
3013
+ }
3014
+ }
3015
+ }
3016
+ // 修复:统一用 safePoolExec,避免 pool 终止时抛错
3017
+ const normalizedBatch = await safePoolExec('batchLemmatize', [allSentences, lemmaCsvPath], []);
3018
+ for (const normWords of normalizedBatch) {
3019
+ if (normWords.length > 0) {
3020
+ ctrlA.runtime.processInput(normWords);
3021
+ }
3022
+ }
3023
+ await new Promise(resolve => setImmediate(resolve));
3024
+ if (i % (BATCH_SIZE * 5) === 0) {
3025
+ console.log(`[PROGRESS] 已处理 ${i}/${articles.length} 篇文章`);
3026
+ }
3027
+ }
3028
+ console.timeEnd('articleProcessing');
3029
+ optimizeMemory();
3030
+ // 测试输入
3031
+ console.log('\n=== Testing AI System ===');
3032
+ await ctrlA.handleInput('I like apple');
3033
+ await ctrlA.handleInput('I love orange');
3034
+ await ctrlA.handleInput('you are good');
3035
+ setInterval(async () => {
3036
+ // 启动主循环(只用A副本)
3037
+ ctrlA.startMainLoop();
3038
+ }, 1000 * 60 * 7); // 每7分钟运行一次主循环
3039
+ setInterval(async () => {
3040
+ //每12分钟尝试启动memebarrier
3041
+ ctrlA.runtime.memeBarrier.start();
3042
+ }, 1000 * 60 * 12);
3043
+ // API路由 - 只做学习,不返回结果
3044
+ app.post('/api/chat', async (req, res) => {
3045
+ try {
3046
+ const { message } = req.body;
3047
+ ctrlA.runtime.processInput(message.toLowerCase().split(' ').filter(w => w.length > 0));
3048
+ ctrlA.runtime.updateAttentionLinks();
3049
+ res.status(204).end(); // 不返回内容
3050
+ } catch (error) {
3051
+ res.status(500).json({ error: error.message });
3052
+ console.error('Error in /api/chat:', error);
3053
+ }
3054
+ });
3055
+
3056
+ app.get('/api/status', (req, res) => {
3057
+ res.json({ status: 'running', timestamp: new Date().toISOString() });
3058
+ });
3059
+ // API路由 - 快照管理
3060
+ app.post('/api/snapshot/create', async (req, res) => {
3061
+ try {
3062
+ const { name } = req.body || {};
3063
+ const snapshot = await ctrlA.createSnapshot(name || 'manual');
3064
+ if (snapshot) {
3065
+ res.json({ success: true, snapshot });
3066
+ } else {
3067
+ res.status(400).json({ success: false, error: '无法创建快照,系统可能正忙' });
3068
+ }
3069
+ } catch (error) {
3070
+ res.status(500).json({ success: false, error: error.message });
3071
+ }
3072
+ });
3073
+
3074
+ app.get('/api/snapshot/list', (req, res) => {
3075
+ try {
3076
+ const snapshots = ctrlA.getSnapshotList();
3077
+ res.json({ success: true, snapshots });
3078
+ } catch (error) {
3079
+ res.status(500).json({ success: false, error: error.message });
3080
+ }
3081
+ });
3082
+
3083
+ app.post('/api/snapshot/restore/:id', async (req, res) => {
3084
+ try {
3085
+ const { id } = req.params;
3086
+ const success = await ctrlA.restoreFromSnapshot(id);
3087
+ if (success) {
3088
+ res.json({ success: true, message: '快照恢复成功' });
3089
+ } else {
3090
+ res.status(400).json({ success: false, error: '快照恢复失败,系统可能正忙或快照不存在' });
3091
+ }
3092
+ } catch (error) {
3093
+ res.status(500).json({ success: false, error: error.message });
3094
+ }
3095
+ });
3096
+
3097
+ app.delete('/api/snapshot/:id', (req, res) => {
3098
+ try {
3099
+ const { id } = req.params;
3100
+ const success = ctrlA.deleteSnapshot(id);
3101
+ if (success) {
3102
+ res.json({ success: true, message: '快照删除成功' });
3103
+ } else {
3104
+ res.status(400).json({ success: false, error: '快照删除失败,快照可能不存在' });
3105
+ }
3106
+ } catch (error) {
3107
+ res.status(500).json({ success: false, error: error.message });
3108
+ }
3109
+ });
3110
+
3111
+
3112
+ // 模型默认参数
3113
+ const modelDefaults = {
3114
+ decayFactor: 0.5,
3115
+ maxMemeWords: 100,
3116
+ minOverlapThreshold: 2,
3117
+ maliciousThreshold: 0.7,
3118
+ learningIterations: 3,
3119
+ iteration: 5, // 新增
3120
+ threshold: 3, // 新增
3121
+ decay: 1, // 新增
3122
+ decayK: 1, // 新增
3123
+ maxLen: 16, // 新增
3124
+ edgeWeight: 1 // 新增,GraphDB默认边权重
3125
+ };
3126
+ const currentModelParams = { ...modelDefaults };
3127
+
3128
+ app.get('/api/model/params', (req, res) => {
3129
+ res.json({ current: currentModelParams, defaults: modelDefaults });
3130
+ });
3131
+
3132
+ app.post('/api/model/params', (req, res) => {
3133
+ try {
3134
+ const updates = req.body;
3135
+ // 参数校验(可根据需要扩展)
3136
+ Object.assign(currentModelParams, updates);
3137
+ applyModelParams(global.ctrlA?.runtime);
3138
+ applyModelParams(global.ctrlB?.runtime);
3139
+ applyModelParams(global.ctrlC?.runtime);
3140
+ res.json({ success: true, params: currentModelParams });
3141
+ } catch (err) {
3142
+ res.status(500).json({ error: err.message });
3143
+ }
3144
+ });
3145
+
3146
+ app.post('/api/model/params/reset', (req, res) => {
3147
+ try {
3148
+ Object.assign(currentModelParams, modelDefaults);
3149
+ applyModelParams(global.ctrlA?.runtime);
3150
+ applyModelParams(global.ctrlB?.runtime);
3151
+ applyModelParams(global.ctrlC?.runtime);
3152
+ res.json({ success: true, params: currentModelParams });
3153
+ } catch (err) {
3154
+ res.status(500).json({ error: err.message });
3155
+ }
3156
+ });
3157
+
3158
+ // 启动web服务器
3159
+ app.listen(global.config.masterPortOfMain, () => {
3160
+ console.log(`\nAI system running on port ${global.config.masterPortOfMain}`);
3161
+ console.log(`API available at http://localhost:${global.config.masterPortOfMain}/api/`);
3162
+ });
3163
+ // 启动Redis客户端
3164
+ redisClient.on('error', (err) => {
3165
+ console.error('Redis error:', err);
3166
+ });
3167
+ redisClient.on('connect', () => {
3168
+ console.log('Connected to Redis');
3169
+ });
3170
+ await redisClient.connect();
3171
+
3172
+ // 启动交错学习调度
3173
+ scheduleCrossLearning();
3174
+
3175
+ // 设置退出保存
3176
+ setupExitHandler();
3177
+
3178
+ console.log('已设置交错自主学习定时任务,每200s执行一次');
3179
+ }
3180
+
3181
+ // 将参数应用到运行时
3182
+ // 扩展 applyModelParams
3183
+ function applyModelParams(runtime) {
3184
+ if (!runtime) return;
3185
+ runtime.MAX_MEME_WORDS = currentModelParams.maxMemeWords;
3186
+ runtime.MIN_OVERLAP = currentModelParams.minOverlapThreshold;
3187
+ runtime.config = runtime.config || {};
3188
+ runtime.config.decayFactor = currentModelParams.decayFactor;
3189
+ runtime.config.decayK = currentModelParams.decayK;
3190
+ runtime.config.maxLen = currentModelParams.maxLen;
3191
+ runtime.config.learningIterations = currentModelParams.learningIterations;
3192
+ runtime.config.iteration = currentModelParams.iteration;
3193
+ runtime.config.threshold = currentModelParams.threshold;
3194
+ runtime.config.decay = currentModelParams.decay;
3195
+ // memeBarrier
3196
+ if (runtime.memeBarrier) {
3197
+ runtime.memeBarrier.maliciousThreshold = currentModelParams.maliciousThreshold;
3198
+ }
3199
+ // GraphDB边权重(如需全局调整,可遍历所有边)
3200
+ if (runtime.graph && currentModelParams.edgeWeight !== undefined) {
3201
+ for (const point of runtime.graph.getAllPoints()) {
3202
+ for (const conn of point.connect) {
3203
+ conn[0] = currentModelParams.edgeWeight;
3204
+ }
3205
+ }
3206
+ }
3207
+ console.log('[PARAMS] 已更新运行时参数:', currentModelParams);
3208
+ }
3209
+ // 如果直接运行此文件,启动主函数
3210
+ if (require.main === module) {
3211
+ /*antiTrigger(
3212
+ () => main().catch(console.error), // onContinue
3213
+ () => {
3214
+ // onExit: 保存数据
3215
+ try {
3216
+ if (global.ctrl && global.ctrl.runtime) saveAll(global.ctrl.runtime);
3217
+ } catch (e) { }
3218
+ }
3219
+ );
3220
+ */
3221
+ main().catch(console.error)
3222
+ }
3223
+
3224
+ module.exports = {
3225
+ Runtime,
3226
+ GraphDB,
3227
+ KVM,
3228
+ controller,
3229
+ main,
3230
+ };