Memory System
最後更新
Agenvoy 的 memory 層針對 conversation memory 有三個層級,另加一個跨 session 的 error memory 層。
| 層級 | 後端 | 範圍 |
|---|---|---|
| 1. Context window(16 則訊息 + summary) | history.json + summary.json | session |
| 2. 語意搜尋(近期) | ToriiDB DBSessionHist(vector) | session |
| 3. 全文封存(全部歷史) | 透過 go-sqlkit 的 SQLite FTS5 | session |
| Error memory | ToriiDB error_memory(90 天 TTL) | 跨 session |
操作者筆記(SQLite
note + note_fts5、find_note 工具、/v1/note*)已於 v1.0.23 移除,不再有全域筆記層。三層 conversation memory
1. Context window(limits.max_history_messages,預設 16)
每個 session 完整保留最近 N 則訊息並餵入 LLM context window。更舊的內容仍留在
history.json,但不送給 LLM。滾動式 summary(
summary.json)將較舊的對話濃縮,讓較舊 context 得以在 N 則訊息窗口之外存續。v1.3.7 起它以 Prior summary JSON 區塊放進 turn context——也就是緊接近期歷史、位於使用者輸入之前的那一則 user 訊息;v1.3.7 以前它是夾在歷史與 turn context 之間的獨立 user 訊息,附帶一大段固定說明(移除於 v1.3.7)。設定了 decision model 時,每個 turn 只保留與這次請求相關的 key_decisions、past_discussions 與 current_discussion 條目(語言、格式或工作流程這類通用規則一律算相關);沒有 decision model 或判斷失敗時全部保留,沒有任何相關條目則整份 summary 不放。使用方式——遵守 key_decisions、其餘當背景、被問到「之前談過什麼」時以文字從中回答——寫在 system prompt 的 Prior Conversation Context 區塊。工具歷史壓縮不再丟掉 summary。增量游標:
.summary_cursor(per-session;舊的 summary.meta.json 在 v1.1.1 以前於啟動時遷移,v1.1.2 起不再讀取)記錄最後一則已摘要訊息的時間(格式 YYYY-MM-DD HH:MM:SS,取自紀錄的 SendAt)。系統 cron(*/15 * * * *)挑出所有非 temp- 開頭、且 history.json 比 summary.json 新的 session(mtime gate),對其執行 Generate:- 已有 summary 但沒有游標的 session,只把游標設為最新訊息時間,不呼叫 LLM
- v1.3.1 起若設定了 decision model,比游標新(或沒有時間戳)的訊息會先分組成一段段對話——一則使用者訊息加上其後的回覆——由 decision model 移除信心至少 0.7 判定為噪音的部分(問候、致謝、重複同一請求、沒有結果的失敗或取消輪次),再從前一份 summary 移除被這些對話推翻或取代的
key_decisions與past_discussions條目。判斷失敗則全部保留;若沒有剩下任何對話,直接儲存精簡後的 summary 與游標,不呼叫 LLM - 比游標新(或沒有時間戳)且仍保留的訊息依
SUMMARY_RUNES(32,000 字元)切成 chunk;chunk 不會停在使用者訊息上 - 每個 chunk 執行一次 LLM call,system prompt 已含前一份 summary(
{{.Summary}}),合併在生成時完成,沒有獨立的合併步驟 - 每個 chunk 成功後儲存
summary.json與游標;某個 chunk 失敗或無法解析就中止,不對後續 chunk 計費,下一次 cron tick 重試
v1.3.1 起 summary prompt 只把之後都適用的規則記為
key_decisions(最多 8 條),v1.3.7 起每條不論輸出語言一律以英文 Do: (同意)或 Don't: (拒絕)開頭——要: / 不要:、する: / しない: 這類隨語言變動的標記已移除於 v1.3.7,既有條目會在下次合併時改寫——各欄位內文則以設定的 reply_lang 撰寫;設為 auto 時跟隨對話語言,中文一律為台灣繁體中文。2. 語意搜尋 —— ToriiDB(近期對話)
chat_history 工具以 mode=search 搭配 match=semantic 透過 ToriiDB db.VSearch 執行 vector 相似度搜尋。每次命中觸發 context window 擴展:命中前 2 則 + 命中後 1 則。ToriiDB 條目於
history.json compaction 期間清理 —— 早於 compact cutoff 的條目被移除,讓 ToriiDB 專注於近期對話。較舊資料存於 SQLite(層級 3)。3. 全文封存 —— SQLite FTS5(全部歷史)
每則寫入
history.json 的訊息都會透過 go-sqlkit 雙寫至 SQLite(~/.config/agenvoy/.store/history.db)。即使 history.json 已 compact,SQLite 始終持有完整對話歷史。chat_history 工具以 mode=search 搭配 match=keyword 對 SQLite 封存執行 FTS5 全文搜尋 + 對近期條目執行 ToriiDB 子字串比對,並合併結果。Compaction: 當
history.json 超過 max_history_bytes(自 v1.0.12 起預設 4 MiB,即 1 MiB 文件上限的四倍)時,最舊訊息在完整的 user+assistant pair 邊界上裁剪至 80%。cutoff timestamp 記錄於 SQLite message_meta.start_at,使 keyword 搜尋排除 history.json 已存在的條目(避免重複)。早於 cutoff 的 ToriiDB 條目亦被移除。Backfill: 首次遇到(SQLite 對某 session 無資料但
history.json 有內容)時,整份既有歷史 backfill 進 SQLite。Timestamps: 以 UTC unix 奈秒儲存。訊息內容中的 timestamp 透過
time.ParseInLocation(本地時區)解析並轉為 UTC 儲存。搜尋查詢使用 time.Now().UnixNano()(已為 UTC)。儲存佈局
| 儲存 | 內容 | 生命週期 |
|---|---|---|
history.json | 近期訊息(hot,LLM 每 turn 讀取) | 4 MiB 時自動 compact |
ToriiDB DBSessionHist | 帶 embeddings 的近期訊息 | compact 時清理(移除早於 cutoff 的條目) |
SQLite messages | 曾寫入的所有訊息(雙寫) | reset / remove-session 時清除 |
SQLite message_meta | start_at —— compact cutoff timestamp | reset / remove-session 時清除 |
summary.json | 滾動式 summary blob | reset 後存續 |
ToriiDB error_memory | 已解決的 tool 錯誤記錄與其修正方式,由 write_error 寫入(error_history(mode=write) 移除於 v1.3.11) | 90 天 TTL(命中時續期) |
Migration 注意
Session 與 error memory 過去存於 per-session JSON 檔,現已改置於 ~/.config/agenvoy/.store/ 底下的嵌入式 store——ToriiDB(
db_0 工具快取、db_1 對話向量、db_2 錯誤記憶、db_3 online marker),history.db 承載 session 設定、訊息封存、action 歷史、檔案歷史與用量。筆記於 v0.35.3 自 ToriiDB 移入 SQLite note 表,並於 v1.0.23 連同 daemon 啟動時的筆記 migration 一併移除;state 表於 v0.35.3 移除。bot.json / bot.md / config.json / status.json / usage.log / summary.meta.json 在 v1.1.1 以前於 daemon 啟動時遷移進來;v1.1.2 移除這些遷移,以及 v1.0 以前資料庫的 SQLite 欄位升級,這麼舊的安裝請先經過 v1.1.1。勿重新引入 JSON 路徑。