fix(記憶固化): 判斷過的短期記憶留痕跡,候選數字才會降
睡眠有做固化,但隔天開機照樣提醒「N 組已達固化條件」,數字只會往上爬 (實測 KIRITO-01:短期記憶 120 筆、候選 222 組)。原因不是睡眠沒做事, 是判斷沒有留下痕跡——promotionCandidates() 無條件掃全部短期記憶,而 consolidate 不在來源那筆上寫任何東西,也不刪它(--forget 是按顯著度刪, 不是按固化過沒有刪),所以同一批每輪都被重算成候選。 - 短期記憶多兩個欄位:reviewed_at(看過、判斷過了)與 promoted_to (固化成了哪一則)。兩者分開記——「看過決定不記」跟「已經記下來」都不該 再進候選,但事後要查「這則長期記憶從哪幾筆長出來」只能靠 promoted_to。 - promotionCandidates() 只看沒有 reviewed_at 的那些;total 照舊算全部。 R6 容量壓力仍看總筆數,但候選只從未判斷的挑,全部判斷完就不再出現 (否則它會單獨把提醒永遠點亮)。 - consolidate --from-short "#N,#N":寫長期記憶時一起標來源。 - candidates --reviewed all|<#N,#N> [--until <ISO>]:看過決定不記的標這裡。 --until 讓「某次睡眠當下判斷過的那批」可以一次收掉。 - candidates 輸出每筆前面加 #N(就是上面兩個旗標要填的編號),並在結尾 提示判斷完要標記。 - 標記不等於刪掉:那幾筆還在短期記憶裡,照舊被 prune 依天數與顯著度裁。 文件:persona-sleep 的流程多一步「標掉判斷過的」並註明漏掉的後果、 persona-memory 補一張「怎麼標」的表、README 的 R1–R6 段補這一層。 selftest 補 13 項(602 → 615 全綠)。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -293,6 +293,52 @@ check("candidates 子指令可輸出 JSON", (() => {
|
||||
}
|
||||
})(), candOut.stdout.slice(0, 120));
|
||||
|
||||
// 判斷過的不再重算成候選。以前這裡是無條件掃全部短期記憶,固化完也不留痕跡,
|
||||
// 所以同一批每輪都被算成候選,提醒的數字只會往上爬——看起來像睡眠沒做固化。
|
||||
{
|
||||
const before = pl.promotionCandidates("alpha");
|
||||
check("候選會回報判斷過幾筆", typeof before.reviewed === "number" && before.reviewed === 0);
|
||||
const target = before.candidates.find((c) => c.kind === "entry");
|
||||
const idx = target.entries[0]._index;
|
||||
check("候選每筆都帶 _index(--from-short/--reviewed 用的編號)", Number.isInteger(idx));
|
||||
|
||||
const consol = cli(["consolidate", "--session", S_HOST, "--name", "reviewed-source", "--body", "從短期記憶長出來的一則",
|
||||
"--type", "event", "--salience", "60", "--from-short", String(idx)]);
|
||||
check("consolidate --from-short 會回報標了幾筆", consol.stdout.includes("已標成判斷過"), consol.stdout.slice(-160));
|
||||
const rowsAfter = pl.readJsonl(pl.shortTermPath("alpha"));
|
||||
check("來源短期記憶留下 reviewed_at 與 promoted_to",
|
||||
Boolean(rowsAfter[idx].reviewed_at) && rowsAfter[idx].promoted_to === "reviewed-source",
|
||||
JSON.stringify(rowsAfter[idx]).slice(0, 160));
|
||||
const mid = pl.promotionCandidates("alpha");
|
||||
check("固化過的那筆不再是候選", !mid.candidates.some((c) => c.entries.some((e) => e._index === idx)));
|
||||
check("固化後判斷過的筆數往上加、總筆數不變", mid.reviewed === 1 && mid.total === before.total);
|
||||
|
||||
// 看過決定不記的那些:候選數字唯一另一個往下扣的地方
|
||||
const rev = cli(["candidates", "--session", S_HOST, "--reviewed", "all"]);
|
||||
check("candidates --reviewed all 會標掉全部", rev.stdout.includes("已標記全部短期記憶為判斷過"), rev.stdout.slice(0, 160));
|
||||
const after = pl.promotionCandidates("alpha");
|
||||
check("全部判斷過之後候選歸零", after.candidates.length === 0, JSON.stringify(after.candidates.map((c) => c.key)));
|
||||
check("判斷過不等於刪掉(短期記憶筆數不變)", after.total === before.total);
|
||||
check("R6 容量壓力不會單獨把提醒點亮", (() => {
|
||||
const rows = pl.readJsonl(pl.shortTermPath("alpha"));
|
||||
return rows.length >= pl.CONSOLIDATE_THRESHOLD ? after.candidates.length === 0 : true;
|
||||
})());
|
||||
|
||||
// 重標一次不會把時間戳往後推(第一次判斷的時間才是真的)
|
||||
const firstAt = pl.readJsonl(pl.shortTermPath("alpha"))[idx].reviewed_at;
|
||||
cli(["candidates", "--session", S_HOST, "--reviewed", "all"]);
|
||||
check("重標不會覆蓋原本的 reviewed_at", pl.readJsonl(pl.shortTermPath("alpha"))[idx].reviewed_at === firstAt);
|
||||
|
||||
// 新寫進來的一筆照樣是候選:標記只擋已經判斷過的,不是把規則關掉
|
||||
cli(["remember", "--session", S_HOST, "--role", "user", "--text", "他說明天早上要一起去看那台車",
|
||||
"--topics", "car", "--salience", "70"]);
|
||||
check("標記過之後新的短期記憶照樣成為候選", pl.promotionCandidates("alpha").candidates.length > 0);
|
||||
|
||||
// --until 只標時間之前的:睡眠當下判斷過的那批可以一次收掉,之後寫的不受影響
|
||||
const marked = pl.markShortTermReviewed("alpha", { until: "2000-01-01T00:00:00Z" });
|
||||
check("--until 不會標到時間之後的紀錄", marked.marked === 0);
|
||||
}
|
||||
|
||||
console.log("⑦ 心智圖 / 思維導圖 / 人際關係圖");
|
||||
cli(["mindmap", "thread", "--persona", "alpha", "--session", S_HOST, "--topic", "壓力來源"]);
|
||||
check("思維導圖建立(Mermaid graph)",
|
||||
|
||||
Reference in New Issue
Block a user