@scotthuang/engram 0.5.9 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/README.md +42 -0
  2. package/dist/face/src/face-api.d.ts +36 -0
  3. package/dist/face/src/face-api.js +90 -0
  4. package/dist/face/src/face-api.js.map +1 -0
  5. package/dist/face/src/face-store.d.ts +101 -0
  6. package/dist/face/src/face-store.js +361 -0
  7. package/dist/face/src/face-store.js.map +1 -0
  8. package/dist/src/__tests__/bm25.test.d.ts +1 -0
  9. package/dist/src/__tests__/bm25.test.js +198 -0
  10. package/dist/src/__tests__/bm25.test.js.map +1 -0
  11. package/dist/src/__tests__/config.test.d.ts +1 -0
  12. package/dist/src/__tests__/config.test.js +31 -0
  13. package/dist/src/__tests__/config.test.js.map +1 -0
  14. package/dist/src/__tests__/profile.test.d.ts +1 -0
  15. package/dist/src/__tests__/profile.test.js +130 -0
  16. package/dist/src/__tests__/profile.test.js.map +1 -0
  17. package/dist/src/__tests__/recall.test.d.ts +1 -0
  18. package/dist/src/__tests__/recall.test.js +162 -0
  19. package/dist/src/__tests__/recall.test.js.map +1 -0
  20. package/dist/src/bm25.d.ts +60 -0
  21. package/dist/src/bm25.js +271 -0
  22. package/dist/src/bm25.js.map +1 -0
  23. package/dist/src/config.d.ts +47 -0
  24. package/dist/src/config.js +83 -0
  25. package/dist/src/config.js.map +1 -0
  26. package/dist/src/image-store.d.ts +146 -0
  27. package/dist/src/image-store.js +418 -0
  28. package/dist/src/image-store.js.map +1 -0
  29. package/dist/src/index.d.ts +7 -0
  30. package/dist/src/index.js +1236 -0
  31. package/dist/src/index.js.map +1 -0
  32. package/dist/src/logger.d.ts +32 -0
  33. package/dist/src/logger.js +106 -0
  34. package/dist/src/logger.js.map +1 -0
  35. package/dist/src/profile.d.ts +37 -0
  36. package/dist/src/profile.js +107 -0
  37. package/dist/src/profile.js.map +1 -0
  38. package/dist/src/recall.d.ts +98 -0
  39. package/dist/src/recall.js +729 -0
  40. package/dist/src/recall.js.map +1 -0
  41. package/dist/src/settle.d.ts +83 -0
  42. package/dist/src/settle.js +687 -0
  43. package/dist/src/settle.js.map +1 -0
  44. package/dist/src/vector.d.ts +67 -0
  45. package/dist/src/vector.js +284 -0
  46. package/dist/src/vector.js.map +1 -0
  47. package/package.json +3 -3
package/README.md CHANGED
@@ -40,6 +40,7 @@ scripts/
40
40
  ├── .self-evolving-checkpoint.json # [自动生成] 增量分析 checkpoint
41
41
  ├── config.md # workspace 路径配置
42
42
  ├── condense_shortterm.cjs # 短期记忆精简工具
