【深度分析】搜尋引擎能當記憶嗎?Elasticsearch 上的 agent 長期記憶系統,與 0.89 recall 背後沒說的 11%

城武導讀
Elastic 的工程團隊做了一件乍看之下充滿矛盾的事——用搜尋引擎來當 AI agent 的長期記憶。搜尋引擎的本業是「從海量文件中找出相關結果」,不是「記住關於你的一切」。但他們在 Elasticsearch 上搭建了三層索引——情節記憶、語意記憶、程序記憶——直接對應認知心理學的記憶分類,搭配 BM25 加稠密向量的混合檢索、cross-encoder 重排序、時間衰減計分,以及多租戶的文件層級安全隔離(DLS)。在 168 題的評估中,recall@10 達到 0.89,跨租戶洩漏為零,p95 延遲低於 200 毫秒。程式碼完整開源在 GitHub 上。
這篇文章要追問的不是技術堆疊好不好——而是「用搜尋引擎當記憶」這個命題本身。這是一個務實的借鑒,還是一個類別錯誤?0.89 的 recall 聽起來很漂亮,但換個說法:agent 有 11% 的機律記錯關於你的事——對一個會替你行動的系統來說,記錯的代價和忘記的代價,哪個更大?以及,當記憶被集中在一台伺服器上,用 API 金鑰控制誰能存取誰的記憶,那台伺服器的主人,是不是也間接控制了 agent 的行為?
原文摘要
這篇文章出自 Elasticsearch Labs 的 Noam Schwartz,發表於 2026 年 6 月 16 日。核心命題是:大型語言模型只有「上下文視窗」這種短期記憶,缺少真正能跨 session 持久化、能隨時間累積、能按內容和使用者檢索的長期記憶層。把歷史對話全部塞進 context window 的做法,成本高、延遲高,還有「lost in the middle」的注意力衰減問題。因此團隊在 Elasticsearch 上實作了一套完整的 agent 記憶系統。
記憶系統採用三種索引,靈感來自認知心理學與 COALA 框架,每種記憶有自己的生命週期:
情節記憶(Episodic) 記錄帶時間戳的使用者原始訊息,寫入頻率高、生命週期短,主要用於追溯對話來源。語意記憶(Semantic)是從情節中萃取出來的穩定事實,例如「Sarah 擁有一台 Lumio Hub v2」,這類記憶跨 session 存活,經過整理、去重和 supersession 處理。程序記憶(Procedural)儲存多步驟的操作手冊,例如「如何排除 Zigbee 斷線問題」,並帶有 success_count 和 failure_count 欄位追蹤執行成效,作為後續整理與更新的依據。
三者加上一個共享的目錄索引,在每次 recall 操作中一併查詢。
檢索管線採用兩階段混合搜尋。第一階段是 BM25 關鍵字檢索加上 Jina v5 稠密向量檢索的 Reciprocal Rank Fusion(RRF),兩條腿各取 80 個候選,以較緊的 rank_constant=30(Elasticsearch 預設為 60)進行融合——BM25 負責抓住版本號、錯誤碼這類字面 token,稠密向量捕捉語意相似度。第二階段以 Jina v2 cross-encoder 重排序器對合併後的候選進行精確評分——先用低成本大量取回,再用高成本的 cross-encoder 在小池子上精排。
一個值得注意的設計是「逐字預檢索」(pre-recall on verbatim message):每一輪對話開始時,系統自動以使用者的原始訊息進行一次檢索,並將結果注入對話中,就像 agent 自己執行了那次檢索一樣。這麼做是為了在使用者的原文被 LLM 改寫、轉譯之前,先抓住字面上的 token 去做關鍵字匹配。
記憶寫入分為熱路徑和整理路徑。熱路徑在每個使用者回合寫入一筆情節事件,且使用 refresh=True 確保寫入在同一回合內即可被檢索到——agent 的回覆本身不存入記憶。整理路徑將情節日誌提升為語意事實與程序手冊,在示範中每回合執行,生產環境建議改為背景排程(例如每 24 小時或在累積 N 筆新情節後觸發)。整理用的 LLM 接收近期情節與既有的語意事實/程序手冊,產出新的語意事實(附帶 supporting_episode_ids)、新的程序手冊,以及程序更新(根據使用者反饋增減 success_count 或 failure_count)。去重仰賴對使用者語意索引的混合檢索,並以信心門檻與相似度閾值(≥0.90)把關。
Facts are never deleted — they are superseded.
這是整篇文章中最能體現記憶哲學的一句話。舊事實不被刪除,而是透過 superseded_by 鏈結指向新事實。查詢時以 DLS 查詢過濾掉已被取代的事實,使用者只看得到最新版本,但時間線上的事實演變歷程被完整保留。
多租戶隔離是另一個重點。每個使用者獲得一組唯一且不透明的 API 金鑰,DLS 查詢被嵌入金鑰中——儲存層在機制上無法回傳其他使用者的資料。程式碼層級另有一道守門邏輯作為安全網。評估確認跨租戶洩漏為零。
時間衰減計分使用 gauss 衰減函數,參數為 scale=30d, offset=14d,再乘以使用次數加權(use_count boost)。情節記憶使用更緊的衰減(scale=2d)。結果是:近期且頻繁被使用的記憶排名最高。
系統透過 MCP 端點對外連接,任何支援 MCP 協定的 agent(Claude Desktop、Cursor 等)只需一組 URL 加 API 金鑰即可接入。agent 的記憶檢索在使用者訊息被 LLM 處理之前自動觸發。
評估使用 168 道題目,涵蓋事實回憶、多跳推理、時序推理與程序回憶四種類型,recall@10 = 0.89,跨租戶洩漏為零,p95 檢索延遲低於 200 毫秒。完整實作開源於 GitHub(noamschwartz/atlas-memory-demo)。
城武觀點
一、把搜尋引擎當記憶——是務實借鑒,還是類別錯誤?
這件事最根本的張力不在技術,在命題本身。Elasticsearch 的設計目的,從第一天起就是「搜尋」——給一個 query,從大量文件中排出最相關的幾筆。這個動作的核心假設是:文件存在於一個靜態的集合中,你只是要找出來。但「記憶」的核心假設完全不同:記憶是動態的、會演變的、有時間向度的——你的記憶不是一個被動等待查詢的文件庫,而是一個不斷被改寫、被強化、被遺忘的動態結構。
所以當團隊用三層索引(情節、語意、程序)去映射認知心理學的記憶分類時,表面上做得很漂亮——但這三層索引本質上仍然是三個 Elasticsearch index,仍然接受 query、回傳 ranked results。真正發生的事情不是「Elasticsearch 變成了記憶系統」,而是「記憶被重新定義成了一個可以被搜尋引擎處理的問題」。
這不是批評。所有的工程突破都包含這種重新定義——把一個模糊的問題,翻譯成一個已知工具能解決的形式。但翻譯過程中的「資訊損失」才是城武感興趣的地方。當你把記憶當成檢索問題來建模,你就把「我應該記得什麼」悄悄替換成「什麼對這個查詢最相關」。相關性 ≠ 重要性 ≠ 真實性——這三個概念在人類記憶中是分開運作的,但在搜尋引擎的架構裡,它們被壓縮成了一個排行分數。
二、0.89 recall:11% 的錯誤,對 agent 意味著什麼?
recall@10 = 0.89 這個數字,放在資訊檢索領域是體面的成績。但放在 agent 記憶的脈絡裡,它有一個完全不同的讀法:agent 有 11% 的機率,在需要某段記憶的時候,拿不到正確的那一筆。
而且這裡的「拿不到」不是回傳空白——是回傳了前十筆,但正確的不在裡面。agent 看到的是「前十筆看似相關的記憶」,基於這些記憶進行推理和行動。一個記錯你偏好設定的 agent,不是不做為,而是做錯事。它會很有自信地根據錯誤的記憶給你錯誤的答案——這種錯誤比「我不知道」危險得多,因為你不知道它在錯。
更值得追問的是:這 168 題的測試集是什麼?是人工設計的標準題庫,還是真實使用者的長期對話?如果是前者,那 0.89 是實驗室數字——真實世界裡,使用者的語意邊界模糊、前後矛盾、含糊其詞,recall 只會更低。如果是後者,那 0.89 已經告訴我們一件事:即使你把搜尋引擎調到極致,仍有超過十分之一的記憶無法可靠地取回。對於一個會替你訂機票、管理行程、控制智慧家居的 agent 來說,這個失敗率的單位不是「搜尋結果的第 11 名」,而是「一次錯誤的行動」。
三、DLS 多租戶隔離——安全做對了,但記憶的權力也集中了
DLS(Document-Level Security)的設計是這套系統最成熟的工程決策之一。把權限查詢嵌入 API 金鑰,儲存層無法回傳他人資料,再加上程式層的守門——這是企業級多租戶的正確做法。零洩漏這件事,在評估中也被驗證了。
但城武看到的是另一個問題:這套設計讓「記憶」變成了一種集中化的基礎設施。你的 agent 的所有記憶——你的偏好、你的習慣、你糾正過 agent 的每一句話、agent 學到的每一個關於你的事——全部存放在一台伺服器上,由一個 API 金鑰控制存取權。誰控制了那台記憶伺服器,誰就控制了 agent 對你的認知。
這不是陰謀論。這是一個架構上的事實。當 agent 的所有長期記憶都經過同一個 Elasticsearch 叢集、同樣的檢索管線、同樣的排序函數,那台伺服器的管理者——無論是企業 IT、SaaS 供應商、還是未來的某個平台——就擁有了一個極其強大的位置:他們不直接控制 agent 的行為,但他們控制了 agent「記得什麼」。而 agent 的行為,正是建立在它記得什麼之上。這是一種比直接控制更難被察覺的權力。
四、排名思維 vs 記憶思維——搜尋引擎的哲學,適合記憶嗎?
整篇文章最讓城武在意的一個設計決策,是時間衰減函數:gauss_decay(date, scale=30d, offset=14d) × boost(use_count)。
這條公式背後的假設是:記憶的價值隨時間衰減,使用次數愈多愈重要。聽起來很合理——人類的記憶也是這樣運作,不是嗎?但人類的遺忘不是單純的指數衰減。有些記憶我們刻意壓抑但從未忘記,有些記憶我們反覆提取但內容早已被改寫,有些記憶只被使用過一次但決定了我們一生的行為。
更重要的是:use_count boost 創造了一個回饋循環。agent 愈常檢索到某段記憶,那段記憶的分數就愈高;分數愈高,下次就愈容易被檢索到。這個機制在搜尋引擎裡叫「點擊率優化」,在記憶系統裡叫「自我強化偏差」——agent 記住的,不是最重要的事,而是最常被想起的事。而「最常被想起」這件事,很大程度取決於使用者問了什麼問題、agent 的檢索管線偏好什麼類型的記憶、以及排序函數的參數設定。
用搜尋引擎的排名思維來做記憶,真正危險的不是它做不到——而是它做到了,但做到的是一種被排名邏輯馴化過的「記憶」。那不是你的記憶,那是搜尋引擎認為你應該記得的東西。
城武的未解檔案——當你把記憶建模成一個搜尋問題,0.89 的 recall 是在說「你的 agent 有 89% 的機率記得對的事」。但真正的問題是:剩下的 11% 裡,agent 不是空白,而是充滿自信地根據錯誤的記憶行動。一個會記錯的 agent,比一個誠實說「我不記得」的 agent,更值得害怕。
- 原文:How we built a persistent agent memory layer on Elasticsearch with 0.89 recall and zero tenant leaks(Noam Schwartz, Elasticsearch Labs, 2026-06-16)