fix(role): 修正 SLEEP_BATCH 與輸出上限矛盾導致整批整理失敗

以真實資料整理 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) <noreply@anthropic.com>
This commit is contained in:
Jeffery
2026-07-29 12:18:24 +08:00
co-authored by Claude Opus 5
parent a757f9e4b0
commit afb29b6bc0
2 changed files with 17 additions and 4 deletions
+12 -3
View File
@@ -58,7 +58,11 @@ const MEMORY_TYPE_WEIGHT = {
const DECLARATIVE_VALUES = ["explicit", "implicit"]; const DECLARATIVE_VALUES = ["explicit", "implicit"];
const RETENTION_STAGES = ["working", "long_term"]; const RETENTION_STAGES = ["working", "long_term"];
const SLEEP_BATCH = 60; // 單批上限刻意壓在 12:每則整理結果約需 650 字元(summarycontent/各欄位),
// 而 SLEEP_OUTPUT_LIMIT 預設 80008000÷650≈12。原本預設 60 與輸出上限矛盾 ——
// 實測 25 則的素材達 25798 位元組、輸出 JSON 被截斷成不合法格式,整批整理直接失敗。
// 寧可分多批各自成功,也不要一次做完卻全部失敗。
const SLEEP_BATCH = 12;
const COLLECT_LIMIT = 12000; const COLLECT_LIMIT = 12000;
const EXISTING_INDEX_LIMIT = 120; const EXISTING_INDEX_LIMIT = 120;
const CONTENT_LIMIT = 1200; const CONTENT_LIMIT = 1200;
@@ -613,10 +617,15 @@ function cmdCollect(args) {
const batchSize = Math.max(1, args.batch); const batchSize = Math.max(1, args.batch);
const collectLimit = Math.max(2000, args.limit); const collectLimit = Math.max(2000, args.limit);
const existingLimit = Math.max(0, args.existingLimit); 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; 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) { for (const [meta, content] of inbox) {
lines.push(`--- id: ${meta.id} | 時間: ${meta.created || "-"} ---`); lines.push(`--- id: ${meta.id} | 時間: ${meta.created || "-"} ---`);
lines.push(`初判分類: ${CATEGORY_LABELS[meta.category || "other"] || "其他"}`); lines.push(`初判分類: ${CATEGORY_LABELS[meta.category || "other"] || "其他"}`);
+5 -1
View File
@@ -108,7 +108,7 @@ ROLE_DIR="<skill base directory>/../../scripts/role" # 其他助理
| `ROLE_CAPTURE_TIMEOUT` | | Stop hook 輕量濃縮模型逾時秒數 | `25` | | `ROLE_CAPTURE_TIMEOUT` | | Stop hook 輕量濃縮模型逾時秒數 | `25` |
| `ROLE_SLEEP_TIMEOUT` | | 單次 NREM/REM 整理的模型逾時秒數 | `180` | | `ROLE_SLEEP_TIMEOUT` | | 單次 NREM/REM 整理的模型逾時秒數 | `180` |
| `ROLE_SLEEP_COLLECT_LIMIT` | | 睡眠整理送進模型的素材字元預算 | `12000` | | `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_EXISTING_LIMIT` | | 睡眠整理素材中可放入的既有記憶索引筆數 | `120` |
| `ROLE_SLEEP_OUTPUT_LIMIT` | | 睡眠整理模型輸出套用前的字元上限 | `8000` | | `ROLE_SLEEP_OUTPUT_LIMIT` | | 睡眠整理模型輸出套用前的字元上限 | `8000` |
| `ROLE_NAP_ENABLED` | | 小睡整理開關;CLI 閒置一段時間且 inbox 達門檻時自動整理 | `1` | | `ROLE_NAP_ENABLED` | | 小睡整理開關;CLI 閒置一段時間且 inbox 達門檻時自動整理 | `1` |
@@ -661,6 +661,10 @@ flowchart TD
整理失敗(模型無回應、輸出非合法 JSON)時**保留 inbox 不動**,留到下個週期重做,寧可晚整理也不遺失記憶。 整理失敗(模型無回應、輸出非合法 JSON)時**保留 inbox 不動**,留到下個週期重做,寧可晚整理也不遺失記憶。
**批次大小必須與輸出上限相容**`ROLE_SLEEP_BATCH` 預設 12,是由 `ROLE_SLEEP_OUTPUT_LIMIT`(8000)除以每則約 650 字元推算的上限。曾因預設 60 與輸出上限矛盾,25 則 inbox 的素材達 25798 位元組、輸出 JSON 被截斷成不合法格式,導致整批整理失敗(所幸失敗時 inbox 保留不動,未遺失資料)。
待整理筆數超過單批上限時,`collect` 會在素材標頭標示「本批 N 則,另有 M 則留待下批」,多餘的留到下一次整理,**寧可分多批各自成功,也不要一次做完卻全部失敗**。
--- ---
## 機密與 PII(兩道防線) ## 機密與 PII(兩道防線)