/** * AI 深度分析 MCP 工具集 — Phase 5 * * 工具: * detect_communities — 标签传播社区发现 * get_followup_reminders — 超 N 天未互动提醒 * activate_contact_profile — 全量聚合分析,更新 analysisStatus * get_conversation_insights — 对话数据分析(话题分布、互动热度) */ const { getGraphStore } = require('../../../db/GraphStore'); /** 解析 name_or_id,先精确查 id 再按名称模糊查 */ function resolvePerson(nameOrId) { const g = getGraphStore(); let node = g.getNode(nameOrId); if (!node || node._type !== 'Person') { const results = g.findByName(nameOrId, 'Person'); node = results[0] || null; } return node; } function createAnalysisTools() { return [ // ── 5.1 社区发现 ───────────────────────────────────────────── { name: 'detect_communities', description: '运行社区发现算法(标签传播),识别人脉中的圈子群体,返回分组结果。', parameters: { type: 'object', properties: { max_iterations: { type: 'number', description: '最大迭代次数,默认 10', }, }, }, async execute(params = {}) { try { const g = getGraphStore(); const result = g.detectCommunities(params.max_iterations || 10); if (result.communities.length === 0) { return '图谱中暂无 Person 节点,无法进行社区发现。'; } const lines = [ `🔍 社区发现完成,共识别 ${result.communities.length} 个圈子:\n`, ]; for (const c of result.communities) { const names = c.nodes.map(n => n.name).join('、'); lines.push(`圈子 ${c.id}(${c.nodes.length} 人):${names}`); } return lines.join('\n'); } catch (err) { return `❌ 社区发现失败:${err.message}`; } }, }, // ── 5.2 跟进提醒 ───────────────────────────────────────────── { name: 'get_followup_reminders', description: '获取超过 N 天未互动的联系人列表,生成跟进提醒建议。', parameters: { type: 'object', properties: { days_since: { type: 'number', description: '超过几天未互动,默认 30', }, limit: { type: 'number', description: '最多返回多少人,默认 20', }, }, }, async execute(params = {}) { try { const g = getGraphStore(); const days = params.days_since || 30; const candidates = g.getFollowUpCandidates(days, params.limit || 20); if (candidates.length === 0) { return `✅ 近 ${days} 天内所有重要联系人均已互动,暂无跟进提醒。`; } const lines = [ `⏰ 超过 ${days} 天未互动的联系人(${candidates.length} 人):\n`, ]; for (const c of candidates) { const lastStr = c.lastInteraction || '从未互动'; lines.push(`• ${c.person.name}(${c.daysSince} 天前:${lastStr})`); lines.push(` → ${c.suggestion}`); } return lines.join('\n'); } catch (err) { return `❌ 跟进提醒获取失败:${err.message}`; } }, }, // ── 5.3 激活联系人档案 ─────────────────────────────────────── { name: 'activate_contact_profile', description: '对指定联系人执行全量聚合分析,更新 analysisStatus 为 complete,返回分析摘要。', parameters: { type: 'object', required: ['person_name_or_id'], properties: { person_name_or_id: { type: 'string', description: '联系人姓名或节点 id', }, }, }, async execute(params) { try { const g = getGraphStore(); const person = resolvePerson(params.person_name_or_id); if (!person) { return `❌ 未找到联系人:${params.person_name_or_id}`; } // 获取所有边和关联节点 const { outgoing, incoming } = g.getEdges(person.id); const allEdges = [...outgoing, ...incoming]; // 统计互动次数(Event 节点) let interactionCount = 0; const relatedOrgs = new Set(); const recentEvents = []; const now = Date.now(); let latestEventDate = null; for (const edge of allEdges) { const neighborId = edge.from_id === person.id ? edge.to_id : edge.from_id; const neighbor = g.getNode(neighborId); if (!neighbor) continue; if (neighbor._type === 'Event') { interactionCount++; const dateStr = neighbor.date || neighbor.createdAt || null; if (dateStr) { recentEvents.push({ name: neighbor.name || neighbor.summary, date: dateStr }); if (!latestEventDate || dateStr > latestEventDate) { latestEventDate = dateStr; } } } else if (neighbor._type === 'Organization') { relatedOrgs.add(neighbor.name || neighborId); } } // 最近 3 个事件 recentEvents.sort((a, b) => (b.date > a.date ? 1 : -1)); const topEvents = recentEvents.slice(0, 3); // 更新 analysisStatus = 'complete' g.setAnalysisStatus(person.id, 'complete'); const daysSinceLast = latestEventDate ? Math.floor((now - new Date(latestEventDate).getTime()) / (1000 * 60 * 60 * 24)) : null; const summary = [ `📊 联系人档案激活完成:${person.name}`, `─────────────────────────────`, `互动次数:${interactionCount} 次`, `关联组织:${relatedOrgs.size > 0 ? [...relatedOrgs].join('、') : '(无)'}`, `最近互动:${latestEventDate || '(无记录)'}${daysSinceLast !== null ? `(${daysSinceLast} 天前)` : ''}`, ]; if (topEvents.length > 0) { summary.push('\n最近事件:'); topEvents.forEach(e => summary.push(` • ${e.date} — ${e.name || '(无标题)'}`)); } summary.push('\nanalysisStatus 已更新为 complete。'); return summary.join('\n'); } catch (err) { return `❌ 档案激活失败:${err.message}`; } }, }, // ── 5.4 对话洞察 ───────────────────────────────────────────── { name: 'get_conversation_insights', description: '分析联系人的对话数据,返回话题分布、互动热度、待跟进事项。', parameters: { type: 'object', required: ['person_name_or_id'], properties: { person_name_or_id: { type: 'string', description: '联系人姓名或节点 id', }, }, }, async execute(params) { try { const g = getGraphStore(); const person = resolvePerson(params.person_name_or_id); if (!person) { return `❌ 未找到联系人:${params.person_name_or_id}`; } const { outgoing, incoming } = g.getEdges(person.id); const allEdges = [...outgoing, ...incoming]; const typeDist = {}; // Event type 分布 const monthDist = {}; // 按月分布(互动热度) const hashtags = {}; // 话题标签(来自 X 动态) let totalEvents = 0; for (const edge of allEdges) { const neighborId = edge.from_id === person.id ? edge.to_id : edge.from_id; const neighbor = g.getNode(neighborId); if (!neighbor || neighbor._type !== 'Event') continue; totalEvents++; // 类型分布 const t = neighbor.type || 'other'; typeDist[t] = (typeDist[t] || 0) + 1; // 月度热度 const dateStr = neighbor.date || neighbor.createdAt; if (dateStr) { const month = String(dateStr).slice(0, 7); // YYYY-MM monthDist[month] = (monthDist[month] || 0) + 1; } // 话题标签 if (Array.isArray(neighbor.hashtags)) { for (const tag of neighbor.hashtags) { if (tag) hashtags[tag] = (hashtags[tag] || 0) + 1; } } } // 按月排序,取最近 6 个月 const sortedMonths = Object.entries(monthDist) .sort((a, b) => b[0].localeCompare(a[0])) .slice(0, 6); // 按频率排序 hashtags const topHashtags = Object.entries(hashtags) .sort((a, b) => b[1] - a[1]) .slice(0, 10); const insights = { personId: person.id, personName: person.name, totalEvents, typeDistribution: typeDist, recentActivity: Object.fromEntries(sortedMonths), topHashtags: Object.fromEntries(topHashtags), analysisStatus: person._analysisStatus, }; const lines = [ `📈 联系人洞察:${person.name}`, `─────────────────────────────`, `关联事件总数:${totalEvents}`, `事件类型分布:${JSON.stringify(typeDist)}`, `近期互动热度(月):${JSON.stringify(Object.fromEntries(sortedMonths))}`, ]; if (topHashtags.length > 0) { lines.push(`常见话题标签:${topHashtags.map(([k, v]) => `${k}(${v})`).join('、')}`); } lines.push('\n---\n原始数据:\n```json\n' + JSON.stringify(insights, null, 2) + '\n```'); return lines.join('\n'); } catch (err) { return `❌ 对话洞察分析失败:${err.message}`; } }, }, ]; } module.exports = { createAnalysisTools };