soul-mirror
Version:
🔮 融合千年易学智慧与现代AI技术,为程序员量身打造的命理情绪分析工具。94.1%情绪识别准确率,秒级响应,支持五行人格分析与实时运势指导。
457 lines (387 loc) • 14 kB
JavaScript
const fs = require('fs');
const path = require('path');
const { learningEngine } = require('./learningEngine');
const ContextEmotionCorrector = require('./contextEmotionCorrector');
const { EmotionGroupAnalyzer } = require('./emotionGroups');
class ChineseMoodDetector {
constructor() {
this.aliases = null;
this.categories = null;
this.contextCorrector = new ContextEmotionCorrector();
this.emotionGroupAnalyzer = new EmotionGroupAnalyzer();
this.loadData();
}
loadData() {
try {
// 优先使用改进版词典
const improvedAliasPath = path.join(__dirname, 'zh-alias-improved.json');
const aliasPath = path.join(__dirname, 'zh-alias.json');
const categoryPath = path.join(__dirname, 'zh-categories.json');
// 尝试加载改进版,失败则回退到原版
try {
this.aliases = JSON.parse(fs.readFileSync(improvedAliasPath, 'utf8'));
console.log('✅ 已加载改进版情绪词典');
} catch {
this.aliases = JSON.parse(fs.readFileSync(aliasPath, 'utf8'));
console.log('⚠️ 回退使用原版情绪词典');
}
this.categories = JSON.parse(fs.readFileSync(categoryPath, 'utf8'));
} catch (error) {
console.error('Failed to load mood data:', error.message);
this.aliases = {};
this.categories = {};
}
}
preprocessText(text) {
if (!text || typeof text !== 'string') return '';
return text
.toLowerCase() // 转换为小写
.replace(/[,.!?;。,!?;:]/g, ' ') // 替换标点符号为空格
.replace(/\s+/g, ' ') // 合并多个空格
.trim(); // 去除首尾空格
}
/**
* 计算关键词匹配得分(优化版:优先词组匹配,避免字符误判)
* @param {string} text - 输入文本
* @param {string[]} keywords - 关键词列表
* @returns {Object} 匹配结果
*/
calculateScore(text, keywords) {
let score = 0;
const hits = [];
const matchedPositions = new Set(); // 记录已匹配位置,避免重复计分
// 按长度排序关键词:优先匹配长词组,避免被短词覆盖
const sortedKeywords = keywords.sort((a, b) => b.length - a.length);
sortedKeywords.forEach(keyword => {
const lowerKeyword = keyword.toLowerCase();
// 查找所有匹配位置
let index = 0;
while ((index = text.indexOf(lowerKeyword, index)) !== -1) {
// 检查是否与已匹配区域重叠
const endIndex = index + lowerKeyword.length;
let hasOverlap = false;
for (let i = index; i < endIndex; i++) {
if (matchedPositions.has(i)) {
hasOverlap = true;
break;
}
}
if (!hasOverlap) {
// 标记已匹配位置
for (let i = index; i < endIndex; i++) {
matchedPositions.add(i);
}
// 根据关键词长度给予不同权重
let matchWeight = 1;
if (lowerKeyword.length >= 4) {
matchWeight = 3; // 长词组权重最高
} else if (lowerKeyword.length >= 2) {
matchWeight = 2; // 中等词组
} else {
matchWeight = 0.5; // 单字权重最低,减少误判
}
score += matchWeight;
hits.push(keyword);
}
index += 1; // 继续查找下一个匹配
}
});
return { score, hits };
}
/**
* 转义正则表达式特殊字符
* @param {string} string - 输入字符串
* @returns {string} 转义后的字符串
*/
escapeRegex(string) {
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}
/**
* 智能判断默认情绪(当没有明确匹配时)
* @param {string} processedText - 处理后的文本
* @param {number} textLength - 文本长度
* @returns {string} 推测的情绪类别
*/
getDefaultMood(processedText, textLength) {
// 适配新词典的情绪类别
const moodMapping = {
'无奈': '平静',
'兴奋': '高兴',
'困惑': '焦虑',
'疲惫': '疲惫'
};
// 基于文本特征的智能判断
if (textLength <= 3) {
// 极短文本,基于字符特征判断
const shortExpressions = {
'呵': '平静', '哈': '高兴', '唉': '悲伤', '嗯': '悲伤',
'累': '疲惫', '困': '疲惫', '烦': '愤怒', '爽': '高兴',
'6': '平静', 'gg': '平静', 'ok': '平静'
};
for (const [char, mood] of Object.entries(shortExpressions)) {
if (processedText.includes(char)) return mood;
}
}
// 基于标点符号和特殊表达判断
if (processedText.includes('...') || processedText.includes('。。。')) {
return '悲伤';
}
if (processedText.includes('!!!') || processedText.includes('!!!')) {
return '高兴';
}
if (processedText.includes('???') || processedText.includes('???')) {
return '焦虑';
}
// 基于文本长度判断
if (textLength > 50) {
// 长文本通常包含复杂情绪,可能是倾诉
return '焦虑';
}
if (textLength < 5) {
// 非常短的文本,可能是敷衍回应
return '无聊';
}
// 默认返回平静
return '平静';
}
/**
* 优化长文本情绪识别
* @param {Object} scores - 情绪得分对象
* @param {Object} allHits - 匹配词汇对象
* @param {string} processedText - 处理后的文本
*/
optimizeLongTextDetection(scores, allHits, processedText) {
// 长文本中的情绪强度增强
const sentences = processedText.split(/[。!?.!?]+/).filter(s => s.length > 3);
// 分析情绪词汇的分布密度
Object.keys(scores).forEach(mood => {
const hits = allHits[mood] || [];
if (hits.length > 0) {
// 计算情绪词汇在文本中的分布密度
const density = hits.length / Math.max(1, sentences.length);
// 密度加成:在长文本中多次出现同一情绪词汇
if (density > 0.3) {
scores[mood] += 2; // 高密度加成
} else if (density > 0.1) {
scores[mood] += 1; // 中密度加成
}
// 情绪词汇的多样性加成
const uniqueHits = [...new Set(hits)];
if (uniqueHits.length > 2) {
scores[mood] += uniqueHits.length * 0.5; // 多样性加成
}
}
});
// 长文本中的情绪转折识别
this.detectEmotionalTransitions(scores, processedText);
}
/**
* 检测情绪转折
* @param {Object} scores - 情绪得分对象
* @param {string} text - 文本内容
*/
detectEmotionalTransitions(scores, text) {
const transitionWords = ['但是', '不过', '然而', '可是', '只是', '虽然', '尽管'];
transitionWords.forEach(word => {
if (text.includes(word)) {
// 检测转折前后的情绪变化
const parts = text.split(word);
if (parts.length === 2) {
const [before, after] = parts;
// 分析转折后的情绪,给予更高权重
const afterScores = {};
Object.keys(this.aliases).forEach(mood => {
const keywords = this.aliases[mood];
const result = this.calculateScore(after, keywords);
afterScores[mood] = result.score;
});
// 转折后的情绪得分加权
Object.keys(afterScores).forEach(mood => {
if (afterScores[mood] > 0) {
scores[mood] += afterScores[mood] * 1.5; // 转折后情绪权重增加
}
});
}
}
});
}
/**
* 检测情绪
* @param {string} text - 输入文本
* @returns {Object} 情绪分析结果
*/
detectMood(text) {
const processedText = this.preprocessText(text);
const textLength = processedText.length;
if (!processedText) {
return {
main: '平静',
secondary: [],
element: '金',
tone: '维持型',
hits: [],
scores: {},
confidence: 0
};
}
const scores = {};
const allHits = {};
// 对每个情绪类别计算得分
Object.keys(this.aliases).forEach(mood => {
const keywords = this.aliases[mood];
const result = this.calculateScore(processedText, keywords);
scores[mood] = result.score;
allHits[mood] = result.hits;
});
// 长文本情绪识别优化
if (textLength > 30) {
this.optimizeLongTextDetection(scores, allHits, processedText);
}
// 找出得分最高的情绪(主情绪)
const sortedMoods = Object.keys(scores).sort((a, b) => scores[b] - scores[a]);
const mainMood = sortedMoods[0];
const mainScore = scores[mainMood];
// 确定次情绪(得分大于0且不是主情绪)
const secondaryMoods = sortedMoods.slice(1).filter(mood => scores[mood] > 0);
// 如果没有匹配到任何情绪,智能判断默认情绪
if (mainScore === 0) {
const defaultMood = this.getDefaultMood(processedText, textLength);
return {
main: defaultMood,
secondary: [],
element: this.categories[defaultMood]?.element || '金',
tone: this.categories[defaultMood]?.tone || '维持型',
emoji: this.categories[defaultMood]?.emoji || '😐',
hits: [],
scores: scores,
confidence: Math.max(20, Math.min(40, textLength * 5)) // 智能置信度
};
}
// 计算置信度(优化版本,确保合理的最小值)
const hitCount = allHits[mainMood]?.length || 0;
const confidence = (() => {
// 如果没有任何匹配,返回智能推测的最小置信度
if (mainScore === 0) {
// 基于文本长度和内容复杂度的智能推测
const intelligentGuess = Math.min(35, Math.max(15, textLength * 2));
return intelligentGuess;
}
// 基础置信度:基于匹配强度(更合理的映射)
let baseConfidence = Math.min(95, mainScore * 20 + 30);
// 文本长度调节:短文本不过度惩罚,长文本适度加成
const lengthFactor = Math.min(1.2, Math.max(0.6, Math.sqrt(textLength) / 4));
// 匹配词汇数量和质量加成
const hitBonus = Math.min(20, hitCount * 5);
// 匹配词汇多样性加成
const uniqueHits = [...new Set(allHits[mainMood] || [])];
const diversityBonus = Math.min(10, uniqueHits.length * 2);
// 最终置信度计算
let finalConfidence = Math.round(
baseConfidence * lengthFactor + hitBonus + diversityBonus
);
// 确保置信度在合理范围内(最低20%,最高95%)
finalConfidence = Math.max(20, Math.min(95, finalConfidence));
return finalConfidence;
})();
const originalResult = {
main: mainMood,
secondary: secondaryMoods.slice(0, 2), // 最多返回2个次情绪
element: this.categories[mainMood]?.element || '土',
tone: this.categories[mainMood]?.tone || '安抚型',
emoji: this.categories[mainMood]?.emoji || '😐',
hits: allHits[mainMood] || [],
scores: scores,
confidence: confidence,
details: {
intensity: this.categories[mainMood]?.intensity || '中',
tags: this.categories[mainMood]?.tags || [],
description: this.categories[mainMood]?.description || ''
}
};
// 使用学习引擎调整结果
const adjustedResult = learningEngine.adjustWithLearning(text, originalResult);
// 应用上下文情绪修正
const contextCorrectedResult = this.contextCorrector.correctWithContext(text, adjustedResult);
// 分析情绪组
const emotionGroupResult = this.emotionGroupAnalyzer.analyzeEmotionGroup(
contextCorrectedResult.main,
contextCorrectedResult.confidence,
contextCorrectedResult.secondary
);
// 整合最终结果
const finalResult = {
...contextCorrectedResult,
emotionGroup: emotionGroupResult,
analysis: {
...contextCorrectedResult,
groupSummary: emotionGroupResult.summary
}
};
// 记录学习数据
learningEngine.learnFromInput(text, finalResult);
return finalResult;
}
/**
* 批量检测情绪(用于测试)
* @param {string[]} texts - 文本数组
* @returns {Object[]} 情绪分析结果数组
*/
batchDetect(texts) {
return texts.map(text => this.detectMood(text));
}
/**
* 获取支持的情绪类别
* @returns {string[]} 情绪类别列表
*/
getSupportedMoods() {
return Object.keys(this.aliases);
}
/**
* 获取情绪类别详细信息
* @param {string} mood - 情绪类别
* @returns {Object|null} 情绪详细信息
*/
getMoodInfo(mood) {
return this.categories[mood] || null;
}
}
// 创建单例实例
const detector = new ChineseMoodDetector();
/**
* 主要导出函数
* @param {string} text - 输入文本
* @returns {Object} 情绪分析结果
*/
function analyzeMood(text) {
return detector.detectMood(text);
}
/**
* 批量分析
* @param {string[]} texts - 文本数组
* @returns {Object[]} 分析结果数组
*/
function batchAnalyzeMood(texts) {
return detector.batchDetect(texts);
}
/**
* 获取支持的情绪类别
* @returns {string[]} 情绪类别列表
*/
function getSupportedMoods() {
return detector.getSupportedMoods();
}
/**
* 获取情绪详细信息
* @param {string} mood - 情绪类别
* @returns {Object|null} 情绪详细信息
*/
function getMoodInfo(mood) {
return detector.getMoodInfo(mood);
}
module.exports = {
analyzeMood,
batchAnalyzeMood,
getSupportedMoods,
getMoodInfo,
ChineseMoodDetector
};