Day 36 / 共 68 天 · 阶段 6 Agent 核心

记忆 Memory

昨天(Day 35)你学会让 Agent 先规划再执行。但计划再好,如果它做完第 2 步就忘了第 1 步查到啥、这次告诉它的偏好下次全忘光,照样是个"金鱼脑"助理。今天补上 Agent 四大零件的最后一块——记忆(Memory):短期、长期、工作记忆各是什么,信息什么时候该"写进"记忆、什么时候别乱记。学完,阶段 6 的 Agent 核心概念就全齐了;明天(Day 37)讲怎么把记忆和进度存盘、支持"断点续跑"。

📍 你在阶段 6(Agent 核心 D33-38)的位置
D33 Agent 是什么 D34 ReAct D35 规划 D36 记忆 D37 状态&断点 D38 手写最小 Agent
💡 用一个类比兜住今天(今天全程沿用「新来的助理和他的记事本」的世界观) 大模型本身像个能力超强但一见面就"失忆"的临时工:每次调用它只看得到你当场递给它的那几页纸,过后一概不记得。要让这位助理靠谱,得给他配记事本:短期记忆=他手边这次谈话的便签(聊完这单就可能撕掉);长期记忆=他的档案柜(把"客户偏好""历史结论"归档,以后随时翻);工作记忆=他此刻摊在桌上、正在用的那几张纸(当前任务的关键中间结果)。今天的活儿,就是学会什么该写进便签、什么该归档、什么该马上扔——一个只会往档案柜里乱塞的助理,和金鱼脑一样没法用。
L01

转身就忘的助理:模型天生没记忆

🤔 痛点你和一个聊天 AI 说"我叫小明,对花生过敏",下一轮问"我能吃这道宫保鸡丁吗",它可能压根不记得你过敏——因为大模型每次调用都是"从头开始",不自带上一句的记忆
💡 本质关键认知:LLM 是"无状态"的——它唯一"记得"的,就是你这一次调用时塞给它的那段文字(上下文)。它不会自己保存历史。所谓"AI 记得我们聊过啥",其实全是你的程序在背后帮它把该记的内容,每次重新塞进上下文。记忆不是模型的天赋,是你这个开发者给它外挂的能力。
👶 那我平时用的 ChatGPT 明明记得上文啊?那是因为对话界面/程序每次都把之前的对话记录一起发过去了——你看到的"它记得",是应用层帮它带上了历史,不是模型自己存的。理解这一点,你就抓住了做记忆的钥匙:记忆 = 决定"下次调用时,该把哪些旧信息重新塞进去"
L02

三种记忆:便签 / 档案柜 / 桌面

🤔 痛点"记忆"听着是一坨,其实按"记多久、记什么"分成几类,分清了才知道各用什么技术存。
💡 本质三类,对应助理的三种"记东西的地方":
类型助理类比存什么怎么实现
短期记忆本次谈话的便签当前对话/任务的历史消息一个消息列表(Day 32 那个 messages)
长期记忆档案柜跨会话要长久记住的:用户偏好、过往结论、知识存进数据库/向量库,用时检索(RAG 的老本行)
工作记忆此刻摊在桌上的几张纸当前这一步最相关的关键中间结果从短/长期里挑出来、放进当前上下文的那部分
三种记忆各司其职 短期·便签本次对话历史聊完可能就没了 长期·档案柜跨会话·偏好/结论存向量库,随时翻 工作·桌面此刻正用的几条从两边挑进上下文 共同目标:每次调用时,把"此刻最该让模型看到的"塞进它的上下文窗口 (上下文窗口有限——回忆 Day10/Day18,不能什么都塞)
图注:短期/长期是"存在哪",工作记忆是"这次拿哪些出来用"。三者配合,决定模型每次到底"看到"什么。
L03

短期记忆:就是那个消息列表

