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