// 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); });