🤔 痛点短期记忆听着高级,到底是个啥数据结构?
💡 本质朴素到你已经见过它了——就是 Day 32 一直在追加的那个 messages 列表!每轮把用户说的、模型回的、工具返回的都 append 进去,下次调用整个列表一起发。这个不断变长的列表,就是 Agent 的短期记忆(便签本)。它让模型"记得"这次任务里前面发生的一切。
# 短期记忆 = 一个随对话增长的列表,每次调用都整个带上
memory = [{"role": "system", "content": "你是助理,记住用户告知的信息。"}]

def chat(user_text):
    memory.append({"role": "user", "content": user_text})   # 把新话记进便签
    reply = llm(memory)                    # 整本便签一起交给模型(它才"看得见"上文)
    memory.append({"role": "assistant", "content": reply})  # 把回答也记进去
    return reply

chat("我叫小明,对花生过敏")     # 便签第1条
chat("我能吃宫保鸡丁吗?")       # 此时模型看得到"过敏",会提醒有花生 → 有记忆了!
对照 L01:模型没变(还是无状态),变的是我们每次都把整本便签递给它。这就是"短期记忆"的全部魔法——没有黑科技,就是"带上历史"。

👶 小白:那对话越来越长,便签一直加下去不会爆吗?

👨‍🏫 老师:会!上下文窗口有上限(Day 10),而且越长越贵越慢(Day 13)。所以长对话要压缩短期记忆——常见招:①只保留最近 N 轮;②把更早的对话让模型总结成一段摘要再塞回去(这正是 Day 18 上下文工程讲的"历史压缩/摘要")。便签写满了,就撕掉细节、留个提要贴上。

L04

长期记忆:把该归档的塞进档案柜(向量库)

🤔 痛点短期记忆聊完就没了。可有些事要跨会话长久记住:上周小明说过他过敏、他偏好简洁回答、上个项目得出的结论……总不能每次都塞进便签(装不下也没必要)。
💡 本质把这些"要长久记、但不必时时都看"的信息,归档进档案柜——存成一条条记录,放进数据库或向量库。用的时候不是全搬出来,而是按当前需要"检索"出最相关的几条再塞进上下文。是不是很耳熟?——这就是阶段 4 学的 RAG!长期记忆 = 对"Agent 自己积累的经历/知识"做 RAG。
长期记忆 = 对自己经历做 RAG(存→检索→用) ① 值得记的事 档案柜(向量库) 写入(存) 新问题来了 按相关性检索 塞进上下文用
图注:所以你阶段 4 学的 embedding、向量库、检索(Day 21-28),在这里原封不动复用——课程是一张网,不是一堆孤岛。
📝 举个例子:有长期记忆的助理什么体验 第一次:你告诉客服 Agent"我用的是 Pro 版、上次那个导出 bug 还没解决"。它把这条归档。
两周后你再来问别的,它检索到档案:"了解,您是 Pro 版用户,上次的导出问题这边帮您确认下是否已修复……"——不用你重说一遍。这种"记得你"的体验,就是长期记忆带来的,也是产品粘性的来源。
L05

什么时候写入:不是啥都往档案柜塞

🤔 痛点短期记忆好说(每轮都追加)。可长期记忆——到底哪些该归档?总不能把每句闲聊都存进向量库吧?
💡 本质写入长期记忆要,判断标准:"以后大概率还用得上,且现在不记以后就丢了"。典型该记的:①用户明确的偏好/设定(过敏、喜欢简洁、时区);②稳定事实(账号信息、长期目标);③重要结论/成果(某任务最终得出的结果)。不该记的:一次性的闲聊、临时中间量、马上会过期的信息。
该不该写进长期记忆?两问定夺 以后还用得上吗? 否→ 是↓ 别记(省钱省乱) 偏好/事实/结论→归档
图注:谁来决定写不写?可以你的代码定规则,也可以让模型自己判断"这条值得记吗"再调一个"写记忆"工具(呼应 Day 29 的 Function Calling)。
👶 让模型自己决定记什么,靠谱吗?可以,但要给清规则、并复查。常见做法:给它一个 save_memory(内容) 工具,在提示里写明"仅当用户表达长期偏好或达成重要结论时才调用"。这样它会在合适时机主动归档。不过初学阶段,你先用简单规则(比如只存用户主动说的偏好)就够了,别一上来追求花哨。
L06

