From afb29b6bc05849e919ff5ce7ab6ce7a2bc412131 Mon Sep 17 00:00:00 2001 From: Jeffery Date: Wed, 29 Jul 2026 12:18:24 +0800 Subject: [PATCH] =?UTF-8?q?fix(role):=20=E4=BF=AE=E6=AD=A3=20SLEEP=5FBATCH?= =?UTF-8?q?=20=E8=88=87=E8=BC=B8=E5=87=BA=E4=B8=8A=E9=99=90=E7=9F=9B?= =?UTF-8?q?=E7=9B=BE=E5=B0=8E=E8=87=B4=E6=95=B4=E6=89=B9=E6=95=B4=E7=90=86?= =?UTF-8?q?=E5=A4=B1=E6=95=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 以真實資料整理 25 則 inbox 時失敗(「整理結果無法套用」)。查證後為參數設計矛盾: - ROLE_SLEEP_BATCH 預設 60,宣稱一次可處理 60 則 - 但每則整理結果約需 650 字元(summary/content/各欄位), 而 ROLE_SLEEP_OUTPUT_LIMIT 為 8000 → 實際只能容納約 12 則 - 實測 25 則的素材達 25798 位元組(COLLECT_LIMIT 為 12000), 輸出 JSON 被截斷成不合法格式,apply 解析失敗,整批無法套用 所幸失敗時 inbox 保留不動的設計生效,沒有遺失任何記憶。 - SLEEP_BATCH 預設 60 → 12,並在程式碼註明推算依據與這次的實測數據 - collect 在超出單批上限時於素材標頭標示「本批 N 則,另有 M 則留待下批」, 避免誤以為已全部整理完 - SKILL.md 註明此參數不可任意調高,需與輸出上限相容 驗證:以 20 則 inbox 確認正確切為 12 + 8 並標示留待下批; 爸爸的 25 則實際記憶以每批 10 則分三次整理完成(新增 9 則、合併 6 則、捨棄 2 則), inbox 清空,且 DIGESTS.md 在真實資料上正確累積 3 筆整理歷史。 版號沿用 0.0.5(master 為 0.0.4,同一 PR 不再累加) Co-Authored-By: Claude Opus 5 (1M context) --- scripts/role/memory.js | 15 ++++++++++++--- skills/role/SKILL.md | 6 +++++- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/scripts/role/memory.js b/scripts/role/memory.js index c55018b..33cfff1 100755 --- a/scripts/role/memory.js +++ b/scripts/role/memory.js @@ -58,7 +58,11 @@ const MEMORY_TYPE_WEIGHT = { const DECLARATIVE_VALUES = ["explicit", "implicit"]; const RETENTION_STAGES = ["working", "long_term"]; -const SLEEP_BATCH = 60; +// 單批上限刻意壓在 12:每則整理結果約需 650 字元(summary/content/各欄位), +// 而 SLEEP_OUTPUT_LIMIT 預設 8000,8000÷650≈12。原本預設 60 與輸出上限矛盾 —— +// 實測 25 則的素材達 25798 位元組、輸出 JSON 被截斷成不合法格式,整批整理直接失敗。 +// 寧可分多批各自成功,也不要一次做完卻全部失敗。 +const SLEEP_BATCH = 12; const COLLECT_LIMIT = 12000; const EXISTING_INDEX_LIMIT = 120; const CONTENT_LIMIT = 1200; @@ -613,10 +617,15 @@ function cmdCollect(args) { const batchSize = Math.max(1, args.batch); const collectLimit = Math.max(2000, args.limit); const existingLimit = Math.max(0, args.existingLimit); - const inbox = listInbox(args.role).slice(0, batchSize); + const all = listInbox(args.role); + const inbox = all.slice(0, batchSize); if (!inbox.length) return 1; + // 明確標示本批未處理的量,避免使用者誤以為已全部整理完 + const deferred = all.length - inbox.length; - const lines = ["=== INBOX(待整理,每則以 id 標識)==="]; + const lines = deferred > 0 + ? [`=== INBOX(待整理,每則以 id 標識;本批 ${inbox.length} 則,另有 ${deferred} 則留待下批)===`] + : ["=== INBOX(待整理,每則以 id 標識)==="]; for (const [meta, content] of inbox) { lines.push(`--- id: ${meta.id} | 時間: ${meta.created || "-"} ---`); lines.push(`初判分類: ${CATEGORY_LABELS[meta.category || "other"] || "其他"}`); diff --git a/skills/role/SKILL.md b/skills/role/SKILL.md index a89d52a..30251ca 100644 --- a/skills/role/SKILL.md +++ b/skills/role/SKILL.md @@ -108,7 +108,7 @@ ROLE_DIR="/../../scripts/role" # 其他助理 | `ROLE_CAPTURE_TIMEOUT` | | Stop hook 輕量濃縮模型逾時秒數 | `25` | | `ROLE_SLEEP_TIMEOUT` | | 單次 NREM/REM 整理的模型逾時秒數 | `180` | | `ROLE_SLEEP_COLLECT_LIMIT` | | 睡眠整理送進模型的素材字元預算 | `12000` | -| `ROLE_SLEEP_BATCH` | | 單次睡眠整理最多處理的 inbox 筆數 | `60` | +| `ROLE_SLEEP_BATCH` | | 單次睡眠整理最多處理的 inbox 筆數。**不可任意調高** —— 每則整理結果約需 650 字元,需與 `ROLE_SLEEP_OUTPUT_LIMIT` 相容(8000÷650≈12),否則輸出 JSON 會被截斷導致整批失敗 | `12` | | `ROLE_SLEEP_EXISTING_LIMIT` | | 睡眠整理素材中可放入的既有記憶索引筆數 | `120` | | `ROLE_SLEEP_OUTPUT_LIMIT` | | 睡眠整理模型輸出套用前的字元上限 | `8000` | | `ROLE_NAP_ENABLED` | | 小睡整理開關;CLI 閒置一段時間且 inbox 達門檻時自動整理 | `1` | @@ -661,6 +661,10 @@ flowchart TD 整理失敗(模型無回應、輸出非合法 JSON)時**保留 inbox 不動**,留到下個週期重做,寧可晚整理也不遺失記憶。 +**批次大小必須與輸出上限相容**:`ROLE_SLEEP_BATCH` 預設 12,是由 `ROLE_SLEEP_OUTPUT_LIMIT`(8000)除以每則約 650 字元推算的上限。曾因預設 60 與輸出上限矛盾,25 則 inbox 的素材達 25798 位元組、輸出 JSON 被截斷成不合法格式,導致整批整理失敗(所幸失敗時 inbox 保留不動,未遺失資料)。 + +待整理筆數超過單批上限時,`collect` 會在素材標頭標示「本批 N 則,另有 M 則留待下批」,多餘的留到下一次整理,**寧可分多批各自成功,也不要一次做完卻全部失敗**。 + --- ## 機密與 PII(兩道防線) -- 2.53.0