refactor(role): 將角色記憶工具改寫為 Node.js
This commit is contained in:
+31
-23
@@ -2,13 +2,13 @@
|
||||
# ==============================================================================
|
||||
# 用途:角色的睡眠與記憶整理。由 cron 於睡眠時段每小時觸發(--run),
|
||||
# 先檢查是否有 AI 正在運行,沒有才進入睡眠並整理記憶:
|
||||
# 分類(重要/興趣/新知/技能/日常/其他)→ 去重合併 → 設標籤與一句話
|
||||
# 總結 → 壓縮內容歸檔 → 日常與其他依使用頻率遺忘。
|
||||
# NREM 鞏固(分類/去噪/去重/合併/優先度)→ REM 整合(跨記憶
|
||||
# 連結/抽象化/提取線索)→ 壓縮歸檔 → 日常與其他依使用頻率與優先度遺忘。
|
||||
# 另提供 --catchup(cron 未執行時的補跑)、--force(手動立即整理)、
|
||||
# --install-cron/--remove-cron(排程安裝與移除)、--status(狀態)。
|
||||
# 更新時間:2026/07/28 00:00:00
|
||||
# 相依:bash、python3、任一 headless CLI、crontab(僅排程安裝需要)、
|
||||
# 同目錄的 role_lib.sh 與 memory.py。
|
||||
# 更新時間:2026/07/28 12:21:11
|
||||
# 相依:bash、node、任一 headless CLI、crontab(僅排程安裝需要)、
|
||||
# 同目錄的 role_lib.sh 與 memory.js。
|
||||
# 退出碼:0 成功或無事可做;1 參數錯誤或整理失敗(cron 觸發時不影響使用者)。
|
||||
# ==============================================================================
|
||||
|
||||
@@ -19,6 +19,7 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
|
||||
CRON_MARKER="# jsc-role-sleep"
|
||||
SLEEP_TIMEOUT="${ROLE_SLEEP_TIMEOUT:-180}"
|
||||
SLEEP_OUTPUT_LIMIT="${ROLE_SLEEP_OUTPUT_LIMIT:-8000}"
|
||||
|
||||
usage() {
|
||||
# 印出用法
|
||||
@@ -46,9 +47,9 @@ require_role() {
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
sleep_cycle() {
|
||||
# 執行一次完整記憶整理:收集素材 → 模型分類去重 → 落檔歸檔 → 遺忘
|
||||
# 執行一次完整記憶整理:收集素材 → NREM 鞏固 → REM 整合 → 落檔歸檔 → 遺忘
|
||||
local reason="$1" cli material prompt result applied forgotten
|
||||
command -v python3 >/dev/null 2>&1 || { role_log "ERR" "找不到 python3,無法整理記憶"; return 1; }
|
||||
command -v node >/dev/null 2>&1 || { role_log "ERR" "找不到 node,無法整理記憶"; return 1; }
|
||||
|
||||
if ! role_lock_acquire "$ROLE"; then
|
||||
role_log "WRN" "另一個整理程序正在執行,本次略過(角色 ${ROLE})"
|
||||
@@ -56,11 +57,11 @@ sleep_cycle() {
|
||||
fi
|
||||
trap 'role_lock_release "$ROLE"' EXIT
|
||||
|
||||
material="$(python3 "${SCRIPT_DIR}/memory.py" collect --role "$ROLE" 2>/dev/null)"
|
||||
material="$(node "${SCRIPT_DIR}/memory.js" collect --role "$ROLE" 2>/dev/null)"
|
||||
if [ -z "$material" ]; then
|
||||
role_log "INF" "沒有待整理記憶(角色 ${ROLE},觸發:${reason})"
|
||||
python3 "${SCRIPT_DIR}/memory.py" mark-sleep --role "$ROLE" >/dev/null 2>&1
|
||||
forgotten="$(python3 "${SCRIPT_DIR}/memory.py" forget --role "$ROLE" 2>/dev/null)"
|
||||
node "${SCRIPT_DIR}/memory.js" mark-sleep --role "$ROLE" >/dev/null 2>&1
|
||||
forgotten="$(node "${SCRIPT_DIR}/memory.js" forget --role "$ROLE" 2>/dev/null)"
|
||||
role_log "INF" "遺忘檢查:${forgotten}"
|
||||
role_lock_release "$ROLE"
|
||||
trap - EXIT
|
||||
@@ -71,21 +72,28 @@ sleep_cycle() {
|
||||
|
||||
prompt="$(cat <<EOF_PROMPT
|
||||
你是角色「${ROLE}」的睡眠記憶整理器。輸入包含兩段:INBOX(本次待整理的記憶)與 EXISTING(既有記憶索引)。
|
||||
請把 INBOX 整理成歸檔用的記憶,規則:
|
||||
請模擬睡眠中的兩階段記憶整理,但最後只輸出一個 JSON 物件。
|
||||
|
||||
1. 只輸出一個 JSON 物件,不要前言、不要結語、不要 code fence,格式為:
|
||||
{"memories":[{"action":"new","category":"skill","summary":"一句話總結","tags":["標籤1","標籤2"],"content":"- 要點\n- 要點","from":["inbox 的 id"]}]}
|
||||
2. action 三選一:
|
||||
{"memories":[{"action":"new","category":"skill","summary":"一句話總結","tags":["標籤1","標籤2"],"priority":4,"relevance":["explicit","future"],"links":["既有記憶 id"],"sleep_stage":"nrem-rem","content":"- 要點\n- 要點","from":["inbox 的 id"]}],"sleepDigest":"本次睡眠整理摘要,80 字內"}
|
||||
2. NREM 鞏固階段先做:去除雜訊與流水帳、遮蔽憑證與個資、分類、去重、合併、壓縮成可長期保存的穩定記憶。
|
||||
3. REM 整合階段再做:找出新記憶與 EXISTING 的關聯,抽出可重複套用的規則、偏好、決策模式、角色語氣調整或未來提取線索。
|
||||
4. action 三選一:
|
||||
- new:新的一則記憶。多則 INBOX 講同一件事時合成一筆,from 列出全部來源 id。
|
||||
- merge:內容已被 EXISTING 中某則涵蓋或重複,填 target 為該既有 id,content 寫合併後的完整內容。
|
||||
- drop:純雜訊、無保存價值,只需填 from。
|
||||
3. category 六選一:important(重要)/interest(興趣)/news(新知)/skill(技能)/daily(日常)/other(其他)。
|
||||
5. category 六選一:important(重要)/interest(興趣)/news(新知)/skill(技能)/daily(日常)/other(其他)。
|
||||
important 放長期偏好、規範、決策與身分背景;interest 放反覆關注的主題;news 放新事實與外部資訊;
|
||||
skill 放可重複套用的做法;daily 放一次性例行工作;其餘歸 other。
|
||||
4. **每一則 INBOX 的 id 都必須出現在某一筆的 from 中**,沒被提及的會留到下個睡眠週期重做。
|
||||
5. content 壓縮成 5 行以內要點(每行以「- 」開頭),總長不超過 400 字,去除重複敘述與流水帳。
|
||||
6. summary 一句話 40 字內;tags 2 至 4 個。全部使用繁體中文(台灣用語)。
|
||||
7. 嚴禁輸出任何憑證與個資:token、密碼、API key、連線字串、Email、電話、姓名、身分證號。
|
||||
6. priority 必填,1 到 5:5=使用者明確要求、長期規範、穩定偏好或核心身分;4=可重複套用的技能/決策;3=有用新知;2=短期日常;1=低價值但暫存。
|
||||
7. relevance 必填 1 至 4 個,從下列語意挑選或用等價繁中詞:explicit(使用者明確要求)、future(未來會用)、repeated(反覆出現)、novelty(新知)、emotional(語氣/情緒/偏好)、temporary(短期)。
|
||||
8. links 可填 EXISTING 中相關記憶 id;沒有就填空陣列。merge 時若有舊 links,應保留並加上新關聯。
|
||||
9. sleep_stage 填 "nrem"、"rem" 或 "nrem-rem"。只有純分類去噪用 nrem;有建立跨記憶連結或抽象規則用 rem 或 nrem-rem。
|
||||
10. **每一則 INBOX 的 id 都必須出現在某一筆的 from 中**,沒被提及的會留到下個睡眠週期重做。
|
||||
11. content 壓縮成 5 行以內要點(每行以「- 」開頭),總長不超過 400 字,去除重複敘述與流水帳。
|
||||
12. summary 一句話 40 字內;tags 2 至 4 個。全部使用繁體中文(台灣用語)。
|
||||
13. sleepDigest 總結本次新增、合併、丟棄、抽象化或建立關聯的重點,80 字內。
|
||||
14. 嚴禁輸出任何憑證與個資:token、密碼、API key、連線字串、Email、電話、姓名、身分證號。
|
||||
|
||||
素材:
|
||||
${material}
|
||||
@@ -100,8 +108,8 @@ EOF_PROMPT
|
||||
return 1
|
||||
fi
|
||||
|
||||
result="$(printf '%s' "$result" | python3 "${SCRIPT_DIR}/transcript.py" redact 2>/dev/null)"
|
||||
applied="$(printf '%s' "$result" | python3 "${SCRIPT_DIR}/memory.py" apply --role "$ROLE" 2>/dev/null)"
|
||||
result="$(printf '%s' "$result" | head -c "$SLEEP_OUTPUT_LIMIT" | node "${SCRIPT_DIR}/transcript.js" redact 2>/dev/null)"
|
||||
applied="$(printf '%s' "$result" | node "${SCRIPT_DIR}/memory.js" apply --role "$ROLE" 2>/dev/null)"
|
||||
if [ -z "$applied" ]; then
|
||||
role_log "ERR" "整理結果無法套用(角色 ${ROLE}),保留待整理記憶到下個週期"
|
||||
role_lock_release "$ROLE"
|
||||
@@ -110,7 +118,7 @@ EOF_PROMPT
|
||||
fi
|
||||
role_log "INF" "記憶整理完成(角色 ${ROLE},觸發:${reason}):${applied}"
|
||||
|
||||
forgotten="$(python3 "${SCRIPT_DIR}/memory.py" forget --role "$ROLE" 2>/dev/null | tr '\n' ';')"
|
||||
forgotten="$(node "${SCRIPT_DIR}/memory.js" forget --role "$ROLE" 2>/dev/null | tr '\n' ';')"
|
||||
role_log "INF" "遺忘檢查:${forgotten}"
|
||||
|
||||
role_lock_release "$ROLE"
|
||||
@@ -201,7 +209,7 @@ show_status() {
|
||||
printf '| cron 服務 | %s |\n' "$cron_service"
|
||||
printf '| 摘要 CLI | %s |\n' "$(role_select_cli 2>/dev/null || printf '找不到可用 CLI')"
|
||||
printf '\n'
|
||||
python3 "${SCRIPT_DIR}/memory.py" stats --role "$ROLE" 2>/dev/null
|
||||
node "${SCRIPT_DIR}/memory.js" stats --role "$ROLE" 2>/dev/null
|
||||
printf '\n'
|
||||
}
|
||||
|
||||
@@ -227,7 +235,7 @@ case "$MODE" in
|
||||
--catchup)
|
||||
role_enabled || exit 0
|
||||
require_role
|
||||
if [ "$(python3 "${SCRIPT_DIR}/memory.py" need-sleep --role "$ROLE" 2>/dev/null)" != "yes" ]; then
|
||||
if [ "$(node "${SCRIPT_DIR}/memory.js" need-sleep --role "$ROLE" 2>/dev/null)" != "yes" ]; then
|
||||
role_log "DBG" "不需補跑整理"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
Reference in New Issue
Block a user