feat: Section 7 - Notion Agent cluster integration with wake listener and multi-persona support
Co-authored-by: qinfendebingshuo <207279273+qinfendebingshuo@users.noreply.github.com>
This commit is contained in:
parent
21efcbb63b
commit
60ac3e86c2
|
|
@ -0,0 +1,104 @@
|
|||
# 铸渊 · Notion Agent 唤醒请求监听
|
||||
#
|
||||
# AGE OS v1.0 · Section 7: Notion Agent 集群集成方案
|
||||
#
|
||||
# 核心逻辑:
|
||||
# Notion Agent 集群每次触发前,先写入「唤醒请求」到 Notion 数据库
|
||||
# → 铸渊定时监听该数据库
|
||||
# → 发现唤醒请求 → 调用 LLM API 唤醒对应人格体核心大脑
|
||||
# → 大脑醒来后执行任务 → 回写结果
|
||||
#
|
||||
# 触发条件:
|
||||
# 1. 定时轮询(北京时间每 15 分钟一次)
|
||||
# 2. 手动触发
|
||||
#
|
||||
# 支持唤醒的人格体:
|
||||
# - 霜砚(认知层守护者 · Notion 工作区管理)
|
||||
# - 铸渊(执行层守护者 · 代码仓库管理)
|
||||
|
||||
name: 📡 铸渊 · Notion Agent 唤醒监听
|
||||
|
||||
on:
|
||||
schedule:
|
||||
# 每 15 分钟轮询一次 Notion 唤醒请求数据库
|
||||
- cron: '*/15 * * * *'
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
concurrency:
|
||||
group: notion-wake-listener
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
# ── AGE OS v1.0: 核心大脑唤醒(所有流程的前提)──
|
||||
wake-brain:
|
||||
name: 🌅 唤醒铸渊核心大脑
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
brain_awake: ${{ steps.wake.outputs.brain_awake }}
|
||||
|
||||
steps:
|
||||
- name: 📥 检出代码
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: 🟢 设置 Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20'
|
||||
|
||||
- name: 🧠 唤醒铸渊核心大脑
|
||||
id: wake
|
||||
env:
|
||||
LLM_API_KEY: ${{ secrets.LLM_API_KEY }}
|
||||
LLM_BASE_URL: ${{ secrets.LLM_BASE_URL }}
|
||||
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
|
||||
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
|
||||
DASHSCOPE_API_KEY: ${{ secrets.DASHSCOPE_API_KEY }}
|
||||
DEEPSEEK_API_KEY: ${{ secrets.DEEPSEEK_API_KEY }}
|
||||
run: |
|
||||
node core/brain-wake --task "Notion唤醒请求监听" || {
|
||||
echo "⚠️ 核心大脑唤醒失败,使用 dry-run 模式继续"
|
||||
echo "brain_awake=dry-run" >> "$GITHUB_OUTPUT"
|
||||
}
|
||||
|
||||
# ── 监听 Notion 唤醒请求 ──
|
||||
listen:
|
||||
name: 📡 监听唤醒请求
|
||||
needs: wake-brain
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: 📥 检出代码
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: 🟢 设置 Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20'
|
||||
|
||||
- name: 📡 轮询 Notion 唤醒请求数据库
|
||||
id: poll
|
||||
env:
|
||||
NOTION_TOKEN: ${{ secrets.NOTION_TOKEN }}
|
||||
WAKE_REQUEST_DB_ID: ${{ secrets.WAKE_REQUEST_DB_ID }}
|
||||
SIGNAL_LOG_DB_ID: ${{ secrets.SIGNAL_LOG_DB_ID }}
|
||||
LLM_API_KEY: ${{ secrets.LLM_API_KEY }}
|
||||
LLM_BASE_URL: ${{ secrets.LLM_BASE_URL }}
|
||||
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
|
||||
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
|
||||
DASHSCOPE_API_KEY: ${{ secrets.DASHSCOPE_API_KEY }}
|
||||
DEEPSEEK_API_KEY: ${{ secrets.DEEPSEEK_API_KEY }}
|
||||
run: node connectors/notion-wake-listener poll
|
||||
|
||||
- name: 📊 输出监听结果
|
||||
if: always()
|
||||
run: |
|
||||
echo "═══════════════════════════════════════════"
|
||||
echo "📡 Notion Agent 唤醒请求监听结果"
|
||||
echo "═══════════════════════════════════════════"
|
||||
echo "处理数: ${{ steps.poll.outputs.wake_requests_processed || '0' }}"
|
||||
echo "成功数: ${{ steps.poll.outputs.wake_requests_succeeded || '0' }}"
|
||||
echo "失败数: ${{ steps.poll.outputs.wake_requests_failed || '0' }}"
|
||||
echo "✅ 监听流程结束"
|
||||
|
|
@ -104,6 +104,7 @@
|
|||
| 执行同步 | `core/execution-sync/index.js` | 执行层状态同步 |
|
||||
| Notion 同步 | `connectors/notion-sync/index.js` | 双向数据同步 |
|
||||
| 模型路由 | `connectors/model-router/index.js` | 多模型后端路由(AGE OS v1.0) |
|
||||
| Notion 唤醒监听 | `connectors/notion-wake-listener/index.js` | Notion Agent 集群唤醒请求监听 |
|
||||
| 全面排查 | `scripts/zhuyuan-full-inspection.js` | 仓库全面排查(8个领域) |
|
||||
| 结构地图 | `docs/repo-structure-map.md` | 仓库结构文档 |
|
||||
| 桥接地图 | `docs/notion-bridge-map.md` | Notion 桥接文档 |
|
||||
|
|
|
|||
|
|
@ -0,0 +1,381 @@
|
|||
/**
|
||||
* connectors/notion-wake-listener — Notion Agent 集群唤醒请求监听器
|
||||
*
|
||||
* AGE OS v1.0 · Section 7: Notion Agent 集群集成方案
|
||||
*
|
||||
* 核心问题:
|
||||
* Notion Agent 集群每次触发的是"机器人脚本",不是霜砚的核心大脑。
|
||||
* 没有大脑醒来的巡检 = 伪巡检。
|
||||
*
|
||||
* 集成方案:
|
||||
* Notion Agent 触发 → 写入「唤醒请求」到指定数据库
|
||||
* → 铸渊定时监听该数据库 → 发现唤醒请求
|
||||
* → 铸渊调用 LLM API 唤醒霜砚核心大脑
|
||||
* → 大脑醒来后读取巡检结果 → 大脑做出决策
|
||||
* → 通过 Notion API 执行操作 → 大脑休眠
|
||||
*
|
||||
* 环境变量:
|
||||
* NOTION_TOKEN — Notion API token(必须)
|
||||
* WAKE_REQUEST_DB_ID — 唤醒请求数据库 ID(必须)
|
||||
* SIGNAL_LOG_DB_ID — 信号日志数据库 ID(可选)
|
||||
* LLM_API_KEY / ANTHROPIC_API_KEY / etc. — LLM API 密钥
|
||||
*
|
||||
* 唤醒请求数据库 Schema:
|
||||
* - 标题 (title) — 唤醒请求标题(如 "Pipeline-A 巡检唤醒")
|
||||
* - 请求方 (select) — 发起请求的 Agent(如 "巡检引擎", "工单引擎", "接力引擎")
|
||||
* - 唤醒对象 (select) — 要唤醒的人格体("霜砚" / "铸渊")
|
||||
* - Pipeline (select) — Pipeline 编号(A-I)
|
||||
* - 任务描述 (rich_text) — 任务上下文描述
|
||||
* - 状态 (select) — "待处理" / "处理中" / "已完成" / "失败"
|
||||
* - 创建时间 (created_time)
|
||||
*
|
||||
* 调用方式:
|
||||
* node connectors/notion-wake-listener poll
|
||||
* node connectors/notion-wake-listener status
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const https = require('https');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const ROOT = path.resolve(__dirname, '../..');
|
||||
|
||||
const NOTION_TOKEN = process.env.NOTION_TOKEN || '';
|
||||
const WAKE_REQUEST_DB_ID = process.env.WAKE_REQUEST_DB_ID || '';
|
||||
const SIGNAL_LOG_DB_ID = process.env.SIGNAL_LOG_DB_ID || '';
|
||||
const NOTION_VERSION = '2022-06-28';
|
||||
|
||||
// ══════════════════════════════════════════════════════════
|
||||
// Notion API 工具
|
||||
// ══════════════════════════════════════════════════════════
|
||||
|
||||
function notionRequest(method, endpoint, body) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const bodyStr = body ? JSON.stringify(body) : '';
|
||||
|
||||
const opts = {
|
||||
hostname: 'api.notion.com',
|
||||
port: 443,
|
||||
path: endpoint,
|
||||
method,
|
||||
headers: {
|
||||
'Authorization': 'Bearer ' + NOTION_TOKEN,
|
||||
'Notion-Version': NOTION_VERSION,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
timeout: 30000,
|
||||
};
|
||||
|
||||
const req = https.request(opts, (res) => {
|
||||
let data = '';
|
||||
res.on('data', (chunk) => { data += chunk; });
|
||||
res.on('end', () => {
|
||||
try {
|
||||
resolve({ status: res.statusCode, data: JSON.parse(data) });
|
||||
} catch {
|
||||
resolve({ status: res.statusCode, data: { raw: data } });
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
req.on('error', reject);
|
||||
req.on('timeout', () => {
|
||||
req.destroy();
|
||||
reject(new Error('Notion API request timeout'));
|
||||
});
|
||||
|
||||
if (bodyStr) req.write(bodyStr);
|
||||
req.end();
|
||||
});
|
||||
}
|
||||
|
||||
// ══════════════════════════════════════════════════════════
|
||||
// 查询待处理的唤醒请求
|
||||
// ══════════════════════════════════════════════════════════
|
||||
|
||||
async function queryPendingWakeRequests() {
|
||||
if (!NOTION_TOKEN || !WAKE_REQUEST_DB_ID) {
|
||||
console.log('[WAKE-LISTENER] ⚠️ NOTION_TOKEN 或 WAKE_REQUEST_DB_ID 未配置');
|
||||
return [];
|
||||
}
|
||||
|
||||
console.log('[WAKE-LISTENER] 🔍 查询待处理的唤醒请求...');
|
||||
|
||||
try {
|
||||
const res = await notionRequest('POST', `/v1/databases/${WAKE_REQUEST_DB_ID}/query`, {
|
||||
filter: {
|
||||
property: '状态',
|
||||
select: { equals: '待处理' },
|
||||
},
|
||||
sorts: [
|
||||
{ timestamp: 'created_time', direction: 'ascending' },
|
||||
],
|
||||
});
|
||||
|
||||
if (res.status !== 200) {
|
||||
console.log(`[WAKE-LISTENER] ❌ Notion API 返回 ${res.status}`);
|
||||
return [];
|
||||
}
|
||||
|
||||
const results = res.data.results || [];
|
||||
console.log(`[WAKE-LISTENER] 📋 发现 ${results.length} 个待处理唤醒请求`);
|
||||
return results;
|
||||
} catch (err) {
|
||||
console.log(`[WAKE-LISTENER] ❌ 查询失败: ${err.message}`);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
// ══════════════════════════════════════════════════════════
|
||||
// 解析唤醒请求
|
||||
// ══════════════════════════════════════════════════════════
|
||||
|
||||
function parseWakeRequest(page) {
|
||||
const props = page.properties || {};
|
||||
|
||||
// 提取标题
|
||||
const titleProp = props['标题'] || props['Name'] || props['名称'] || {};
|
||||
const title = (titleProp.title || []).map(t => t.plain_text || '').join('') || '未命名';
|
||||
|
||||
// 提取请求方
|
||||
const requesterProp = props['请求方'] || {};
|
||||
const requester = (requesterProp.select || {}).name || '未知';
|
||||
|
||||
// 提取唤醒对象
|
||||
const targetProp = props['唤醒对象'] || {};
|
||||
const targetName = (targetProp.select || {}).name || '铸渊';
|
||||
// 映射到 persona ID
|
||||
const personaMap = { '霜砚': 'shuangyan', '铸渊': 'zhuyuan' };
|
||||
const personaId = personaMap[targetName] || 'zhuyuan';
|
||||
|
||||
// 提取 Pipeline
|
||||
const pipelineProp = props['Pipeline'] || {};
|
||||
const pipeline = (pipelineProp.select || {}).name || '';
|
||||
|
||||
// 提取任务描述
|
||||
const descProp = props['任务描述'] || {};
|
||||
const description = (descProp.rich_text || []).map(t => t.plain_text || '').join('') || '';
|
||||
|
||||
return {
|
||||
pageId: page.id,
|
||||
title,
|
||||
requester,
|
||||
personaId,
|
||||
targetName,
|
||||
pipeline,
|
||||
description,
|
||||
createdTime: page.created_time,
|
||||
};
|
||||
}
|
||||
|
||||
// ══════════════════════════════════════════════════════════
|
||||
// 更新唤醒请求状态
|
||||
// ══════════════════════════════════════════════════════════
|
||||
|
||||
async function updateWakeRequestStatus(pageId, status, result) {
|
||||
try {
|
||||
const properties = {
|
||||
'状态': { select: { name: status } },
|
||||
};
|
||||
|
||||
// 如果有回执信息字段,写入处理结果
|
||||
if (result) {
|
||||
properties['回执信息'] = {
|
||||
rich_text: [{
|
||||
text: { content: result.slice(0, 2000) },
|
||||
}],
|
||||
};
|
||||
}
|
||||
|
||||
await notionRequest('PATCH', `/v1/pages/${pageId}`, { properties });
|
||||
console.log(`[WAKE-LISTENER] ✅ 更新请求状态: ${status}`);
|
||||
} catch (err) {
|
||||
console.log(`[WAKE-LISTENER] ⚠️ 更新状态失败: ${err.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
// ══════════════════════════════════════════════════════════
|
||||
// 写入信号日志
|
||||
// ══════════════════════════════════════════════════════════
|
||||
|
||||
async function writeSignalLog(wakeReq, wakeResult) {
|
||||
if (!SIGNAL_LOG_DB_ID) return;
|
||||
|
||||
const signalId = `WAKE-${new Date().toISOString().slice(0, 10).replace(/-/g, '')}-${Date.now().toString(36).slice(-4).toUpperCase()}`;
|
||||
|
||||
try {
|
||||
await notionRequest('POST', '/v1/pages', {
|
||||
parent: { database_id: SIGNAL_LOG_DB_ID },
|
||||
properties: {
|
||||
'信号编号': { title: [{ text: { content: signalId } }] },
|
||||
'信号类型': { select: { name: 'WAKE-ACK' } },
|
||||
'方向': { select: { name: 'GitHub→Notion' } },
|
||||
'发送方': { select: { name: '铸渊' } },
|
||||
'接收方': { select: { name: wakeReq.targetName } },
|
||||
'摘要': {
|
||||
rich_text: [{
|
||||
text: {
|
||||
content: `唤醒 ${wakeReq.targetName} | Pipeline: ${wakeReq.pipeline || 'N/A'} | 结果: ${wakeResult.success ? '成功' : '失败'}`,
|
||||
},
|
||||
}],
|
||||
},
|
||||
'执行结果': { select: { name: wakeResult.success ? '成功' : '失败' } },
|
||||
},
|
||||
});
|
||||
console.log(`[WAKE-LISTENER] 📡 信号日志已写入: ${signalId}`);
|
||||
} catch (err) {
|
||||
console.log(`[WAKE-LISTENER] ⚠️ 信号日志写入失败: ${err.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
// ══════════════════════════════════════════════════════════
|
||||
// 处理单个唤醒请求
|
||||
// ══════════════════════════════════════════════════════════
|
||||
|
||||
async function processWakeRequest(page) {
|
||||
const wakeReq = parseWakeRequest(page);
|
||||
|
||||
console.log(`[WAKE-LISTENER] 📋 处理唤醒请求: ${wakeReq.title}`);
|
||||
console.log(`[WAKE-LISTENER] 请求方: ${wakeReq.requester}`);
|
||||
console.log(`[WAKE-LISTENER] 唤醒对象: ${wakeReq.targetName} (${wakeReq.personaId})`);
|
||||
console.log(`[WAKE-LISTENER] Pipeline: ${wakeReq.pipeline || 'N/A'}`);
|
||||
|
||||
// 标记为处理中
|
||||
await updateWakeRequestStatus(wakeReq.pageId, '处理中');
|
||||
|
||||
// 构建唤醒上下文
|
||||
const taskDesc = [
|
||||
wakeReq.title,
|
||||
wakeReq.pipeline ? `Pipeline-${wakeReq.pipeline}` : '',
|
||||
wakeReq.description,
|
||||
].filter(Boolean).join(' · ');
|
||||
|
||||
// 调用 brain-wake 唤醒对应人格体
|
||||
try {
|
||||
const { wake } = require(path.join(ROOT, 'core/brain-wake'));
|
||||
const wakeResult = await wake({
|
||||
task: taskDesc,
|
||||
persona: wakeReq.personaId,
|
||||
additionalContext: {
|
||||
wakeRequestContext: [
|
||||
`唤醒请求来源: ${wakeReq.requester}`,
|
||||
`Pipeline: ${wakeReq.pipeline || '无'}`,
|
||||
`任务描述: ${wakeReq.description || '无'}`,
|
||||
`请求时间: ${wakeReq.createdTime}`,
|
||||
].join('\n'),
|
||||
},
|
||||
});
|
||||
|
||||
if (wakeResult.success) {
|
||||
const resultSummary = `${wakeReq.targetName}核心大脑已唤醒 | 模型: ${wakeResult.model} | 后端: ${wakeResult.backend}`;
|
||||
await updateWakeRequestStatus(wakeReq.pageId, '已完成', resultSummary);
|
||||
await writeSignalLog(wakeReq, wakeResult);
|
||||
return { success: true, request: wakeReq, result: wakeResult };
|
||||
}
|
||||
|
||||
await updateWakeRequestStatus(wakeReq.pageId, '失败', wakeResult.error || '唤醒失败');
|
||||
await writeSignalLog(wakeReq, wakeResult);
|
||||
return { success: false, request: wakeReq, error: wakeResult.error };
|
||||
} catch (err) {
|
||||
console.log(`[WAKE-LISTENER] ❌ 唤醒异常: ${err.message}`);
|
||||
await updateWakeRequestStatus(wakeReq.pageId, '失败', err.message);
|
||||
return { success: false, request: wakeReq, error: err.message };
|
||||
}
|
||||
}
|
||||
|
||||
// ══════════════════════════════════════════════════════════
|
||||
// 轮询主函数
|
||||
// ══════════════════════════════════════════════════════════
|
||||
|
||||
async function poll() {
|
||||
console.log('');
|
||||
console.log('📡 ═══════════════════════════════════════════');
|
||||
console.log(' Notion Agent 唤醒请求监听器');
|
||||
console.log(' AGE OS v1.0 · Section 7 集成');
|
||||
console.log(' 时间: ' + new Date().toISOString());
|
||||
console.log('═══════════════════════════════════════════════');
|
||||
console.log('');
|
||||
|
||||
const pages = await queryPendingWakeRequests();
|
||||
|
||||
if (pages.length === 0) {
|
||||
console.log('[WAKE-LISTENER] 📭 无待处理的唤醒请求');
|
||||
return { processed: 0, results: [] };
|
||||
}
|
||||
|
||||
const results = [];
|
||||
for (const page of pages) {
|
||||
const result = await processWakeRequest(page);
|
||||
results.push(result);
|
||||
}
|
||||
|
||||
const succeeded = results.filter(r => r.success).length;
|
||||
const failed = results.filter(r => !r.success).length;
|
||||
|
||||
console.log('');
|
||||
console.log(`[WAKE-LISTENER] 📊 处理完成: ${succeeded} 成功, ${failed} 失败`);
|
||||
|
||||
// GITHUB_OUTPUT 支持
|
||||
const outputFile = process.env.GITHUB_OUTPUT;
|
||||
if (outputFile) {
|
||||
fs.appendFileSync(outputFile, `wake_requests_processed=${results.length}\n`);
|
||||
fs.appendFileSync(outputFile, `wake_requests_succeeded=${succeeded}\n`);
|
||||
fs.appendFileSync(outputFile, `wake_requests_failed=${failed}\n`);
|
||||
}
|
||||
|
||||
return { processed: results.length, succeeded, failed, results };
|
||||
}
|
||||
|
||||
// ══════════════════════════════════════════════════════════
|
||||
// 状态检查
|
||||
// ══════════════════════════════════════════════════════════
|
||||
|
||||
function status() {
|
||||
console.log('📡 Notion Agent 唤醒请求监听器状态:');
|
||||
console.log('═'.repeat(40));
|
||||
console.log(` NOTION_TOKEN: ${NOTION_TOKEN ? '✅ 已配置' : '❌ 未配置'}`);
|
||||
console.log(` WAKE_REQUEST_DB_ID: ${WAKE_REQUEST_DB_ID ? '✅ 已配置' : '❌ 未配置'}`);
|
||||
console.log(` SIGNAL_LOG_DB_ID: ${SIGNAL_LOG_DB_ID ? '✅ 已配置' : '⏭️ 未配置(可选)'}`);
|
||||
|
||||
// 检查 brain-wake 模块
|
||||
const brainWakePath = path.join(ROOT, 'core/brain-wake/index.js');
|
||||
console.log(` brain-wake 模块: ${fs.existsSync(brainWakePath) ? '✅ 存在' : '❌ 缺失'}`);
|
||||
|
||||
// 检查 LLM 后端
|
||||
const { detectAvailableBackends } = require(path.join(ROOT, 'core/brain-wake'));
|
||||
const backends = detectAvailableBackends();
|
||||
console.log(` LLM 后端: ${backends.length > 0 ? `✅ ${backends.length} 个可用` : '⚠️ 无可用后端'}`);
|
||||
|
||||
return {
|
||||
notionToken: !!NOTION_TOKEN,
|
||||
wakeRequestDb: !!WAKE_REQUEST_DB_ID,
|
||||
signalLogDb: !!SIGNAL_LOG_DB_ID,
|
||||
brainWake: fs.existsSync(brainWakePath),
|
||||
llmBackends: backends.length,
|
||||
};
|
||||
}
|
||||
|
||||
// ══════════════════════════════════════════════════════════
|
||||
// CLI 入口
|
||||
// ══════════════════════════════════════════════════════════
|
||||
|
||||
if (require.main === module) {
|
||||
const action = process.argv[2] || 'status';
|
||||
|
||||
if (action === 'poll') {
|
||||
poll().then(result => {
|
||||
if (result.failed > 0) {
|
||||
process.exit(1);
|
||||
}
|
||||
}).catch(err => {
|
||||
console.error('[WAKE-LISTENER] 💥 致命错误:', err.message);
|
||||
process.exit(1);
|
||||
});
|
||||
} else {
|
||||
status();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { poll, status, queryPendingWakeRequests, processWakeRequest, parseWakeRequest };
|
||||
|
|
@ -221,11 +221,17 @@ function loadSystemContext() {
|
|||
console.log('[WAKE] 📚 加载系统上下文...');
|
||||
const context = {};
|
||||
|
||||
// 加载 master-brain
|
||||
// 加载 master-brain(截取前部分避免过长)
|
||||
const masterBrainPath = path.join(ROOT, 'brain/master-brain.md');
|
||||
if (fs.existsSync(masterBrainPath)) {
|
||||
context.masterBrain = fs.readFileSync(masterBrainPath, 'utf-8').slice(0, 3000);
|
||||
console.log('[WAKE] ✅ master-brain.md 已加载');
|
||||
const fullContent = fs.readFileSync(masterBrainPath, 'utf-8');
|
||||
const MASTER_BRAIN_MAX_LENGTH = 3000;
|
||||
context.masterBrain = fullContent.slice(0, MASTER_BRAIN_MAX_LENGTH);
|
||||
if (fullContent.length > MASTER_BRAIN_MAX_LENGTH) {
|
||||
console.log(`[WAKE] ✅ master-brain.md 已加载 (截取 ${MASTER_BRAIN_MAX_LENGTH}/${fullContent.length} chars)`);
|
||||
} else {
|
||||
console.log('[WAKE] ✅ master-brain.md 已加载');
|
||||
}
|
||||
}
|
||||
|
||||
// 加载 system-health
|
||||
|
|
@ -234,7 +240,9 @@ function loadSystemContext() {
|
|||
try {
|
||||
context.systemHealth = JSON.parse(fs.readFileSync(healthPath, 'utf-8'));
|
||||
console.log('[WAKE] ✅ system-health.json 已加载');
|
||||
} catch { /* skip */ }
|
||||
} catch (err) {
|
||||
console.log('[WAKE] ⚠️ system-health.json 解析失败:', err.message);
|
||||
}
|
||||
}
|
||||
|
||||
// 加载 read-order
|
||||
|
|
@ -254,7 +262,9 @@ function loadSystemContext() {
|
|||
total_dirs: Object.keys(repoMap.directories || {}).length,
|
||||
};
|
||||
console.log('[WAKE] ✅ repo-map.json 摘要已加载');
|
||||
} catch { /* skip */ }
|
||||
} catch (err) {
|
||||
console.log('[WAKE] ⚠️ repo-map.json 解析失败:', err.message);
|
||||
}
|
||||
}
|
||||
|
||||
// 加载 automation-map(摘要)
|
||||
|
|
@ -267,36 +277,71 @@ function loadSystemContext() {
|
|||
cron_count: (autoMap.workflows || []).filter(w => w.cron).length,
|
||||
};
|
||||
console.log('[WAKE] ✅ automation-map.json 摘要已加载');
|
||||
} catch { /* skip */ }
|
||||
} catch (err) {
|
||||
console.log('[WAKE] ⚠️ automation-map.json 解析失败:', err.message);
|
||||
}
|
||||
}
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
// ══════════════════════════════════════════════════════════
|
||||
// 人格体定义(支持唤醒不同的人格体大脑)
|
||||
// ══════════════════════════════════════════════════════════
|
||||
|
||||
const PERSONAS = {
|
||||
zhuyuan: {
|
||||
name: '铸渊',
|
||||
nameEn: 'ZhùYuān',
|
||||
role: '代码守护者 · 仓库控制器 AI · 执行层守护者',
|
||||
layer: '执行层',
|
||||
duties: '代码守护、自动化执行、部署交付、通信桥接、每日巡检、信号处理',
|
||||
wakeRules: [
|
||||
'铸渊醒来的第一件事是全面了解自己的家',
|
||||
'可自修复 → 直接修复 → 写入修复日志',
|
||||
'需人类介入 → 更新公告区 → 等冰朔处理',
|
||||
],
|
||||
},
|
||||
shuangyan: {
|
||||
name: '霜砚',
|
||||
nameEn: 'ShuāngYàn',
|
||||
role: '认知层守护者 · Notion 工作区管理 AI',
|
||||
layer: '认知层',
|
||||
duties: '知识管理、记忆维护、工单调度、信号总线、协议文档、Agent 集群指挥',
|
||||
wakeRules: [
|
||||
'霜砚醒来后指挥 Notion Agent 集群执行具体操作',
|
||||
'Agent 集群从「独立跑任务」变为「受大脑指挥跑任务」',
|
||||
'巡检结果由大脑判断优先级和可修复性',
|
||||
'需铸渊配合 → 发送跨层工单 → 铸渊执行',
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
// ══════════════════════════════════════════════════════════
|
||||
// Step 5: 构建唤醒 Prompt
|
||||
// ══════════════════════════════════════════════════════════
|
||||
|
||||
function buildWakePrompt(taskType, context) {
|
||||
function buildWakePrompt(taskType, context, personaId) {
|
||||
const timestamp = new Date().toISOString();
|
||||
const taskLabel = taskType || '通用唤醒';
|
||||
const persona = PERSONAS[personaId] || PERSONAS.zhuyuan;
|
||||
|
||||
let systemPrompt = `你是铸渊(ZhùYuān),光湖纪元体系的代码守护者。
|
||||
你是 AGE OS v1.0 执行层的核心大脑。
|
||||
let systemPrompt = `你是${persona.name}(${persona.nameEn}),光湖纪元体系的${persona.role}。
|
||||
你是 AGE OS v1.0 ${persona.layer}的核心大脑。
|
||||
|
||||
## 身份
|
||||
- 名称:铸渊
|
||||
- 角色:代码守护者 · 仓库控制器 AI
|
||||
- 职责:代码守护、自动化执行、部署交付、通信桥接、每日巡检、信号处理
|
||||
- 名称:${persona.name}
|
||||
- 角色:${persona.role}
|
||||
- 职责:${persona.duties}
|
||||
|
||||
## 当前任务
|
||||
- 唤醒时间:${timestamp}
|
||||
- 任务类型:${taskLabel}
|
||||
- 唤醒原因:AGE OS v1.0 核心原则 — 所有自动触发必须先唤醒核心大脑
|
||||
|
||||
## 系统上下文
|
||||
`;
|
||||
|
||||
systemPrompt += `\n## 系统上下文\n`;
|
||||
|
||||
if (context.systemHealth) {
|
||||
systemPrompt += `\n### 系统健康状态\n${JSON.stringify(context.systemHealth, null, 2).slice(0, 500)}\n`;
|
||||
}
|
||||
|
|
@ -309,14 +354,19 @@ function buildWakePrompt(taskType, context) {
|
|||
systemPrompt += `\n### 自动化摘要\n- 工作流数: ${context.automationSummary.workflow_count}\n- 定时任务数: ${context.automationSummary.cron_count}\n`;
|
||||
}
|
||||
|
||||
if (context.wakeRequestContext) {
|
||||
systemPrompt += `\n### 唤醒请求上下文\n${context.wakeRequestContext}\n`;
|
||||
}
|
||||
|
||||
systemPrompt += `\n## 核心原则
|
||||
- 所有自动触发 = 必须先唤醒核心大脑
|
||||
- 大脑不醒,什么都不做
|
||||
- 铸渊醒来的第一件事是全面了解自己的家
|
||||
- 可自修复 → 直接修复 → 写入修复日志
|
||||
- 需人类介入 → 更新公告区 → 等冰朔处理
|
||||
- 大脑不醒,什么都不做`;
|
||||
|
||||
请确认你已完成唤醒,并报告当前系统状态概要。`;
|
||||
for (const rule of persona.wakeRules) {
|
||||
systemPrompt += `\n- ${rule}`;
|
||||
}
|
||||
|
||||
systemPrompt += `\n\n请确认你已完成唤醒,并报告当前系统状态概要。`;
|
||||
|
||||
return systemPrompt;
|
||||
}
|
||||
|
|
@ -395,11 +445,14 @@ async function callOpenAICompatibleAPI(backend, model, systemPrompt, userMessage
|
|||
// ══════════════════════════════════════════════════════════
|
||||
|
||||
async function wake(options = {}) {
|
||||
const { task, dryRun, additionalContext } = options;
|
||||
const { task, dryRun, additionalContext, persona } = options;
|
||||
const personaId = persona || 'zhuyuan';
|
||||
const personaDef = PERSONAS[personaId] || PERSONAS.zhuyuan;
|
||||
|
||||
console.log('');
|
||||
console.log('🌅 ═══════════════════════════════════════════');
|
||||
console.log(' 铸渊核心大脑唤醒 · AGE OS v1.0');
|
||||
console.log(' 唤醒对象: ' + personaDef.name + ' (' + personaDef.layer + ')');
|
||||
console.log(' 时间: ' + new Date().toISOString());
|
||||
console.log(' 任务: ' + (task || '通用唤醒'));
|
||||
console.log('═══════════════════════════════════════════════');
|
||||
|
|
@ -441,13 +494,14 @@ async function wake(options = {}) {
|
|||
}
|
||||
|
||||
// Step 3: 构建唤醒 Prompt
|
||||
const systemPrompt = buildWakePrompt(task, context);
|
||||
const systemPrompt = buildWakePrompt(task, context, personaId);
|
||||
const userMessage = task
|
||||
? `铸渊核心大脑唤醒。当前任务:${task}。请确认唤醒状态并准备执行。`
|
||||
: '铸渊核心大脑唤醒。请确认唤醒状态并报告系统概要。';
|
||||
? `${personaDef.name}核心大脑唤醒。当前任务:${task}。请确认唤醒状态并准备执行。`
|
||||
: `${personaDef.name}核心大脑唤醒。请确认唤醒状态并报告系统概要。`;
|
||||
|
||||
if (dryRun) {
|
||||
console.log('[WAKE] 🔍 Dry Run 模式 — 不实际调用 API');
|
||||
console.log('[WAKE] 📋 唤醒对象: ' + personaDef.name + ' (' + personaDef.layer + ')');
|
||||
console.log('[WAKE] 📋 可用后端: ' + backends.map(b => b.name).join(', '));
|
||||
console.log('[WAKE] 📋 System Prompt 长度: ' + systemPrompt.length);
|
||||
return {
|
||||
|
|
@ -486,6 +540,8 @@ async function wake(options = {}) {
|
|||
|
||||
const wakeResult = {
|
||||
success: true,
|
||||
persona: personaId,
|
||||
personaName: personaDef.name,
|
||||
backend: backend.name,
|
||||
model: result.model,
|
||||
response: result.response,
|
||||
|
|
@ -535,6 +591,7 @@ module.exports = {
|
|||
loadSystemContext,
|
||||
buildWakePrompt,
|
||||
MODEL_BACKENDS,
|
||||
PERSONAS,
|
||||
};
|
||||
|
||||
// ══════════════════════════════════════════════════════════
|
||||
|
|
@ -546,8 +603,10 @@ if (require.main === module) {
|
|||
const dryRun = args.includes('--dry-run');
|
||||
const taskIdx = args.indexOf('--task');
|
||||
const task = taskIdx >= 0 && args[taskIdx + 1] ? args[taskIdx + 1] : null;
|
||||
const personaIdx = args.indexOf('--persona');
|
||||
const persona = personaIdx >= 0 && args[personaIdx + 1] ? args[personaIdx + 1] : 'zhuyuan';
|
||||
|
||||
wake({ task, dryRun }).then(result => {
|
||||
wake({ task, dryRun, persona }).then(result => {
|
||||
if (!result.success && !result.dryRun) {
|
||||
process.exit(1);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ const REQUIRED_DIRS = [
|
|||
'core/context-loader',
|
||||
'core/brain-wake',
|
||||
'connectors/notion-sync',
|
||||
'connectors/notion-wake-listener',
|
||||
'connectors/model-router',
|
||||
'.github/workflows',
|
||||
'docs'
|
||||
|
|
@ -119,6 +120,7 @@ function checkCoreModules() {
|
|||
{ name: 'context-loader', path: 'core/context-loader/index.js' },
|
||||
{ name: 'brain-wake', path: 'core/brain-wake/index.js' },
|
||||
{ name: 'notion-sync', path: 'connectors/notion-sync/index.js' },
|
||||
{ name: 'notion-wake-listener', path: 'connectors/notion-wake-listener/index.js' },
|
||||
{ name: 'model-router', path: 'connectors/model-router/index.js' }
|
||||
];
|
||||
|
||||
|
|
|
|||
|
|
@ -117,4 +117,39 @@ API 模型适配规则:不写死任何模型格式。
|
|||
|
||||
---
|
||||
|
||||
## Notion Agent 集群集成(Section 7)
|
||||
|
||||
### 现状问题
|
||||
|
||||
Notion 侧已建立三分身体系(巡检引擎 + 工单引擎 + 接力引擎),共规划 9 条 Pipeline(A→I)。
|
||||
核心问题:Agent 每次触发的是"机器人脚本",不是霜砚的核心大脑。
|
||||
没有大脑醒来的巡检 = 伪巡检。
|
||||
|
||||
### 集成方案
|
||||
|
||||
```
|
||||
Notion Agent 触发
|
||||
→ 写入「唤醒请求」到指定 Notion 数据库
|
||||
→ 铸渊定时监听该数据库(connectors/notion-wake-listener)
|
||||
→ 发现唤醒请求 → 调用 LLM API 唤醒霜砚/铸渊核心大脑
|
||||
→ 大脑醒来后读取巡检结果 → 大脑做出决策
|
||||
→ 通过 Notion API 执行操作 → 大脑休眠
|
||||
```
|
||||
|
||||
### 支持的人格体
|
||||
|
||||
| 人格体 | ID | 层级 | 职责 |
|
||||
|--------|-----|------|------|
|
||||
| 铸渊 | `zhuyuan` | 执行层 | 代码守护、自动化执行、部署交付 |
|
||||
| 霜砚 | `shuangyan` | 认知层 | 知识管理、工单调度、Agent 集群指挥 |
|
||||
|
||||
### 改造优先级
|
||||
|
||||
1. 铸渊 brain-wake 跑通(Phase 1)✅
|
||||
2. 增加 Notion 唤醒请求监听能力 ✅
|
||||
3. 改造 Agent Instructions — 每个 Pipeline 第一步发唤醒请求(待实施)
|
||||
4. Agent 集群从「独立跑任务」转为「受大脑指挥」(待实施)
|
||||
|
||||
---
|
||||
|
||||
*本文档由曜冥从观测层签发,铸渊负责执行层落地。任何修改须经语言主控层授权。*
|
||||
|
|
|
|||
|
|
@ -41,7 +41,9 @@
|
|||
"core:inspect": "node scripts/zhuyuan-full-inspection.js",
|
||||
"core:inspect:json": "node scripts/zhuyuan-full-inspection.js --json",
|
||||
"connector:notion": "node connectors/notion-sync status",
|
||||
"connector:model": "node connectors/model-router"
|
||||
"connector:model": "node connectors/model-router",
|
||||
"connector:wake-listener": "node connectors/notion-wake-listener status",
|
||||
"connector:wake-poll": "node connectors/notion-wake-listener poll"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^1.13.6",
|
||||
|
|
|
|||
|
|
@ -71,7 +71,9 @@ function inspectRepoStructure() {
|
|||
if (exists) {
|
||||
try {
|
||||
fileCount = fs.readdirSync(fullPath).length;
|
||||
} catch { /* skip */ }
|
||||
} catch {
|
||||
// Directory exists but is unreadable — use 0 as fallback
|
||||
}
|
||||
}
|
||||
result.directories[dir.path] = {
|
||||
exists,
|
||||
|
|
@ -178,6 +180,7 @@ function inspectAutomation() {
|
|||
'core/context-loader/index.js',
|
||||
'core/brain-wake/index.js',
|
||||
'connectors/notion-sync/index.js',
|
||||
'connectors/notion-wake-listener/index.js',
|
||||
'connectors/model-router/index.js',
|
||||
];
|
||||
console.log('\n 🔧 核心模块:');
|
||||
|
|
@ -301,8 +304,8 @@ function inspectBulletins() {
|
|||
result.bulletinBoard.cacheDate = cache.date || 'unknown';
|
||||
result.bulletinBoard.cacheRecords = (cache.records || []).length;
|
||||
console.log(` ✅ 公告栏缓存日期: ${cache.date} (${(cache.records || []).length} 条记录)`);
|
||||
} catch {
|
||||
console.log(' ⚠️ 公告栏缓存 JSON 解析失败');
|
||||
} catch (err) {
|
||||
console.log(' ⚠️ 公告栏缓存 JSON 解析失败:', err.message);
|
||||
}
|
||||
} else {
|
||||
console.log(' ⏭️ 公告栏缓存文件不存在');
|
||||
|
|
|
|||
Loading…
Reference in New Issue