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:
+45
-5
@@ -916,10 +916,26 @@ commands.candidates = ({ flags }) => {
|
||||
const session = requireSession(flags);
|
||||
const slug = hostOf(flags, session);
|
||||
requireOwner(slug, session);
|
||||
// `--reviewed`:把判斷過的短期記憶標掉。**這是候選數字唯一會往下扣的地方**——
|
||||
// 固化完不標,同一批下一輪照樣被算成候選,提醒永遠亮著(看起來像睡眠沒做事)。
|
||||
if (flags.reviewed !== undefined) {
|
||||
const spec = flags.reviewed === true ? "all" : str(flags.reviewed) || "all";
|
||||
const indexes = spec === "all" ? null : csv(spec).map(Number);
|
||||
if (indexes && (!indexes.length || indexes.some((n) => !Number.isInteger(n)))) {
|
||||
die("`--reviewed` 只能是 `all` 或以逗號分隔的編號(候選輸出裡每筆前面的 `#N`)。");
|
||||
}
|
||||
const until = str(flags.until) || null;
|
||||
const res = pl.markShortTermReviewed(slug, { indexes, until });
|
||||
const scope = indexes ? `${indexes.length} 筆指名的` : until ? `${until} 之前的` : "全部";
|
||||
ok(`已標記${scope}短期記憶為判斷過(新標 ${res.marked} 筆,本來就標過 ${res.already} 筆)。`);
|
||||
const after = pl.promotionCandidates(slug);
|
||||
say(` 現在還有 ${after.candidates.length} 組候選(短期記憶 ${after.total} 筆,判斷過 ${after.reviewed} 筆)。`);
|
||||
return;
|
||||
}
|
||||
const result = pl.promotionCandidates(slug);
|
||||
const lines = [
|
||||
`人格 \`${slug}\`:短期記憶 ${result.total} 筆,達固化條件的候選 ${result.candidates.length} 組` +
|
||||
`${result.pressure ? "(已達容量壓力 R6)" : ""}`,
|
||||
`人格 \`${slug}\`:短期記憶 ${result.total} 筆(判斷過 ${result.reviewed} 筆),` +
|
||||
`達固化條件的候選 ${result.candidates.length} 組${result.pressure ? "(已達容量壓力 R6)" : ""}`,
|
||||
"轉入條件:",
|
||||
...pl.PROMOTION_RULES.map((r) => ` ${r.id} ${r.label}`),
|
||||
];
|
||||
@@ -938,10 +954,19 @@ commands.candidates = ({ flags }) => {
|
||||
`\n[${cand.rules.join("+")}] ${cand.kind}「${cand.key}」→ 建議 type=${cand.suggested_type}, ` +
|
||||
`salience=${cand.suggested_salience}(${cand.entries.length} 筆依據)`,
|
||||
);
|
||||
// `#N` 就是 `--from-short` 與 `--reviewed` 要填的編號
|
||||
for (const entry of cand.entries.slice(0, 6)) {
|
||||
lines.push(` - [${entry.role || "?"}] ${String(entry.text || "").slice(0, 90)}(顯著度 ${entry.salience ?? "?"})`);
|
||||
lines.push(` - #${entry._index} [${entry.role || "?"}] ${String(entry.text || "").slice(0, 90)}(顯著度 ${entry.salience ?? "?"})`);
|
||||
}
|
||||
}
|
||||
if (result.candidates.length) {
|
||||
lines.push(
|
||||
"",
|
||||
"判斷完之後要把來源標掉,不然下一輪同一批又會被算成候選:",
|
||||
" 固化的 `consolidate --from-short <#N,#N> ...`(寫長期記憶時一起標)",
|
||||
" 看過不記的`candidates --reviewed <#N,#N>`",
|
||||
);
|
||||
}
|
||||
emit(result, flags.json, lines);
|
||||
};
|
||||
|
||||
@@ -1029,6 +1054,16 @@ commands.consolidate = ({ flags }) => {
|
||||
];
|
||||
pl.writeText(file, front.join("\n"));
|
||||
const total = pl.rebuildIndex(slug);
|
||||
// 來源短期記憶標成判斷過:不標的話這幾筆下一輪又會被算成固化候選。
|
||||
// 編號看 `candidates` 輸出裡每筆前面的 `#N`。
|
||||
if (flags["from-short"] !== undefined) {
|
||||
const indexes = csv(flags["from-short"]).map((s) => Number(String(s).replace(/^#/, "")));
|
||||
if (!indexes.length || indexes.some((n) => !Number.isInteger(n))) {
|
||||
die("`--from-short` 要的是以逗號分隔的編號(候選輸出裡每筆前面的 `#N`)。");
|
||||
}
|
||||
const res = pl.markShortTermReviewed(slug, { indexes, promotedTo: name });
|
||||
say(` 來源的 ${res.marked} 筆短期記憶已標成判斷過(promoted_to: ${name})。`);
|
||||
}
|
||||
const forget = num(flags.forget, null);
|
||||
if (forget !== null) {
|
||||
// 跟 prune 套同一層保護:承諾、界線與今天的紀錄不能被固化順手洗掉(R4)
|
||||
@@ -2536,10 +2571,15 @@ const HELP = `persona.mjs — jsc-persona 人格 / 記憶 / 情緒 / 關係圖 C
|
||||
睡眠收尾(機械性):關係時間戳→裁短期→收思維導圖→情緒衰減 8 小時→重建索引
|
||||
→修剪 said→壓縮舊 journal→寫 sleep.json→Gitea 兩區 push+驗證。
|
||||
預設保留載入鎖(--release 才收工)。--json 只回報「睡完了沒、哪一步出錯」。
|
||||
candidates --session <id> 列出達到「短期→長期」條件的候選與依據
|
||||
candidates --session <id> [--reviewed all|<#N,#N> [--until <ISO>]]
|
||||
列出達到「短期→長期」條件的候選與依據(每筆前面的 #N 就是它的編號)。
|
||||
--reviewed 把判斷過的短期記憶標掉(看過決定不記的用這個)——
|
||||
候選數字只有這裡跟 consolidate --from-short 會往下扣,
|
||||
判斷完不標,同一批下一輪又會被算成候選。
|
||||
consolidate --session <id> --name <n> --body <b> [--type --about --topics --salience --strength --emotion
|
||||
--when --where --mood --gist --detail --rules --source --forget]
|
||||
--when --where --mood --gist --detail --rules --source --forget --from-short <#N,#N>]
|
||||
內文會切成「主旨/細節」兩層:衰減先吃細節,主旨最後才掉。
|
||||
--from-short 標記這則是從哪幾筆短期記憶長出來的(順便標成判斷過)。
|
||||
prune / reindex --session <id>
|
||||
migrate --session <id> [--all] [--dry-run] [--json]
|
||||
長期記憶升格式(補 strength、切主旨/細節)。--all 掃所有人格,只回報數量不印內容。
|
||||
|
||||
Reference in New Issue
Block a user