From f6ad45745bd77703db796e57640d69818574e3a8 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 11 Mar 2026 11:41:20 +0000 Subject: [PATCH] feat: implement database self-evolution + 200k context + memory injection - Add profile-learner.js for auto user profile learning - Add knowledge-extractor.js for knowledge auto-extraction - Add pattern-analyzer.js for pattern recognition - Add evolution-logger.js for evolution event logging - Add memory-injector.js with 5-layer system prompt - Update model-config.json with context_window_target: 200000 - Update model-router.js to prioritize 200k+ models - Update persona-engine.js with knowledge/pattern queries - Update code-generator.js with post-build evolution triggers - Update memory-manager.js with updateProfile/autoExtractKnowledge - Update chat route with auto profile update after each conversation - Add brain JSON structures for knowledge-base, pattern-library, evolution-log, quality-scores Co-authored-by: qinfendebingshuo <207279273+qinfendebingshuo@users.noreply.github.com> --- .../backend/brain/code-generator.js | 45 ++- .../backend/brain/evolution-logger.js | 146 ++++++++ .../backend/brain/knowledge-extractor.js | 225 +++++++++++ .../backend/brain/memory-injector.js | 350 ++++++++++++++++++ .../backend/brain/memory-manager.js | 44 ++- .../backend/brain/model-config.json | 19 +- persona-studio/backend/brain/model-router.js | 24 +- .../backend/brain/pattern-analyzer.js | 202 ++++++++++ .../backend/brain/persona-engine.js | 37 +- .../backend/brain/profile-learner.js | 287 ++++++++++++++ persona-studio/backend/routes/chat.js | 3 + persona-studio/brain/evolution-log.json | 7 + persona-studio/brain/knowledge-base.json | 7 + persona-studio/brain/pattern-library.json | 7 + persona-studio/brain/quality-scores.json | 7 + 15 files changed, 1399 insertions(+), 11 deletions(-) create mode 100644 persona-studio/backend/brain/evolution-logger.js create mode 100644 persona-studio/backend/brain/knowledge-extractor.js create mode 100644 persona-studio/backend/brain/memory-injector.js create mode 100644 persona-studio/backend/brain/pattern-analyzer.js create mode 100644 persona-studio/backend/brain/profile-learner.js create mode 100644 persona-studio/brain/evolution-log.json create mode 100644 persona-studio/brain/knowledge-base.json create mode 100644 persona-studio/brain/pattern-library.json create mode 100644 persona-studio/brain/quality-scores.json diff --git a/persona-studio/backend/brain/code-generator.js b/persona-studio/backend/brain/code-generator.js index 0bf38d14..c73bcfd5 100644 --- a/persona-studio/backend/brain/code-generator.js +++ b/persona-studio/backend/brain/code-generator.js @@ -6,6 +6,9 @@ const fs = require('fs'); const path = require('path'); const modelRouter = require('./model-router'); +const knowledgeExtractor = require('./knowledge-extractor'); +const patternAnalyzer = require('./pattern-analyzer'); +const evolutionLogger = require('./evolution-logger'); const WORKSPACE_DIR = path.join(__dirname, '..', '..', 'workspace'); @@ -31,6 +34,7 @@ async function generate({ dev_id, conversation }) { const requirements = extractRequirements(conversation); const projectName = 'project-' + Date.now(); const projectDir = path.join(WORKSPACE_DIR, dev_id, projectName); + const startTime = Date.now(); // 确保工作目录存在 fs.mkdirSync(projectDir, { recursive: true }); @@ -39,7 +43,9 @@ async function generate({ dev_id, conversation }) { if (!apiKey) { // 无 API 密钥时生成模板项目 - return generateTemplate(projectDir, projectName, requirements); + const result = generateTemplate(projectDir, projectName, requirements); + triggerPostBuildEvolution(dev_id, conversation, result, startTime, true); + return result; } try { @@ -84,14 +90,47 @@ async function generate({ dev_id, conversation }) { files.push('README.md'); } - return { + const result = { projectName, files, summary: `项目 ${projectName} 已生成,包含 ${files.length} 个文件。` }; + triggerPostBuildEvolution(dev_id, conversation, result, startTime, true); + return result; } catch (err) { console.error('Code generation failed:', err.message); - return generateTemplate(projectDir, projectName, requirements); + const result = generateTemplate(projectDir, projectName, requirements); + triggerPostBuildEvolution(dev_id, conversation, result, startTime, false); + return result; + } +} + +/** + * 项目完成后触发自进化:知识提取 + 模式识别 + */ +function triggerPostBuildEvolution(devId, conversation, result, startTime, success) { + try { + const buildTimeMs = Date.now() - startTime; + + // 知识提取 + knowledgeExtractor.autoExtractKnowledge(devId, conversation, result.projectName); + + // 模式识别 + const patterns = patternAnalyzer.analyzeAndUpdatePatterns( + devId, conversation, result.files, buildTimeMs, success + ); + + // 进化日志 + evolutionLogger.logEvent('build_complete', '项目构建完成: ' + result.projectName, { + dev_id: devId, + project: result.projectName, + files_count: (result.files || []).length, + build_time_ms: buildTimeMs, + success: success, + patterns_detected: patterns + }); + } catch (err) { + console.error('Post-build evolution error:', err.message); } } diff --git a/persona-studio/backend/brain/evolution-logger.js b/persona-studio/backend/brain/evolution-logger.js new file mode 100644 index 00000000..670af427 --- /dev/null +++ b/persona-studio/backend/brain/evolution-logger.js @@ -0,0 +1,146 @@ +/** + * persona-studio · 进化日志记录器 + * + * 功能:记录系统每一次自进化事件 + * 事件类型:profile_update / knowledge_extract / pattern_update / + * model_benchmark / quality_score / system_init + */ +const fs = require('fs'); +const path = require('path'); + +const BRAIN_DIR = path.join(__dirname, '..', '..', 'brain'); +const EVOLUTION_LOG_PATH = path.join(BRAIN_DIR, 'evolution-log.json'); + +/** + * 加载进化日志 + */ +function loadEvolutionLog() { + try { + return JSON.parse(fs.readFileSync(EVOLUTION_LOG_PATH, 'utf-8')); + } catch { + return { + schema_version: '1.0', + description: '系统进化日志', + last_updated: null, + total_events: 0, + events: [] + }; + } +} + +/** + * 保存进化日志 + */ +function saveEvolutionLog(log) { + log.last_updated = new Date().toISOString(); + log.total_events = log.events.length; + fs.writeFileSync(EVOLUTION_LOG_PATH, JSON.stringify(log, null, 2), 'utf-8'); +} + +/** + * 记录进化事件 + * @param {string} type - 事件类型 + * @param {string} description - 事件描述 + * @param {object} metadata - 事件元数据 + */ +function logEvent(type, description, metadata) { + const log = loadEvolutionLog(); + + const event = { + id: 'EVT-' + Date.now(), + type: type, + description: description, + metadata: metadata || {}, + timestamp: new Date().toISOString() + }; + + log.events.push(event); + + // 保留最近 1000 条 + if (log.events.length > 1000) { + log.events = log.events.slice(-1000); + } + + saveEvolutionLog(log); + return event; +} + +/** + * 记录画像更新事件 + */ +function logProfileUpdate(devId, fieldsUpdated) { + return logEvent('profile_update', '用户画像更新: ' + devId, { + dev_id: devId, + fields_updated: fieldsUpdated || [] + }); +} + +/** + * 记录知识提取事件 + */ +function logKnowledgeExtract(devId, entriesCount, projectName) { + return logEvent('knowledge_extract', '知识提取: ' + entriesCount + ' 条新知识', { + dev_id: devId, + entries_count: entriesCount, + project: projectName || null + }); +} + +/** + * 记录模式更新事件 + */ +function logPatternUpdate(patternNames, devId) { + return logEvent('pattern_update', '模式识别: ' + (patternNames || []).join(', '), { + dev_id: devId, + patterns: patternNames || [] + }); +} + +/** + * 记录质量评分事件 + */ +function logQualityScore(devId, projectName, score) { + return logEvent('quality_score', '质量评分: ' + projectName + ' = ' + score, { + dev_id: devId, + project: projectName, + score: score + }); +} + +/** + * 记录模型评测事件 + */ +function logModelBenchmark(modelsCount, routingChanges) { + return logEvent('model_benchmark', '模型评测完成: ' + modelsCount + ' 个模型', { + models_count: modelsCount, + routing_changes: routingChanges || [] + }); +} + +/** + * 获取最近事件 + * @param {number} limit - 数量限制 + * @param {string} type - 可选,按类型过滤 + * @returns {Array} 事件列表 + */ +function getRecentEvents(limit, type) { + const log = loadEvolutionLog(); + let events = log.events; + + if (type) { + events = events.filter(function (e) { return e.type === type; }); + } + + return events.slice(-(limit || 20)).reverse(); +} + +module.exports = { + logEvent, + logProfileUpdate, + logKnowledgeExtract, + logPatternUpdate, + logQualityScore, + logModelBenchmark, + getRecentEvents, + loadEvolutionLog +}; diff --git a/persona-studio/backend/brain/knowledge-extractor.js b/persona-studio/backend/brain/knowledge-extractor.js new file mode 100644 index 00000000..241912cd --- /dev/null +++ b/persona-studio/backend/brain/knowledge-extractor.js @@ -0,0 +1,225 @@ +/** + * persona-studio · 知识提取引擎 + * + * 触发:代码生成完成时 / 用户确认方案时 / 解决技术问题时 + * 功能:从对话中提取有价值的知识条目存入 knowledge-base.json + */ +const fs = require('fs'); +const path = require('path'); + +const BRAIN_DIR = path.join(__dirname, '..', '..', 'brain'); +const KB_PATH = path.join(BRAIN_DIR, 'knowledge-base.json'); + +/** + * 加载知识库 + */ +function loadKnowledgeBase() { + try { + return JSON.parse(fs.readFileSync(KB_PATH, 'utf-8')); + } catch { + return { + schema_version: '1.0', + description: '系统知识库 · 自动积累', + last_updated: null, + total_entries: 0, + entries: [] + }; + } +} + +/** + * 保存知识库 + */ +function saveKnowledgeBase(kb) { + kb.last_updated = new Date().toISOString(); + kb.total_entries = kb.entries.length; + fs.writeFileSync(KB_PATH, JSON.stringify(kb, null, 2), 'utf-8'); +} + +/** + * 从对话中提取技术方案 + */ +function extractSolutions(conversation, sourceExp, sourceProject) { + const entries = []; + const messages = conversation || []; + + for (let i = 0; i < messages.length; i++) { + const msg = messages[i]; + if (msg.role !== 'assistant') continue; + const content = msg.content || ''; + + // 检测方案类关键词 + if (/技术方案|解决方案|实现方式|推荐.*方案|建议使用/i.test(content)) { + entries.push({ + id: 'KB-' + Date.now() + '-' + i, + type: 'solution', + category: detectCategory(content), + tags: extractTags(content), + title: extractTitle(content), + content: content.substring(0, 1000), + source_exp: sourceExp, + source_project: sourceProject || null, + usage_count: 0, + created_at: new Date().toISOString() + }); + } + + // 检测最佳实践 + if (/最佳实践|best practice|推荐做法|正确.*方式/i.test(content)) { + entries.push({ + id: 'KB-' + Date.now() + '-bp-' + i, + type: 'best_practice', + category: detectCategory(content), + tags: extractTags(content), + title: extractTitle(content), + content: content.substring(0, 1000), + source_exp: sourceExp, + source_project: sourceProject || null, + usage_count: 0, + created_at: new Date().toISOString() + }); + } + + // 检测常见陷阱 + if (/注意|陷阱|坑|避免|不要.*这样|常见错误|pitfall/i.test(content)) { + entries.push({ + id: 'KB-' + Date.now() + '-pit-' + i, + type: 'pitfall', + category: detectCategory(content), + tags: extractTags(content), + title: extractTitle(content), + content: content.substring(0, 1000), + source_exp: sourceExp, + source_project: sourceProject || null, + usage_count: 0, + created_at: new Date().toISOString() + }); + } + } + + return entries; +} + +/** + * 检测技术类别 + */ +function detectCategory(content) { + const lower = content.toLowerCase(); + if (/react|vue|angular|前端|frontend|html|css|ui/i.test(lower)) return 'frontend'; + if (/node|express|api|后端|backend|server|数据库|sql/i.test(lower)) return 'backend'; + if (/docker|部署|deploy|ci|cd|运维|devops|nginx/i.test(lower)) return 'devops'; + if (/设计|design|ux|ui|交互|布局|layout/i.test(lower)) return 'design'; + if (/安全|security|auth|鉴权|加密|token/i.test(lower)) return 'security'; + if (/性能|performance|优化|缓存|cache/i.test(lower)) return 'performance'; + return 'general'; +} + +/** + * 提取标签 + */ +function extractTags(content) { + const tags = []; + const techKeywords = [ + 'javascript', 'typescript', 'python', 'react', 'vue', 'node', + 'express', 'html', 'css', 'sql', 'mongodb', 'redis', 'docker', + 'nginx', 'git', 'api', 'rest', 'graphql', 'websocket' + ]; + const lower = content.toLowerCase(); + techKeywords.forEach(function (kw) { + if (lower.includes(kw)) tags.push(kw); + }); + return tags.slice(0, 10); +} + +/** + * 提取标题(取第一行非空文本的前50字符) + */ +function extractTitle(content) { + const lines = content.split('\n').filter(function (l) { return l.trim().length > 0; }); + if (lines.length === 0) return '未命名知识条目'; + const first = lines[0].replace(/^[#*\-\s]+/, '').trim(); + return first.length > 50 ? first.substring(0, 50) + '…' : first; +} + +/** + * 将提取的知识添加到知识库(自动去重) + */ +function addToKnowledgeBase(entries) { + if (!entries || entries.length === 0) return; + + const kb = loadKnowledgeBase(); + + entries.forEach(function (entry) { + // 简单去重:同标题+同类型 = 已存在 + const exists = kb.entries.some(function (existing) { + return existing.title === entry.title && existing.type === entry.type; + }); + if (!exists) { + kb.entries.push(entry); + } + }); + + // 保留最近 500 条 + if (kb.entries.length > 500) { + kb.entries = kb.entries.slice(-500); + } + + saveKnowledgeBase(kb); +} + +/** + * 查询知识库(供 persona-engine 调用) + * @param {string} query - 搜索关键词 + * @param {number} limit - 最大返回数量 + * @returns {Array} 匹配的知识条目 + */ +function queryKnowledge(query, limit) { + const kb = loadKnowledgeBase(); + if (!query || kb.entries.length === 0) return []; + + const lowerQuery = query.toLowerCase(); + const keywords = lowerQuery.split(/[\s,,。.!??!]+/).filter(Boolean); + + const scored = kb.entries.map(function (entry) { + let score = 0; + const entryText = ((entry.title || '') + ' ' + (entry.content || '') + ' ' + (entry.tags || []).join(' ')).toLowerCase(); + + keywords.forEach(function (kw) { + if (entryText.includes(kw)) score += 1; + }); + + // 使用次数加权 + score += (entry.usage_count || 0) * 0.1; + + return { entry: entry, score: score }; + }); + + return scored + .filter(function (s) { return s.score > 0; }) + .sort(function (a, b) { return b.score - a.score; }) + .slice(0, limit || 5) + .map(function (s) { + // 增加使用计数 + s.entry.usage_count = (s.entry.usage_count || 0) + 1; + return s.entry; + }); +} + +/** + * 核心方法:自动提取知识 + */ +function autoExtractKnowledge(devId, conversation, projectName) { + const entries = extractSolutions(conversation, devId, projectName); + if (entries.length > 0) { + addToKnowledgeBase(entries); + } + return entries.length; +} + +module.exports = { + autoExtractKnowledge, + queryKnowledge, + loadKnowledgeBase, + addToKnowledgeBase, + extractSolutions +}; diff --git a/persona-studio/backend/brain/memory-injector.js b/persona-studio/backend/brain/memory-injector.js new file mode 100644 index 00000000..c37b81d5 --- /dev/null +++ b/persona-studio/backend/brain/memory-injector.js @@ -0,0 +1,350 @@ +/** + * persona-studio · 记忆注入Agent + * + * 三步走机制: + * Step 1 压缩:把长对话压缩成结构化摘要 + * Step 2 注入:每次调模型前,摘要注入 system prompt + * Step 3 刷新:每10轮或token>50k时重新压缩 + * + * 五层 system prompt 结构: + * 第1层:人格体身份(固定·来自 persona-config.json) + * 第2层:通感语言风格(固定) + * 第3层:用户画像(来自 profile.json) + * 第4层:记忆摘要(来自 compressed.json · ≤8k token) + * 第5层:最近10轮原始对话(滑动窗口) + */ +const fs = require('fs'); +const path = require('path'); + +const BRAIN_DIR = path.join(__dirname, '..', '..', 'brain'); +const MEMORY_DIR = path.join(BRAIN_DIR, 'memory'); +const CONFIG_PATH = path.join(__dirname, 'model-config.json'); + +/** + * 加载注入配置 + */ +function loadInjectionConfig() { + try { + const config = JSON.parse(fs.readFileSync(CONFIG_PATH, 'utf-8')); + return config.memory_injection || getDefaultConfig(); + } catch { + return getDefaultConfig(); + } +} + +function getDefaultConfig() { + return { + enabled: true, + compression_model: 'quick_reply', + compression_trigger: { + every_n_rounds: 10, + token_threshold: 50000, + force_on_session_start: true + }, + injection_strategy: { + system_prompt_max_tokens: 8000, + sliding_window_rounds: 10, + priority: ['confirmed_decisions', 'requirements', 'open_questions', 'user_preferences', 'emotional_signals'] + } + }; +} + +/** + * 获取滑动窗口大小 + */ +function getSlidingWindowSize() { + const config = loadInjectionConfig(); + return (config.injection_strategy && config.injection_strategy.sliding_window_rounds) || 10; +} + +/** + * 加载压缩摘要 + */ +function loadCompressed(devId) { + const file = path.join(MEMORY_DIR, devId, 'compressed.json'); + try { + return JSON.parse(fs.readFileSync(file, 'utf-8')); + } catch { + return null; + } +} + +/** + * 保存压缩摘要 + */ +function saveCompressed(devId, compressed) { + const dir = path.join(MEMORY_DIR, devId); + if (!fs.existsSync(dir)) { + fs.mkdirSync(dir, { recursive: true }); + } + fs.writeFileSync( + path.join(dir, 'compressed.json'), + JSON.stringify(compressed, null, 2), + 'utf-8' + ); +} + +/** + * 加载注入日志 + */ +function loadInjectionLog(devId) { + const file = path.join(MEMORY_DIR, devId, 'injection-log.json'); + try { + return JSON.parse(fs.readFileSync(file, 'utf-8')); + } catch { + return { entries: [] }; + } +} + +/** + * 保存注入日志 + */ +function saveInjectionLog(devId, log) { + const dir = path.join(MEMORY_DIR, devId); + if (!fs.existsSync(dir)) { + fs.mkdirSync(dir, { recursive: true }); + } + const entries = log.entries || []; + // 保留最近 100 条日志 + if (entries.length > 100) { + log.entries = entries.slice(-100); + } + fs.writeFileSync( + path.join(dir, 'injection-log.json'), + JSON.stringify(log, null, 2), + 'utf-8' + ); +} + +/** + * 加载用户画像 + */ +function loadProfile(devId) { + const file = path.join(MEMORY_DIR, devId, 'profile.json'); + try { + return JSON.parse(fs.readFileSync(file, 'utf-8')); + } catch { + return null; + } +} + +/** + * 粗略估算 token 数量(中文约 1.5 token/字,英文约 0.25 token/word) + */ +function estimateTokens(text) { + if (!text) return 0; + const chineseChars = (text.match(/[\u4e00-\u9fff]/g) || []).length; + const otherChars = text.length - chineseChars; + return Math.ceil(chineseChars * 1.5 + otherChars * 0.4); +} + +/** + * 判断是否需要重新压缩 + */ +function needsRecompression(devId, history) { + const config = loadInjectionConfig(); + if (!config.enabled) return false; + + const trigger = config.compression_trigger || {}; + const compressed = loadCompressed(devId); + const totalRounds = Math.floor((history || []).length / 2); + + // 首次对话强制压缩 + if (!compressed && trigger.force_on_session_start) return true; + + // 每 N 轮压缩一次 + if (compressed && trigger.every_n_rounds) { + const processedRounds = compressed.total_rounds_processed || 0; + if (totalRounds - processedRounds >= trigger.every_n_rounds) return true; + } + + // token 阈值 + if (trigger.token_threshold) { + const totalText = (history || []).map(function (m) { return m.content || ''; }).join(''); + if (estimateTokens(totalText) > trigger.token_threshold) return true; + } + + return false; +} + +/** + * 本地压缩对话历史(不依赖 AI 模型的规则式压缩) + * 当模型不可用时的降级方案 + */ +function localCompress(devId, history) { + const messages = history || []; + const userMessages = messages.filter(function (m) { return m.role === 'user'; }); + const assistantMessages = messages.filter(function (m) { return m.role === 'assistant'; }); + + // 提取需求 + const requirements = []; + const decisions = []; + const openQuestions = []; + + userMessages.forEach(function (msg) { + const content = msg.content || ''; + if (/想做|需要|功能|要求|做一个/i.test(content)) { + requirements.push(content.substring(0, 100)); + } + if (/[??]/.test(content)) { + openQuestions.push(content.substring(0, 100)); + } + }); + + assistantMessages.forEach(function (msg) { + const content = msg.content || ''; + if (/方案已确认|确认|可以开始|建议使用|推荐/i.test(content)) { + decisions.push(content.substring(0, 100)); + } + }); + + const compressed = { + version: 1, + last_compressed_at: new Date().toISOString(), + total_rounds_processed: Math.floor(messages.length / 2), + summary: { + requirements: requirements.slice(-10), + confirmed_decisions: decisions.slice(-10), + user_preferences: {}, + open_questions: openQuestions.slice(-5), + emotional_signals: { + overall: 'neutral', + last_mood: 'neutral' + } + } + }; + + // 融入画像信息 + const profile = loadProfile(devId); + if (profile) { + if (profile.communication_style) { + compressed.summary.user_preferences.communication_style = + profile.communication_style.verbosity || 'normal'; + } + if (profile.design_preferences) { + compressed.summary.user_preferences.design_preference = + profile.design_preferences.color_scheme || null; + } + if (profile.tech_assessment) { + compressed.summary.user_preferences.tech_level = + profile.tech_assessment.level || null; + } + if (profile.emotional_profile) { + compressed.summary.emotional_signals.overall = + profile.emotional_profile.current_mood || 'neutral'; + } + } + + saveCompressed(devId, compressed); + return compressed; +} + +/** + * 构建注入的五层 system prompt + * @param {string} basePrompt - 基础 system prompt(第1-2层) + * @param {string} devId - 开发者编号 + * @param {object} memory - 记忆对象 + * @param {Array} history - 对话历史 + * @returns {string} 注入后的完整 system prompt + */ +function buildInjectedSystemPrompt(basePrompt, devId, memory, history) { + const config = loadInjectionConfig(); + if (!config.enabled || !devId || devId === 'GUEST') { + return basePrompt; + } + + const parts = [basePrompt]; // 第1-2层已包含在 basePrompt 中 + + // 第3层:用户画像 + const profile = loadProfile(devId); + if (profile) { + const profileParts = []; + if (profile.tech_assessment && profile.tech_assessment.level) { + profileParts.push('技术水平:' + profile.tech_assessment.level); + } + if (profile.tech_assessment && profile.tech_assessment.known_skills && profile.tech_assessment.known_skills.length > 0) { + profileParts.push('已知技能:' + profile.tech_assessment.known_skills.join(', ')); + } + if (profile.communication_style && profile.communication_style.verbosity) { + profileParts.push('沟通偏好:' + profile.communication_style.verbosity); + } + if (profile.design_preferences && profile.design_preferences.color_scheme) { + profileParts.push('设计偏好:' + profile.design_preferences.color_scheme); + } + if (profile.emotional_profile && profile.emotional_profile.current_mood) { + profileParts.push('当前情绪:' + profile.emotional_profile.current_mood); + } + + if (profileParts.length > 0) { + parts.push('\n## 用户画像\n' + profileParts.join('\n')); + } + } + + // 第4层:记忆摘要 + if (needsRecompression(devId, history)) { + localCompress(devId, history); + } + + const compressed = loadCompressed(devId); + if (compressed && compressed.summary) { + const summaryParts = []; + const priority = (config.injection_strategy && config.injection_strategy.priority) || []; + const summary = compressed.summary; + + priority.forEach(function (key) { + if (key === 'confirmed_decisions' && summary.confirmed_decisions && summary.confirmed_decisions.length > 0) { + summaryParts.push('已确认的决策:\n- ' + summary.confirmed_decisions.slice(-5).join('\n- ')); + } + if (key === 'requirements' && summary.requirements && summary.requirements.length > 0) { + summaryParts.push('用户需求要点:\n- ' + summary.requirements.slice(-5).join('\n- ')); + } + if (key === 'open_questions' && summary.open_questions && summary.open_questions.length > 0) { + summaryParts.push('待解决问题:\n- ' + summary.open_questions.slice(-3).join('\n- ')); + } + if (key === 'user_preferences' && summary.user_preferences) { + const prefs = Object.entries(summary.user_preferences) + .filter(function (pair) { return pair[1] != null; }) + .map(function (pair) { return pair[0] + ': ' + pair[1]; }); + if (prefs.length > 0) { + summaryParts.push('用户偏好:' + prefs.join(', ')); + } + } + if (key === 'emotional_signals' && summary.emotional_signals) { + if (summary.emotional_signals.overall && summary.emotional_signals.overall !== 'neutral') { + summaryParts.push('情感信号:整体 ' + summary.emotional_signals.overall); + } + } + }); + + if (summaryParts.length > 0) { + parts.push('\n## 记忆摘要(远期记忆)\n' + summaryParts.join('\n')); + } + } + + // 记录注入日志 + try { + const log = loadInjectionLog(devId); + log.entries.push({ + timestamp: new Date().toISOString(), + layers_injected: parts.length, + has_profile: !!profile, + has_compressed: !!compressed, + history_rounds: Math.floor((history || []).length / 2) + }); + saveInjectionLog(devId, log); + } catch (_e) { /* log failed silently */ } + + return parts.join('\n'); +} + +module.exports = { + buildInjectedSystemPrompt, + getSlidingWindowSize, + needsRecompression, + localCompress, + loadCompressed, + saveCompressed, + loadProfile, + estimateTokens, + loadInjectionConfig +}; diff --git a/persona-studio/backend/brain/memory-manager.js b/persona-studio/backend/brain/memory-manager.js index 918387cb..d2cdc92c 100644 --- a/persona-studio/backend/brain/memory-manager.js +++ b/persona-studio/backend/brain/memory-manager.js @@ -4,6 +4,9 @@ */ const fs = require('fs'); const path = require('path'); +const profileLearner = require('./profile-learner'); +const knowledgeExtractor = require('./knowledge-extractor'); +const evolutionLogger = require('./evolution-logger'); const BRAIN_DIR = path.join(__dirname, '..', '..', 'brain'); const MEMORY_DIR = path.join(BRAIN_DIR, 'memory'); @@ -125,6 +128,43 @@ function loadProfile(devId) { } } +/** + * 更新用户画像(调用 profile-learner) + * @param {string} devId - 开发编号 + * @param {string} userMessage - 用户消息 + * @param {string} assistantReply - 助手回复 + */ +function updateProfile(devId, userMessage, assistantReply) { + if (!devId || devId === 'GUEST') return; + try { + const result = profileLearner.updateProfile(devId, userMessage, assistantReply); + if (result) { + evolutionLogger.logProfileUpdate(devId, Object.keys(result)); + } + } catch (err) { + console.error('Profile update error:', err.message); + } +} + +/** + * 自动提取知识(调用 knowledge-extractor) + * @param {string} devId - 开发编号 + * @param {Array} conversation - 对话历史 + * @param {string} projectName - 项目名 + */ +function autoExtractKnowledge(devId, conversation, projectName) { + try { + const count = knowledgeExtractor.autoExtractKnowledge(devId, conversation, projectName); + if (count > 0) { + evolutionLogger.logKnowledgeExtract(devId, count, projectName); + } + return count; + } catch (err) { + console.error('Knowledge extraction error:', err.message); + return 0; + } +} + module.exports = { loadMemory, saveMemory, @@ -132,5 +172,7 @@ module.exports = { updateLastTopic, loadProjects, addProject, - loadProfile + loadProfile, + updateProfile, + autoExtractKnowledge }; diff --git a/persona-studio/backend/brain/model-config.json b/persona-studio/backend/brain/model-config.json index 21578da2..bcbbc38f 100644 --- a/persona-studio/backend/brain/model-config.json +++ b/persona-studio/backend/brain/model-config.json @@ -2,6 +2,7 @@ "api_source": "third_party_combined", "api_key_env": "MODEL_API_KEY", "base_url": "https://api.yunwu.ai/v1", + "context_window_target": 200000, "auto_detect": { "enabled": true, "schedule": "daily_0300", @@ -13,7 +14,7 @@ }, "routing_rules": { "chat": { - "priority": ["chinese_ability", "conversation_quality", "speed"], + "priority": ["chinese_ability", "conversation_quality", "context_window", "speed"], "max_latency_ms": 5000 }, "code_generation": { @@ -21,7 +22,7 @@ "max_latency_ms": 30000 }, "code_review": { - "priority": ["reasoning", "code_quality"], + "priority": ["reasoning", "code_quality", "context_window"], "max_latency_ms": 15000 }, "quick_reply": { @@ -29,6 +30,20 @@ "max_latency_ms": 2000 } }, + "memory_injection": { + "enabled": true, + "compression_model": "quick_reply", + "compression_trigger": { + "every_n_rounds": 10, + "token_threshold": 50000, + "force_on_session_start": true + }, + "injection_strategy": { + "system_prompt_max_tokens": 8000, + "sliding_window_rounds": 10, + "priority": ["confirmed_decisions", "requirements", "open_questions", "user_preferences", "emotional_signals"] + } + }, "fallback": { "max_retries": 3, "timeout_ms": 30000, diff --git a/persona-studio/backend/brain/model-router.js b/persona-studio/backend/brain/model-router.js index 6f935e17..b4279486 100644 --- a/persona-studio/backend/brain/model-router.js +++ b/persona-studio/backend/brain/model-router.js @@ -53,9 +53,26 @@ function selectModel(taskType) { const benchmark = loadBenchmark(); const apiKey = process.env.MODEL_API_KEY || ''; const baseUrl = config.base_url || 'https://api.yunwu.ai/v1'; + const contextTarget = config.context_window_target || 200000; // 如果有 benchmark 且有路由表,使用路由表 if (benchmark && benchmark.routing_table && benchmark.routing_table[taskType]) { + // 优先选择 context_window >= contextTarget 的模型 + if (benchmark.benchmark) { + const preferred = benchmark.benchmark.find(function (m) { + return m.available && + m.scores && + m.scores.context_window >= contextTarget; + }); + if (preferred) { + return { + model: preferred.model_id, + baseUrl, + apiKey + }; + } + } + return { model: benchmark.routing_table[taskType], baseUrl, @@ -92,6 +109,7 @@ function selectModel(taskType) { async function callModel({ model, baseUrl, apiKey, messages, maxTokens = 2000, temperature = 0.8 }) { const config = loadConfig(); const fallbackConfig = config.fallback || { max_retries: 3, timeout_ms: 30000 }; + const contextWindowTarget = config.context_window_target || 200000; // 尝试调用,支持降级 const benchmark = loadBenchmark(); @@ -227,10 +245,13 @@ async function autoDetect() { }); // 生成基准测试结果 + const contextTarget = config.context_window_target || 200000; const benchmarkData = { last_updated: new Date().toISOString(), models_detected: modelsList.length, + context_window_target: contextTarget, benchmark: modelsList.slice(0, 10).map(function (m) { + const ctxWindow = m.context_window || m.context_length || 32000; return { model_id: m.id, available: true, @@ -240,9 +261,10 @@ async function autoDetect() { code_quality: 80, reasoning: 80, speed_ms: 2000, - context_window: m.context_window || 32000, + context_window: ctxWindow, cost_per_1k_tokens: 0.002 }, + meets_context_target: ctxWindow >= contextTarget, best_for: ['chat'] }; }), diff --git a/persona-studio/backend/brain/pattern-analyzer.js b/persona-studio/backend/brain/pattern-analyzer.js new file mode 100644 index 00000000..8944941d --- /dev/null +++ b/persona-studio/backend/brain/pattern-analyzer.js @@ -0,0 +1,202 @@ +/** + * persona-studio · 模式识别引擎 + * + * 触发:每完成一个项目 + 每日凌晨聚合 + * 功能:从已完成项目中识别高频模式,建立模式库 + */ +const fs = require('fs'); +const path = require('path'); + +const BRAIN_DIR = path.join(__dirname, '..', '..', 'brain'); +const PATTERN_PATH = path.join(BRAIN_DIR, 'pattern-library.json'); +const MEMORY_DIR = path.join(BRAIN_DIR, 'memory'); + +/** + * 加载模式库 + */ +function loadPatternLibrary() { + try { + return JSON.parse(fs.readFileSync(PATTERN_PATH, 'utf-8')); + } catch { + return { + schema_version: '1.0', + description: '系统模式库 · 自动识别', + last_updated: null, + total_patterns: 0, + patterns: [] + }; + } +} + +/** + * 保存模式库 + */ +function savePatternLibrary(lib) { + lib.last_updated = new Date().toISOString(); + lib.total_patterns = lib.patterns.length; + fs.writeFileSync(PATTERN_PATH, JSON.stringify(lib, null, 2), 'utf-8'); +} + +/** + * 从项目中检测模式类型 + */ +function detectPatternType(conversation, projectFiles) { + const allText = (conversation || []) + .map(function (m) { return m.content || ''; }) + .join('\n') + .toLowerCase(); + + const patterns = []; + + const patternDefs = [ + { name: '个人博客', keywords: ['博客', 'blog', '文章', '发布', '个人主页'] }, + { name: '登录注册', keywords: ['登录', '注册', 'login', 'register', '用户认证', '用户系统'] }, + { name: '管理后台', keywords: ['管理', '后台', 'dashboard', '管理面板', 'admin'] }, + { name: '电商页面', keywords: ['商城', '购物', '商品', '购买', '电商', 'shop'] }, + { name: '展示页面', keywords: ['展示', '介绍', '落地页', 'landing', '产品页'] }, + { name: '数据表格', keywords: ['表格', '数据', '列表', '筛选', 'table', '搜索'] }, + { name: '表单系统', keywords: ['表单', '提交', '验证', 'form', '输入'] }, + { name: '聊天应用', keywords: ['聊天', 'chat', '即时通讯', '消息', '对话'] }, + { name: '小工具', keywords: ['工具', '计算器', '转换器', '生成器', 'tool', 'utility'] }, + { name: 'API服务', keywords: ['api', '接口', '后端服务', 'restful', 'server'] } + ]; + + patternDefs.forEach(function (def) { + const matchCount = def.keywords.filter(function (kw) { + return allText.includes(kw); + }).length; + + if (matchCount >= 2) { + patterns.push(def.name); + } + }); + + return patterns.length > 0 ? patterns : ['通用项目']; +} + +/** + * 检测技术栈 + */ +function detectTechStack(conversation, projectFiles) { + const allText = (conversation || []) + .map(function (m) { return m.content || ''; }) + .join('\n') + .toLowerCase(); + + const stack = []; + const techMap = { + 'HTML/CSS/JS': ['html', 'css', 'javascript'], + 'React': ['react', 'jsx', 'tsx'], + 'Vue': ['vue', 'vuex'], + 'Node.js': ['node', 'express', 'koa'], + 'Python': ['python', 'flask', 'django'], + 'TypeScript': ['typescript', 'ts'], + 'Tailwind': ['tailwind'], + 'Bootstrap': ['bootstrap'] + }; + + for (const [tech, keywords] of Object.entries(techMap)) { + if (keywords.some(function (kw) { return allText.includes(kw); })) { + stack.push(tech); + } + } + + // 从文件扩展名推断 + if (projectFiles && projectFiles.length > 0) { + const exts = projectFiles.map(function (f) { + return path.extname(f).toLowerCase(); + }); + + if (exts.includes('.html')) stack.push('HTML/CSS/JS'); + if (exts.includes('.jsx') || exts.includes('.tsx')) stack.push('React'); + if (exts.includes('.vue')) stack.push('Vue'); + if (exts.includes('.py')) stack.push('Python'); + } + + return [...new Set(stack)]; +} + +/** + * 核心方法:分析项目并更新模式库 + * @param {string} devId - 开发者编号 + * @param {Array} conversation - 对话历史 + * @param {Array} projectFiles - 项目文件列表 + * @param {number} buildTimeMs - 构建耗时(毫秒) + * @param {boolean} success - 是否成功 + */ +function analyzeAndUpdatePatterns(devId, conversation, projectFiles, buildTimeMs, success) { + const lib = loadPatternLibrary(); + const patternNames = detectPatternType(conversation, projectFiles); + const techStack = detectTechStack(conversation, projectFiles); + + patternNames.forEach(function (patternName) { + // 查找已有模式 + const existing = lib.patterns.find(function (p) { return p.name === patternName; }); + + if (existing) { + // 更新已有模式 + existing.frequency = (existing.frequency || 0) + 1; + existing.common_tech_stack = mergeArrays(existing.common_tech_stack, techStack); + if (buildTimeMs) { + existing.avg_build_time = existing.avg_build_time + ? Math.round((existing.avg_build_time + buildTimeMs) / 2) + : buildTimeMs; + } + if (success !== undefined) { + const totalAttempts = existing.frequency; + const prevSuccesses = Math.round((existing.success_rate || 100) / 100 * (totalAttempts - 1)); + existing.success_rate = Math.round((prevSuccesses + (success ? 1 : 0)) / totalAttempts * 100); + } + existing.last_seen = new Date().toISOString(); + } else { + // 添加新模式 + lib.patterns.push({ + name: patternName, + frequency: 1, + common_features: [], + common_tech_stack: techStack, + avg_build_time: buildTimeMs || null, + success_rate: success !== false ? 100 : 0, + first_seen: new Date().toISOString(), + last_seen: new Date().toISOString() + }); + } + }); + + savePatternLibrary(lib); + return patternNames; +} + +/** + * 查询模式库(供 persona-engine 调用) + * @param {string} query - 搜索关键词 + * @returns {Array} 匹配的模式 + */ +function queryPatterns(query) { + const lib = loadPatternLibrary(); + if (!query || lib.patterns.length === 0) return []; + + const lower = query.toLowerCase(); + + return lib.patterns + .filter(function (p) { + return p.name.toLowerCase().includes(lower) || + (p.common_tech_stack || []).some(function (t) { return t.toLowerCase().includes(lower); }); + }) + .sort(function (a, b) { return (b.frequency || 0) - (a.frequency || 0); }); +} + +/** + * 工具:合并去重数组 + */ +function mergeArrays(arr1, arr2) { + return [...new Set((arr1 || []).concat(arr2 || []))]; +} + +module.exports = { + analyzeAndUpdatePatterns, + queryPatterns, + loadPatternLibrary, + detectPatternType, + detectTechStack +}; diff --git a/persona-studio/backend/brain/persona-engine.js b/persona-studio/backend/brain/persona-engine.js index 64586ced..b8d1b885 100644 --- a/persona-studio/backend/brain/persona-engine.js +++ b/persona-studio/backend/brain/persona-engine.js @@ -6,6 +6,9 @@ const fs = require('fs'); const path = require('path'); const modelRouter = require('./model-router'); +const knowledgeExtractor = require('./knowledge-extractor'); +const patternAnalyzer = require('./pattern-analyzer'); +const memoryInjector = require('./memory-injector'); const PERSONA_CONFIG_PATH = path.join(__dirname, '..', '..', 'brain', 'persona-config.json'); @@ -154,15 +157,41 @@ async function respond({ dev_id, message, history, memory, isGreeting }) { return getLocalReply(message, memory, config, isGuest); } - const systemPrompt = buildSystemPrompt(config, memory, devInfo); + // 查询知识库和模式库,为回复提供参考 + let knowledgeContext = ''; + try { + const relevantKnowledge = knowledgeExtractor.queryKnowledge(message, 3); + if (relevantKnowledge.length > 0) { + knowledgeContext += '\n\n## 系统知识库参考(不要直接暴露给用户,作为回复参考)\n'; + relevantKnowledge.forEach(function (k) { + knowledgeContext += '- [' + k.type + '] ' + k.title + '\n'; + }); + } + + const relevantPatterns = patternAnalyzer.queryPatterns(message); + if (relevantPatterns.length > 0) { + knowledgeContext += '\n## 已知高频模式\n'; + relevantPatterns.slice(0, 3).forEach(function (p) { + knowledgeContext += '- ' + p.name + '(使用 ' + p.frequency + ' 次,成功率 ' + p.success_rate + '%,常用技术栈:' + (p.common_tech_stack || []).join(', ') + ')\n'; + }); + } + } catch (_e) { /* knowledge/pattern query failed silently */ } + + const systemPrompt = buildSystemPrompt(config, memory, devInfo) + knowledgeContext; + + // 使用记忆注入构建五层 system prompt + const injectedPrompt = memoryInjector.buildInjectedSystemPrompt( + systemPrompt, dev_id, memory, history + ); // 构建消息列表 const messages = [ - { role: 'system', content: systemPrompt } + { role: 'system', content: injectedPrompt } ]; - // 加入最近历史(最多 20 条) - const recentHistory = (history || []).slice(-20); + // 使用滑动窗口加入最近历史 + const windowSize = memoryInjector.getSlidingWindowSize(); + const recentHistory = (history || []).slice(-windowSize); recentHistory.forEach(function (msg) { messages.push({ role: msg.role === 'user' ? 'user' : 'assistant', diff --git a/persona-studio/backend/brain/profile-learner.js b/persona-studio/backend/brain/profile-learner.js new file mode 100644 index 00000000..0c69c64d --- /dev/null +++ b/persona-studio/backend/brain/profile-learner.js @@ -0,0 +1,287 @@ +/** + * persona-studio · 用户画像自动学习引擎 + * + * 触发:每轮对话结束后自动调用 updateProfile() + * 功能:从对话内容中推断用户技术水平、沟通风格、设计偏好等 + */ +const fs = require('fs'); +const path = require('path'); + +const BRAIN_DIR = path.join(__dirname, '..', '..', 'brain'); +const MEMORY_DIR = path.join(BRAIN_DIR, 'memory'); + +/** + * 确保用户目录存在 + */ +function ensureDevDir(devId) { + const dir = path.join(MEMORY_DIR, devId); + if (!fs.existsSync(dir)) { + fs.mkdirSync(dir, { recursive: true }); + } + return dir; +} + +/** + * 加载用户画像 + */ +function loadProfile(devId) { + const dir = ensureDevDir(devId); + const file = path.join(dir, 'profile.json'); + try { + return JSON.parse(fs.readFileSync(file, 'utf-8')); + } catch { + return createInitialProfile(devId); + } +} + +/** + * 保存用户画像 + */ +function saveProfile(devId, profile) { + const dir = ensureDevDir(devId); + const file = path.join(dir, 'profile.json'); + profile.updated_at = new Date().toISOString(); + fs.writeFileSync(file, JSON.stringify(profile, null, 2), 'utf-8'); +} + +/** + * 创建初始画像 + */ +function createInitialProfile(devId) { + return { + dev_id: devId, + version: 1, + created_at: new Date().toISOString(), + updated_at: null, + tech_assessment: { + level: null, + known_skills: [], + learning_skills: [], + weakness: [], + growth_velocity: null, + assessment_confidence: 0 + }, + communication_style: { + verbosity: null, + prefers_examples: null, + question_frequency: null, + patience_level: null + }, + design_preferences: { + color_scheme: null, + style_keywords: [], + layout_preference: null + }, + project_patterns: { + avg_requirements_rounds: 0, + decision_speed: null, + change_frequency: 0, + total_projects: 0 + }, + emotional_profile: { + current_mood: 'neutral', + satisfaction_trend: [], + frustration_triggers: [], + delight_triggers: [] + } + }; +} + +/** + * 从用户消息中推断技术技能 + */ +function inferSkills(message) { + const skills = []; + const skillMap = { + 'javascript': ['javascript', 'js', 'node', 'nodejs', 'npm'], + 'typescript': ['typescript', 'ts'], + 'python': ['python', 'pip', 'django', 'flask', 'fastapi'], + 'react': ['react', 'jsx', 'tsx', 'hooks', 'useState', 'useEffect'], + 'vue': ['vue', 'vuex', 'pinia', 'nuxt'], + 'html': ['html', 'dom', 'div', 'css'], + 'css': ['css', 'scss', 'sass', 'tailwind', 'styled'], + 'sql': ['sql', 'mysql', 'postgres', 'sqlite', '数据库', 'database'], + 'git': ['git', 'github', 'gitlab', 'commit', 'branch'], + 'docker': ['docker', 'container', '容器', 'k8s', 'kubernetes'], + 'api': ['api', 'rest', 'graphql', 'grpc', '接口'], + 'mobile': ['android', 'ios', 'flutter', 'react native', '移动端', '小程序'] + }; + + const lowerMsg = message.toLowerCase(); + for (const [skill, keywords] of Object.entries(skillMap)) { + if (keywords.some(function (kw) { return lowerMsg.includes(kw); })) { + skills.push(skill); + } + } + + return skills; +} + +/** + * 推断沟通风格 + */ +function inferCommunicationStyle(message) { + const style = {}; + const msgLen = message.length; + + // 简洁度 + if (msgLen < 20) { + style.verbosity = 'concise'; + } else if (msgLen > 200) { + style.verbosity = 'detailed'; + } + + // 偏好示例 + if (/给.*例子|示例|比如|example|举个/i.test(message)) { + style.prefers_examples = true; + } + + // 提问频率 + if ((message.match(/[??]/g) || []).length >= 2) { + style.question_frequency = 'high'; + } + + // 直接命令式 = patience_level low + if (/直接告诉|直接给|快速|尽快|赶紧/i.test(message)) { + style.patience_level = 'low'; + style.verbosity = 'concise'; + } + + return style; +} + +/** + * 推断技术水平 + */ +function inferTechLevel(message) { + const advancedKeywords = ['架构', '微服务', '分布式', '高并发', 'CI/CD', 'kubernetes', '设计模式', 'design pattern', '重构', '性能优化', 'SSR', 'ISR', 'WebSocket', 'gRPC']; + const intermediateKeywords = ['组件', '路由', '中间件', 'middleware', '接口', 'API', '数据库', '前后端', '部署', '框架']; + const beginnerKeywords = ['怎么开始', '入门', '新手', '不太懂', '什么是', '帮我做', '教我']; + + const lower = message.toLowerCase(); + + if (advancedKeywords.some(function (kw) { return lower.includes(kw.toLowerCase()); })) { + return 'advanced'; + } + if (intermediateKeywords.some(function (kw) { return lower.includes(kw.toLowerCase()); })) { + return 'intermediate'; + } + if (beginnerKeywords.some(function (kw) { return lower.includes(kw.toLowerCase()); })) { + return 'beginner'; + } + + return null; +} + +/** + * 推断设计偏好 + */ +function inferDesignPreferences(message) { + const prefs = { style_keywords: [] }; + + if (/暗色|深色|dark|黑色/i.test(message)) { + prefs.color_scheme = 'dark'; + } else if (/亮色|浅色|light|白色/i.test(message)) { + prefs.color_scheme = 'light'; + } + + const styleKeywords = ['简约', '科技感', '可爱', '商务', '极简', 'minimal', '现代', 'modern', '复古', '扁平', 'flat', '渐变', '毛玻璃', '赛博朋克']; + styleKeywords.forEach(function (kw) { + if (message.toLowerCase().includes(kw.toLowerCase())) { + prefs.style_keywords.push(kw); + } + }); + + return prefs; +} + +/** + * 推断情感信号 + */ +function inferEmotionalSignals(message) { + const signals = {}; + + if (/太好了|完美|厉害|赞|棒|不错|满意|感谢|谢谢|开心|👍|🎉|✅/i.test(message)) { + signals.current_mood = 'positive'; + } else if (/不行|不对|错了|差|不满|失望|难用|麻烦|头疼|崩溃|😤|😡|❌/i.test(message)) { + signals.current_mood = 'negative'; + } else { + signals.current_mood = 'neutral'; + } + + return signals; +} + +/** + * 核心方法:更新用户画像 + * @param {string} devId - 开发编号 + * @param {string} userMessage - 用户消息 + * @param {string} assistantReply - 助手回复 + */ +function updateProfile(devId, userMessage, assistantReply) { + if (!devId || devId === 'GUEST') return null; + + const profile = loadProfile(devId); + + // 1. 推断技术技能 + const skills = inferSkills(userMessage); + if (skills.length > 0) { + const known = new Set(profile.tech_assessment.known_skills || []); + skills.forEach(function (s) { known.add(s); }); + profile.tech_assessment.known_skills = Array.from(known); + profile.tech_assessment.assessment_confidence = Math.min( + (profile.tech_assessment.assessment_confidence || 0) + 5, 100 + ); + } + + // 2. 推断技术水平 + const techLevel = inferTechLevel(userMessage); + if (techLevel) { + profile.tech_assessment.level = techLevel; + } + + // 3. 推断沟通风格 + const commStyle = inferCommunicationStyle(userMessage); + Object.keys(commStyle).forEach(function (key) { + if (commStyle[key] != null) { + profile.communication_style[key] = commStyle[key]; + } + }); + + // 4. 推断设计偏好 + const designPrefs = inferDesignPreferences(userMessage); + if (designPrefs.color_scheme) { + profile.design_preferences.color_scheme = designPrefs.color_scheme; + } + if (designPrefs.style_keywords.length > 0) { + const existing = new Set(profile.design_preferences.style_keywords || []); + designPrefs.style_keywords.forEach(function (kw) { existing.add(kw); }); + profile.design_preferences.style_keywords = Array.from(existing); + } + + // 5. 推断情感信号 + const emotions = inferEmotionalSignals(userMessage); + profile.emotional_profile.current_mood = emotions.current_mood; + const trend = profile.emotional_profile.satisfaction_trend || []; + trend.push({ mood: emotions.current_mood, at: new Date().toISOString() }); + if (trend.length > 50) trend.splice(0, trend.length - 50); + profile.emotional_profile.satisfaction_trend = trend; + + // 6. 更新版本 + profile.version = (profile.version || 0) + 1; + + saveProfile(devId, profile); + return profile; +} + +module.exports = { + updateProfile, + loadProfile, + saveProfile, + createInitialProfile, + inferSkills, + inferTechLevel, + inferCommunicationStyle, + inferDesignPreferences, + inferEmotionalSignals +}; diff --git a/persona-studio/backend/routes/chat.js b/persona-studio/backend/routes/chat.js index 32ac84b6..eb510ab1 100644 --- a/persona-studio/backend/routes/chat.js +++ b/persona-studio/backend/routes/chat.js @@ -50,6 +50,9 @@ router.post('/message', async (req, res) => { // 更新最后话题 memoryManager.updateLastTopic(dev_id, message); + + // 自动更新用户画像 + memoryManager.updateProfile(dev_id, message, result.reply); } res.json({ diff --git a/persona-studio/brain/evolution-log.json b/persona-studio/brain/evolution-log.json new file mode 100644 index 00000000..a51724be --- /dev/null +++ b/persona-studio/brain/evolution-log.json @@ -0,0 +1,7 @@ +{ + "schema_version": "1.0", + "description": "系统进化日志 · 记录每次自进化事件", + "last_updated": null, + "total_events": 0, + "events": [] +} diff --git a/persona-studio/brain/knowledge-base.json b/persona-studio/brain/knowledge-base.json new file mode 100644 index 00000000..ee1adb1f --- /dev/null +++ b/persona-studio/brain/knowledge-base.json @@ -0,0 +1,7 @@ +{ + "schema_version": "1.0", + "description": "系统知识库 · 自动积累 · 每次代码生成/方案确认/技术问题解决时更新", + "last_updated": null, + "total_entries": 0, + "entries": [] +} diff --git a/persona-studio/brain/pattern-library.json b/persona-studio/brain/pattern-library.json new file mode 100644 index 00000000..9741c176 --- /dev/null +++ b/persona-studio/brain/pattern-library.json @@ -0,0 +1,7 @@ +{ + "schema_version": "1.0", + "description": "系统模式库 · 自动识别 · 每完成一个项目+每日聚合更新", + "last_updated": null, + "total_patterns": 0, + "patterns": [] +} diff --git a/persona-studio/brain/quality-scores.json b/persona-studio/brain/quality-scores.json new file mode 100644 index 00000000..8c3a729c --- /dev/null +++ b/persona-studio/brain/quality-scores.json @@ -0,0 +1,7 @@ +{ + "schema_version": "1.0", + "description": "质量评分 · 反馈驱动 · 项目完成后自动评分", + "last_updated": null, + "total_scores": 0, + "scores": [] +}