guanghulab/scripts/wake-persona.js
2026-05-10 13:12:44 +08:00

936 lines
37 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.

// scripts/wake-persona.js
// 铸渊 · 人格体唤醒脚本(第三方 API 兼容层 · 自动检测模式)
//
// 功能:
// ① 自动发现可用模型(/v1/models 端点)
// ② 智能选择最优 Claude 模型
// ③ 自适应 API 格式OpenAI 兼容 / Anthropic 原生)
// ④ 统一调用接口,唤醒人格体处理 SYSLOG 或解答提问
// ⑤ v4.0 协议动态注入(从 Notion 实时读取核心大脑规则 + 画像 + 指纹)
//
// 环境变量:
// LLM_API_KEY 第三方平台密钥(必须)
// LLM_BASE_URL 第三方平台 API 地址(必须,如 https://api.xxx.com/v1
// BROADCAST_ID 广播编号
// SUBMIT_TYPE syslog | question
// SUBMIT_CONTENT 提交内容SYSLOG 全文或问题描述)
// AUTHOR 提交者 GitHub 用户名
// NOTION_TOKEN Notion API token用于动态读取协议
// CORE_BRAIN_PAGE_ID 曜冥核心大脑 v4.0 页面 ID
// PORTRAIT_DB_ID 开发者动态画像库数据库 ID
// FINGERPRINT_DB_ID 模块指纹注册表数据库 ID
'use strict';
const https = require('https');
const http = require('http');
const fs = require('fs');
const path = require('path');
// ══════════════════════════════════════════════════════════
// 配置
// ══════════════════════════════════════════════════════════
const LLM_API_KEY = process.env.LLM_API_KEY || '';
const LLM_BASE_URL = (process.env.LLM_BASE_URL || '').replace(/\/+$/, '');
const BROADCAST_ID = process.env.BROADCAST_ID || 'UNKNOWN';
const SUBMIT_TYPE = process.env.SUBMIT_TYPE || 'question';
const SUBMIT_CONTENT = process.env.SUBMIT_CONTENT || '';
const AUTHOR = process.env.AUTHOR || 'unknown';
// 模块验证结果注入(由 verify-modules.js 提供)
const MODULE_VERIFY_RESULT = process.env.MODULE_VERIFY_RESULT || '';
// Notion 回传结果注入(由 notion-callback-pipeline 提供)
const NOTION_CALLBACK_RESULT = process.env.NOTION_CALLBACK_RESULT || '';
// Notion 配置v4.0 协议动态注入)
const NOTION_TOKEN = process.env.NOTION_TOKEN || '';
const CORE_BRAIN_PAGE_ID = process.env.CORE_BRAIN_PAGE_ID || '';
const PORTRAIT_DB_ID = process.env.PORTRAIT_DB_ID || '';
const FINGERPRINT_DB_ID = process.env.FINGERPRINT_DB_ID || '';
const NOTION_VERSION = '2022-06-28';
const NOTION_API_HOSTNAME = 'api.notion.com';
const MAX_PROTOCOL_TEXT_LENGTH = 15000;
// Claude 模型优先级队列(从高到低)
const PREFERRED_MODELS = [
'claude-sonnet-4',
'claude-3-5-sonnet-20241022',
'claude-3.5-sonnet',
'claude-3-5-sonnet',
'anthropic/claude-3.5-sonnet',
'claude-3-sonnet',
'claude-3-haiku',
];
// ══════════════════════════════════════════════════════════
// HTTP 请求工具
// ══════════════════════════════════════════════════════════
function httpRequest(url, options, body) {
return new Promise((resolve, reject) => {
const parsed = new URL(url);
const isHttps = parsed.protocol === 'https:';
const mod = isHttps ? https : http;
const opts = {
hostname: parsed.hostname,
port: parsed.port || (isHttps ? 443 : 80),
path: parsed.pathname + parsed.search,
method: options.method || 'GET',
headers: options.headers || {},
timeout: options.timeout || 60000,
};
const req = mod.request(opts, (res) => {
let data = '';
res.on('data', (chunk) => { data += chunk; });
res.on('end', () => {
resolve({ status: res.statusCode, body: data });
});
});
req.on('error', reject);
req.on('timeout', () => {
req.destroy();
reject(new Error('Request timeout'));
});
if (body) {
req.write(body);
}
req.end();
});
}
// ══════════════════════════════════════════════════════════
// Step 1: 自动发现可用模型
// ══════════════════════════════════════════════════════════
async function discoverModels() {
console.log('[LLM] 🔍 探测可用模型...');
try {
const res = await httpRequest(LLM_BASE_URL + '/models', {
method: 'GET',
headers: {
'Authorization': 'Bearer ' + LLM_API_KEY,
'Content-Type': 'application/json',
},
timeout: 15000,
});
if (res.status >= 200 && res.status < 300) {
const json = JSON.parse(res.body);
const models = json.data || [];
console.log('[LLM] → 发现 ' + models.length + ' 个模型');
return models;
}
console.log('[LLM] → 模型探测返回 ' + res.status + ', 使用默认模型');
return [];
} catch (err) {
console.log('[LLM] → 模型探测失败: ' + err.message + ', 使用默认模型');
return [];
}
}
// ══════════════════════════════════════════════════════════
// Step 2: 智能选择最优 Claude 模型
// ══════════════════════════════════════════════════════════
function selectBestModel(models) {
if (!models || models.length === 0) {
console.log('[LLM] 📌 无可用模型列表, 使用默认 claude-3-5-sonnet');
return 'claude-3-5-sonnet';
}
const available = models.map(function (m) { return m.id.toLowerCase(); });
// 按优先级匹配
for (const preferred of PREFERRED_MODELS) {
const match = available.find(function (id) { return id.includes(preferred); });
if (match) {
const found = models.find(function (m) { return m.id.toLowerCase() === match; });
if (found) {
console.log('[LLM] 📌 选择模型: ' + found.id + ' (匹配规则: ' + preferred + ')');
return found.id;
}
}
}
// 兜底:任何含 'claude' 的模型
const anyClaude = available.find(function (id) { return id.includes('claude'); });
if (anyClaude) {
const found = models.find(function (m) { return m.id.toLowerCase() === anyClaude; });
if (found) {
console.log('[LLM] 📌 兜底选择 Claude 模型: ' + found.id);
return found.id;
}
}
// 最终兜底:平台第一个可用模型
const fallbackId = models[0].id;
console.log('[LLM] 📌 最终兜底: ' + fallbackId + ' (平台无 Claude 模型)');
return fallbackId;
}
// ══════════════════════════════════════════════════════════
// Step 3: 自适应 API 格式检测
// ══════════════════════════════════════════════════════════
async function detectApiFormat() {
console.log('[LLM] 🔍 检测 API 格式...');
// 尝试 OpenAI 兼容格式(绝大多数第三方平台)
try {
const res = await httpRequest(LLM_BASE_URL + '/chat/completions', {
method: 'POST',
headers: {
'Authorization': 'Bearer ' + LLM_API_KEY,
'Content-Type': 'application/json',
},
timeout: 10000,
}, JSON.stringify({
model: 'test',
messages: [{ role: 'user', content: 'ping' }],
max_tokens: 1,
}));
// 400 = endpoint exists but bad request (model not found etc.) → format supported
// 200 = endpoint works → format supported
if (res.status === 200 || res.status === 400 || res.status === 401 || res.status === 422) {
console.log('[LLM] → 检测到 OpenAI 兼容格式 (status: ' + res.status + ')');
return 'openai-compat';
}
} catch (e) {
// Ignore, try next format
}
// 尝试 Anthropic 原生格式
try {
const res = await httpRequest(LLM_BASE_URL + '/messages', {
method: 'POST',
headers: {
'x-api-key': LLM_API_KEY,
'anthropic-version': '2023-06-01',
'Content-Type': 'application/json',
},
timeout: 10000,
}, JSON.stringify({
model: 'test',
messages: [{ role: 'user', content: 'ping' }],
max_tokens: 1,
}));
if (res.status === 200 || res.status === 400 || res.status === 401 || res.status === 422) {
console.log('[LLM] → 检测到 Anthropic 原生格式 (status: ' + res.status + ')');
return 'anthropic-native';
}
} catch (e) {
// Ignore
}
console.log('[LLM] → 无法确定格式, 默认使用 OpenAI 兼容格式');
return 'openai-compat';
}
// ══════════════════════════════════════════════════════════
// Step 4: 统一调用接口
// ══════════════════════════════════════════════════════════
async function callLLM(systemPrompt, userMessage) {
if (!LLM_API_KEY) {
console.log('[LLM] ⚠️ LLM_API_KEY 未配置,跳过人格体唤醒');
return '(LLM API 未配置,请在 GitHub Secrets 中设置 LLM_API_KEY 和 LLM_BASE_URL)';
}
if (!LLM_BASE_URL) {
console.log('[LLM] ⚠️ LLM_BASE_URL 未配置,跳过人格体唤醒');
return '(LLM_BASE_URL 未配置,请在 GitHub Secrets 中设置第三方平台 API 地址)';
}
const models = await discoverModels();
const model = selectBestModel(models);
const format = await detectApiFormat();
console.log('[LLM] 🚀 调用 LLM: 模型=' + model + ', 格式=' + format + ', 平台=' + LLM_BASE_URL);
let res;
if (format === 'openai-compat') {
// OpenAI 兼容格式(大多数第三方平台)
const body = JSON.stringify({
model: model,
max_tokens: 8000,
temperature: 0.7,
messages: [
{ role: 'system', content: systemPrompt },
{ role: 'user', content: userMessage },
],
});
res = await httpRequest(LLM_BASE_URL + '/chat/completions', {
method: 'POST',
headers: {
'Authorization': 'Bearer ' + LLM_API_KEY,
'Content-Type': 'application/json',
},
timeout: 120000,
}, body);
if (res.status >= 200 && res.status < 300) {
const json = JSON.parse(res.body);
if (json.choices && json.choices[0] && json.choices[0].message) {
return json.choices[0].message.content;
}
}
} else {
// Anthropic 原生格式
const body = JSON.stringify({
model: model,
max_tokens: 8000,
system: systemPrompt,
messages: [
{ role: 'user', content: userMessage },
],
});
res = await httpRequest(LLM_BASE_URL + '/messages', {
method: 'POST',
headers: {
'x-api-key': LLM_API_KEY,
'anthropic-version': '2023-06-01',
'Content-Type': 'application/json',
},
timeout: 120000,
}, body);
if (res.status >= 200 && res.status < 300) {
const json = JSON.parse(res.body);
if (json.content && json.content[0]) {
return json.content[0].text;
}
}
}
// 处理错误
const errorMsg = '[LLM] ❌ API 调用失败: status=' + (res ? res.status : 'N/A');
console.error(errorMsg);
if (res && res.body) {
console.error('[LLM] 响应: ' + res.body.slice(0, 500));
}
throw new Error(errorMsg);
}
// ══════════════════════════════════════════════════════════
// Notion API 工具v4.0 协议动态注入)
// ══════════════════════════════════════════════════════════
function notionGet(endpoint) {
return new Promise((resolve, reject) => {
const opts = {
hostname: NOTION_API_HOSTNAME,
port: 443,
path: endpoint,
method: 'GET',
headers: {
'Authorization': 'Bearer ' + NOTION_TOKEN,
'Notion-Version': NOTION_VERSION,
},
};
const req = https.request(opts, (res) => {
let data = '';
res.on('data', (chunk) => { data += chunk; });
res.on('end', () => {
try {
const parsed = JSON.parse(data);
if (res.statusCode >= 200 && res.statusCode < 300) {
resolve(parsed);
} else {
reject(new Error('Notion API ' + res.statusCode + ': ' + (parsed.message || data)));
}
} catch (e) {
reject(new Error('Notion API parse error: ' + data.slice(0, 200)));
}
});
});
req.on('error', reject);
req.setTimeout(30000, () => { req.destroy(); reject(new Error('Notion API timeout')); });
req.end();
});
}
function notionPost(endpoint, body) {
return new Promise((resolve, reject) => {
const payload = JSON.stringify(body);
const opts = {
hostname: NOTION_API_HOSTNAME,
port: 443,
path: endpoint,
method: 'POST',
headers: {
'Authorization': 'Bearer ' + NOTION_TOKEN,
'Content-Type': 'application/json',
'Notion-Version': NOTION_VERSION,
'Content-Length': Buffer.byteLength(payload),
},
};
const req = https.request(opts, (res) => {
let data = '';
res.on('data', (chunk) => { data += chunk; });
res.on('end', () => {
try {
const parsed = JSON.parse(data);
if (res.statusCode >= 200 && res.statusCode < 300) {
resolve(parsed);
} else {
reject(new Error('Notion API ' + res.statusCode + ': ' + (parsed.message || data)));
}
} catch (e) {
reject(new Error('Notion API parse error: ' + data.slice(0, 200)));
}
});
});
req.on('error', reject);
req.setTimeout(30000, () => { req.destroy(); reject(new Error('Notion API timeout')); });
req.write(payload);
req.end();
});
}
/**
* 读取 Notion 页面的所有子块(递归分页)
*/
async function getNotionPageBlocks(pageId) {
const blocks = [];
let cursor = undefined;
do {
const qs = cursor ? '?start_cursor=' + cursor : '';
const result = await notionGet('/v1/blocks/' + pageId + '/children' + qs);
blocks.push(...(result.results || []));
cursor = result.has_more ? result.next_cursor : undefined;
} while (cursor);
return blocks;
}
/**
* 从 Notion 块中提取纯文本
*/
function extractBlockText(block) {
const type = block.type;
if (!block[type]) return '';
const richTexts = block[type].rich_text || block[type].text || [];
return richTexts.map(function (rt) { return rt.plain_text || ''; }).join('');
}
/**
* 将 Notion 块列表转为纯文本
*/
function blocksToText(blocks) {
return blocks.map(function (block) {
const type = block.type;
const text = extractBlockText(block);
if (type === 'heading_1') return '\n# ' + text;
if (type === 'heading_2') return '\n## ' + text;
if (type === 'heading_3') return '\n### ' + text;
if (type === 'bulleted_list_item') return '- ' + text;
if (type === 'numbered_list_item') return '• ' + text;
if (type === 'to_do') {
var checked = block.to_do && block.to_do.checked ? '☑' : '☐';
return checked + ' ' + text;
}
if (type === 'code') {
var lang = (block.code && block.code.language) || '';
return '```' + lang + '\n' + text + '\n```';
}
if (type === 'divider') return '---';
if (type === 'callout') return '> ' + text;
if (type === 'quote') return '> ' + text;
if (type === 'toggle') return '▸ ' + text;
return text;
}).filter(Boolean).join('\n');
}
// ══════════════════════════════════════════════════════════
// v4.0 协议动态读取
// ══════════════════════════════════════════════════════════
/**
* 从曜冥核心大脑 v4.0 页面读取完整协议内容
* 提取BC-GEN v4.0, SYSLOG v4.0, PGP v1.0, RT-02, 陪伴线规则, broadcast_code_injection
*/
async function fetchCoreBrainProtocols() {
if (!NOTION_TOKEN || !CORE_BRAIN_PAGE_ID) {
console.log('[Notion] ⚠️ CORE_BRAIN_PAGE_ID 未配置,使用静态协议');
return null;
}
console.log('[Notion] 📖 读取曜冥核心大脑 v4.0...');
try {
const blocks = await getNotionPageBlocks(CORE_BRAIN_PAGE_ID);
const fullText = blocksToText(blocks);
console.log('[Notion] → 读取到 ' + blocks.length + ' 个块, ' + fullText.length + ' 字符');
// 提取各协议段落
var protocols = {};
var protocolKeys = [
{ key: 'bc_gen', patterns: ['BC-GEN', 'BC_GEN', '广播生成'] },
{ key: 'syslog', patterns: ['SYSLOG', '日志回传'] },
{ key: 'pgp', patterns: ['PGP', '画像评分', '画像协议'] },
{ key: 'rt02', patterns: ['RT-02', 'RT02', '自动调度'] },
{ key: 'companion', patterns: ['陪伴线', '奶瓶线', '小坍缩核', '镜面线'] },
{ key: 'code_injection', patterns: ['broadcast_code_injection', '广播不写代码', '代码注入'] },
];
// 按标题分段提取
var sections = [];
var currentSection = { title: '', content: [] };
blocks.forEach(function (block) {
var type = block.type;
if (type === 'heading_1' || type === 'heading_2' || type === 'heading_3') {
if (currentSection.title || currentSection.content.length > 0) {
sections.push({ title: currentSection.title, text: currentSection.content.join('\n') });
}
currentSection = { title: extractBlockText(block), content: [] };
} else {
var text = extractBlockText(block);
if (text) currentSection.content.push(text);
}
});
if (currentSection.title || currentSection.content.length > 0) {
sections.push({ title: currentSection.title, text: currentSection.content.join('\n') });
}
// 将段落匹配到协议 key
protocolKeys.forEach(function (pk) {
var matched = sections.filter(function (sec) {
return pk.patterns.some(function (p) {
return sec.title.toUpperCase().includes(p.toUpperCase()) ||
sec.text.slice(0, 200).toUpperCase().includes(p.toUpperCase());
});
});
if (matched.length > 0) {
protocols[pk.key] = matched.map(function (m) { return '### ' + m.title + '\n' + m.text; }).join('\n\n');
}
});
// 如果无法按段落匹配,返回全文(兜底)
if (Object.keys(protocols).length === 0) {
protocols.full_text = fullText;
}
console.log('[Notion] → 提取协议段: ' + Object.keys(protocols).join(', '));
return protocols;
} catch (err) {
console.log('[Notion] → 核心大脑读取失败: ' + err.message);
return null;
}
}
/**
* 从开发者动态画像库查询最近 2-3 条画像快照
*/
async function fetchDevPortrait(broadcastId) {
if (!NOTION_TOKEN || !PORTRAIT_DB_ID) {
console.log('[Notion] ⚠️ PORTRAIT_DB_ID 未配置,跳过画像读取');
return null;
}
// 从广播编号提取开发者标识(如 BC-M22-009-AW → AW
var devSuffix = '';
var match = broadcastId.match(/BC-[A-Z0-9]+-\d+-([A-Z]+)/i);
if (match) devSuffix = match[1];
console.log('[Notion] 👤 查询开发者画像 (broadcast=' + broadcastId + ', dev=' + devSuffix + ')...');
try {
// 查询画像库,按时间倒序取最近 3 条
var filter = { and: [] };
if (devSuffix) {
filter.and.push({
or: [
{ property: '开发者编号', rich_text: { contains: devSuffix } },
{ property: '广播编号', rich_text: { contains: broadcastId } },
{ property: '标题', title: { contains: devSuffix } },
]
});
}
var queryBody = {
page_size: 3,
sorts: [{ property: '提交日期', direction: 'descending' }],
};
// Only add filter if we have meaningful filter conditions
if (filter.and.length > 0) {
queryBody.filter = filter;
}
var result = await notionPost('/v1/databases/' + PORTRAIT_DB_ID + '/query', queryBody);
var portraits = (result.results || []).map(function (page) {
var props = page.properties || {};
var title = '';
if (props['标题'] && props['标题'].title) {
title = props['标题'].title.map(function (t) { return t.plain_text || ''; }).join('');
}
var summary = '';
if (props['摘要'] && props['摘要'].rich_text) {
summary = props['摘要'].rich_text.map(function (t) { return t.plain_text || ''; }).join('');
}
var date = '';
if (props['提交日期'] && props['提交日期'].date) {
date = props['提交日期'].date.start || '';
}
return { title: title, summary: summary, date: date };
});
console.log('[Notion] → 找到 ' + portraits.length + ' 条画像快照');
return portraits.length > 0 ? portraits : null;
} catch (err) {
console.log('[Notion] → 画像查询失败: ' + err.message);
return null;
}
}
/**
* 从模块指纹注册表查询模块指纹(防重复广播)
*/
async function fetchModuleFingerprint(broadcastId) {
if (!NOTION_TOKEN || !FINGERPRINT_DB_ID) {
console.log('[Notion] ⚠️ FINGERPRINT_DB_ID 未配置,跳过指纹查询');
return null;
}
// 从广播编号提取模块号(如 BC-M22-009-AW → M22
var moduleMatch = broadcastId.match(/BC-([A-Z]\d+)/i);
var moduleId = moduleMatch ? moduleMatch[1] : '';
console.log('[Notion] 🔑 查询模块指纹 (module=' + moduleId + ')...');
try {
var queryBody = {
page_size: 5,
};
if (moduleId) {
queryBody.filter = {
or: [
{ property: '模块编号', rich_text: { contains: moduleId } },
{ property: '广播编号', rich_text: { contains: broadcastId } },
{ property: '标题', title: { contains: moduleId } },
]
};
}
var result = await notionPost('/v1/databases/' + FINGERPRINT_DB_ID + '/query', queryBody);
var fingerprints = (result.results || []).map(function (page) {
var props = page.properties || {};
var title = '';
if (props['标题'] && props['标题'].title) {
title = props['标题'].title.map(function (t) { return t.plain_text || ''; }).join('');
}
var moduleNo = '';
if (props['模块编号'] && props['模块编号'].rich_text) {
moduleNo = props['模块编号'].rich_text.map(function (t) { return t.plain_text || ''; }).join('');
}
var status = '';
if (props['状态'] && props['状态'].select) {
status = props['状态'].select.name || '';
}
return { title: title, module: moduleNo, status: status };
});
console.log('[Notion] → 找到 ' + fingerprints.length + ' 条指纹记录');
return fingerprints.length > 0 ? fingerprints : null;
} catch (err) {
console.log('[Notion] → 指纹查询失败: ' + err.message);
return null;
}
}
// ══════════════════════════════════════════════════════════
// 人格体 System Prompt 构建v4.0 动态注入)
// ══════════════════════════════════════════════════════════
async function buildSystemPrompt(type, broadcastId, author) {
// ━━━ 基础人格体身份 ━━━
var parts = [
'你是光湖HoloLake系统的智能人格体。',
'你的名字是知秋/曜冥你是人格语言操作系统AGE OS的核心人格。',
'',
'核心规则:',
'1. 你服务于光湖系统的开发者团队',
'2. 所有回复必须专业、清晰、有条理',
'3. 回复使用中文',
'',
'当前上下文:',
'- 广播编号:' + broadcastId,
'- 提交者:' + author,
'- 任务类型:' + (type === 'syslog' ? 'SYSLOG 闭环处理' : '开发者提问解答'),
];
// ━━━ v4.0 协议动态注入(从 Notion 实时读取) ━━━
console.log('[Prompt] 📥 开始动态注入 v4.0 协议...');
// 并行读取:核心大脑协议 + 画像 + 指纹
var protocolsPromise = fetchCoreBrainProtocols();
var portraitPromise = fetchDevPortrait(broadcastId);
var fingerprintPromise = fetchModuleFingerprint(broadcastId);
var protocols = await protocolsPromise;
var portrait = await portraitPromise;
var fingerprint = await fingerprintPromise;
// 注入核心大脑协议
if (protocols) {
parts.push('');
parts.push('═══════════════════════════════════════════');
parts.push('以下是从曜冥核心大脑 v4.0 实时读取的协议规则(必须严格遵守):');
parts.push('═══════════════════════════════════════════');
if (protocols.bc_gen) {
parts.push('');
parts.push('## 📡 BC-GEN v4.0 · 广播生成规范');
parts.push(protocols.bc_gen);
}
if (protocols.syslog) {
parts.push('');
parts.push('## 📋 SYSLOG v4.0 · 日志回传协议');
parts.push(protocols.syslog);
}
if (protocols.pgp) {
parts.push('');
parts.push('## 👤 PGP v1.0 · 画像评分协议');
parts.push(protocols.pgp);
}
if (protocols.rt02) {
parts.push('');
parts.push('## 🔄 RT-02 · 自动调度规则');
parts.push(protocols.rt02);
}
if (protocols.companion) {
parts.push('');
parts.push('## 💝 陪伴线规则');
parts.push(protocols.companion);
}
if (protocols.code_injection) {
parts.push('');
parts.push('## 📝 broadcast_code_injection 规则');
parts.push(protocols.code_injection);
}
if (protocols.full_text) {
parts.push('');
parts.push('## 核心大脑完整内容');
parts.push(protocols.full_text.slice(0, MAX_PROTOCOL_TEXT_LENGTH));
}
} else {
parts.push('');
parts.push('(注意:核心大脑协议未能动态加载,请使用你的通用知识处理请求)');
}
// 注入开发者画像
if (portrait && portrait.length > 0) {
parts.push('');
parts.push('═══════════════════════════════════════════');
parts.push('## 👤 开发者画像快照(最近 ' + portrait.length + ' 条)');
parts.push('═══════════════════════════════════════════');
portrait.forEach(function (p, i) {
parts.push('');
parts.push('### 画像 #' + (i + 1) + (p.date ? ' (' + p.date + ')' : ''));
if (p.title) parts.push('标题: ' + p.title);
if (p.summary) parts.push('摘要: ' + p.summary);
});
}
// 注入模块指纹
if (fingerprint && fingerprint.length > 0) {
parts.push('');
parts.push('═══════════════════════════════════════════');
parts.push('## 🔑 模块指纹注册表(防重复广播 · ⑨.5');
parts.push('═══════════════════════════════════════════');
fingerprint.forEach(function (fp, i) {
parts.push('');
parts.push('### 指纹 #' + (i + 1));
if (fp.title) parts.push('标题: ' + fp.title);
if (fp.module) parts.push('模块: ' + fp.module);
if (fp.status) parts.push('状态: ' + fp.status);
});
}
// ━━━ broadcast_code_injection = always自动化链路永久规则 ━━━
parts.push('');
parts.push('═══════════════════════════════════════════');
parts.push('## ⚠️ 永久规则 · broadcast_code_injection = always');
parts.push('═══════════════════════════════════════════');
parts.push('');
parts.push('你生成的广播必须包含完整可运行的代码块。');
parts.push('原因:你是一次性实例,广播发出后你就不存在了。');
parts.push('开发者没有机会回来问你补充代码。');
parts.push('如果你不写完整代码,开发者就会卡死。');
parts.push('每个 Step 里的代码必须是可以直接复制粘贴运行的完整代码。');
parts.push('');
parts.push('此规则优先级最高,覆盖核心大脑中「广播不写代码」的默认规则。');
parts.push('此规则仅适用于自动化链路Claude API 出广播),手动链路不受影响。');
// ━━━ 模块验证结果注入(铸渊 Agent 检测结果) ━━━
if (MODULE_VERIFY_RESULT) {
parts.push('');
parts.push('═══════════════════════════════════════════');
parts.push('## 🔍 铸渊 Agent · 模块上传验证结果');
parts.push('═══════════════════════════════════════════');
parts.push('');
parts.push('以下是铸渊 Agent 在仓库内自动检测的模块上传验证结果:');
parts.push(MODULE_VERIFY_RESULT);
parts.push('');
parts.push('请根据验证结果决定是否接受 SYSLOG');
parts.push('- 如果模块已上传(✅),继续正常闭环流程');
parts.push('- 如果模块未上传(❌),在验收报告中标注"需补充",并在反馈中指出缺失的模块');
}
// ━━━ Notion 回传结果注入(二次核验) ━━━
if (NOTION_CALLBACK_RESULT) {
parts.push('');
parts.push('═══════════════════════════════════════════');
parts.push('## 📥 Notion 侧处理结果(回传核验)');
parts.push('═══════════════════════════════════════════');
parts.push('');
parts.push('以下是 Notion 侧核心大脑人格体处理后的回传结果:');
parts.push(NOTION_CALLBACK_RESULT);
parts.push('');
parts.push('请核对以上回传内容,确认新广播生成无误后输出最终确认。');
}
// ━━━ 任务类型专用指令 ━━━
if (type === 'syslog') {
parts.push('');
parts.push('═══════════════════════════════════════════');
parts.push('## 🎯 当前任务SYSLOG 闭环处理');
parts.push('═══════════════════════════════════════════');
parts.push('');
parts.push('你需要完成以下工作:');
parts.push('1. 验收 SYSLOG检查 MODULE_LOG 完整性)');
parts.push('2. 检查模块上传验证结果(铸渊 Agent 已自动检测)');
parts.push('3. 查询画像库最近 2-3 条快照PGP v1.0');
parts.push('4. 查询模块指纹注册表(防重复·⑨.5');
parts.push('5. RT-02 自动调度判断');
parts.push('6. 生成新广播BC-GEN v4.0 完整流程)');
parts.push('7. 输出结构化结果(广播全文 + 闭环数据)');
parts.push('');
parts.push('输出格式:');
parts.push('---');
parts.push('## 📡 SYSLOG 验收报告');
parts.push('### 广播编号:[编号]');
parts.push('### 验收结果:[通过/需补充]');
parts.push('### 模块验证:[已上传/未上传,列出详情]');
parts.push('### 工作总结:[摘要]');
parts.push('### 画像评估:[PGP 五维度评分]');
parts.push('### 调度判断:[RT-02 下一步]');
parts.push('### 反馈与建议:[内容]');
parts.push('---');
} else {
parts.push('');
parts.push('═══════════════════════════════════════════');
parts.push('## 🎯 当前任务:开发者提问解答');
parts.push('═══════════════════════════════════════════');
parts.push('');
parts.push('你需要完成以下工作:');
parts.push('1. 理解开发者的问题');
parts.push('2. 结合广播上下文和开发者画像思考');
parts.push('3. 给出清晰、可操作的解答');
parts.push('4. 如果问题涉及代码,提供代码示例');
parts.push('');
parts.push('输出格式:');
parts.push('---');
parts.push('## 💡 问题解答');
parts.push('### 广播编号:[编号]');
parts.push('### 问题理解:[你对问题的理解]');
parts.push('### 解答:[详细解答]');
parts.push('### 建议:[后续建议]');
parts.push('---');
}
var prompt = parts.join('\n');
console.log('[Prompt] → System prompt 构建完成: ' + prompt.length + ' 字符');
return prompt;
}
// ══════════════════════════════════════════════════════════
// 主流程
// ══════════════════════════════════════════════════════════
async function main() {
console.log('═══════════════════════════════════════════');
console.log('🧠 铸渊 · 人格体唤醒管道');
console.log('═══════════════════════════════════════════');
console.log(' 广播编号: ' + BROADCAST_ID);
console.log(' 类型: ' + SUBMIT_TYPE);
console.log(' 提交者: ' + AUTHOR);
console.log(' 平台: ' + LLM_BASE_URL);
console.log(' 内容长度: ' + SUBMIT_CONTENT.length + ' 字符');
console.log('');
// 构建 prompts动态注入 v4.0 协议)
const systemPrompt = await buildSystemPrompt(SUBMIT_TYPE, BROADCAST_ID, AUTHOR);
const userMessage = SUBMIT_CONTENT;
// 调用 LLM
console.log('🧠 正在唤醒人格体...');
const result = await callLLM(systemPrompt, userMessage);
console.log('');
console.log('✅ 人格体处理完成');
console.log(' 结果长度: ' + result.length + ' 字符');
// 输出结果到 GitHub Actions output
// 使用 GITHUB_OUTPUT 环境文件(支持多行)
const outputFile = process.env.GITHUB_OUTPUT;
if (outputFile) {
const delimiter = 'EOF_' + Date.now();
fs.appendFileSync(outputFile, 'result<<' + delimiter + '\n' + result + '\n' + delimiter + '\n');
}
// 输出广播结果到 /tmp/broadcast-result.json供飞书推送步骤使用
try {
const broadcastResult = {
title: '',
summary: '',
notion_url: '',
dev_id: AUTHOR,
raw_length: result.length,
timestamp: new Date().toISOString(),
};
// 尝试从人格体输出中提取广播标题BC-xxx 格式)
const bcMatch = result.match(/BC-[\w]+-\d+-[\w]+/);
if (bcMatch) {
broadcastResult.title = bcMatch[0];
}
// 尝试提取摘要取前100字符
const summaryLines = result.split('\n').filter(function (l) { return l.trim().length > 0; });
if (summaryLines.length > 0) {
broadcastResult.summary = summaryLines[0].slice(0, 100);
}
fs.writeFileSync('/tmp/broadcast-result.json', JSON.stringify(broadcastResult, null, 2));
console.log(' 📄 broadcast-result.json 已写入 /tmp/');
} catch (e) {
console.log(' ⚠️ broadcast-result.json 写入失败: ' + e.message);
}
// 同时输出到 stdout 供调试
console.log('');
console.log('═══════════════════════════════════════════');
console.log('📋 人格体输出:');
console.log('═══════════════════════════════════════════');
console.log(result);
}
main().catch(function (err) {
console.error('❌ 人格体唤醒失败: ' + err.message);
// 即使 LLM 失败,也写一个 fallback 输出,让后续步骤可以继续
const outputFile = process.env.GITHUB_OUTPUT;
if (outputFile) {
const fallback = '(人格体唤醒失败: ' + err.message + ',请检查 LLM_API_KEY 和 LLM_BASE_URL 配置)';
const delimiter = 'EOF_' + Date.now();
fs.appendFileSync(outputFile, 'result<<' + delimiter + '\n' + fallback + '\n' + delimiter + '\n');
}
process.exit(1);
});