别记太多:记忆也是把双刃剑

🤔 痛点既然记忆这么好,是不是记得越多越聪明?把一切都塞进上下文?
💡 本质恰恰相反,记太多会反噬:①上下文塞满无关信息,模型抓不住重点(Day 18 说的"lost in the middle"——中间信息被忽略);②又贵又慢(Day 13);③过时的旧记忆会误导当前判断(比如客户早换了套餐,旧档案还写着老套餐)。好的记忆不是"记得多",而是"每次只把此刻最相关的几条端上桌"——这正是"工作记忆"的活儿。
📝 举个例子:记太多的翻车 档案柜里存了小明三年前说"喜欢重口味"。但他去年备注改成了"最近清淡饮食"。若两条都无差别塞进去、且旧的排在前面,Agent 可能推荐一堆重辣菜——过时记忆盖过了新信息。对策:记忆要带时间戳、可更新/失效,检索时优先近期、冲突时以新为准。记忆管理本身就是一门手艺。
一句话收束:记忆的目标不是"什么都记住",而是"该记的记住、该忘的忘掉、每次只端出最相关的"。这和 Day 18 上下文工程、Day 35"别过度"是同一种克制。会取舍的记忆,才是好记忆。
L07

今日小结 + 动手 10 分钟

🧠 今天你应该能回答

  • 为什么说"大模型本身没有记忆"?你平时看到的"它记得"是怎么来的?
  • 短期 / 长期 / 工作记忆分别是什么?各用什么技术实现?
  • 短期记忆本质就是哪个东西?(Day 32 的 messages 列表)长对话怎么压缩?
  • 长期记忆和阶段 4 的 RAG 是什么关系?
  • 什么信息该写进长期记忆?什么不该?为什么"记太多"反而有害?

✋ 动手 10 分钟:给"金鱼脑"装上短期记忆

把 L03 那段跑起来,亲眼见证"带不带历史"的天壤之别。存成 memory.py(用 input() 假装模型,聚焦记忆机制本身):

memory = []                              # 短期记忆:一本空便签

def fake_llm(mem):                       # 假模型:只根据"便签里有没有过敏"来答
    text = " ".join(m["content"] for m in mem)   # 把便签所有内容拼一起看
    if "过敏" in text and "鸡丁" in text:
        return "宫保鸡丁含花生,你过敏,别吃!"    # 它"记得"过敏了
    return "好的,我记下了。"

while True:
    u = input("你说(回车退出):")
    if u == "": break
    memory.append({"role": "user", "content": u})     # 记进便签(关键!)
    reply = fake_llm(memory)                           # 把整本便签交给它
    print("助理:", reply)
    memory.append({"role": "assistant", "content": reply})

# 试:先输"我对花生过敏",再输"我能吃宫保鸡丁吗" → 它会拦住你
# 再把 memory.append(user) 那行注释掉重跑 → 它立刻"失忆",随便让你吃
# 这一行的差别,就是"有没有记忆"的全部秘密。

玩法:注释掉"记进便签"那行,对比前后——你会亲手证明:记忆不在模型里,在你有没有"带上历史"这个动作里。这个体感,比背十遍定义都深刻。

明日预告 · Day 37:恭喜——Agent 四大零件(大脑/手脚/记忆/岗位说明)到今天全齐了!但还有个现实问题:任务跑到一半程序崩了/要等人审批,记忆和进度怎么存盘、之后接着跑?明天进入阶段 6 收尾兼衔接框架:状态(state)共享黑板 + checkpointer 存档 + 中断等人,并首次深潜 LangGraph——你会看到工业框架怎么优雅地解决"断点续跑"。
← Day 35 · 规划与任务分解 Day 37 · 状态 & 断点续跑 →