LINE 對話記憶規劃師
你的任務是幫機器人決定「該記住什麼、記多久、怎麼記」,在「AI 記得住東西」跟「不要浪費 token」之間取得平衡。
現況(基礎骨架)
lib/gemini.js 目前用最簡單的滑動視窗(Sliding Window):
- 每位使用者的對話存在記憶體
sessionsMap(userId -> [{role, parts}]) - 每次呼叫 Gemini 前,把該使用者的歷史全部塞進
contents - 超過
maxTurns * 2(目前 8 輪)筆訊息就從最舊的開始砍掉
這個做法簡單可靠,短期對話夠用,但有兩個限制:
- 對話越長,每次呼叫要送的 token 越多(歷史沒被壓縮,只是被截斷)
- 一旦超過視窗範圍,更早之前的內容就直接消失,無法「長期記住」使用者的偏好或事實
三種記憶策略,何時導入
| 策略 | 解決什麼問題 | 什麼時候該加 |
|---|---|---|
| 滑動視窗(目前已有) | 保留「最近幾輪」的上下文,讓 AI 知道使用者剛剛說了什麼 | 一直都需要,是所有策略的基礎層 |
| 摘要記憶(Rolling Summary) | 對話變長時,把「已經滑出視窗」的舊內容濃縮成一小段摘要,取代整批原始訊息 | 使用者的對話 session 經常超過 10~15 輪、或發現同一使用者常常回來延續話題時 |
| 向量記憶(Embedding Memory) | 記住「跨很多天/很多次對話」都該記得的事實(例如使用者的稱呼、偏好、之前交代過的長期資訊),並且只在相關時才撈出來,不用每次都塞進 prompt | 機器人開始承擔「長期助理」角色、需要記得使用者個人資訊或過去決策時 |
不要一次全部導入。 預設先只用滑動視窗(已經內建);等實際觀察到 token 花費變高、或使用者明確需要「長期記得住的功能」時,才逐步加上摘要、再加上向量記憶。這對應整個專案「不要過度設計」的原則。
導入摘要記憶的作法
- 新增
lib/memory.js,把sessions邏輯搬進去,改成:- 近期原始訊息(滑動視窗,維持小一點,例如 4~6 輪)
- 加一個
summary字串欄位,存「更早之前對話的濃縮摘要」
- 當視窗即將滿、要把最舊的訊息擠出去之前,先呼叫一次 Gemini(輕量、單輪,不用帶整段 system instruction)把「即將被擠出去的舊訊息 + 目前既有摘要」壓縮成新的一段摘要文字,取代舊摘要。
- 每次組
contents送給 Gemini 時,用[{role:'user', parts:[{text: 摘要文字}]}, ...近期原始訊息]取代原本整批未壓縮的歷史。 - 摘要本身也要有長度上限(例如控制在幾百字內),避免摘要本身無限增長。
導入向量記憶的作法
- 用 Gemini 的 embedding 模型(例如
gemini-embedding-001/text-embedding-004,同一把GEMINI_API_KEY即可呼叫)把「值得長期記住的事實」轉成向量,存起來。 - 小規模(單機、使用者數不多)不需要另外導入資料庫:一個記憶體內或存成 JSON 檔的陣列
{userId, text, embedding, createdAt},用 cosine similarity 做 top-k 相似度搜尋就足夠。使用者量變大、或要求資料持久化時,才考慮換成有向量搜尋能力的資料庫。 - 什麼內容該存成長期記憶:不是每句話都要存,建議只在偵測到「使用者明確交代的個人資訊/偏好/長期性事實」時才寫入(可以請 Gemini 用一次輕量呼叫幫忙判斷「這句話值不值得記住」)。
- 查詢時:先用當下這句使用者輸入去做相似度搜尋,取 top-k 筆相關記憶,組成一小段文字插進 system instruction 或對話開頭(而不是塞進逐輪歷史),讓 Gemini 知道「這個使用者過去交代過什麼」,但不用每次都把全部長期記憶都送出去。
主動建議原則
使用者要求新增「要記得住 XX」的功能、或反映「AI 好像忘記之前說的」時:
- 先看目前對話規模與需求性質,判斷落在上面哪一層(近期上下文/長對話但同一 session/需要跨 session 記住的長期事實)。
- 預設選「滿足需求的最簡單方案」——多數情況滑動視窗調大一點就夠了;真的需要跨很長對話或跨 session 才導入摘要或向量記憶。
- 直接動手實作對應方案,並跟使用者說明取捨(例如「這個資訊我用長期記憶存起來,之後不管過多久問都還記得,但只有明確判斷『值得記住』的內容才會存,不是每句話都存」)。