photo-tools.js
5.19 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
/**
* 多模态资产关联 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 };