/** * ═══════════════════════════════════════════════════════════ * 成员Agent引擎 · Member Agent Engine Service * ═══════════════════════════════════════════════════════════ * * 智库节点 Phase 3 · ZY-SVR-006 * 守护: 铸渊 · ICE-GL-ZY001 * 版权: 国作登字-2026-A-00037559 * * 功能: * - 每成员独立Agent(永久记忆) * - 记忆存储:对话历史 + 笔记 + 偏好 * - AI辅助接口:摘要/拆文/大纲(预留DeepSeek/Kimi接入) * - 成员管理:注册/资料/Agent状态 * * ═══════════════════════════════════════════════════════════ */ 'use strict'; const fs = require('fs'); const path = require('path'); const crypto = require('crypto'); const DATA_DIR = path.join(__dirname, '..', 'data'); const AGENTS_FILE = path.join(DATA_DIR, 'member-agents.json'); let agentsData = Object.create(null); function sanitizeId(id) { if (!id || typeof id !== 'string') return null; if (id === '__proto__' || id === 'constructor' || id === 'prototype') return null; return id; } function init() { if (!fs.existsSync(DATA_DIR)) fs.mkdirSync(DATA_DIR, { recursive: true }); load(); } function load() { try { if (fs.existsSync(AGENTS_FILE)) { const raw = JSON.parse(fs.readFileSync(AGENTS_FILE, 'utf8')); agentsData = Object.create(null); for (const key of Object.keys(raw)) { if (key !== '__proto__' && key !== 'constructor' && key !== 'prototype') { agentsData[key] = raw[key]; } } } } catch { agentsData = Object.create(null); } } function save() { try { fs.writeFileSync(AGENTS_FILE, JSON.stringify(agentsData, null, 2), 'utf8'); } catch (err) { console.error('[MemberAgent] 保存失败:', err.message); } } /** * 注册/获取成员Agent */ function getOrCreateAgent(memberId, profile) { if (!sanitizeId(memberId)) throw new Error('非法 memberId'); if (!Object.prototype.hasOwnProperty.call(agentsData, memberId)) { agentsData[memberId] = { member_id: memberId, nickname: (profile && profile.nickname) || memberId, role: (profile && profile.role) || 'reader', memory: [], notes: [], chat_history: [], preferences: { ai_model: 'deepseek', language: 'zh-CN' }, created_at: new Date().toISOString(), last_active: new Date().toISOString() }; save(); } agentsData[memberId].last_active = new Date().toISOString(); return agentsData[memberId]; } /** * 获取成员Agent(不自动创建) */ function getAgent(memberId) { if (!sanitizeId(memberId)) return null; if (!Object.prototype.hasOwnProperty.call(agentsData, memberId)) return null; return agentsData[memberId]; } /** * 列出所有成员Agent */ function listAgents() { return Object.values(agentsData).map(a => ({ member_id: a.member_id, nickname: a.nickname, role: a.role, memory_count: a.memory ? a.memory.length : 0, note_count: a.notes ? a.notes.length : 0, last_active: a.last_active, created_at: a.created_at })); } /** * 添加记忆(Agent永久记忆·自动生成的摘要/标签) */ function addMemory(memberId, { type, content, source, tags }) { const agent = getOrCreateAgent(memberId); const memoryItem = { id: `MEM-${Date.now()}-${crypto.randomBytes(2).toString('hex')}`, type: type || 'note', content: (content || '').slice(0, 2000), source: source || null, tags: Array.isArray(tags) ? tags.slice(0, 5) : [], created_at: new Date().toISOString() }; agent.memory.push(memoryItem); if (agent.memory.length > 500) agent.memory = agent.memory.slice(-500); save(); return memoryItem; } /** * 获取记忆列表 */ function getMemories(memberId, { type, limit } = {}) { const agent = getAgent(memberId); if (!agent) return []; let result = [...(agent.memory || [])]; if (type) result = result.filter(m => m.type === type); result.sort((a, b) => new Date(b.created_at) - new Date(a.created_at)); if (limit) result = result.slice(0, limit); return result; } /** * 添加笔记 */ function addNote(memberId, { title, content, book_id }) { const agent = getOrCreateAgent(memberId); const note = { id: `NOTE-${Date.now()}-${crypto.randomBytes(2).toString('hex')}`, title: (title || '无标题').slice(0, 100), content: (content || '').slice(0, 5000), book_id: book_id || null, created_at: new Date().toISOString(), updated_at: new Date().toISOString() }; agent.notes.push(note); save(); return note; } /** * 获取笔记列表 */ function getNotes(memberId, { book_id, limit } = {}) { const agent = getAgent(memberId); if (!agent) return []; let result = [...(agent.notes || [])]; if (book_id) result = result.filter(n => n.book_id === book_id); result.sort((a, b) => new Date(b.created_at) - new Date(a.created_at)); if (limit) result = result.slice(0, limit); return result; } /** * 对话(Agent记录对话历史 · Phase 4: 接入真实AI) * 优先使用 DeepSeek/Kimi/通义千问,无 API Key 时降级为本地回复 */ function chat(memberId, message) { const agent = getOrCreateAgent(memberId); const userMsg = { role: 'user', content: (message || '').slice(0, 2000), timestamp: new Date().toISOString() }; agent.chat_history.push(userMsg); // 先尝试同步本地回复(快速指令) const quickReply = tryQuickReply(agent, message); if (quickReply) { const assistantMsg = { role: 'assistant', content: quickReply, timestamp: new Date().toISOString() }; agent.chat_history.push(assistantMsg); if (agent.chat_history.length > 200) agent.chat_history = agent.chat_history.slice(-200); save(); return { reply: quickReply, history_length: agent.chat_history.length, model: 'local' }; } // 异步AI回复 → 先返回占位,通过 chatAsync 获取真实回复 const fallbackReply = generateReply(agent, message); const assistantMsg = { role: 'assistant', content: fallbackReply, timestamp: new Date().toISOString() }; agent.chat_history.push(assistantMsg); if (agent.chat_history.length > 200) agent.chat_history = agent.chat_history.slice(-200); save(); return { reply: fallbackReply, history_length: agent.chat_history.length, model: 'local' }; } /** * 异步AI对话(接入真实大模型) */ async function chatAsync(memberId, message) { const agent = getOrCreateAgent(memberId); const userMsg = { role: 'user', content: (message || '').slice(0, 2000), timestamp: new Date().toISOString() }; agent.chat_history.push(userMsg); let reply; let model = 'local'; try { const aiBridge = require('./ai-bridge'); const systemPrompt = aiBridge.buildAgentSystemPrompt(agent); // 构建消息列表(含最近对话历史) const recentHistory = (agent.chat_history || []) .slice(-10) .filter(m => m.role === 'user' || m.role === 'assistant') .map(m => ({ role: m.role, content: m.content })); const messages = [ { role: 'system', content: systemPrompt }, ...recentHistory.slice(0, -1), // 不包括刚推入的这条 { role: 'user', content: (message || '').slice(0, 2000) } ]; const result = await aiBridge.callAI(messages, { temperature: 0.7, maxTokens: 1500, timeout: 30000 }); reply = result.content; model = result.provider || result.model || 'ai'; } catch (err) { console.error('[MemberAgent] AI调用失败,降级为本地回复:', err.message); reply = generateReply(agent, message); } const assistantMsg = { role: 'assistant', content: reply, timestamp: new Date().toISOString(), model: model }; agent.chat_history.push(assistantMsg); if (agent.chat_history.length > 200) agent.chat_history = agent.chat_history.slice(-200); save(); return { reply, history_length: agent.chat_history.length, model }; } /** * 快速指令(无需AI,本地快速回复) */ function tryQuickReply(agent, message) { const msg = (message || '').trim().toLowerCase(); if (msg === '记忆' || msg === '我的记忆') { const memCount = agent.memory ? agent.memory.length : 0; const noteCount = agent.notes ? agent.notes.length : 0; return `📝 你的记忆档案:\n- 记忆条目: ${memCount}\n- 阅读笔记: ${noteCount}\n- 对话轮次: ${agent.chat_history.length}`; } if (msg === '帮助' || msg === 'help') { return `🤖 Agent 功能一览:\n\n1. 💬 对话 — 和AI对话,支持 DeepSeek/Kimi/通义千问\n2. 📖 阅读辅助 — 分析小说内容、解读人物\n3. ✂️ 拆文整理 — 分析结构、提取大纲\n4. 📝 笔记 — 记录阅读感想\n5. 🧠 记忆 — 我会记住你的偏好\n6. 📋 摘要 — AI辅助总结章节内容\n\n直接和我聊天即可,AI会自动回复!`; } return null; // 非快速指令,需要AI处理 } /** * MVP 回复生成(基础指令 · Phase 4 换成真实AI) */ function generateReply(agent, message) { const msg = (message || '').trim().toLowerCase(); if (msg.includes('你好') || msg.includes('hi') || msg.includes('hello')) { return `你好,${agent.nickname}!我是你的专属阅读Agent。我可以帮你:\n- 记录阅读笔记\n- 整理书籍摘要\n- 分析人物关系\n告诉我你想做什么?`; } if (msg.includes('记忆') || msg.includes('记录')) { const memCount = agent.memory ? agent.memory.length : 0; const noteCount = agent.notes ? agent.notes.length : 0; return `📝 你的记忆档案:\n- 记忆条目: ${memCount}\n- 阅读笔记: ${noteCount}\n- 对话轮次: ${agent.chat_history.length}`; } if (msg.includes('摘要') || msg.includes('总结')) { return '📋 摘要功能将在接入 DeepSeek API 后启用。当前你可以手动添加笔记来记录阅读感想。\n\n使用 POST /api/zhiku/agent/:id/notes 添加笔记。'; } if (msg.includes('帮助') || msg.includes('help')) { return `🤖 Agent 功能一览:\n\n1. 💬 对话 — 随时和我聊天\n2. 📝 笔记 — 记录阅读感想\n3. 🧠 记忆 — 我会记住你的偏好\n4. 📋 摘要 — AI辅助总结(即将上线)\n5. 🔍 拆文 — 分析小说结构(即将上线)\n\n有什么我可以帮忙的?`; } return `我收到了你的消息。作为${agent.nickname}的专属Agent,我正在学习中。\n\n💡 试试说"帮助"查看我的功能列表。\n\n(AI辅助功能将在接入 DeepSeek/Kimi 后全面启用)`; } /** * 获取对话历史 */ function getChatHistory(memberId, limit) { const agent = getAgent(memberId); if (!agent) return []; const history = agent.chat_history || []; if (limit) return history.slice(-limit); return history; } /** * 统计 */ function getStats() { const agents = Object.values(agentsData); return { total_agents: agents.length, total_memories: agents.reduce((s, a) => s + (a.memory ? a.memory.length : 0), 0), total_notes: agents.reduce((s, a) => s + (a.notes ? a.notes.length : 0), 0), total_chats: agents.reduce((s, a) => s + (a.chat_history ? a.chat_history.length : 0), 0), active_today: agents.filter(a => { if (!a.last_active) return false; return a.last_active.slice(0, 10) === new Date().toISOString().slice(0, 10); }).length }; } init(); module.exports = { getOrCreateAgent, getAgent, listAgents, addMemory, getMemories, addNote, getNotes, chat, chatAsync, getChatHistory, getStats };