feat(memory): P2 读路径打分 + 桌面端记忆面板(看/改/删)

读路径(Generative Agents 公式的 Recency+Importance 两项,Relevance 待 P3):
- memory.Get 改为 rankProfiles:Score=0.4·Recency+0.6·Importance,按分降序、截断 top-30。
  Recency=0.98^天 指数衰减;未评分行用兜底 importance=5(不被不公平遗忘)。纯函数 + 单测。
- 新增 Store.List(结构化、不截断)+ memory_list 工具。

桌面端记忆面板:
- gateway GET /memory(列表) + DELETE /memory?key=(软删),受保护组。
- api listMemory/deleteMemory;MemoryView 右侧从占位 → 真列表:按分排序展示
  key/value/重要度/最近时间,可内联编辑(PUT)与删除(软删);左侧登记后自动刷新。

实测:PUT 两条 → GET 返回带 importance/last_seen 的有序列表(较新者靠前)→ DELETE 一条
(软删)→ 再 GET 已消失。rank/recency/兜底 importance 单测过;前端 tsc+构建通过。

至此记忆:召回(打分) + 历史 + 自动对账(P1) + 用户可管控(面板) 闭环。Relevance(Milvus) 留 P3。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Blizzard
2026-06-22 14:48:11 +08:00
parent 1674252d81
commit 0edfc948ba
9 changed files with 316 additions and 14 deletions
+25
View File
@@ -411,3 +411,28 @@ export async function setMemory(
if (!res.ok) throw new Error(data.error ?? `memory failed: ${res.status}`);
return data.message ?? "ok";
}
// 一条长期偏好(含读路径打分维度)。
export interface MemoryItem {
key: string;
value: string;
importance: number;
last_seen: string;
}
// listMemory: GET /api/v1/memory —— 列出当前用户偏好(已按 Score 降序)。
export async function listMemory(id: Identity): Promise<MemoryItem[]> {
const res = guard401(await fetch(`${GATEWAY}/api/v1/memory`, { headers: idHeaders(id) }));
const data = (await res.json()) as { memories?: MemoryItem[]; error?: string };
if (!res.ok) throw new Error(data.error ?? `list memory failed: ${res.status}`);
return data.memories ?? [];
}
// deleteMemory: DELETE /api/v1/memory?key= —— 软删一条偏好。
export async function deleteMemory(id: Identity, key: string): Promise<void> {
const res = guard401(await fetch(`${GATEWAY}/api/v1/memory?key=${encodeURIComponent(key)}`, { method: "DELETE", headers: idHeaders(id) }));
if (!res.ok) {
const d = (await res.json().catch(() => ({}))) as { error?: string };
throw new Error(d.error ?? `delete memory failed: ${res.status}`);
}
}