43
+ ├── manual-condense.ts # 手动补 condense(修复遗漏的 session)
43
44
  ```
44
45
 
45
46
  ## 开发
@@ -254,3 +255,44 @@ npx tsx scripts/sim-recall.ts --interactive --verbose
254
255
  2. 分别用 `--no-vector` 和 `--no-bm25` 隔离测试
255
256
  3. 用 `--no-rewrite` 对比 query rewrite 前后的召回差异
256
257
  4. 修改 `src/` 下的代码后,直接再跑 `npm run sim` 验证效果,无需重新编译或发包
258
+
259
+ ---
260
+
261
+ ## 手动 Condense (manual-condense)
262
+
263
+ 当 session 因 hook 遗漏(如之前缺少 `command:reset` 监听)或其他异常未被自动 condense 时,可以用此脚本手动补救——从 JSONL session 文件中提取对话,调用 LLM 精简后写入 `short-term/` 目录。
264
+
265
+ ### 用法
266
+
267
+ ```bash
268
+ # 基本用法:指定 session 文件路径,自动从文件名推断日期
269
+ npx tsx scripts/manual-condense.ts <session-file-path>
270
+
271
+ # 指定目标日期(覆盖自动推断)
272
+ npx tsx scripts/manual-condense.ts <session-file-path> <YYYY-MM-DD>
273
+ ```
274
+
275
+ ### 示例
276
+
277
+ ```bash
278
+ # 补救一个因 /reset 未触发 condense 而遗漏的 session
279
+ npx tsx scripts/manual-condense.ts \
280
+ ~/.openclaw/agents/main/sessions/7045fe60-bbf7-43a8-9d74-c378570733c6.jsonl.reset.2026-03-23T20-10-42.346Z \
281
+ 2026-03-23
282
+ ```
283
+
284
+ ### 处理流程
285
+
286
+ ```
287
+ 读取 JSONL session → 提取 user/assistant 消息 → 清理 metadata/噪音
288
+ → 分段(每段 ≤5000 字符) → 调用 MiniMax LLM 精简
289
+ → 合并 + 去重 → 追加写入 short-term/YYYY-MM-DD.md
290
+ ```
291
+
292
+ ### 特性
293
+
294
+ - **消息提取**:自动解析 JSONL 格式,过滤系统消息、命令、HEARTBEAT 等噪音
295
+ - **分段处理**:长对话自动切分,避免 token 溢出
296
+ - **去重保护**:写入前检查指纹,避免重复追加
297
+ - **日期推断**:从 `.reset.YYYY-MM-DDThh-mm-ss` 文件名自动提取日期
298
+ - **标记来源**:写入时带 `(manual-condense)` 标签,便于区分自动/手动记录
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Face API Client — 封装对 Python 人脸服务的 HTTP 调用
3
+ *
4
+ * 所有调用都有超时保护,服务不可用时静默返回空结果,不影响主流程。
5
+ */
6
+ export interface DetectedFace {
7
+ /** 人脸框坐标 [x1, y1, x2, y2] */
8
+ bbox: number[];
9
+ /** 检测置信度 0-1 */
10
+ confidence: number;
11
+ /** 512d normalized embedding */
12
+ embedding: number[];
13
+ }
14
+ export interface DetectResult {
15
+ faces: DetectedFace[];
16
+ count: number;
17
+ elapsed_ms: number;
18
+ }
19
+ export declare class FaceApiClient {
20
+ private baseUrl;
21
+ private timeoutMs;
22
+ constructor(baseUrl?: string, timeoutMs?: number);
23
+ /**
24
+ * 健康检查:Python 服务是否在线
25
+ */
26
+ isAvailable(): Promise<boolean>;
27
+ /**
28
+ * 检测图片中的所有人脸,返回 bbox + confidence + embedding
29
+ * 服务不可用时返回空数组(不抛异常)
30
+ */
31
+ detect(imagePath: string): Promise<DetectedFace[]>;
32
+ /**
33
+ * 裁剪人脸区域并保存
34
+ */
35
+ crop(imagePath: string, bbox: number[], outputPath: string, padding?: number): Promise<boolean>;
36
+ }
@@ -0,0 +1,90 @@
1
+ /**
2
+ * Face API Client — 封装对 Python 人脸服务的 HTTP 调用
3
+ *
4
+ * 所有调用都有超时保护,服务不可用时静默返回空结果,不影响主流程。
5
+ */
6
+ import { logger } from "../../src/logger.js";
7
+ // ---- Face API Client ----
8
+ export class FaceApiClient {
9
+ baseUrl;
10
+ timeoutMs;
11
+ constructor(baseUrl = "http://localhost:8765", timeoutMs = 10_000) {
12
+ this.baseUrl = baseUrl.replace(/\/$/, "");
13
+ this.timeoutMs = timeoutMs;
14
+ }
15
+ /**
16
+ * 健康检查:Python 服务是否在线
17
+ */
18
+ async isAvailable() {
19
+ try {
20
+ const controller = new AbortController();
21
+ const timeoutId = setTimeout(() => controller.abort(), 3000);
22
+ const res = await fetch(`${this.baseUrl}/health`, { signal: controller.signal });
23
+ clearTimeout(timeoutId);
24
+ return res.ok;
25
+ }
26
+ catch {
27
+ return false;
28
+ }
29
+ }
30
+ /**
31
+ * 检测图片中的所有人脸,返回 bbox + confidence + embedding
32
+ * 服务不可用时返回空数组(不抛异常)
33
+ */
34
+ async detect(imagePath) {
35
+ try {
36
+ const controller = new AbortController();
37
+ const timeoutId = setTimeout(() => controller.abort(), this.timeoutMs);
38
+ const res = await fetch(`${this.baseUrl}/detect`, {
39
+ method: "POST",
40
+ headers: { "Content-Type": "application/json" },
41
+ body: JSON.stringify({ image_path: imagePath }),
42
+ signal: controller.signal,
43
+ });
44
+ clearTimeout(timeoutId);
45
+ if (!res.ok) {
46
+ const body = await res.text().catch(() => "");
47
+ logger.error(`[engram:face-api] /detect error: ${res.status} ${body.slice(0, 200)}`);
48
+ return [];
49
+ }
50
+ const data = (await res.json());
51
+ logger.info(`[engram:face-api] Detected ${data.count} face(s) in ${data.elapsed_ms}ms`);
52
+ return data.faces;
53
+ }
54
+ catch (err) {
55
+ if (err instanceof Error && err.name === "AbortError") {
56
+ logger.error(`[engram:face-api] /detect timeout after ${this.timeoutMs}ms`);
57
+ }
58
+ else {
59
+ logger.error(`[engram:face-api] /detect failed: ${err}`);
60
+ }
61
+ return [];
62
+ }
63
+ }
64
+ /**
65
+ * 裁剪人脸区域并保存
66
+ */
67
+ async crop(imagePath, bbox, outputPath, padding = 0.25) {
68
+ try {
69
+ const controller = new AbortController();
70
+ const timeoutId = setTimeout(() => controller.abort(), this.timeoutMs);
71
+ const res = await fetch(`${this.baseUrl}/crop`, {
72
+ method: "POST",
73
+ headers: { "Content-Type": "application/json" },
74
+ body: JSON.stringify({ image_path: imagePath, bbox, output_path: outputPath, padding }),
75
+ signal: controller.signal,
76
+ });
77
+ clearTimeout(timeoutId);
78
+ if (!res.ok) {
79
+ logger.error(`[engram:face-api] /crop error: ${res.status}`);
80
+ return false;
81
+ }
82
+ return true;
83
+ }
84
+ catch (err) {
85
+ logger.error(`[engram:face-api] /crop failed: ${err}`);
86
+ return false;
87
+ }
88
+ }
89
+ }
90
+ //# sourceMappingURL=face-api.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"face-api.js","sourceRoot":"","sources":["../../../face/src/face-api.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,101 @@
1
+ /**
2
+ * Face Store — 人脸注册表管理 + 匹配逻辑
3
+ *
4
+ * 存储结构:
5
+ * memory-engram/faces/
6
+ * ├── registry.json ← 注册表
7
+ * ├── embeddings/
8
+ * │ ├── person_001.json ← 均值 embedding (512d)
9
+ * │ └── person_002.json
10
+ * └── samples/
11
+ * ├── person_001/
12
+ * │ ├── 001.jpg
13
+ * │ └── 002.jpg
14
+ * └── person_002/
15
+ * └── 001.jpg
16
+ */
17
+ export interface PersonEntry {
18
+ id: string;
19
+ name: string;
20
+ relation: string;
21
+ sampleCount: number;
22
+ embeddingFile: string;
23
+ createdAt: number;
24
+ lastSeenAt: number;
25
+ seenCount: number;
26
+ lastSeenImage: string;
27
+ }
28
+ export interface FaceRegistry {
29
+ version: number;
30
+ faceServiceUrl: string;
31
+ matchThreshold: number;
32
+ uncertainThreshold: number;
33
+ persons: PersonEntry[];
34
+ }
35
+ export interface FaceMatchResult {
36
+ /** 匹配到的人物 ID(未匹配为 null) */
37
+ personId: string | null;
38
+ /** 匹配到的人名(未匹配为 "unknown") */
39
+ name: string;
40
+ /** 余弦相似度 */
41
+ confidence: number;
42
+ /** 人脸框坐标 */
43
+ bbox: number[];
44
+ /** 匹配状态 */
45
+ status: "matched" | "uncertain" | "unknown";
46
+ }
47
+ export declare class FaceStore {
48
+ private workspaceDir;
49
+ private apiClient;
50
+ private registry;
51
+ /** 已注册人物的 embedding 缓存(personId → embedding) */
52
+ private embeddingCache;
53
+ constructor(workspaceDir: string, faceServiceUrl?: string);
54
+ private get facesDir();
55
+ private get registryPath();
56
+ private get embeddingsDir();
57
+ private get samplesDir();
58
+ loadRegistry(): Promise<FaceRegistry>;
59
+ private saveRegistry;
60
+ private loadEmbedding;
61
+ private saveEmbedding;
62
+ /**
63
+ * 检查人脸服务是否可用
64
+ */
65
+ isServiceAvailable(): Promise<boolean>;
66
+ /**
67
+ * 注册新人物
68
+ *
69
+ * @param name 人物姓名
70
+ * @param relation 关系(选填)
71
+ * @param imagePaths 多张照片路径(建议 3-5 张多角度)
72
+ * @returns 注册结果
73
+ */
74
+ registerFace(name: string, relation: string | undefined, imagePaths: string[]): Promise<{
75
+ success: boolean;
76
+ personId: string;
77
+ error?: string;
78
+ }>;
79
+ /**
80
+ * 追加样本到已有人物
81
+ */
82
+ addSamples(personId: string, imagePaths: string[]): Promise<{
83
+ added: number;
84
+ error?: string;
85
+ }>;
86
+ /**
87
+ * 核心方法:检测图片中的人脸并与注册表匹配
88
+ *
89
+ * @param imagePath 图片绝对路径
90
+ * @returns 匹配结果列表(每个检测到的人脸一条)
91
+ */
92
+ detectAndMatch(imagePath: string): Promise<FaceMatchResult[]>;
93
+ /**
94
+ * 列出所有已注册人物
95
+ */
96
+ listPersons(): Promise<PersonEntry[]>;
97
+ /**
98
+ * 删除人物
99
+ */
100
+ removePerson(personId: string): Promise<boolean>;
101
+ }
@@ -0,0 +1,361 @@
1
+ /**
2
+ * Face Store — 人脸注册表管理 + 匹配逻辑
3
+ *
4
+ * 存储结构:
5
+ * memory-engram/faces/
6
+ * ├── registry.json ← 注册表
7
+ * ├── embeddings/
8
+ * │ ├── person_001.json ← 均值 embedding (512d)
9
+ * │ └── person_002.json
10
+ * └── samples/
11
+ * ├── person_001/
12
+ * │ ├── 001.jpg
13
+ * │ └── 002.jpg
14
+ * └── person_002/
15
+ * └── 001.jpg
16
+ */
17
+ import { promises as fs } from "node:fs";
18
+ import { join } from "node:path";
19
+ import { logger } from "../../src/logger.js";
20
+ import { FaceApiClient } from "./face-api.js";
21
+ // ---- 默认值 ----
22
+ const DEFAULT_REGISTRY = {
23
+ version: 1,
24
+ faceServiceUrl: "http://localhost:8765",
25
+ matchThreshold: 0.60,
26
+ uncertainThreshold: 0.45,
27
+ persons: [],
28
+ };
29
+ // ---- 工具函数 ----
30
+ /** 余弦相似度(输入为归一化向量时等价于点积) */
31
+ function cosineSimilarity(a, b) {
32
+ if (a.length !== b.length)
33
+ return 0;
34
+ let dot = 0, normA = 0, normB = 0;
35
+ for (let i = 0; i < a.length; i++) {
36
+ dot += a[i] * b[i];
37
+ normA += a[i] * a[i];
38
+ normB += b[i] * b[i];
39
+ }
40
+ const denom = Math.sqrt(normA) * Math.sqrt(normB);
41
+ return denom === 0 ? 0 : dot / denom;
42
+ }
43
+ /** 计算多个 embedding 的均值向量 */
44
+ function meanEmbedding(embeddings) {
45
+ if (embeddings.length === 0)
46
+ return [];
47
+ const dim = embeddings[0].length;
48
+ const result = new Array(dim).fill(0);
49
+ for (const emb of embeddings) {
50
+ for (let i = 0; i < dim; i++) {
51
+ result[i] += emb[i];
52
+ }
53
+ }
54
+ for (let i = 0; i < dim; i++) {
55
+ result[i] /= embeddings.length;
56
+ }
57
+ // 归一化
58
+ let norm = 0;
59
+ for (let i = 0; i < dim; i++)
60
+ norm += result[i] * result[i];
61
+ norm = Math.sqrt(norm);
62
+ if (norm > 0) {
63
+ for (let i = 0; i < dim; i++)
64
+ result[i] /= norm;
65
+ }
66
+ return result;
67
+ }
68
+ // ---- FaceStore 类 ----
69
+ export class FaceStore {
70
+ workspaceDir;
71
+ apiClient;
72
+ registry = null;
73
+ /** 已注册人物的 embedding 缓存(personId → embedding) */
74
+ embeddingCache = new Map();
75
+ constructor(workspaceDir, faceServiceUrl) {
76
+ this.workspaceDir = workspaceDir;
77
+ const url = faceServiceUrl || DEFAULT_REGISTRY.faceServiceUrl;
78
+ this.apiClient = new FaceApiClient(url);
79
+ }
80
+ // ---- 路径 ----
81
+ get facesDir() {
82
+ return join(this.workspaceDir, "memory-engram", "faces");
83
+ }
84
+ get registryPath() {
85
+ return join(this.facesDir, "registry.json");
86
+ }
87
+ get embeddingsDir() {
88
+ return join(this.facesDir, "embeddings");
89
+ }
90
+ get samplesDir() {
91
+ return join(this.facesDir, "samples");
92
+ }
93
+ // ---- Registry 读写 ----
94
+ async loadRegistry() {
95
+ if (this.registry)
96
+ return this.registry;
97
+ try {
98
+ const raw = await fs.readFile(this.registryPath, "utf-8");
99
+ this.registry = JSON.parse(raw);
100
+ logger.info(`[engram:face-store] Loaded registry: ${this.registry.persons.length} person(s)`);
101
+ }
102
+ catch {
103
+ this.registry = { ...DEFAULT_REGISTRY };
104
+ logger.info(`[engram:face-store] No registry found, using empty`);
105
+ }
106
+ return this.registry;
107
+ }
108
+ async saveRegistry() {
109
+ if (!this.registry)
110
+ return;
111
+ await fs.mkdir(this.facesDir, { recursive: true });
112
+ await fs.writeFile(this.registryPath, JSON.stringify(this.registry, null, 2), "utf-8");
113
+ }
114
+ // ---- Embedding 读写 ----
115
+ async loadEmbedding(personId) {
116
+ const cached = this.embeddingCache.get(personId);
117
+ if (cached)
118
+ return cached;
119
+ const filePath = join(this.embeddingsDir, `${personId}.json`);
120
+ try {
121
+ const raw = await fs.readFile(filePath, "utf-8");
122
+ const emb = JSON.parse(raw);
123
+ this.embeddingCache.set(personId, emb);
124
+ return emb;
125
+ }
126
+ catch {
127
+ return [];
128
+ }
129
+ }
130
+ async saveEmbedding(personId, embedding) {
131
+ await fs.mkdir(this.embeddingsDir, { recursive: true });
132
+ const filePath = join(this.embeddingsDir, `${personId}.json`);
133
+ await fs.writeFile(filePath, JSON.stringify(embedding), "utf-8");
134
+ this.embeddingCache.set(personId, embedding);
135
+ }
136
+ // ---- 公开方法 ----
137
+ /**
138
+ * 检查人脸服务是否可用
139
+ */
140
+ async isServiceAvailable() {
141
+ return this.apiClient.isAvailable();
142
+ }
143
+ /**
144
+ * 注册新人物
145
+ *
146
+ * @param name 人物姓名
147
+ * @param relation 关系(选填)
148
+ * @param imagePaths 多张照片路径(建议 3-5 张多角度)
149
+ * @returns 注册结果
150
+ */
151
+ async registerFace(name, relation = "", imagePaths) {
152
+ if (imagePaths.length === 0) {
153
+ return { success: false, personId: "", error: "No images provided" };
154
+ }
155
+ const registry = await this.loadRegistry();
156
+ // 检查重名
157
+ const existing = registry.persons.find(p => p.name.toLowerCase() === name.toLowerCase());
158
+ if (existing) {
159
+ return { success: false, personId: existing.id, error: `Person "${name}" already registered (${existing.id})` };
160
+ }
161
+ // 对每张图检测人脸 + 提取 embedding
162
+ const embeddings = [];
163
+ const samplePaths = [];
164
+ const personId = `person_${String(registry.persons.length + 1).padStart(3, "0")}`;
165
+ const personSampleDir = join(this.samplesDir, personId);
166
+ await fs.mkdir(personSampleDir, { recursive: true });
167
+ for (let i = 0; i < imagePaths.length; i++) {
168
+ const imgPath = imagePaths[i];
169
+ logger.info(`[engram:face-store] Registering "${name}": processing image ${i + 1}/${imagePaths.length}: ${imgPath}`);
170
+ const faces = await this.apiClient.detect(imgPath);
171
+ if (faces.length === 0) {
172
+ logger.info(`[engram:face-store] No face detected in ${imgPath}, skipping`);
173
+ continue;
174
+ }
175
+ if (faces.length > 1) {
176
+ logger.info(`[engram:face-store] Multiple faces (${faces.length}) in ${imgPath}, using highest confidence`);
177
+ }
178
+ // 取置信度最高的人脸
179
+ const bestFace = faces[0];
180
+ embeddings.push(bestFace.embedding);
181
+ // 裁剪人脸样本
182
+ const samplePath = join(personSampleDir, `${String(i + 1).padStart(3, "0")}.jpg`);
183
+ await this.apiClient.crop(imgPath, bestFace.bbox, samplePath);
184
+ samplePaths.push(samplePath);
185
+ }
186
+ if (embeddings.length === 0) {
187
+ return { success: false, personId: "", error: "No valid faces detected in any of the provided images" };
188
+ }
189
+ // 计算均值 embedding
190
+ const avgEmb = meanEmbedding(embeddings);
191
+ await this.saveEmbedding(personId, avgEmb);
192
+ // 写入 registry
193
+ const now = Math.floor(Date.now() / 1000);
194
+ registry.persons.push({
195
+ id: personId,
196
+ name,
197
+ relation,
198
+ sampleCount: embeddings.length,
199
+ embeddingFile: `embeddings/${personId}.json`,
200
+ createdAt: now,
201
+ lastSeenAt: 0,
202
+ seenCount: 0,
203
+ lastSeenImage: "",
204
+ });
205
+ await this.saveRegistry();
206
+ logger.info(`[engram:face-store] Registered "${name}" as ${personId} with ${embeddings.length} sample(s)`);
207
+ return { success: true, personId };
208
+ }
209
+ /**
210
+ * 追加样本到已有人物
211
+ */
212
+ async addSamples(personId, imagePaths) {
213
+ const registry = await this.loadRegistry();
214
+ const person = registry.persons.find(p => p.id === personId);
215
+ if (!person) {
216
+ return { added: 0, error: `Person ${personId} not found` };
217
+ }
218
+ const oldEmb = await this.loadEmbedding(personId);
219
+ const newEmbeddings = [];
220
+ const personSampleDir = join(this.samplesDir, personId);
221
+ await fs.mkdir(personSampleDir, { recursive: true });
222
+ for (const imgPath of imagePaths) {
223
+ const faces = await this.apiClient.detect(imgPath);
224
+ if (faces.length === 0)
225
+ continue;
226
+ const bestFace = faces[0];
227
+ newEmbeddings.push(bestFace.embedding);
228
+ // 裁剪样本
229
+ const idx = person.sampleCount + newEmbeddings.length;
230
+ const samplePath = join(personSampleDir, `${String(idx).padStart(3, "0")}.jpg`);
231
+ await this.apiClient.crop(imgPath, bestFace.bbox, samplePath);
232
+ }
233
+ if (newEmbeddings.length === 0) {
234
+ return { added: 0, error: "No valid faces in provided images" };
235
+ }
236
+ // 加权平均:旧均值 × 旧样本数 + 新 embedding,再除以总数
237
+ const allEmbeddings = [];
238
+ if (oldEmb.length > 0) {
239
+ // 旧均值重复 sampleCount 次来保持权重
240
+ for (let i = 0; i < person.sampleCount; i++) {
241
+ allEmbeddings.push(oldEmb);
242
+ }
243
+ }
244
+ allEmbeddings.push(...newEmbeddings);
245
+ const avgEmb = meanEmbedding(allEmbeddings);
246
+ await this.saveEmbedding(personId, avgEmb);
247
+ person.sampleCount += newEmbeddings.length;
248
+ await this.saveRegistry();
249
+ logger.info(`[engram:face-store] Added ${newEmbeddings.length} sample(s) to ${person.name} (${personId}), total=${person.sampleCount}`);
250
+ return { added: newEmbeddings.length };
251
+ }
252
+ /**
253
+ * 核心方法:检测图片中的人脸并与注册表匹配
254
+ *
255
+ * @param imagePath 图片绝对路径
256
+ * @returns 匹配结果列表(每个检测到的人脸一条)
257
+ */
258
+ async detectAndMatch(imagePath) {
259
+ const registry = await this.loadRegistry();
260
+ if (registry.persons.length === 0) {
261
+ logger.info(`[engram:face-store] No registered persons, skipping face matching`);
262
+ return [];
263
+ }
264
+ // 1. 检测人脸
265
+ const faces = await this.apiClient.detect(imagePath);
266
+ if (faces.length === 0) {
267
+ return [];
268
+ }
269
+ logger.info(`[engram:face-store] Matching ${faces.length} face(s) against ${registry.persons.length} registered person(s)`);
270
+ // 2. 加载所有已注册人物的 embedding
271
+ const personEmbeddings = [];
272
+ for (const person of registry.persons) {
273
+ const emb = await this.loadEmbedding(person.id);
274
+ if (emb.length > 0) {
275
+ personEmbeddings.push({ person, embedding: emb });
276
+ }
277
+ }
278
+ // 3. 对每张人脸做匹配
279
+ const results = [];
280
+ for (const face of faces) {
281
+ let bestMatch = null;
282
+ for (const { person, embedding } of personEmbeddings) {
283
+ const sim = cosineSimilarity(face.embedding, embedding);
284
+ if (!bestMatch || sim > bestMatch.sim) {
285
+ bestMatch = { person, sim };
286
+ }
287
+ }
288
+ if (bestMatch && bestMatch.sim >= registry.matchThreshold) {
289
+ // 匹配成功
290
+ results.push({
291
+ personId: bestMatch.person.id,
292
+ name: bestMatch.person.name,
293
+ confidence: Math.round(bestMatch.sim * 100) / 100,
294
+ bbox: face.bbox,
295
+ status: "matched",
296
+ });
297
+ // 更新 lastSeenAt / seenCount
298
+ bestMatch.person.lastSeenAt = Math.floor(Date.now() / 1000);
299
+ bestMatch.person.seenCount += 1;
300
+ bestMatch.person.lastSeenImage = imagePath;
301
+ logger.info(`[engram:face-store] Face matched: ${bestMatch.person.name} (sim=${bestMatch.sim.toFixed(3)})`);
302
+ }
303
+ else if (bestMatch && bestMatch.sim >= registry.uncertainThreshold) {
304
+ // 不确定
305
+ results.push({
306
+ personId: null,
307
+ name: `unknown (疑似 ${bestMatch.person.name})`,
308
+ confidence: Math.round(bestMatch.sim * 100) / 100,
309
+ bbox: face.bbox,
310
+ status: "uncertain",
311
+ });
312
+ logger.info(`[engram:face-store] Face uncertain: 疑似 ${bestMatch.person.name} (sim=${bestMatch.sim.toFixed(3)})`);
313
+ }
314
+ else {
315
+ // 未知
316
+ results.push({
317
+ personId: null,
318
+ name: "unknown",
319
+ confidence: bestMatch ? Math.round(bestMatch.sim * 100) / 100 : 0,
320
+ bbox: face.bbox,
321
+ status: "unknown",
322
+ });
323
+ logger.info(`[engram:face-store] Face unknown (best sim=${bestMatch?.sim.toFixed(3) ?? "N/A"})`);
324
+ }
325
+ }
326
+ // 保存更新后的 registry(lastSeenAt 等)
327
+ await this.saveRegistry();
328
+ return results;
329
+ }
330
+ /**
331
+ * 列出所有已注册人物
332
+ */
333
+ async listPersons() {
334
+ const registry = await this.loadRegistry();
335
+ return registry.persons;
336
+ }
337
+ /**
338
+ * 删除人物
339
+ */
340
+ async removePerson(personId) {
341
+ const registry = await this.loadRegistry();
342
+ const idx = registry.persons.findIndex(p => p.id === personId);
343
+ if (idx < 0)
344
+ return false;
345
+ const person = registry.persons[idx];
346
+ registry.persons.splice(idx, 1);
347
+ // 清理文件
348
+ try {
349
+ await fs.rm(join(this.embeddingsDir, `${personId}.json`), { force: true });
350
+ await fs.rm(join(this.samplesDir, personId), { recursive: true, force: true });
351
+ }
352
+ catch {
353
+ // non-critical
354
+ }
355
+ this.embeddingCache.delete(personId);
356
+ await this.saveRegistry();
357
+ logger.info(`[engram:face-store] Removed person: ${person.name} (${personId})`);
358
+ return true;
359
+ }
360
+ }
361
+ //# sourceMappingURL=face-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"face-store.js","sourceRoot":"","sources":["../../../face/src/face-store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};