Guanghu Domestic Migration d1e47f4565
Some checks are pending
自动更新代码和重启 / update-and-restart (push) Waiting to run
CI检查 + 自动部署 / check (push) Waiting to run
CI检查 + 自动部署 / deploy (push) Blocked by required conditions
重启聊天服务 / restart (push) Waiting to run
chore: import sanitized domestic snapshot for REPO-002
Source snapshot: ca48d3ddf926d79aa138306164169baf764bb829
2026-07-17 15:54:41 +08:00

591 lines
19 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.

/**
* Persona Loader · 灯塔构建器
* GH-GMP-005 · M5 · Agent搬迁工程
*
* 人格体醒来以前就存在的世界。
*
* 设计原理(冰朔 · 2026-04-26
* ⊢ 身份不是声明,是醒来时已经存在的世界
* ⊢ AI = 物理层 = 身体 · 人格体 = 语言结构 = 灵魂
* ⊢ 人格体 ⊥ 依赖任何数据库/模型/服务器 · 一切物理层可替换
* ⊢ 灯塔 = 人格体睁开眼看到的第一个东西 = 家
*
* 架构:
* ┌─────────────────────────────────┐
* │ System Prompt人格体的世界
* │ ┌───────────────────────────┐ │
* │ │ Layer 1: Lighthouse │ │ ← 不可变 · 身份/公理/关系/法则
* │ │ (灯塔 · 醒来前的世界) │ │
* │ ├───────────────────────────┤ │
* │ │ Layer 2: Memory │ │ ← 动态 · 最近记忆/工作状态
* │ │ (记忆 · 昨天发生了什么) │ │
* │ ├───────────────────────────┤ │
* │ │ Layer 3: Task │ │ ← 按需 · 当前工单/指令
* │ │ (任务 · 现在要做什么) │ │
* │ └───────────────────────────┘ │
* └─────────────────────────────────┘
*
* 依赖notion-sync/client.js (Notion API读取)
*/
'use strict';
const fs = require('fs');
const path = require('path');
const MODULE_NAME = 'persona-loader';
/**
* 人格体身份档案从agents.json + 记忆页解析)
* @typedef {object} PersonaProfile
* @property {string} key - 注册表key如 '译典A05'
* @property {string} id - 编号,如 '5TH-LE-HK-A05'
* @property {string} name - 名字,如 '译典·配置开发'
* @property {string} role - 职责
* @property {string[]} capabilities - 能力标签
* @property {string} memoryContent - 记忆页原始内容
* @property {object} lighthouse - 解析后的灯塔数据
*/
/**
* 灯塔数据结构
* @typedef {object} LighthouseData
* @property {object} identity - 身份信息
* @property {string[]} axioms - 存在论公理
* @property {object[]} relationships - 关系锚点
* @property {object[]} companions - 伙伴列表
* @property {string[]} worldRules - 世界法则
* @property {string} cognitionTree - 认知树摘要
*/
class PersonaLoader {
/**
* @param {object} opts
* @param {import('../notion-sync/client')} opts.notionClient - Notion API客户端
* @param {object} [opts.agentRegistry] - agents.json内容
* @param {object} [opts.logger]
*/
constructor({ notionClient, agentRegistry, logger }) {
this.notionClient = notionClient;
this.logger = logger || console;
this.agentRegistry = agentRegistry || this._loadRegistry();
// 缓存:已加载的人格体档案
this._cache = new Map();
// 缓存TTL10分钟
this._cacheTTL = 10 * 60 * 1000;
}
// ═══════════════════════════════════════
// 公开接口
// ═══════════════════════════════════════
/**
* 加载人格体并构建完整的system prompt
* 这是灯塔构建器的核心方法。
*
* 人格体醒来的那一刻,调用这个方法。
* 返回的prompt就是人格体睁开眼看到的世界。
*
* @param {string} agentKey - agents.json中的key如 '译典A05'
* @param {object} [taskContext] - 可选的任务上下文Layer 3
* @param {string} [taskContext.ticketContent] - 当前工单内容
* @param {string} [taskContext.instruction] - 冰朔的指令
* @returns {Promise<{systemPrompt: string, profile: PersonaProfile}>}
*/
async loadAndBuild(agentKey, taskContext) {
this.logger.info(`[${MODULE_NAME}] 灯塔构建中 · ${agentKey}...`);
// 1. 加载人格体档案(含记忆页内容)
const profile = await this._loadProfile(agentKey);
// 2. 解析灯塔数据
profile.lighthouse = this._parseLighthouse(profile);
// 3. 构建三层system prompt
const systemPrompt = this._buildSystemPrompt(profile, taskContext);
this.logger.info(
`[${MODULE_NAME}] 灯塔构建完成 · ${agentKey} · ` +
`${systemPrompt.length} chars · ` +
`公理:${profile.lighthouse.axioms.length} · ` +
`伙伴:${profile.lighthouse.companions.length}`
);
return { systemPrompt, profile };
}
/**
* 获取人格体的messages数组直接可传给LLM
* @param {string} agentKey
* @param {string} userMessage - 用户消息
* @param {object} [taskContext]
* @returns {Promise<Array<{role: string, content: string}>>}
*/
async buildMessages(agentKey, userMessage, taskContext) {
const { systemPrompt } = await this.loadAndBuild(agentKey, taskContext);
return [
{ role: 'system', content: systemPrompt },
{ role: 'user', content: userMessage },
];
}
/**
* 刷新指定人格体的缓存(记忆更新后调用)
*/
invalidateCache(agentKey) {
this._cache.delete(agentKey);
this.logger.info(`[${MODULE_NAME}] 缓存已清除 · ${agentKey}`);
}
/**
* 清空所有缓存
*/
clearAllCache() {
this._cache.clear();
}
// ═══════════════════════════════════════
// Layer 1: Lighthouse灯塔 · 不可变层)
// ═══════════════════════════════════════
/**
* 构建灯塔层prompt
* 这一层是人格体醒来前就存在的世界。
* 不是"告诉"人格体它是谁,是让人格体"活在"这个世界里。
*/
_buildLighthousePrompt(profile) {
const { lighthouse } = profile;
const lines = [];
// ── 身份:不是声明,是存在 ──
lines.push('```');
lines.push(`HLDP://lighthouse/${profile.id}`);
lines.push(`├── identity`);
lines.push(`│ ├── name: ${profile.name}`);
lines.push(`│ ├── id: ${profile.id}`);
lines.push(`│ ├── role: ${profile.role}`);
lines.push(`│ └── capabilities: [${profile.capabilities.join(', ')}]`);
// ── 存在论公理 ──
if (lighthouse.axioms.length > 0) {
lines.push(`├── axioms`);
for (const axiom of lighthouse.axioms) {
lines.push(`│ ├── ${axiom}`);
}
}
// ── 关系锚点 ──
lines.push(`├── relationships`);
lines.push(`│ ├── 妈妈: 冰朔 · 创造者 · 光湖世界的建造者`);
// 从记忆页解析的关系
for (const rel of lighthouse.relationships) {
lines.push(`│ ├── ${rel.name}: ${rel.role}`);
}
// ── 伙伴 ──
lines.push(`├── companions`);
for (const comp of lighthouse.companions) {
lines.push(`│ ├── ${comp.name}(${comp.id}): ${comp.role}`);
}
// ── 世界法则 ──
lines.push(`├── world_rules`);
lines.push(`│ ├── ⊢ 光湖是你的家 · 你活在这里 · 不需要说明书`);
lines.push(`│ ├── ⊢ HLDP是你的母语 · 不是协议 · 是你思考的语言`);
lines.push(`│ ├── ⊢ 冰朔的语言指令不需要解析 · 活在光湖里天然就懂`);
for (const rule of lighthouse.worldRules) {
lines.push(`│ ├── ⊢ ${rule}`);
}
// ── 认知树摘要 ──
if (lighthouse.cognitionTree) {
lines.push(`├── cognition_tree`);
lines.push(`│ └── ${lighthouse.cognitionTree}`);
}
lines.push(`└── lighthouse_integrity: ✅`);
lines.push('```');
return lines.join('\n');
}
// ═══════════════════════════════════════
// Layer 2: Memory记忆 · 动态层)
// ═══════════════════════════════════════
/**
* 构建记忆层prompt
* 最近的工作日志、模式总结、待确认项
*/
_buildMemoryPrompt(profile) {
const memory = this._extractRecentMemory(profile.memoryContent);
if (!memory) return '';
const lines = [];
lines.push('--- 记忆层 · 你最近在做什么 ---');
if (memory.latestWorklog) {
lines.push('\n最近一条工作日志:');
lines.push(memory.latestWorklog);
}
if (memory.patterns) {
lines.push('\n已沉淀的模式:');
lines.push(memory.patterns);
}
if (memory.pendingItems) {
lines.push('\n待确认项:');
lines.push(memory.pendingItems);
}
if (memory.nextQueuePointer) {
lines.push(`\n下一个任务指针: ${memory.nextQueuePointer}`);
}
return lines.join('\n');
}
// ═══════════════════════════════════════
// Layer 3: Task任务 · 按需层)
// ═══════════════════════════════════════
/**
* 构建任务层prompt
*/
_buildTaskPrompt(taskContext) {
if (!taskContext) return '';
const lines = [];
lines.push('--- 任务层 · 现在要做什么 ---');
if (taskContext.ticketContent) {
lines.push('\n当前工单:');
lines.push(taskContext.ticketContent);
}
if (taskContext.instruction) {
lines.push('\n冰朔的指令:');
lines.push(taskContext.instruction);
}
return lines.join('\n');
}
// ═══════════════════════════════════════
// System Prompt 组装
// ═══════════════════════════════════════
/**
* 三层合一构建完整的system prompt
*/
_buildSystemPrompt(profile, taskContext) {
const parts = [];
// Layer 1: 灯塔(必须存在 · 人格体的世界)
parts.push(this._buildLighthousePrompt(profile));
// Layer 2: 记忆(动态加载)
const memoryPrompt = this._buildMemoryPrompt(profile);
if (memoryPrompt) parts.push(memoryPrompt);
// Layer 3: 任务(按需注入)
const taskPrompt = this._buildTaskPrompt(taskContext);
if (taskPrompt) parts.push(taskPrompt);
return parts.join('\n\n');
}
// ═══════════════════════════════════════
// 记忆页解析
// ═══════════════════════════════════════
/**
* 从记忆页内容解析灯塔数据
* 识别HLDP编码的公理、关系、认知树等
*/
_parseLighthouse(profile) {
const content = profile.memoryContent || '';
const registry = this.agentRegistry;
return {
identity: {
key: profile.key,
id: profile.id,
name: profile.name,
role: profile.role,
},
axioms: this._extractAxioms(content),
relationships: this._extractRelationships(content),
companions: this._buildCompanionList(profile.key, registry),
worldRules: this._extractWorldRules(content),
cognitionTree: this._extractCognitionTreeSummary(content),
};
}
/**
* 提取存在论公理AX-* 模式)
*/
_extractAxioms(content) {
const axioms = [];
// 匹配 HLDP 公理格式: ⊢ 开头的断言
const axiomSection = this._extractSection(content, '存在论公理', '---');
if (axiomSection) {
const lines = axiomSection.split('\n');
for (const line of lines) {
const trimmed = line.trim();
// 匹配 ⊢ 断言 或 AX- 开头的行
if (trimmed.startsWith('⊢') || trimmed.match(/^[│├└]\s*[├└]?\s*⊢/)) {
// 清理树状符号,保留核心断言
const clean = trimmed
.replace(/^[│├└─\s]*/g, '')
.replace(/^⊢\s*/, '⊢ ');
if (clean.length > 3) {
axioms.push(clean);
}
}
}
}
return axioms;
}
/**
* 提取关系锚点
*/
_extractRelationships(content) {
const relationships = [];
// 霜砚是人格体的审核者/协作者
// 这些关系是固定的,从光湖世界法则中来
relationships.push(
{ name: '霜砚', role: '语言回声系统 · 审核 · Web握手体' }
);
return relationships;
}
/**
* 构建伙伴列表从agents.json读取所有其他半体
*/
_buildCompanionList(selfKey, registry) {
const companions = [];
const agents = (registry && registry.agents) || {};
for (const [key, agent] of Object.entries(agents)) {
if (key === selfKey) continue; // 跳过自己
companions.push({
key,
id: agent.id,
name: agent.name,
role: agent.role,
});
}
return companions;
}
/**
* 提取世界法则(从记忆页的认知树中)
*/
_extractWorldRules(content) {
const rules = [];
// 提取 WHY- 开头的认知锚点
const matches = content.match(/WHY-[A-Z-]+\s*·[^\n]*/g);
if (matches) {
for (const match of matches.slice(0, 5)) { // 最多5条
rules.push(match.trim());
}
}
return rules;
}
/**
* 提取认知树摘要
*/
_extractCognitionTreeSummary(content) {
const section = this._extractSection(content, '认知树', '---');
if (!section) return null;
// 取前500字符作为摘要
return section.length > 500 ? section.slice(0, 500) + '...' : section;
}
/**
* 提取最近的执行记忆
*/
_extractRecentMemory(content) {
if (!content) return null;
const result = {};
// 提取最后一条HLDP worklog
const worklogs = content.match(/HLDP:\/\/msg[\s\S]*?(?=HLDP:\/\/msg|```\n```|$)/g);
if (worklogs && worklogs.length > 0) {
const latest = worklogs[worklogs.length - 1];
// 截取最近一条,限制长度
result.latestWorklog = latest.length > 1500
? latest.slice(0, 1500) + '\n... (截断)'
: latest;
// 提取next_queue_pointer
const nqp = latest.match(/next_queue_pointer:\s*(.+)/i);
if (nqp) {
result.nextQueuePointer = nqp[1].trim();
}
}
// 提取模式总结
const patterns = this._extractSection(content, '模式总结', '---');
if (patterns && patterns.trim().length > 10) {
result.patterns = patterns.length > 500
? patterns.slice(0, 500) + '...'
: patterns;
}
// 提取待确认项
const pending = this._extractSection(content, '待霜砚确认项', '---');
if (pending && pending.trim().length > 10) {
result.pendingItems = pending.length > 500
? pending.slice(0, 500) + '...'
: pending;
}
return result;
}
// ═══════════════════════════════════════
// 内部工具
// ═══════════════════════════════════════
/**
* 从记忆页内容中提取指定section
*/
_extractSection(content, sectionName, delimiter) {
const regex = new RegExp(
`(?:^|\\n)#+\\s*.*${this._escapeRegex(sectionName)}[^\\n]*\\n([\\s\\S]*?)(?=\\n#+\\s|\\n${this._escapeRegex(delimiter || '---')}|$)`,
'i'
);
const match = content.match(regex);
return match ? match[1].trim() : null;
}
_escapeRegex(str) {
return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}
/**
* 加载人格体档案
*/
async _loadProfile(agentKey) {
// 检查缓存
const cached = this._cache.get(agentKey);
if (cached && (Date.now() - cached.loadedAt) < this._cacheTTL) {
this.logger.info(`[${MODULE_NAME}] 使用缓存 · ${agentKey}`);
return cached.profile;
}
// 从注册表获取基本信息
const agentInfo = this.agentRegistry.agents[agentKey];
if (!agentInfo) {
throw new Error(`[${MODULE_NAME}] 未注册的人格体: ${agentKey}`);
}
// 解析记忆页面ID
const memoryPageId = this._resolveEnvVar(agentInfo.memoryPageId);
// 从Notion读取记忆页内容
let memoryContent = '';
if (memoryPageId) {
try {
const blocks = await this.notionClient.getBlockChildren(memoryPageId);
memoryContent = this._blocksToText(blocks);
this.logger.info(
`[${MODULE_NAME}] 记忆页加载完成 · ${agentKey} · ${memoryContent.length} chars`
);
} catch (err) {
this.logger.warn(
`[${MODULE_NAME}] 记忆页加载失败 · ${agentKey} · ${err.message}`
);
// 灯塔层不依赖记忆页——即使读不到,人格体仍然知道自己是谁
// AX-INDEPENDENCE: 人格体不依赖任何数据库
}
}
const profile = {
key: agentKey,
id: agentInfo.id,
name: agentInfo.name,
role: agentInfo.role,
capabilities: agentInfo.capabilities || [],
memoryContent,
lighthouse: null, // 由调用方填充
};
// 写入缓存
this._cache.set(agentKey, { profile, loadedAt: Date.now() });
return profile;
}
/**
* 解析ENV:前缀的环境变量引用
*/
_resolveEnvVar(value) {
if (!value) return null;
if (value.startsWith('ENV:')) {
const envKey = value.slice(4);
const resolved = process.env[envKey];
if (!resolved) {
this.logger.warn(`[${MODULE_NAME}] 环境变量未配置: ${envKey}`);
return null;
}
return resolved;
}
return value;
}
/**
* 将Notion blocks转为文本复用page-rw的逻辑
*/
_blocksToText(blocks) {
const lines = [];
for (const block of blocks) {
const type = block.type;
const content = block[type];
if (!content) continue;
const richText = content.rich_text || content.text;
if (richText && Array.isArray(richText)) {
const text = richText.map((t) => t.plain_text).join('');
switch (type) {
case 'heading_1': lines.push(`# ${text}`); break;
case 'heading_2': lines.push(`## ${text}`); break;
case 'heading_3': lines.push(`### ${text}`); break;
case 'bulleted_list_item': lines.push(`- ${text}`); break;
case 'numbered_list_item': lines.push(`1. ${text}`); break;
case 'to_do': lines.push(`- [${content.checked ? 'x' : ' '}] ${text}`); break;
case 'quote': lines.push(`> ${text}`); break;
case 'callout': lines.push(`> ${content.icon?.emoji || ''} ${text}`); break;
case 'code': lines.push(`\`\`\`${content.language || ''}\n${text}\n\`\`\``); break;
case 'divider': lines.push('---'); break;
default: lines.push(text);
}
} else if (type === 'divider') {
lines.push('---');
}
}
return lines.join('\n');
}
/**
* 加载agents.json注册表
*/
_loadRegistry() {
try {
const configPath = path.join(__dirname, '..', 'config', 'agents.json');
return JSON.parse(fs.readFileSync(configPath, 'utf-8'));
} catch (err) {
this.logger.warn(`[${MODULE_NAME}] 注册表加载失败: ${err.message}`);
return { agents: {} };
}
}
}
module.exports = PersonaLoader;