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
@@ -4,7 +4,7 @@ import { setMemory, type Identity } from "../lib/api";
import { Button, Input, Textarea, Field, useToast } from "../ui";
// 偏好记忆面板 —— 让用户显式登记/纠正模型对自己的记忆(→ PUT /api/v1/memory)。
export function MemoryPanel({ identity }: { identity: Identity }) {
export function MemoryPanel({ identity, onSaved }: { identity: Identity; onSaved?: () => void }) {
const toast = useToast();
const [key, setKey] = useState("回答偏好");
const [value, setValue] = useState("简洁、中文、多给要点");
@@ -16,6 +16,7 @@ export function MemoryPanel({ identity }: { identity: Identity }) {
const m = await setMemory(identity, key.trim(), value.trim());
setSaved((s) => [{ key: key.trim(), value: value.trim() }, ...s.filter((x) => x.key !== key.trim())]);
toast.push("success", m);
onSaved?.();
} catch (e) {
toast.push("error", (e as Error).message);
}