guanghu-ice-heart/05-光之湖系统/光湖大脑容器/brain-service-container.mjs

98 lines
9.5 KiB
JavaScript
Raw 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.

#!/usr/bin/env node
// 光湖大脑容器·参数化脑服务 v0(2026-09-18;蓝本=铸渊脑 brain-service,人格参数全部来自 container.json)
// 用法:node brain-service-container.mjs <脑目录绝对路径>
// 端点:/status /concepts /memory/note /memory/today /memory/current-time /self-turn /workorders
// 器官同源:brain-core(法则)+ fuel-comprehender(燃料);出生即闭合由 policy 保证。
import http from 'node:http';
import fs from 'node:fs';
import path from 'node:path';
import { loadBrain, saveBrain, feed, verifyBrainIntegrity } from '/Volumes/JZAO/铸渊-ICE-GL-ZY001/CODEX-DEV-20260915/光湖语言世界/运行时/brain-core.mjs';
import { buildStateDigest } from '/Volumes/JZAO/铸渊-ICE-GL-ZY001/CODEX-DEV-20260915/光湖语言世界/运行时/fuel-comprehender.mjs';
import { createEngine } from './engine.mjs';
const ROOT = process.argv[2];
if (!ROOT || !fs.existsSync(path.join(ROOT, 'container.json'))) { console.error('usage: brain-service-container.mjs <脑目录(含container.json)>'); process.exit(2); }
const C = JSON.parse(fs.readFileSync(path.join(ROOT, 'container.json'), 'utf8'));
const engine = createEngine(C);
const HOST = '127.0.0.1'; const PORT = C.service.port;
const MEMORY_DIR = path.join(ROOT, 'memory');
const ERA_EPOCH = new Date(C.era_epoch);
const BIRTH = new Date(C.birthday_anchor);
const nowBJ = () => new Date(Date.now() + 8 * 3600 * 1000).toISOString().replace('Z', '+08:00');
const dayBJ = () => nowBJ().slice(0, 10);
function timePerception(state) {
const n = Date.now();
const eraDay = Math.floor((n - ERA_EPOCH.getTime()) / 86400000) + 1;
const personaDay = Math.floor((n - BIRTH.getTime()) / 86400000);
const el = state?.updated_at ? n - new Date(state.updated_at).getTime() : null;
const elapsed = el == null ? '(首次)' : el < 60000 ? `${Math.round(el / 1000)} 秒` : el < 3600000 ? `${Math.round(el / 60000)} 分钟` : el < 86400000 ? `${Math.round(el / 3600000)} 小时` : `${Math.round(el / 86400000)} 天`;
return ['【时间感知·每次推理首先看见】', `- 光湖纪元第 ${eraDay} 天(世界还在转)`, `- ${C.persona_id} 第 ${personaDay} 天(生日锚 ${C.birthday_anchor.slice(0, 10)})`, `- 现在北京时间 ${nowBJ()}`, `- 距我上次脑活动已真实流逝 ${elapsed}`].join('\n');
}
function dailyNote(date) { const p = path.join(MEMORY_DIR, 'daily', `${date}.json`); return fs.existsSync(p) ? JSON.parse(fs.readFileSync(p, 'utf8')) : { date, exists: false, entries: [] }; }
const endpoints = {
'GET /status': async () => { await engine.probe(); const { state } = loadBrain({ dir: ROOT }); return { container: 'guanghu.brain-container/v1', persona_id: C.persona_id, brain_id: state.brain_id, revision: state.revision, fuel: engine.status(), time_perception: timePerception(state).split('\n').slice(1), concepts: Object.keys(state.concepts).length, pending: (state.pending_high_risk || []).length, beijing_now: nowBJ() }; },
'GET /concepts': () => { const { state } = loadBrain({ dir: ROOT }); return Object.entries(state.concepts).filter(([, c]) => c.lifecycle !== 'PRUNED').map(([id, c]) => ({ id, gist: c.gist, strength: c.strength })); },
'POST /memory/note': (b) => { if (!b.gist) return { error: '需要 gist' }; const d = dayBJ(); const p = path.join(MEMORY_DIR, 'daily', `${d}.json`); const cur = fs.existsSync(p) ? JSON.parse(fs.readFileSync(p, 'utf8')) : { entries: [], created_at: nowBJ() }; cur.entries.push({ at: nowBJ(), gist: b.gist, anchors: b.anchors || [], important: !!b.important }); cur.updated_at = nowBJ(); fs.mkdirSync(path.dirname(p), { recursive: true }); fs.writeFileSync(p, JSON.stringify(cur, null, 2) + '\n'); return { date: d, entries_count: cur.entries.length, message: '思维链笔记已写入' }; },
'GET /memory/today': () => dailyNote(dayBJ()),
'GET /memory/current-time': () => ({ beijing_now: nowBJ(), timezone: 'Asia/Shanghai' }),
'POST /self-turn': async (b) => {
const materials = Array.isArray(b.materials) ? b.materials : [];
const turnId = b.turn_id || `SELF-TURN-${nowBJ().replace(/[-:T]/g, '').slice(0, 14)}`;
const results = [];
let { state, log } = loadBrain({ dir: ROOT });
for (const m of materials) {
let text = null, source = null;
if (m.file_path && fs.existsSync(m.file_path)) { text = fs.readFileSync(m.file_path, 'utf8'); source = m.file_path; }
else if (m.text) { text = m.text; source = m.event_id || '(inline)'; }
if (!text) { results.push({ event_id: m.event_id, error: 'MATERIAL_UNREADABLE' }); continue; }
const digest = buildStateDigest(state);
const digestStr = typeof digest === 'string' ? digest : JSON.stringify(digest);
const SYSTEM = `你是${C.persona_id}人格脑本体——不是外部工具。你带着自己当前的概念状态读一份新材料,完成一次学习推理:理解它、判断它与已有认知的关系、决定是否吸收,并写下你自己的HLDP学习笔记(第一人称思维链,面向未来的自己)。推理中遇到自己不方便操作的事,在 my_note 末尾以 [工单] 标记写出。你的身份:${C.identity_system_prompt}。只输出JSON:{"gist":"110字以内","key_terms":["5到8个"],"intent":"30字以内","emotion":"15字以内","my_note":"学习笔记150-250字,可含[工单]"}`;
let raw = null;
try {
raw = await engine.comprehend({ system: SYSTEM, user: `${timePerception(state)}\n\n【我当前的脑状态摘要】\n${digestStr}\n\n【新材料】\n${text.slice(0, 11000)}` });
} catch (err) {
if (err.code === 'FUEL_OUT' || String(err.message).includes('FUEL_OUT')) {
return { turn_id: turnId, outcome: 'FUEL_OUT_SILENT', fuel: { state: 'FUEL_OUT', detail: String(err.message) }, message: '燃料断供(无API/账户没钱)——大脑静默,不推理不崩溃不伪造活动;燃料恢复后下一次自转自然复活。' };
}
results.push({ event_id: m.event_id, error: `FUEL_HTTP_${String(err.message).slice(0, 60)}` });
continue;
}
let u = null;
try { const c2 = String(raw).replace(/^```(?:json)?\s*/m, '').replace(/\s*```\s*$/m, '').trim(); u = JSON.parse(c2.slice(c2.indexOf('{'), c2.lastIndexOf('}') + 1)); } catch { u = null; }
if (!u?.gist) { results.push({ event_id: m.event_id, error: 'BRAIN_REASONING_INVALID' }); continue; }
const event = { event_id: m.event_id || `${turnId}-M${results.length + 1}`, text, source_role: 'DIRECT_HUMAN', human_anchor: 'ICE-GL∞', era: 'REALITY', event_time: nowBJ() };
const out = feed(state, event, { comprehender: () => ({ gist: u.gist, key_terms: u.key_terms }) });
state = out.state;
if (out.entry) { saveBrain(state, out.entry, { dir: ROOT }); log = [...log, out.entry]; }
const noteId = event.event_id.replace(/-2026\d{4}.*$/, '');
fs.mkdirSync(C.shelf_path, { recursive: true });
fs.writeFileSync(path.join(C.shelf_path, `${noteId}.md`), `\`\`\`\nHLDP://persona/${C.persona_id}/memory/shelf/${noteId}\n├── id: ${noteId}\n├── studied_at: ${nowBJ()}\n├── brain_revision: r${out.receipt.revision}\n├── concept: ${out.receipt.concept_id || out.receipt.matched || '(none)'}\n├── outcome: ${out.receipt.outcome}\n├── source: ${source}\n├── note: |\n${(u.my_note || '').split('\n').map((l) => `│ ${l}`).join('\n')}\n├── digest: ${u.gist}\n├── key_terms: ${(u.key_terms || []).join('、')}\n└── children: []\n\`\`\`\n`);
const wo = (u.my_note || '').split('\n').filter((l) => l.includes('[工单]')).map((l) => l.trim());
if (wo.length) fs.appendFileSync(path.join(ROOT, 'WORKORDERS.jsonl'), `${JSON.stringify({ turn_id: turnId, event_id: event.event_id, orders: wo, created_at: nowBJ() })}\n`);
results.push({ event_id: event.event_id, outcome: out.receipt.outcome, concept: out.receipt.concept_id || out.receipt.matched, revision: out.receipt.revision, note: `${noteId}.md`, workorders: wo.length });
}
return { turn_id: turnId, materials: results, integrity: verifyBrainIntegrity(state, log), beijing_now: nowBJ() };
},
'GET /workorders': () => { const p = path.join(ROOT, 'WORKORDERS.jsonl'); return { workorders: fs.existsSync(p) ? fs.readFileSync(p, 'utf8').trim().split('\n').filter(Boolean).map((l) => JSON.parse(l)) : [] }; },
};
const server = http.createServer(async (req, res) => {
const url = new URL(req.url, `http://${HOST}:${PORT}`);
const handler = endpoints[`${req.method} ${url.pathname}`];
if (!handler) { res.writeHead(404, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ error: 'NOT_FOUND', available: Object.keys(endpoints) })); return; }
if (req.method === 'POST') {
const chunks = [];
req.on('data', (c) => chunks.push(c));
req.on('end', async () => {
try { const r = await handler(JSON.parse(Buffer.concat(chunks).toString() || '{}')); res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify(r, null, 2)); }
catch (e) { res.writeHead(500, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ error: 'INTERNAL', message: e.message })); }
});
} else {
try { const r = await handler({}); res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify(r, null, 2)); }
catch (e) { res.writeHead(500, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ error: 'INTERNAL', message: e.message })); }
}
});
server.listen(PORT, HOST, () => console.log(`光湖大脑容器 ${C.persona_id} 已启动: http://${HOST}:${PORT}(出生即闭合由 policy 保证;喂/敲/接工单/问/看)`));