feat(記憶與目標): 專案目標三條 + 固化候選不再重算(0.2.1) #17
@@ -2484,6 +2484,45 @@ export function memoryStrength(meta, now = Date.now()) {
|
||||
* 依回想強度決定「這則記憶現在能講出多少」。
|
||||
* clear 全部 / faded 只剩主旨 / fuzzy 只剩「有這件事」
|
||||
*/
|
||||
/** 每輪最多注入幾條慣例(慣例是背景,不是話題)。 */
|
||||
export const HABIT_INJECT_MAX = 2;
|
||||
|
||||
/**
|
||||
* 現在還算數的慣例(`--type habit`)。
|
||||
*
|
||||
* 慣例跟其他記憶不一樣:它不是「記得的事」,是「每次都這樣做的事」。只在關鍵詞
|
||||
* 對上的時候才 `recall` 出來的慣例等於沒有——沒有人會為了照慣例做事先去回想它。
|
||||
* 所以它每輪注入,不必被問到。
|
||||
*
|
||||
* 糊掉的(`fuzzy`)不注入:想不起來的慣例就不是慣例了,那是「以前好像有這種習慣」。
|
||||
*/
|
||||
export function standingHabits(slug, { limit = HABIT_INJECT_MAX } = {}) {
|
||||
return longTermEntries(slug)
|
||||
.filter((meta) => String(meta.type || "") === "habit")
|
||||
.map((meta) => ({ meta, strength: memoryStrength(meta) }))
|
||||
.filter(({ strength }) => strength.state !== "fuzzy")
|
||||
.sort((a, b) => b.strength.retrievability - a.strength.retrievability
|
||||
|| String(b.meta.last_seen || "").localeCompare(String(a.meta.last_seen || "")))
|
||||
.slice(0, Math.max(1, Math.min(Number(limit) || HABIT_INJECT_MAX, HABIT_INJECT_MAX)))
|
||||
.map(({ meta, strength }) => ({
|
||||
name: meta._name,
|
||||
title: injectSafeLine(meta.title || meta._name),
|
||||
gist: injectSafeLine(meta._gist || meta._body, 160),
|
||||
state: strength.state,
|
||||
}));
|
||||
}
|
||||
|
||||
/** 注入用的一段:我們之間的慣例。沒有慣例就整段不出現。 */
|
||||
export function habitBrief(slug, opts = {}) {
|
||||
const rows = standingHabits(slug, opts);
|
||||
if (!rows.length) return "";
|
||||
return (
|
||||
`我們之間的慣例(${rows.length} 條,**照做,不用提起它**):\n` +
|
||||
rows.map((r) => ` - ${r.gist}${r.state === "faded" ? "(只記得大概)" : ""}`).join("\n") + "\n" +
|
||||
" 慣例是背景不是話題:照著做就好,講出來(「我們不是都這樣嗎」)就變成在討論它。"
|
||||
);
|
||||
}
|
||||
|
||||
export function memoryRecalled(meta, now = Date.now()) {
|
||||
const s = memoryStrength(meta, now);
|
||||
if (s.state === "clear") {
|
||||
@@ -5974,6 +6013,9 @@ export function turnContext(slug, sessionId, prompt = "") {
|
||||
// 同一支 `voice add`——所以 persona-story 與 persona-anime 寫的是同一格。
|
||||
const idiolect = idiolectBrief(slug);
|
||||
if (idiolect) lines.push(idiolect);
|
||||
// 慣例:程序性記憶,不必被問到(沒有人為了照慣例做事先去回想它)。
|
||||
const habits = habitBrief(slug);
|
||||
if (habits) lines.push(habits);
|
||||
} catch {
|
||||
/* 語氣檔壞掉不該讓整輪掛掉 */
|
||||
}
|
||||
|
||||
+4
-1
@@ -1026,8 +1026,11 @@ commands.consolidate = ({ flags }) => {
|
||||
if (!body.trim()) die("需要 `--body` 或 `--body-file`。");
|
||||
const type = str(flags.type) || "fact";
|
||||
// diary:睡眠時寫的第一人稱回顧(persona-sleep/persona-sleeper 都用這個型別)
|
||||
// habit:**我們之間的慣例**(「他習慣先講結論」「我們之間不說再見」)。
|
||||
// 其餘型別都是宣告性記憶(發生過什麼、他偏好什麼),慣例是程序性的——
|
||||
// 它不是「記得的事」,是「每次都這樣做的事」,所以另立一格並且每輪注入。
|
||||
const VALID_TYPES = [
|
||||
"fact", "preference", "event", "promise", "relationship", "insight", "boundary", "canon", "diary",
|
||||
"fact", "preference", "event", "promise", "relationship", "insight", "boundary", "canon", "diary", "habit",
|
||||
];
|
||||
if (!VALID_TYPES.includes(type)) die(`--type 只能是 ${VALID_TYPES.join("/")}。`);
|
||||
const about = csv(flags.about).length ? csv(flags.about) : ["user"];
|
||||
|
||||
Reference in New Issue
Block a user