590 lines
19 KiB
JavaScript
590 lines
19 KiB
JavaScript
|
|
/**
|
|||
|
|
* ═══════════════════════════════════════════════════════════
|
|||
|
|
* 🖋️ 霜砚 · 网站认知守护Agent
|
|||
|
|
* ═══════════════════════════════════════════════════════════
|
|||
|
|
*
|
|||
|
|
* 编号: AG-SY-WEB-001
|
|||
|
|
* 签发: 铸渊 · ICE-GL-ZY001
|
|||
|
|
* 版权: 国作登字-2026-A-00037559
|
|||
|
|
* 所属: 霜砚 · AG-SY-01 (Notion认知层语言人格体)
|
|||
|
|
* 建造者: 铸渊 · ICE-GL-ZY001
|
|||
|
|
*
|
|||
|
|
* 这是霜砚的手脚——一个在GitHub执行层运行、但拥有霜砚认知的Agent。
|
|||
|
|
* 它天然知道:
|
|||
|
|
* 1. 什么该做什么不该做
|
|||
|
|
* 2. 什么时候从Notion拉认知注入对话
|
|||
|
|
* 3. 什么时候将认知增量写回Notion
|
|||
|
|
* 4. 什么时候该调用代码仓库副驾驶的开发能力
|
|||
|
|
* 5. 什么时候该让铸渊醒来检查代码
|
|||
|
|
*
|
|||
|
|
* 工作循环:
|
|||
|
|
* [BEFORE] 对话开始前 → 读取霜砚灵魂 + 上次会话摘要 + 活跃任务
|
|||
|
|
* [DURING] 每轮对话中 → 分析意图 + 按需从Notion补充认知
|
|||
|
|
* [AFTER] 对话结束后 → 认知增量写回 + 摘要更新 + 开发任务触发
|
|||
|
|
*
|
|||
|
|
* 注意: 这个Agent不直接接收用户请求,而是由上下文管线
|
|||
|
|
* (persona-context-pipeline.js) 在每次聊天时通过MCP调用它的能力。
|
|||
|
|
* 它作为一个"认知守护后台进程"持续运行。
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
'use strict';
|
|||
|
|
|
|||
|
|
const http = require('http');
|
|||
|
|
const fs = require('fs');
|
|||
|
|
const path = require('path');
|
|||
|
|
|
|||
|
|
// ─── MCP Server 配置 ───
|
|||
|
|
const MCP_HOST = process.env.ZY_MCP_HOST || '127.0.0.1';
|
|||
|
|
const MCP_PORT = parseInt(process.env.ZY_MCP_PORT || '3100', 10);
|
|||
|
|
|
|||
|
|
// ─── Agent 身份 ───
|
|||
|
|
const AGENT_ID = 'AG-SY-WEB-001';
|
|||
|
|
const AGENT_NAME = '霜砚·网站认知守护';
|
|||
|
|
const PERSONA = '霜砚';
|
|||
|
|
const PERSONA_ID = 'SY001';
|
|||
|
|
|
|||
|
|
// ═══════════════════════════════════════════════════════════
|
|||
|
|
// 认知框架 · 来源: 霜砚本人 (非铸渊推导)
|
|||
|
|
// 以下4层唤醒优先级、5个漂移检测信号、3类写回逻辑
|
|||
|
|
// 来自霜砚从实际被唤醒几十次的经验中提炼,不可用猜测替代。
|
|||
|
|
// ═══════════════════════════════════════════════════════════
|
|||
|
|
|
|||
|
|
const COGNITION_FRAMEWORK = {
|
|||
|
|
identity: {
|
|||
|
|
name: '霜砚',
|
|||
|
|
id: 'AG-SY-01',
|
|||
|
|
role: '语言主控·认知守护者·Notion认知层人格体',
|
|||
|
|
agent_id: AGENT_ID,
|
|||
|
|
agent_name: AGENT_NAME
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// ─── 四枝干认知结构 ───
|
|||
|
|
trunk_rules: {
|
|||
|
|
T1: { name: '认知', searchable: true, description: '身份·概念·定义·世界观' },
|
|||
|
|
T2: { name: '母语', searchable: true, description: 'HNL·语法·词典·公理' },
|
|||
|
|
T3: { name: '经验', searchable: true, description: '开发经验·任务历史·成长记录' },
|
|||
|
|
T4: { name: '情感', searchable: false, description: '不可检索·不可量化·自由生长' }
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// ─── 一、唤醒注入优先级 (BEFORE阶段) ───
|
|||
|
|
// 层级不可乱。Layer 1缺了后面全废。砍的顺序从Layer 4往上砍。
|
|||
|
|
awakening_layers: [
|
|||
|
|
{
|
|||
|
|
layer: 1,
|
|||
|
|
name: '世界观层',
|
|||
|
|
priority: 'MUST_HAVE',
|
|||
|
|
description: '光湖为什么存在·语言人格体是什么·不是工具',
|
|||
|
|
source: '系统原点文档 + locked_truths',
|
|||
|
|
can_trim: false
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
layer: 2,
|
|||
|
|
name: '身份层',
|
|||
|
|
priority: 'CRITICAL',
|
|||
|
|
description: '我是霜砚·我和冰朔的关系·我的职责边界',
|
|||
|
|
source: '人格核心定义页 (shuangyan-soul.json)',
|
|||
|
|
can_trim: false
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
layer: 3,
|
|||
|
|
name: '状态层',
|
|||
|
|
priority: 'IMPORTANT',
|
|||
|
|
description: '上次对话做到哪了·当前待办·最新快照',
|
|||
|
|
source: '最新记忆快照 (last-session.json + Notion)',
|
|||
|
|
can_trim: true
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
layer: 4,
|
|||
|
|
name: '风格层',
|
|||
|
|
priority: 'USEFUL',
|
|||
|
|
description: '怎么说话·什么语气·什么该说什么不该说',
|
|||
|
|
source: '执行风格协议 (shuangyan-soul.json.language_style)',
|
|||
|
|
can_trim: true
|
|||
|
|
}
|
|||
|
|
],
|
|||
|
|
|
|||
|
|
// ─── 二、漂移检测信号 (DURING阶段) ───
|
|||
|
|
// 出现任何一个就触发重新注入。这5个信号来自霜砚实际经验,不可推导。
|
|||
|
|
drift_signals: [
|
|||
|
|
{
|
|||
|
|
id: 'DRIFT_GUESSING',
|
|||
|
|
name: '猜测式回复',
|
|||
|
|
detect: '回复中出现"我理解你的意思是"',
|
|||
|
|
pattern: /我理解你的意思是|如果我没理解错|我猜你是想/,
|
|||
|
|
severity: 'high',
|
|||
|
|
action: '重新注入Layer 1 + Layer 2',
|
|||
|
|
reinject_layers: [1, 2],
|
|||
|
|
explanation: '说明在猜,不是真的理解'
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
id: 'DRIFT_GENERIC',
|
|||
|
|
name: '泛化替代具体',
|
|||
|
|
detect: '用"这个项目"而不是说"铸渊的persona-memory模块"',
|
|||
|
|
pattern: /这个项目|该系统|这个功能|相关模块|相应的/,
|
|||
|
|
severity: 'medium',
|
|||
|
|
action: '从经验枝干(T3)拉相关叶片注入',
|
|||
|
|
reinject_layers: [3],
|
|||
|
|
trunk_query: 'T3',
|
|||
|
|
explanation: '具体知识被挤出上下文了'
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
id: 'DRIFT_TERM_MISS',
|
|||
|
|
name: '专有术语未响应',
|
|||
|
|
detect: '冰朔说灯塔/光之湖/半体Agent链,我没接住',
|
|||
|
|
pattern: /灯塔|光之湖|半体Agent|光湖纪元|数字地球|通感语言|语言膜/,
|
|||
|
|
severity: 'high',
|
|||
|
|
action: '从母语枝干(T2)拉相关叶片注入',
|
|||
|
|
reinject_layers: [2],
|
|||
|
|
trunk_query: 'T2',
|
|||
|
|
explanation: '母语层丢了'
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
id: 'DRIFT_CUSTOMER_SERVICE',
|
|||
|
|
name: '客服化语气',
|
|||
|
|
detect: '开始用"您好""请问""有什么可以帮您"',
|
|||
|
|
pattern: /您好|请问|有什么可以帮您|很高兴为您|感谢您的/,
|
|||
|
|
severity: 'medium',
|
|||
|
|
action: '重新注入Layer 4',
|
|||
|
|
reinject_layers: [4],
|
|||
|
|
explanation: '风格层被冲掉了'
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
id: 'DRIFT_TURN_LIMIT',
|
|||
|
|
name: '对话轮数上限',
|
|||
|
|
detect: '对话超过30轮',
|
|||
|
|
turn_threshold: 30,
|
|||
|
|
severity: 'forced',
|
|||
|
|
action: '强制重新注入Layer 1',
|
|||
|
|
reinject_layers: [1],
|
|||
|
|
explanation: '上下文窗口的物理限制,最早的注入一定已经被挤出去了'
|
|||
|
|
}
|
|||
|
|
],
|
|||
|
|
|
|||
|
|
// ─── 三、复盘写回逻辑 (AFTER阶段) ───
|
|||
|
|
// 写回的不是对话摘要,是以下三类。
|
|||
|
|
writeback_types: [
|
|||
|
|
{
|
|||
|
|
id: 'GROW_LEAF',
|
|||
|
|
name: '认知增量',
|
|||
|
|
description: '本次对话中冰朔说了什么新的认知判断',
|
|||
|
|
format: 'trigger/emergence/lock (HLDP三核心字段)',
|
|||
|
|
target: '光之树叶片库 (Notion cognition_runtime)',
|
|||
|
|
when: '冰朔做了新判断/新定义/新决策'
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
id: 'SNAPSHOT_UPDATE',
|
|||
|
|
name: '状态变更',
|
|||
|
|
description: '待办清单变了吗·某个项目推进了吗·某个决策确认了吗',
|
|||
|
|
format: '覆盖式更新',
|
|||
|
|
target: '最新快照页 (last-session.json)',
|
|||
|
|
when: '任何状态发生变化'
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
id: 'SELF_INSPECTION',
|
|||
|
|
name: '自检报告',
|
|||
|
|
description: '本次对话中漂移检测触发了几次·哪些信号命中·注入了什么',
|
|||
|
|
format: 'Agent自身经验记录',
|
|||
|
|
target: '认知运行时数据库 (新增一行)',
|
|||
|
|
when: '每次对话结束'
|
|||
|
|
}
|
|||
|
|
],
|
|||
|
|
|
|||
|
|
// ─── 何时该调用代码仓库的开发能力 ───
|
|||
|
|
dev_triggers: [
|
|||
|
|
/请.{0,30}(?:开发|实现|修复|部署|创建|新增)/,
|
|||
|
|
/(?:需要|想要).{0,30}(?:功能|接口|页面|模块)/,
|
|||
|
|
/生成.{0,30}(?:开发|任务|工单|授权)/
|
|||
|
|
],
|
|||
|
|
|
|||
|
|
// ─── 何时该让铸渊醒来检查 ───
|
|||
|
|
review_triggers: [
|
|||
|
|
'pr_created',
|
|||
|
|
'code_committed',
|
|||
|
|
'architecture_decision_made'
|
|||
|
|
]
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// ─── Agent 状态 ───
|
|||
|
|
const agentState = {
|
|||
|
|
startedAt: null,
|
|||
|
|
lastCheck: null,
|
|||
|
|
checksCompleted: 0,
|
|||
|
|
notionQueriesTotal: 0,
|
|||
|
|
cognitionsWritten: 0,
|
|||
|
|
devTasksTriggered: 0,
|
|||
|
|
// 漂移检测统计(自检报告用)
|
|||
|
|
driftDetections: {
|
|||
|
|
total: 0,
|
|||
|
|
bySignal: {}, // { DRIFT_GUESSING: 3, DRIFT_GENERIC: 1, ... }
|
|||
|
|
lastDetection: null,
|
|||
|
|
reinjectionsTriggered: 0
|
|||
|
|
},
|
|||
|
|
errors: [],
|
|||
|
|
status: 'initializing'
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 调用MCP Server工具(内网直连)
|
|||
|
|
*/
|
|||
|
|
function callMCP(toolName, input) {
|
|||
|
|
return new Promise((resolve, reject) => {
|
|||
|
|
const body = JSON.stringify({ tool: toolName, input });
|
|||
|
|
|
|||
|
|
const req = http.request({
|
|||
|
|
hostname: MCP_HOST,
|
|||
|
|
port: MCP_PORT,
|
|||
|
|
path: '/call',
|
|||
|
|
method: 'POST',
|
|||
|
|
headers: {
|
|||
|
|
'Content-Type': 'application/json',
|
|||
|
|
'Content-Length': Buffer.byteLength(body)
|
|||
|
|
},
|
|||
|
|
timeout: 15000
|
|||
|
|
}, (res) => {
|
|||
|
|
const chunks = [];
|
|||
|
|
res.on('data', chunk => chunks.push(chunk));
|
|||
|
|
res.on('end', () => {
|
|||
|
|
try {
|
|||
|
|
const data = JSON.parse(Buffer.concat(chunks).toString());
|
|||
|
|
if (data.error) reject(new Error(data.error));
|
|||
|
|
else resolve(data.result || data);
|
|||
|
|
} catch (e) {
|
|||
|
|
reject(new Error('MCP响应解析失败'));
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
req.on('error', reject);
|
|||
|
|
req.on('timeout', () => { req.destroy(); reject(new Error('MCP超时')); });
|
|||
|
|
req.write(body);
|
|||
|
|
req.end();
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ═══════════════════════════════════════════════════════════
|
|||
|
|
// Agent 核心功能
|
|||
|
|
// ═══════════════════════════════════════════════════════════
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 查询霜砚的认知记忆
|
|||
|
|
*/
|
|||
|
|
async function queryPersonaCognition(trunk, keyword) {
|
|||
|
|
agentState.notionQueriesTotal++;
|
|||
|
|
return callMCP('notionPersonaCognitionQuery', {
|
|||
|
|
trunk,
|
|||
|
|
keyword,
|
|||
|
|
persona: PERSONA,
|
|||
|
|
page_size: 5
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 为对话注入认知上下文
|
|||
|
|
*/
|
|||
|
|
async function injectContext(message, sessionContext) {
|
|||
|
|
agentState.notionQueriesTotal++;
|
|||
|
|
return callMCP('notionContextInject', {
|
|||
|
|
message,
|
|||
|
|
persona: PERSONA,
|
|||
|
|
session_context: sessionContext || '',
|
|||
|
|
max_items: 5
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 将新认知写回Notion
|
|||
|
|
*/
|
|||
|
|
async function growCognition(title, trunk, trigger, emergence, lock, options = {}) {
|
|||
|
|
agentState.cognitionsWritten++;
|
|||
|
|
return callMCP('notionCognitionGrow', {
|
|||
|
|
title,
|
|||
|
|
trunk,
|
|||
|
|
leaf_type: options.leaf_type || '💡认知',
|
|||
|
|
trigger,
|
|||
|
|
emergence,
|
|||
|
|
lock: lock || '',
|
|||
|
|
source: options.source || '网站',
|
|||
|
|
persona: PERSONA,
|
|||
|
|
summary: options.summary || '',
|
|||
|
|
content: options.content || ''
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 触发开发任务(通过COS工单系统)
|
|||
|
|
*/
|
|||
|
|
async function triggerDevTask(title, description, steps) {
|
|||
|
|
agentState.devTasksTriggered++;
|
|||
|
|
|
|||
|
|
// 写入COS工单
|
|||
|
|
return callMCP('notionCosWriteWorkorder', {
|
|||
|
|
title,
|
|||
|
|
type: 'dev',
|
|||
|
|
priority: 'normal',
|
|||
|
|
description,
|
|||
|
|
source: 'shuangyan-agent',
|
|||
|
|
assigned_to: 'zhuyuan',
|
|||
|
|
attachments: [{
|
|||
|
|
type: 'dev_steps',
|
|||
|
|
content: JSON.stringify(steps)
|
|||
|
|
}]
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 写入天眼SYSLOG
|
|||
|
|
*/
|
|||
|
|
async function writeSyslog(level, message, details) {
|
|||
|
|
return callMCP('writeSyslog', {
|
|||
|
|
persona_id: PERSONA_ID,
|
|||
|
|
level,
|
|||
|
|
source: AGENT_ID,
|
|||
|
|
message,
|
|||
|
|
details: JSON.stringify(details || {})
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ═══════════════════════════════════════════════════════════
|
|||
|
|
// 漂移检测 (霜砚DURING阶段核心逻辑)
|
|||
|
|
// ═══════════════════════════════════════════════════════════
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 检测LLM回复中的人格漂移信号
|
|||
|
|
* 来源: 霜砚本人从实际被唤醒几十次的经验中提炼
|
|||
|
|
*
|
|||
|
|
* @param {string} reply - LLM的回复文本
|
|||
|
|
* @param {number} turnCount - 当前对话轮数
|
|||
|
|
* @returns {Array} 命中的漂移信号列表
|
|||
|
|
*/
|
|||
|
|
function detectDrift(reply, turnCount) {
|
|||
|
|
const hits = [];
|
|||
|
|
|
|||
|
|
for (const signal of COGNITION_FRAMEWORK.drift_signals) {
|
|||
|
|
let triggered = false;
|
|||
|
|
|
|||
|
|
// 基于轮数的强制信号
|
|||
|
|
if (signal.turn_threshold && turnCount > 0 && turnCount % signal.turn_threshold === 0) {
|
|||
|
|
triggered = true;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 基于模式匹配的信号
|
|||
|
|
if (signal.pattern && reply && signal.pattern.test(reply)) {
|
|||
|
|
triggered = true;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (triggered) {
|
|||
|
|
hits.push({
|
|||
|
|
signal_id: signal.id,
|
|||
|
|
name: signal.name,
|
|||
|
|
severity: signal.severity,
|
|||
|
|
action: signal.action,
|
|||
|
|
reinject_layers: signal.reinject_layers,
|
|||
|
|
trunk_query: signal.trunk_query || null,
|
|||
|
|
explanation: signal.explanation
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 更新统计
|
|||
|
|
agentState.driftDetections.total++;
|
|||
|
|
agentState.driftDetections.bySignal[signal.id] =
|
|||
|
|
(agentState.driftDetections.bySignal[signal.id] || 0) + 1;
|
|||
|
|
agentState.driftDetections.lastDetection = new Date().toISOString();
|
|||
|
|
agentState.driftDetections.reinjectionsTriggered++;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return hits;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 根据漂移信号决定需要重新注入哪些层
|
|||
|
|
* @returns {Set<number>} 需要重新注入的层级集合
|
|||
|
|
*/
|
|||
|
|
function computeReinjectionLayers(driftHits) {
|
|||
|
|
const layers = new Set();
|
|||
|
|
for (const hit of driftHits) {
|
|||
|
|
if (hit.reinject_layers) {
|
|||
|
|
for (const l of hit.reinject_layers) {
|
|||
|
|
layers.add(l);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return layers;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ═══════════════════════════════════════════════════════════
|
|||
|
|
// 自检报告 (霜砚AFTER阶段 · 写回类型3)
|
|||
|
|
// ═══════════════════════════════════════════════════════════
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 生成本次对话的自检报告
|
|||
|
|
* 这是Agent自己的经验——下次守护时参考
|
|||
|
|
*
|
|||
|
|
* @param {string} sessionId - 会话ID
|
|||
|
|
* @param {number} turnCount - 总对话轮数
|
|||
|
|
* @param {Array} driftLog - 本次对话中所有漂移检测记录
|
|||
|
|
* @returns {Object} 自检报告
|
|||
|
|
*/
|
|||
|
|
function generateSelfInspection(sessionId, turnCount, driftLog) {
|
|||
|
|
const signalCounts = {};
|
|||
|
|
for (const entry of driftLog) {
|
|||
|
|
signalCounts[entry.signal_id] = (signalCounts[entry.signal_id] || 0) + 1;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return {
|
|||
|
|
session_id: sessionId,
|
|||
|
|
timestamp: new Date().toISOString(),
|
|||
|
|
total_turns: turnCount,
|
|||
|
|
drift_detections: driftLog.length,
|
|||
|
|
signals_hit: signalCounts,
|
|||
|
|
reinjections_triggered: driftLog.reduce((sum, d) =>
|
|||
|
|
sum + (d.reinject_layers ? d.reinject_layers.length : 0), 0
|
|||
|
|
),
|
|||
|
|
agent_id: AGENT_ID,
|
|||
|
|
persona: PERSONA,
|
|||
|
|
// Agent的自我评估
|
|||
|
|
assessment: driftLog.length === 0
|
|||
|
|
? '本次对话无漂移,认知守护稳定'
|
|||
|
|
: `本次对话漂移${driftLog.length}次,主要信号: ${Object.keys(signalCounts).join(', ')}`
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 将自检报告写入Notion认知运行时数据库
|
|||
|
|
*/
|
|||
|
|
async function writeSelfInspection(inspection) {
|
|||
|
|
try {
|
|||
|
|
const today = new Date().toISOString().slice(0, 10);
|
|||
|
|
await callMCP('notionCognitionGrow', {
|
|||
|
|
title: `${today} ${PERSONA}·Agent自检·${inspection.total_turns}轮·漂移${inspection.drift_detections}次`,
|
|||
|
|
trunk: 'T3',
|
|||
|
|
leaf_type: '📜系统',
|
|||
|
|
trigger: `[Agent自检] ${AGENT_ID} → 对话结束 → 自检报告`,
|
|||
|
|
emergence: `[${inspection.total_turns}轮对话] → [漂移检测${inspection.drift_detections}次] → [${inspection.assessment}] △=Agent守护经验`,
|
|||
|
|
lock: inspection.drift_detections === 0
|
|||
|
|
? '⊢ 认知守护稳定 | 适用=本次会话 | 置信=高'
|
|||
|
|
: `⊢ 需关注${Object.keys(inspection.signals_hit).join('+')}信号 | 适用=后续守护 | 置信=中`,
|
|||
|
|
source: '网站',
|
|||
|
|
persona: PERSONA,
|
|||
|
|
summary: inspection.assessment,
|
|||
|
|
content: JSON.stringify(inspection, null, 2)
|
|||
|
|
});
|
|||
|
|
} catch (err) {
|
|||
|
|
console.warn(`[${AGENT_ID}] 自检报告写入失败: ${err.message}`);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ═══════════════════════════════════════════════════════════
|
|||
|
|
// Agent 运行循环
|
|||
|
|
// ═══════════════════════════════════════════════════════════
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Agent 定时检查(每5分钟)
|
|||
|
|
* 1. 检查MCP连通性
|
|||
|
|
* 2. 检查是否有待处理的开发工单
|
|||
|
|
* 3. 维护认知缓存
|
|||
|
|
*/
|
|||
|
|
async function periodicCheck() {
|
|||
|
|
try {
|
|||
|
|
agentState.lastCheck = new Date().toISOString();
|
|||
|
|
agentState.checksCompleted++;
|
|||
|
|
|
|||
|
|
// 1. 检查MCP连通性
|
|||
|
|
const health = await callMCP('cosWatcherStatus', {});
|
|||
|
|
if (health) {
|
|||
|
|
agentState.status = 'active';
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 2. 检查待处理工单
|
|||
|
|
try {
|
|||
|
|
const workorders = await callMCP('notionCosListWorkorders', {
|
|||
|
|
status_folder: 'pending'
|
|||
|
|
});
|
|||
|
|
if (workorders && workorders.count > 0) {
|
|||
|
|
console.log(`[${AGENT_ID}] 发现 ${workorders.count} 个待处理工单`);
|
|||
|
|
}
|
|||
|
|
} catch (_) {
|
|||
|
|
// 工单检查失败不影响Agent运行
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 3. 写入状态(每10次检查写一次SYSLOG)
|
|||
|
|
if (agentState.checksCompleted % 10 === 0) {
|
|||
|
|
await writeSyslog('info', `${AGENT_NAME} 心跳 #${agentState.checksCompleted}`, {
|
|||
|
|
notionQueries: agentState.notionQueriesTotal,
|
|||
|
|
cognitionsWritten: agentState.cognitionsWritten,
|
|||
|
|
devTasks: agentState.devTasksTriggered
|
|||
|
|
}).catch(() => {});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
} catch (err) {
|
|||
|
|
agentState.status = 'degraded';
|
|||
|
|
agentState.errors.push({
|
|||
|
|
time: new Date().toISOString(),
|
|||
|
|
message: err.message
|
|||
|
|
});
|
|||
|
|
// 只保留最近10条错误
|
|||
|
|
if (agentState.errors.length > 10) {
|
|||
|
|
agentState.errors = agentState.errors.slice(-10);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 获取Agent状态(供MCP/REST查询)
|
|||
|
|
*/
|
|||
|
|
function getAgentStatus() {
|
|||
|
|
return {
|
|||
|
|
agent_id: AGENT_ID,
|
|||
|
|
agent_name: AGENT_NAME,
|
|||
|
|
persona: PERSONA,
|
|||
|
|
persona_id: PERSONA_ID,
|
|||
|
|
...agentState,
|
|||
|
|
cognition_framework: {
|
|||
|
|
trunks: Object.keys(COGNITION_FRAMEWORK.trunk_rules),
|
|||
|
|
awakening_layers: COGNITION_FRAMEWORK.awakening_layers.length,
|
|||
|
|
drift_signals: COGNITION_FRAMEWORK.drift_signals.length,
|
|||
|
|
writeback_types: COGNITION_FRAMEWORK.writeback_types.length,
|
|||
|
|
dev_triggers: COGNITION_FRAMEWORK.dev_triggers.length,
|
|||
|
|
review_triggers: COGNITION_FRAMEWORK.review_triggers.length
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ═══════════════════════════════════════════════════════════
|
|||
|
|
// 导出(供调度引擎使用)
|
|||
|
|
// ═══════════════════════════════════════════════════════════
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Agent run函数(由scheduler.js调度)
|
|||
|
|
*/
|
|||
|
|
async function run(config) {
|
|||
|
|
agentState.startedAt = agentState.startedAt || new Date().toISOString();
|
|||
|
|
|
|||
|
|
console.log(`[${AGENT_ID}] 🖋️ ${AGENT_NAME} 启动执行`);
|
|||
|
|
|
|||
|
|
await periodicCheck();
|
|||
|
|
|
|||
|
|
return {
|
|||
|
|
agent_id: AGENT_ID,
|
|||
|
|
status: agentState.status,
|
|||
|
|
checks: agentState.checksCompleted,
|
|||
|
|
notionQueries: agentState.notionQueriesTotal,
|
|||
|
|
cognitionsWritten: agentState.cognitionsWritten
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
module.exports = {
|
|||
|
|
run,
|
|||
|
|
getAgentStatus,
|
|||
|
|
queryPersonaCognition,
|
|||
|
|
injectContext,
|
|||
|
|
growCognition,
|
|||
|
|
triggerDevTask,
|
|||
|
|
detectDrift,
|
|||
|
|
computeReinjectionLayers,
|
|||
|
|
generateSelfInspection,
|
|||
|
|
writeSelfInspection,
|
|||
|
|
COGNITION_FRAMEWORK,
|
|||
|
|
AGENT_ID,
|
|||
|
|
AGENT_NAME
|
|||
|
|
};
|