Guanghu Domestic Migration d1e47f4565
Some checks are pending
自动更新代码和重启 / update-and-restart (push) Waiting to run
CI检查 + 自动部署 / check (push) Waiting to run
CI检查 + 自动部署 / deploy (push) Blocked by required conditions
重启聊天服务 / restart (push) Waiting to run
chore: import sanitized domestic snapshot for REPO-002
Source snapshot: ca48d3ddf926d79aa138306164169baf764bb829
2026-07-17 15:54:41 +08:00

288 lines
8.1 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 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
};