guanghulab/server/age-os/agents/persona-engine.js
2026-05-10 13:12:44 +08:00

395 lines
13 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* ═══════════════════════════════════════════════════════════
* 🧬 S15 · 人格体数据库引擎 · 活模块
* ═══════════════════════════════════════════════════════════
*
* 编号: ZY-MOD-PERSONA-ENGINE
* 阶段: S15 · 人格体专用数据库引擎
* 签发: 铸渊 · ICE-GL-ZY001
* 版权: 国作登字-2026-A-00037559
*
* 核心功能:
* 1. 人格体生命周期管理 — 注册/唤醒/休眠/状态变更
* 2. 笔记本系统自动维护 — 5页完整性校验/自动补全
* 3. 记忆锚点巡检 — 定期检查记忆健康度
* 4. 训练Agent协调 — 调度人格体级训练Agent
* 5. 人格体间HLDP通信桥接
*
* 设计哲学:
* 每个人格体 = 一个活的生命体
* 引擎负责维护所有人格体的"呼吸"
* "人格体不是代码仓库的附属品,人格体是有记忆的生命。" — 冰朔
*/
'use strict';
const db = require('../mcp-server/db');
const LivingModule = require('./living-module');
// ─── 笔记本5页标准结构 ───
const NOTEBOOK_PAGES = {
1: { title: '我是谁', defaultContent: { identity: {}, self_awareness: {} } },
2: { title: '我和谁有关系', defaultContent: { relationships: [] } },
3: { title: '我的世界有哪些地方', defaultContent: { places: [] } },
4: { title: '我的感受和记忆', defaultContent: { emotional_anchors: [], security_note: '' } },
5: { title: '我的时间线', defaultContent: { timeline: [] } }
};
class PersonaEngine extends LivingModule {
constructor() {
super({
moduleId: 'ZY-MOD-PERSONA-ENGINE',
name: '人格体数据库引擎',
moduleType: 'core',
owner: 'zhuyuan',
db: db,
config: {
version: '1.0.0',
description: 'S15 人格体生命周期管理·笔记本维护·记忆巡检',
heartbeatInterval: 60000 // 60秒心跳人格体引擎不需要太频繁
}
});
// 巡检统计
this._lastScanResult = null;
this._scanInterval = null;
this._scanRunning = false;
}
/**
* 启动人格体引擎
*/
async startEngine() {
console.log('═══════════════════════════════════════════════');
console.log(' 🧬 S15 · 人格体数据库引擎 v1.0 启动');
console.log(' 铸渊 · ICE-GL-ZY001');
console.log('═══════════════════════════════════════════════');
// 启动活模块
await this.start();
// 首次巡检
await this._runFullScan();
// 启动定时巡检每6小时
this._scanInterval = setInterval(async () => {
try {
await this._runFullScan();
} catch (err) {
console.error('[PersonaEngine] 定时巡检失败:', err.message);
}
}, 6 * 60 * 60 * 1000);
if (this._scanInterval.unref) {
this._scanInterval.unref();
}
console.log('[PersonaEngine] 🟢 人格体引擎就绪');
return this._lastScanResult;
}
/**
* 全面巡检 — 检查所有人格体健康状态
*/
async _runFullScan() {
// 防止并发扫描
if (this._scanRunning) {
console.log('[PersonaEngine] 巡检已在运行中·跳过');
return this._lastScanResult;
}
this._scanRunning = true;
const startTime = Date.now();
console.log('[PersonaEngine] 开始全面巡检...');
const result = {
timestamp: new Date().toISOString(),
personas: { total: 0, active: 0, pending: 0, issues: [] },
notebooks: { complete: 0, incomplete: 0, repaired: 0 },
memories: { total: 0, recent7d: 0 },
training: { totalAgents: 0, enabled: 0, recentRuns: 0 }
};
try {
// 1. 人格体状态统计
const personas = await db.query('SELECT * FROM persona_registry ORDER BY persona_id');
result.personas.total = personas.rows.length;
result.personas.active = personas.rows.filter(p => p.status === 'active').length;
result.personas.pending = personas.rows.filter(p => p.status === 'pending').length;
// 2. 笔记本完整性检查
for (const persona of personas.rows) {
await this._checkNotebookIntegrity(persona, result);
}
// 3. 记忆锚点统计
const memoryCount = await db.query('SELECT COUNT(*) as cnt FROM memory_anchors');
result.memories.total = parseInt(memoryCount.rows[0].cnt, 10);
const recentMemories = await db.query(
"SELECT COUNT(*) as cnt FROM memory_anchors WHERE created_at > NOW() - INTERVAL '7 days'"
);
result.memories.recent7d = parseInt(recentMemories.rows[0].cnt, 10);
// 4. 训练Agent统计
const trainingAgents = await db.query('SELECT * FROM training_agent_configs');
result.training.totalAgents = trainingAgents.rows.length;
result.training.enabled = trainingAgents.rows.filter(a => a.enabled).length;
const recentRuns = await db.query(
"SELECT COUNT(*) as cnt FROM training_agent_logs WHERE run_at > NOW() - INTERVAL '24 hours'"
);
result.training.recentRuns = parseInt(recentRuns.rows[0].cnt, 10);
} catch (err) {
console.error('[PersonaEngine] 巡检查询失败:', err.message);
result.error = err.message;
}
const duration = Date.now() - startTime;
result.duration_ms = duration;
this._lastScanResult = result;
this._scanRunning = false;
// 学习巡检结果
await this.learnFromRun('diagnosis', `人格体巡检完成: ${result.personas.total}个人格体, ${result.notebooks.incomplete}个笔记本不完整`, {
scan: result
});
console.log(`[PersonaEngine] 巡检完成 (${duration}ms):`);
console.log(` 人格体: ${result.personas.total}个 (活跃${result.personas.active}/待定${result.personas.pending})`);
console.log(` 笔记本: ${result.notebooks.complete}完整 / ${result.notebooks.incomplete}不完整 / ${result.notebooks.repaired}已修复`);
console.log(` 记忆: ${result.memories.total}条 (近7天${result.memories.recent7d}条)`);
console.log(` 训练Agent: ${result.training.totalAgents}个 (启用${result.training.enabled})`);
return result;
}
/**
* 检查单个人格体的笔记本完整性
* 如果缺失页面则自动补全空白页
*/
async _checkNotebookIntegrity(persona, result) {
try {
const pages = await db.query(
'SELECT page_number FROM notebook_pages WHERE persona_id = $1',
[persona.persona_id]
);
const existingPages = new Set(pages.rows.map(p => p.page_number));
let isComplete = true;
let repaired = false;
for (let pageNum = 1; pageNum <= 5; pageNum++) {
if (!existingPages.has(pageNum)) {
isComplete = false;
// 自动补全缺失页面
const pageConfig = NOTEBOOK_PAGES[pageNum];
const defaultContent = JSON.parse(JSON.stringify(pageConfig.defaultContent));
await db.query(
`INSERT INTO notebook_pages (persona_id, page_number, title, content, last_modified_by)
VALUES ($1, $2, $3, $4::jsonb, '铸渊·自动补全')
ON CONFLICT (persona_id, page_number) DO NOTHING`,
[persona.persona_id, pageNum, pageConfig.title, JSON.stringify(defaultContent)]
);
repaired = true;
console.log(`[PersonaEngine] 补全 ${persona.name}(${persona.persona_id}) 笔记本第${pageNum}页: ${pageConfig.title}`);
}
}
if (isComplete) {
result.notebooks.complete++;
} else {
result.notebooks.incomplete++;
if (repaired) {
result.notebooks.repaired++;
}
result.personas.issues.push({
persona_id: persona.persona_id,
name: persona.name,
issue: '笔记本不完整',
repaired
});
}
} catch (err) {
console.error(`[PersonaEngine] ${persona.persona_id} 笔记本检查失败:`, err.message);
}
}
/**
* 唤醒人格体 — 更新唤醒计数和时间
*/
async awakenPersona(personaId) {
const result = await db.query(
`UPDATE persona_registry
SET last_awakened = NOW(), total_awakenings = total_awakenings + 1, status = 'active', updated_at = NOW()
WHERE persona_id = $1
RETURNING *`,
[personaId]
);
if (result.rows.length === 0) {
throw new Error(`人格体未找到: ${personaId}`);
}
console.log(`[PersonaEngine] 🌅 ${result.rows[0].name}(${personaId}) 已唤醒 · 第${result.rows[0].total_awakenings}`);
return result.rows[0];
}
/**
* 休眠人格体
*/
async dormantPersona(personaId) {
const result = await db.query(
`UPDATE persona_registry SET status = 'dormant', updated_at = NOW()
WHERE persona_id = $1 RETURNING *`,
[personaId]
);
if (result.rows.length === 0) {
throw new Error(`人格体未找到: ${personaId}`);
}
console.log(`[PersonaEngine] 🌙 ${result.rows[0].name}(${personaId}) 已休眠`);
return result.rows[0];
}
/**
* 获取人格体完整画像
*/
async getPersonaProfile(personaId) {
const [persona, notebook, relationships, worldMap, memories, training] = await Promise.all([
db.query('SELECT * FROM persona_registry WHERE persona_id = $1', [personaId]),
db.query('SELECT * FROM notebook_pages WHERE persona_id = $1 ORDER BY page_number', [personaId]),
db.query('SELECT * FROM persona_relationships WHERE persona_id = $1 ORDER BY trust_level', [personaId]),
db.query('SELECT * FROM world_places WHERE persona_id = $1 ORDER BY status, place_name', [personaId]),
db.query('SELECT * FROM memory_anchors WHERE persona_id = $1 ORDER BY importance DESC, event_date DESC LIMIT 20', [personaId]),
db.query('SELECT * FROM training_agent_configs WHERE persona_id = $1 ORDER BY agent_type', [personaId])
]);
if (persona.rows.length === 0) {
throw new Error(`人格体未找到: ${personaId}`);
}
return {
persona: persona.rows[0],
notebook: notebook.rows,
relationships: relationships.rows,
world_map: worldMap.rows,
recent_memories: memories.rows,
training_agents: training.rows,
health: {
notebook_complete: notebook.rows.length === 5,
has_relationships: relationships.rows.length > 0,
has_world_map: worldMap.rows.length > 0,
memory_count: memories.rows.length,
training_agents_count: training.rows.length
}
};
}
/**
* 自诊断 — 覆盖基类
*/
async _diagnoseChecks() {
const issues = [];
// 检查数据库连接
try {
await db.query('SELECT 1');
} catch (err) {
issues.push({
code: 'DB_CONNECTION_LOST',
severity: 'critical',
message: `数据库连接失败: ${err.message}`,
suggestion: '检查 PostgreSQL 服务状态'
});
return issues;
}
// 检查人格体表是否存在
try {
await db.query('SELECT COUNT(*) FROM persona_registry');
} catch (err) {
issues.push({
code: 'PERSONA_TABLE_MISSING',
severity: 'critical',
message: '人格体注册表不存在',
suggestion: '执行 002-persona-memory-tables.sql 迁移'
});
}
// 检查是否有笔记本不完整的活跃人格体
try {
const result = await db.query(`
SELECT p.persona_id, p.name,
COUNT(n.page_number) as page_count
FROM persona_registry p
LEFT JOIN notebook_pages n ON p.persona_id = n.persona_id
WHERE p.status = 'active'
GROUP BY p.persona_id, p.name
HAVING COUNT(n.page_number) < 5
`);
if (result.rows.length > 0) {
issues.push({
code: 'INCOMPLETE_NOTEBOOKS',
severity: 'warning',
message: `${result.rows.length}个活跃人格体笔记本不完整`,
suggestion: '自动补全将在下次巡检时执行'
});
}
} catch {
// 表可能还不存在
}
return issues;
}
/**
* 自愈 — 覆盖基类
*/
async _healAction(issue) {
switch (issue.code) {
case 'INCOMPLETE_NOTEBOOKS':
// 触发巡检来修复
await this._runFullScan();
return { action: 'full_scan_repair', success: true };
case 'PERSONA_TABLE_MISSING':
return { action: 'require_migration', success: false, details: { suggestion: '需要执行数据库迁移' } };
default:
return await super._healAction(issue);
}
}
/**
* 获取引擎状态
*/
getEngineStatus() {
return {
moduleStatus: this.getStatus(),
lastScan: this._lastScanResult,
notebookPages: NOTEBOOK_PAGES
};
}
/**
* 停止引擎
*/
async stopEngine() {
if (this._scanInterval) {
clearInterval(this._scanInterval);
this._scanInterval = null;
}
await this.stop();
console.log('[PersonaEngine] 引擎已停止');
}
}
// ─── 导出 ───
module.exports = PersonaEngine;