記憶外掛把舊對話切成片段、靠相似度塞回 prompt,留得住的是有人在維護的文件

https://liao.gg/blog/agents-dont-need-memory

2026 年 10 月 3 日,Kevin Liao 在 liao.gg 寫記憶外掛實際在做的事。外掛分析人的對話,產生一千則彼此孤立的片段,塞進向量資料庫。人每下一則 prompt,它從庫裡拿出最相似的五則,附在 prompt 上。Agent 搞混了,就自己再搜,多拿幾則。他在括號裡補了一句:它本來就會搞混。他們把這個產品叫做記憶。

他沒有點名是哪一家。一千則、最相似的五則,還有文裡後來寫到的五百則、一萬筆、一千萬 token、三年,都是他描述這套做法時用的數字。

先看人要解決的是什麼,這套產品對不上。人要 Agent 懂這份專案。功能在哪裡,是它落在這份程式的哪個位置。為什麼做,是當初的理由。同意過什麼,是已經定下來的做法。在乎什麼,是人在意的事。人打下 prompt,要的是 Agent 照這些往下做。外掛交出來的卻是一場 RAG 抽獎:附上哪幾則由相似度決定,人只能盼有用的自己浮上來。

就算這次浮上來的剛好有用,Agent 手上仍是幾則從舊對話切出來的片段。他認為這不等於懂專案。整個記憶外掛的生態系解錯了問題。Agent 要的是文件。

他看到的每套,都是切片段、進庫、抽五則

他說市面上每一套記憶外掛都是同一條路。先掃一次次工作留下的對話逐字稿。再從逐字稿切出他們稱為記憶的片段。片段放進 RAG 用的資料庫。之後每一次 prompt,都取出最相似的五則,塞回這則 prompt。五則不夠用,就給 Agent 一個工具,讓它自己進庫裡再搜。

人沒有在 prompt 裡指定那五則。外掛按相似度自己附上,連同 prompt 一起送進模型。Agent 搞混了,再用工具多拿,多拿的仍是同一座庫裡的片段。他說架構就這些。

花俏加在這五步後面。有的讓 Agent 把舊逐字稿逐字搜過,一個字一個字對。有的把記憶分成短期和長期。有的在背景跑程式,做檢查、做合併,或把重複的去掉。有一種叫做 Dreamers 的,人沒在下 prompt 的時候,由它過夜把已經存進庫的記憶重寫一遍。也有人一直把上下文壓短。也有人再重排一次。這一類他沒有全部列完。

逐字搜、分層、過夜重寫、壓短、再重排,拿回來的都還是過去的對話。加料會燒 token。他認為這些功能是在補同一套、底下已經有問題的架構。所以他的判斷是,沒有一套因此就靠得住。

片段靠相似度浮出來,人稽核不了這座庫

這些外掛碰到的是同一類問題。他點了五個,還說問題不止這五個。每一套都試著修,也都沒修好。前提是同一個。

相似度只排兩段有多近。文字變成向量,距離近的排前面。近是這張排序唯一在答的事。哪一則是對的,哪一則現在還算數,哪一件該知道的事根本沒進庫,它都不答。人每次 prompt 拿到的就是這五則最像的。像,推不出該信哪一則。

片段存下來的時候,上下文不在裡面。一則 RAG 片段裝得下的字就那麼多,裝不下的留在外面。他點了留在外面的:當時的脈絡,做這件事的動機,學到的教訓,周圍的環境,還有更多。動機是當初為什麼定成這樣。教訓是上次哪裡出過事。環境是當時的程式和限制。這些被切掉之後,Agent 讀到的是一句能被搜到的舊話。

召回把過去當成現在還成立。搜逐字稿是把舊對話再找出來。搜向量庫也是。找出來的是當時說過的話。程式碼不會停在那天。他寫,程式碼每天都在改。改過之後,片段還停在改之前的那次對話。他問:身分驗證若有五百則片段,每一則現在還有多準。

搜尋工具也補不上另一個洞。工具可以交給 Agent。交出去之後,他問的是:Agent 怎麼知道現在該用。要知道該用,得先知道自己缺了一塊。Agent 不知道自己不知道的事。缺的那塊沒被它當成缺,搜尋就不會發生。庫裡就算有,也不會自己冒出來。

庫本身稽核不了。這裡的稽核,是人要能查庫裡到底有什麼。他寫,那裡有一萬筆 embedding,放在 SQLite。哪些記憶在?哪些過期了?哪些放進去之後從來沒被取回過?哪些是錯的,卻在暗中影響 Agent 怎麼做事?這四問要有人答得出來,庫才算查得了。他認為這座庫答不出。錯的那幾則人還沒看見,仍可能在下一次 prompt 裡被附上去。

人留知識是寫下來再用

他把這個生態系的前提這樣講。Agent 會忘。所以要修的是記得。要記得更好,就抓更多進來,把索引做更好,取回的時候更聰明。抓更多,庫裡的片段就更多。索引做更好,相近的就更好被撈到。取回更聰明,五則之外還可以再搜、再挑一次。三步處理的都是舊對話。

