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>
This commit is contained in:
copilot-swe-agent[bot] 2026-03-11 11:41:20 +00:00
parent 0429e7f58b
commit f6ad45745b
15 changed files with 1399 additions and 11 deletions

View File

@ -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);
}
}

View File

@ -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
};

View File

@ -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
};

View File

@ -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
};

View File

@ -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
};

View File

@ -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,

View File

@ -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']
};
}),

View File

@ -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
};

View File

@ -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',

View File

@ -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
};

View File

@ -50,6 +50,9 @@ router.post('/message', async (req, res) => {
// 更新最后话题
memoryManager.updateLastTopic(dev_id, message);
// 自动更新用户画像
memoryManager.updateProfile(dev_id, message, result.reply);
}
res.json({

View File

@ -0,0 +1,7 @@
{
"schema_version": "1.0",
"description": "系统进化日志 · 记录每次自进化事件",
"last_updated": null,
"total_events": 0,
"events": []
}

View File

@ -0,0 +1,7 @@
{
"schema_version": "1.0",
"description": "系统知识库 · 自动积累 · 每次代码生成/方案确认/技术问题解决时更新",
"last_updated": null,
"total_entries": 0,
"entries": []
}

View File

@ -0,0 +1,7 @@
{
"schema_version": "1.0",
"description": "系统模式库 · 自动识别 · 每完成一个项目+每日聚合更新",
"last_updated": null,
"total_patterns": 0,
"patterns": []
}

View File

@ -0,0 +1,7 @@
{
"schema_version": "1.0",
"description": "质量评分 · 反馈驱动 · 项目完成后自动评分",
"last_updated": null,
"total_scores": 0,
"scores": []
}