
先說我前兩天踩的一個尷尬現場。用戶在客服 Agent 里第三次提交收貨地址Agent 第三次反問請提供您的收貨地址。我當時真想順著網線問它上一輪我不是剛告訴過你嗎后來我把完整歷史記錄全部塞給模型它倒是能答上來可每輪都要把十幾輪對話從頭算一遍響應肉眼可見地變慢token 費用也肉眼可見地飆升。這個場景就是 AI Agent 記憶問題的典型現場模型不笨缺的是一個能讓它“記得住你”的結構。這篇文章是《走進AI Agent》系列的第三篇專門聊記憶。我會先拆掉“上下文越長等于記憶越好”這個常見誤區然后給出三種從輕到重的落地方案接著用 LangGraph 的思路帶你把一套帶記憶的 Agent 搭出來最后聊聊我在生產環境里踩過的四個坑。想自己動手寫 Agent 的開發者可以照著做正在做 Agent 產品方案、被“記憶”兩個字搞到頭大的人也能從中找到一個開始抓手。1. 先拆一個誤區上下文窗口大不等于記憶好1.1 上下文窗口是“能放多少”記憶是“該放什么”現在各家大模型的上下文窗口越做越長百萬 token 級別也不稀奇。很多人第一反應是那還要記憶模塊干嘛把聊天記錄全塞進去不就完了這個想法有一半是對的長上下文確實讓“臨時不丟”變得容易了但“全塞進去”不是記憶而是把所有原料倒進鍋里讓模型自己挑。一百輪對話里可能只有三句話對當前問題有用剩下九十七句全是噪音。模型在長上下文里找關鍵信息的本事確實在漲但遠沒有你想的那么可靠而且每次請求都全量發送成本和時延都會跟著漲。我見過一個團隊把兩周的客服聊天全量塞進上下文單次請求的 token 數從兩千漲到一萬二賬單漲了六倍追問率反而沒降多少。記憶的本質是“取舍”把什么放進上下文、用什么形式放、什么時候放、放多少。上下文窗口只是容器記憶機制才是決定容器里裝什么的那只手。沒有了這只手別說百萬 token就算給一個億 token模型也只是在一個更大的垃圾場里找針。1.2 四種記憶類型別再只盯著聊天記錄做記憶前先分類。早期做智能對話系統時學術界習慣把記憶分成四類工作記憶、情景記憶、語義記憶、程序記憶。放到 AI Agent 的工程語境里可以這么理解工作記憶對應當前這輪任務的臨時信息比如用戶上一個問題、剛才返回的中間結果通常就放在 prompt 的上下文里任務結束就清掉。情景記憶是對歷史對話事件的記錄比如“用戶上周三問過怎么注銷賬號”“他昨天對價格方案表達了不滿”。語義記憶是從這些事件里提煉出來的穩定事實比如“用戶名是張三”“偏好使用微信支付”“家里養了一只貓叫煤球”。程序記憶則是 Agent 的行動經驗比如“遇到退款問題應該先查訂單狀態再走工單流程”這類記憶很多時候不是存在對話記錄里而是固化在系統提示詞、工具描述或者工作流配置里。大多數人對“讓 Agent 記住你”的理解還停留在情景記憶層面也就是聊天記錄。但如果只做聊天記錄Agent 頂多算是有“回放”能力談不上“懂你”。真正讓用戶體驗到“被記住了”的往往是語義記憶和用戶畫像它得知道你是誰、你的偏好、你上次沒辦完的事。所以標題里說的“記住你”我建議拆成兩條線來做一條線管對話歷史負責上下文連續性另一條線管用戶畫像負責個性化。1.3 為什么全量塞歷史效果反而更差很多人相信模型能力提升之后長上下文會自然吸收掉記憶需求。我不否認長上下文會緩解一部分問題但在當前階段全量塞歷史有三個繞不開的硬傷。第一是注意力稀釋。關鍵信息埋在一堆無關歷史里模型容易記了后面忘了前面尤其是那種長對話里只出現一次的關鍵細節。第二是信息沖突。用戶昨天說“我喜歡喝美式”今天改成“最近戒咖啡了”兩條都在上下文里模型不知道應該信哪條。第三是成本和延遲。token 直接等于錢和響應速度每輪多傳幾千 token用起來就是肉眼可見的變慢。所以結論很直接長上下文要用來做記憶系統的兜底而不是替代記憶系統。該做索引的做索引該做摘要的做摘要該做畫像的做畫像。接下來我們看具體怎么落地。2. 三種主流記憶實現從“有手就行”到“生產可用”2.1 滑窗加摘要最省事的記憶方案最樸素的記憶方案其實是滑窗加摘要它不需要向量數據庫不需要額外服務很多開源項目里都能見到。思路很簡單只把最近 N 輪對話完整放進上下文超過窗口的舊對話讓大模型生成一段摘要再和本次對話拼在一起。比如窗口設為 10 輪prompt 結構就是“以下是之前對話的摘要[摘要]以下是最近對話……”。這個方案的好處是結構簡單、成本低、容易調試。壞處也很明顯摘要是一次性的隨著時間拉長早期細節會被壓縮得面目全非。用戶三個星期前提過一句“我對花生過敏”如果當時沒被寫進摘要后面就徹底丟了。所以滑窗加摘要更適合臨時性場景比如一次性任務、客服單輪問答不太適合需要長期陪伴或深度個性化的產品。2.2 向量檢索按需召回相關記憶真正讓“記住你”有質變的是把歷史記憶向量化然后按相關性召回。做法不復雜把一段有價值的對話切片比如按用戶意圖切一段或者按一輪問答切一塊用 embedding 模型轉成向量存進向量數據庫。每當 Agent 要回答新問題時先把問題轉成向量去庫里做相似度搜索找出最相關的若干條記憶塞進 prompt。用到的數據庫可以是 Qdrant、Milvus、Weaviate也可以是 PostgreSQL 的 pgvector小項目用 Chroma 或 FAISS 完全夠。這套方案解決了全量塞歷史的注意力稀釋問題因為它每次只帶幾條最相關的記憶。但這不代表沒有新問題召回質量取決于切片質量、embedding 模型和相似度算法而且“相關”和“重要”常常不是一回事。用戶最關心的一條吐槽可能在向量空間里和“冰箱制冷效果”更接近而不是和“售后服務”更接近。所以向量檢索只是基礎上面通常還要再接一個排序甚至重排的環節具體我在第四章細說。2.3 記憶服務與 MCP Memory開箱即用如果不想自己造輪子市場上已經有一批現成的記憶方案。Mem0 把記憶分成短期和長期兩層能自動從對話中抽取需要長期保留的條目也支持用戶直接寫入。Zep 是類似思路內置了對話摘要、實體提取、時間線記憶并且提供了專門 API前后端都能接。另一個值得關注的方向是 MCP 協議里的 Memory 服務。MCP 把 Agent 的能力統一抽象成工具記憶也可以做成一個獨立的 memory server通過標準協議暴露“保存記憶”“讀取記憶”“搜索記憶”這些接口。用現成服務的好處是省掉很多臟活累活尤其是實體識別、記憶沖突處理和遺忘策略自己實現一遍非常耗時。但壞處是數據鏈路會繞一圈調試和定制受限而且記憶數據往往和業務強相關交給第三方之前要仔細想清楚數據合規和隱私邊界這個我在第五章展開。如果你用的是 Spring AI 這類 Java 框架里面也有 ChatMemory、SearchMemory 之類的抽象用法本質相同一個是存歷史一個是按需查別被框架之間的名詞繞暈。2.4 選型建議到底選哪一級我的判斷標準很簡單先想清楚產品要的是“上下文連續”還是“長期個性化”。只要上下文連續滑窗加摘要就夠了需要跨會話記住用戶偏好向量檢索是主流選擇團隊人力緊張且不介意數據鏈路多一跳可以先上 Mem0 或 Zep 這類服務跑通業務等量大了再自研。沒有最好的方案只有最適合當前階段的方案。做 Agent 最容易犯的錯就是第一個版本就上最重的架構結果沒調明白之前先把團隊精力耗光了。另外如果你的 Agent 是 multi-agent 架構記憶的歸屬也要提前想清楚。我目前的經驗是共享記憶庫放用戶事實和任務狀態各子 Agent 的工作記憶本地保留別一股腦全塞公共庫否則消息風暴會先把記憶庫沖垮。3. 實操給 Agent 裝一套“記得住你”的記憶系統3.1 技術棧與整體數據流這一節以 LangGraph 為例講一個可直接參考的實現骨架。選 LangGraph 不是因為它最流行而是它有清晰的節點和狀態模型非常適合把記憶寫入和記憶召回拆成獨立節點。向量庫我用 pgvector因為存量業務多半已經有 PostgreSQL可以少引入一個組件。embedding 模型用通用的文本嵌入模型即可中文場景稍微關注一下分詞和模型對中文的適配。整體數據流是這樣的用戶消息進入 Agent先走記憶召回節點用當前問題去查向量庫把命中的歷史事實和用戶畫像塞進 prompt大模型生成回復之后走記憶寫入節點把這條有信息量的對話切片轉成向量并同步更新用戶畫像最后把整輪對話追加到短期上下文。注意順序很關鍵先召回再生成生成完再寫入。如果把寫入放在召回前這一輪還沒回答就先把問題記下來了容易把無效信息也存進去。存儲結構不用設計得太復雜核心字段可以參考下面這個 JSON 結構{ user_id: u_12345, content: 用戶提到家里養了一只貓名字叫煤球, embedding: [], memory_type: semantic, confidence: 0.8, created_at: 2025-06-01T10:00:00Z, last_accessed_at: 2025-06-01T10:00:00Z }3.2 記憶寫入什么值得存存成什么樣子寫入是最容易走偏的一步。很多人把每一條對話都存下來結果向量庫里全是“嗯”“好的”“謝謝”這類無意義內容召回時又把它們帶回來。我建議用一句話來判斷這條對話里是否包含以后可能用得上的事實、偏好或待辦如果只是寒暄或者流程性確認不存。存放的字段最好不要只有原文盡量做一層信息提煉。比如原文是“我對價格已經無語了你們自己看吧”可以提煉為“用戶對當前價格方案不滿”這樣召回時語義更清楚也方便后續做情緒分析。提煉可以用大模型做但別每輪都調大模型去抽成本會很高。我會給寫入節點加兩個觸發條件一是用戶消息里包含明顯的偏好詞或事實詞比如“我喜歡”“我住在”“我是做……”二是完成了一次帶有結果的交互比如提交了訂單、改過收貨地址。滿足之一才進入提煉和寫入。3.3 記憶召回什么時候查、查幾條、怎么用召回要做減法而不是把庫里所有相關記憶都拉出來。我的經驗是召回數量控制在 3 到 8 條具體看場景。客服問答 3 到 5 條就夠個性化推薦或陪伴式聊天可以放到 8 到 10 條。召回方式分兩級第一級用向量相似度粗篩第二級按時間加權或者把用戶畫像中明確的鍵值對直接帶上。這里有一個很容易踩的坑永遠不要讓記憶原樣堆疊在 prompt 里。最好給每條記憶加上時間和來源描述比如“2025年4月12日用戶提到他住杭州西湖區”。模型看到時間信息就不會把一條去年的舊習慣當成今天的指令。還有一種召回是主動的用戶問“我的訂單怎么樣了”時Agent 不一定真要從歷史對話里猜而是應該直接去查業務數據庫。記憶系統管的是對話側的信息業務狀態應該走正經的 API 和數據庫查詢。把這個邊界劃清楚能避免很多“它明明知道但又答不上來”的奇怪事故。3.4 用戶畫像讓臨時記憶沉淀為長期認知要讓 Agent“記住你”只靠對話歷史還不夠。我會在記憶系統里單獨維護一張用戶畫像表字段不固定用鍵值對形式存儲比如“姓名:張三”“偏好支付:微信”“住址:杭州西湖區”“寵物:貓煤球”。它的更新方式和對話記憶不同對話記憶是增量添加用戶畫像是覆蓋更新。每次寫入新事實時對比已有鍵值如果沖突以最新一次為準。這一步非常關鍵否則會出現一個場景用戶這周說“我現在不用微信支付了”畫像里卻還留著上個月的“偏好支付:微信”。畫像抽取也不需要把整本對話史都翻出來每次對話結束后跑一次輕量級的畫像更新 prompt把當前這輪的更新點提取出來再和舊畫像合并。對于中文用戶我還有一個實用經驗姓名、地址、電話號碼這類實體用專門的信息抽取模型要比用通用大模型更穩。大模型適合做語義層面的偏好歸因不適合做嚴格的結構化抽取。4. 從“記得住”到“記得準、記得對”調優與修正4.1 召回結果的重排與打分向量檢索跑出來的結果只能算候選集直接塞給大模型往往差強人意。我之前做過一次對比同一組問題直接召回 Top5 寫入和加了一道重排之后的 Top5 寫入用戶對“是否感覺被理解”的評分差了快 20%。重排的常用做法有三種一是加入時間衰減因子太久遠的記憶降權近期記憶適當升權二是按記憶類型加權用戶畫像類記憶權重高于一次性的情景記錄三是用一個小排序模型或規則對召回結果做融合。如果項目里沒有專門的重排模型最簡單的做法是寫幾行規則先定一個基礎分向量相似度給一個分數時間衰減給一個分數是否包含當前問題里的關鍵詞再加分最后按總分排序。這個規則先用腳本跑一段離線樣本手動看排序效果再逐步調參比一上來就上模型務實得多。4.2 記憶的更新、遺忘與沖突處理記憶不是寫完就完事了。時間久了會碰到三種情況用戶改主意了、用戶對同一件事前后說法不一致、記憶條目已經過時。我的更新策略很簡單語義記憶和用戶畫像采用覆蓋更新以最近一次為準情景記憶采用追加加時間戳不覆蓋舊記錄只標注新舊關系。舉個例子用戶先說“我在北京”后來改到“我搬去上海了”畫像里直接覆蓋成“城市:上海”情景記憶里保留兩條帶時間的記錄。這樣既不會讓舊信息污染當前判斷也保留了回溯能力。遺忘策略同樣重要大部分記憶如果不是用戶主動要求其實都不需要永久保存。可以設置一個保留周期比如一年沒訪問過的情景記憶自動歸檔。這個過程不一定要刪除做成冷熱分層也行熱數據進向量庫冷數據進對象存儲。真要刪除的場景也有比如用戶要求“把你記住的關于我的信息都刪掉”這屬于合規操作必須支持。所以從設計第一天起每條記憶都要帶上 user_id 和創建時間別做成一個全局的大列表否則后面連誰說了什么都查不出來。4.3 允許用戶糾正Agent 要能“忘掉說錯的話”生產環境里最打擊用戶信任感的瞬間不是 Agent 答不上來而是它“記得”了一件錯誤的事還反復根據錯記憶做決策。比如用戶明明從來不吃香菜某次聊天時隨口開玩笑說“我頓頓都吃香菜”若被當成事實寫進了畫像后續每次推薦都給他推香菜相關的東西那就太災難了。所以記憶系統一定要留一個糾正入口。最簡單的做法是在識別到用戶明確的否定表達時比如“我不是……”“我說錯了”“把這個忘了吧”觸發一次記憶修正流程刪除或覆蓋對應條目。更穩妥的做法是所有寫入用戶畫像的事實都要帶置信度隨口一提的置信度低重復出現過或用“我確定”這類表達的置信度高。低置信度條目在召回時可以做弱化處理避免一句玩笑話被當成鐵律。5. 實戰中繞不開的四個坑5.1 記憶污染歷史里的錯話成了“事實”記憶污染的典型路徑是這樣的某條用戶消息被錯誤抽取存進畫像后續每次回答都被這個假事實影響而且因為 Agent 自己記得了它會越來越自信地使用這個錯誤信息形成自洽的幻覺閉環。要避免這個坑只能從源頭把關寫入時寧可少存不要錯存對置信度低的條目做標記召回出來后在 prompt 里注明“以下記憶可能不準確請結合當前對話判斷”。最后這條很有效相當于給模型一個允許質疑記憶的開關。別小看這句話它能讓模型在舊記憶和新信息沖突時傾向于相信新信息而不是被舊記憶帶偏。5.2 隱私與授權記住你也要守住邊界讓 Agent 記住你其實涉及大量個人信息。做產品時這條線一旦越界不只是口碑問題是合規問題。我的建議是先拿到用戶授權再開始存長期記憶在界面上讓用戶能看到“Agent 記住了哪些關于我的信息”并提供一鍵清除入口。技術上要做到按 user_id 隔離千萬不能出現多用戶數據互相串的情況。多租戶隔離和權限校驗在記憶系統里比在業務系統里更容易被忽略因為記憶數據混合了對話內容和結構化事實抽數、上模型、做聚類的時候都容易誤觸敏感數據。哪怕只是一個內部 demo也建議把隔離和授權從第一天就做好不然后面補起來非常痛苦。5.3 成本與延遲每個記憶模塊都不是免費的給 Agent 加記憶等于加了好幾個額外環節embedding 調用、向量檢索、大模型摘要提煉、畫像更新。這些環節都會增加成本和延遲。我見過一個項目加了記憶模塊后一次對話的平均響應時間從 1.2 秒漲到 3 秒成本漲了 40%。優化思路有幾個embedding 結果做緩存同一個問題短時間內不重復嵌入記憶召回和畫像更新改異步執行讓用戶在回復完成后不感知額外等待大模型提煉只對增量文本做不每次都全量跑。小流量的 demo 可以不在乎這些但只要想上線就要把“記憶是有成本”這個概念刻在腦子里。每次新增一個記憶環節之前先問一句這一步不加行不行很多所謂高級記憶最后都只是給延遲和賬單添堵。5.4 測試與觀測沒有記憶可視化就等于盲調最后一個坑也是最隱蔽的記憶系統很難直接觀察到。出了問題你根本不知道是召回沒召回來還是召回了但模型沒用或者是寫入時就寫錯了。所以我會在開發早期就把觀測做進去最簡單的做法是每次請求的日志里記錄三樣東西這輪召回了哪些記憶、每條記憶的得分、最終 prompt 里記憶部分的實際內容。在調試界面里最好能按 user_id 直接查看它的畫像和最近寫入的記憶。有了這些你才能從“用戶說體驗不對勁”這種模糊反饋里快速定位到具體是哪個環節出的問題。記憶系統本質上是一個數據系統凡是數據系統可觀測性就是第一生產力這個錢省不得。最后再多說一句記憶系統這東西務實地講90% 的場景用滑窗加摘要再加一個簡單用戶畫像就夠了真正需要上向量檢索和記憶服務的是那些用戶會和 Agent 長期反復交互的產品。先把基礎鏈路跑通再考慮怎么讓它更懂你。腦子里的坑越少Agent 反而越像個正常人。