別人留知識,靠的是寫下來的紀錄。沒有人會把三年前的團隊會議再看一遍,只為了記起某個功能的限制當初怎麼定。會議當時怎麼講,人把它寫成紀錄。之後要查,人翻的是那份紀錄。限制若只活在三年前的對話裡,就得把整場再看一次才找得到。

解法不是給 Agent 一個搜尋工具,讓它到價值一千萬 token 的舊對話裡翻,把發生過的事用碎片重組起來。重組出來的仍是對話碎片。他寫的解法,是用文件當記憶。

現在這件事更急。很多人用 AI,以很快的速度灌出產品、功能,還有 slop。一行程式都不讀,也不懂。東西可以在沒人讀過程式的情況下交出去。文件就很容易被排到後面,變成事後才想起來補。他認為這時候文件該比以前更要緊。人不讀程式,功能為什麼存在、同意過什麼、人在乎什麼,Agent 還讀得到的,就是有人寫下來、並且還在維護的那些檔。

一份 AGENTS.md 留不住整份專案

人早就知道 Agent 需要脈絡,所以才有 AGENTS.md。這個檔讓 Agent 不要瞎進一份程式碼庫。他說這有用。常常,有用的結果是:整份專案的文件就這一個檔。

一個檔不夠。審查怎麼走、跟使用者討論過的規格、查過的函式庫、做過的決策、給 Agent 找檔用的索引,他要分開放,不要擠在同一份 AGENTS.md。他要一整個工作區,自己稱為 brain。brain 指的是一組有結構的檔,Agent 不必等人先開口,也可以把東西寫進去。裡面放指示、規格、決策、研究、索引,該留的都能留。

指示寫事情怎麼做。程式碼審查怎麼走,放在固定的檔。規格寫跟使用者討論過的內容。研究寫專案原本不熟的東西,一套陌生的函式庫,或一支 API。查過的結果留成檔,下次還能再用。

開工前,Agent 去這個工作區讀相關的檔。他要的脈絡是相關的,也是完整的。完整是指做法、決定、研究還在檔裡,Agent 讀到的是整份紀錄。做完才更新。他把更新的時間卡在這輪:事情剛做完,整張圖還在這輪的上下文裡。過期的檔在這時候改,缺的地方補上新文件。這輪一旦結束,那張圖就不在上下文裡了,過期的檔就不會自己改掉。

舊迴圈是下了 prompt 就做,做完這輪就忘。他要的迴圈是下了 prompt 先查文件,查出相關的檔再做,做完把文件更新。下一輪進來的 Agent 沒有上一輪的對話。它查到的,是上一輪改過的那些檔。

以前的記法,是在 Agent 旁邊再掛一座 RAG 資料庫,每次 prompt 從那座庫抽片段。他要把記憶放進這個工作區。人讀得了,改得了,也可以把檔分享出去。Agent 要懂的那四件事,寫在有人繼續維護的檔裡。下一輪要懂專案,讀的也是這些檔。

一年多前,他叫 Agent 寫進 internal

他說這個問題自己一年多前就看到了。那時他才開始用 AI 寫程式,要的是 Agent 換一個工作階段之後,還記得上一輪做過的工作。上一輪若只留在對話裡,新的一輪就接不到。他建了一個 internal/ 資料夾,叫 Agent 把規格、計畫、索引寫成檔,放在這個資料夾裡。規矩一起寫上:動手之前先讀該讀的文件和索引,做完一定更新。索引讓它知道該讀哪幾份。

那幾條一開始很粗,就是交代 Agent 的話。慢慢地,粗的指示變成一套正式做法,再變成外掛,叫做 Operator Memory。他說自己各個專案現在都固定在用。這一節是他自述這套做法怎麼長出來的:先是資料夾,後來才是這個外掛的名字。

Operator Memory 照他的說法,就是用文件當記憶。它給 Agent 一份 Markdown 工作區,留的是指示、規格、研究、索引。每次開工前一定先查,查的是跟這次工作有關的文件,查完才做。做完一定再更新這份工作區:過期的重新看過,缺了檔的地方補上。兩步中間才是做事。

他不用的東西寫得很具體。沒有向量資料庫。沒有 embedding。背景沒有做摘要的程式,沒有整理記憶的,沒有專門負責更新記憶的,沒有 Dreamers,也沒有其他會燒 token 的背景程式。沒有黑箱取回。黑箱取回指的是:命中了哪一則、為什麼命中,人看不見。他這裡要看記憶,就打開那些 Markdown,字在檔裡。

前面那座庫,他寫過一萬筆 embedding 放在 SQLite,人問不出哪些過期、哪些從未被取回、哪些是錯的。到了這套,記憶是檔。檔在不在,打開就知道。內容舊了,人看得到字。稽核做的是讀檔、改檔。

在他這套裡,每樣東西都是普通的 Markdown。人可以讀,可以改,可以 commit 進版控,也可以跟團隊分享同一份。他說這套自己用超過一年。想試的人,他說是免費、開源的。連結是他給的:https://github.com/aerovato/operator-memory