/** * 多模态资产关联 MCP 工具集 — Phase 6 * * 工具: * link_photo_to_person — 将照片关联到联系人(Asset:photo 节点 + EXIF 提取) * search_photos_by_person — 查询联系人关联的所有照片 */ const { getGraphStore } = require('../../../db/GraphStore'); const { PhotoService } = require('../../../services/photo/photo-service'); /** 解析 name_or_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 createPhotoTools() { return [ // ── 6.1 关联照片到联系人 ───────────────────────────────────── { name: 'link_photo_to_person', description: '将本地照片文件关联到指定联系人(创建 Asset:photo 节点),自动提取 EXIF 时空信息。', parameters: { type: 'object', required: ['image_path', 'person_name_or_id'], properties: { image_path: { type: 'string', description: '照片文件绝对路径', }, person_name_or_id: { type: 'string', description: '联系人姓名或节点 id', }, }, }, async execute(params) { try { const svc = new PhotoService(params.image_path); const result = await svc.linkPhotoToPerson( params.image_path, params.person_name_or_id ); const lines = [ `✅ 照片已关联:${params.image_path}`, `联系人:${result.personName}(id: ${result.personId})`, `资产节点 id:${result.assetId}`, ]; if (result.exif) { if (result.exif.lat !== undefined) { lines.push(`GPS 坐标:${result.exif.lat.toFixed(6)}, ${result.exif.lng.toFixed(6)}`); } if (result.exif.datetime) { lines.push(`拍摄时间:${result.exif.datetime}`); } } else { lines.push('(未提取到 EXIF 信息)'); } return lines.join('\n'); } catch (err) { return `❌ 照片关联失败:${err.message}`; } }, }, // ── 6.2 查询联系人照片 ─────────────────────────────────────── { name: 'search_photos_by_person', 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 } = g.getEdges(person.id); const photos = []; for (const edge of outgoing) { if (edge.relation !== 'CONTAINS' && edge.relation !== 'ASSOCIATED') continue; const asset = g.getNode(edge.to_id); if (asset && asset._type === 'Asset' && asset.type === 'photo') { photos.push(asset); } } if (photos.length === 0) { return `📷 ${person.name} 暂无关联照片。`; } const lines = [`📷 ${person.name} 的照片(${photos.length} 张):\n`]; photos.forEach((p, i) => { const loc = p.lat !== undefined ? ` GPS: ${Number(p.lat).toFixed(4)}, ${Number(p.lng).toFixed(4)}` : ''; const time = p.takenAt ? ` 拍摄时间: ${p.takenAt}` : ''; lines.push(`${i + 1}. ${p.name || p.id}\n 路径: ${p.filePath || '(未知)'}${loc}${time}`); }); return lines.join('\n'); } catch (err) { return `❌ 照片查询失败:${err.message}`; } }, }, ]; } module.exports = { createPhotoTools };