想象你请了一位金牌管家。宴会上,他手里总揣着一本流水小本子——谁刚说了什么、菜上到哪一道、客人刚提的要求,全记在上面,宴会散场就翻篇。但他的书房里还有一只档案柜,里面存着你的口味偏好、过敏史、会员等级,这些事实十年不变。更神奇的是,你随口说一句“上次那种辣辣的炖菜不错”,他不用翻档案凭感觉就能联想起类似的旧事——那是他脑子里一张按“意义”编织的联想网。Agent 的记忆,就是这位管家的三本账:短期记忆存当下对话上下文,长期记忆存用户偏好与事实知识,向量记忆做语义相似召回。本文要讲清楚的,正是这三类记忆如何分工、如何选存储介质、如何写入更新、如何协同检索,以及它们如何迁移整合——最终让 Agent 拥有类人的、有深度的记忆体系。

岗位关联

掌握三类记忆的完整设计,你搭建的 Agent 才能跨会话记住用户、保持个性化——这是从“每轮都失忆的玩具机器人”走向“有记忆的智能助手”的分水岭,也是 Agent 工程师的核心竞争力。

目录
  1. 可视化演示
  2. 记忆分类总览
  3. 短期记忆:当下的小本子
  4. 长期记忆:主人的档案柜
  5. 向量记忆:意义的联想网
  6. 存储介质选择:三类东西三个口袋
  7. 写入更新策略:记什么、忘什么
  8. 协同检索机制:三本账一起翻
  9. 记忆迁移整合:从流水到档案
  10. AI 应用场景
  11. Python 演示
  12. 常见误区
  13. 面试题

可视化演示

展示Agent短期、长期、向量三类记忆的层级架构、检索流程与多维特性对比。

本节配有交互动效(进入视口后自动加载,约 3KB)。

三层记忆架构 短期记忆:上下文窗口,快速但容量有限 长期记忆:数据库持久化,容量大但检索慢 Memory = STM ∪ LTM ∪ VM 三类记忆协同工作,平衡速度、容量与智能检索

记忆分类总览

管家有三本账:怀里的小本子记当下,书房的档案柜记旧事,脑子里的联想网找“相似的过往”。缺了任何一本,他都当不好管家——只记当下会忘性大,只靠档案会太死板,只靠联想会不靠谱。Agent 的记忆系统也是如此:不是一种存储方式打天下,而是三类记忆各司其职。

Agent 也一样。没有记忆的 Agent 每轮对话都像刚睡醒的陌生人,同一个问题问八遍;有了记忆,它才能“越用越懂你”。业界把 Agent 记忆拆成三类:短期记忆负责“此刻”,长期记忆负责“长久”,向量记忆负责“按意思找”。三者不是孤立的模块,而是同一套记忆系统的三个视图。

短期记忆保存当前对话的上下文窗口;长期记忆保存用户偏好和稳定事实;向量记忆把历史记忆编码成向量,供语义相似召回。工程上,它们的存储介质、更新策略各不相同,但最终都要汇入同一次检索,供模型生成回答。

管家三本账是同一套记忆的三个抽屉,各司其职又相互引用。接下来我们逐个打开抽屉,看每类记忆的内部结构。

M=S∪L∪V\mathcal{M} = \mathcal{S} \cup \mathcal{L} \cup \mathcal{V}
这个式子表示:Agent 的完整记忆 \mathcal{M} 由短期记忆集合 \mathcal{S}、长期记忆集合 \mathcal{L}、向量记忆集合 \mathcal{V} 协同构成,\cup 不是简单相加,而是“分工又互援”。
  1. 短期集合\mathcal{S} 是短期记忆里所有消息片段的集合,类比管家怀里还没翻页的流水小本子。
    S={m1,m2,…,mn}\mathcal{S} = \{m_1, m_2, \dots, m_n\}
  2. 长期集合\mathcal{L} 是长期记忆里偏好与事实条目的集合,类比档案柜中一张张档案卡。
    L={p1,f1,p2,f2,… }\mathcal{L} = \{p_1, f_1, p_2, f_2, \dots\}
  3. 向量集合\mathcal{V} 是向量记忆中被编码为向量的历史记忆集合,类比联想网上一个个“意义坐标”。
    V={v1,v2,…,vk}\mathcal{V} = \{\mathbf{v}_1, \mathbf{v}_2, \dots, \mathbf{v}_k\}
  4. 协同并集\cup 表示三者协同——同一件事可能在三个集合中都有记录,检索时互相印证、互为补充。
    M=S∪L∪V\mathcal{M} = \mathcal{S} \cup \mathcal{L} \cup \mathcal{V}

短期记忆:当下的小本子

宴会正进行,客人刚说“麻烦再来一杯温水”。管家不会去档案柜查三年前的事,他只需要小本子上最近几行。这就是短期记忆的定位:当下、此刻、正在进行的事。它的特点是读写得快、内容新鲜、生命周期短——宴会一结束,这一页就该翻过去。

技术上讲,短期记忆就是“对话上下文”。每一轮 User 和 Assistant 的消息按顺序进入一个窗口,窗口有大小上限。模型每次回答时,只把这窗口里的内容作为上下文;窗口之外的事,模型“看不到”。这也是为什么 Agent 需要其他两类记忆——短期记忆天生记不住太久以前的事。

最常见的实现是滑动窗口:只保留最近 W 条消息,超出就淘汰。更进阶的做法是“摘要压缩”:窗口塞不下时,把前面十几轮对话总结成一段摘要继续携带,就像小本子写满了,管家会把旧页浓缩成一行备注。这类实现通常用内存或 Redis 这类高速缓存,因为每次请求都要快速读写。

小本子的优点是快、准、新鲜,代价是健忘。所以管家才需要档案柜和联想网——短期负责“接住当下”,把“该记住的”交给更稳的地方。

Ct={ mi∣i∈[ t−W+1,  t ] }\displaystyle C_t = \{\, m_i \mid i \in [\,t - W + 1,\; t\,] \,\}
这个式子表示:t 时刻短期记忆窗口里的内容是连续 W 条消息,最早只能追溯到第 t−W+1 条,更早的消息都被“挤出了小本子”。
  1. 当前窗口C_t 表示第 t 轮对话时短期记忆的内容,即管家此刻小本子上还看得见的流水。
    Ct={ mi∣i∈[ t−W+1,  t ] }C_t = \{\, m_i \mid i \in [\,t - W + 1,\; t\,] \,\}
  2. 单条消息m_i 是对话中第 i 条消息,每条都带有角色、文本和时间戳。
    mi=(rolei, texti, ti)m_i = (\text{role}_i,\ \text{text}_i,\ t_i)
  3. 窗口大小W 是窗口容量,通常取最近 20 到 50 条,超出即被淘汰。
    ∣Ct∣≤W|C_t| \le W
  4. 连续区间[t−W+1, t] 表示只保留最近 W 条连续消息,旧消息自然滚出窗口。
    i∈[ t−W+1,  t ]i \in [\,t - W + 1,\; t\,]

长期记忆:主人的档案柜

管家档案柜里永远有几张醒目的卡:“主人不吃香菜”“主人是金卡会员”“夫人对花粉过敏”。这些事实跨越每次宴会,稳定可靠。这就是长期记忆的定位:跨会话、跨场景的稳定信息,是管家对主人“长期的了解”。

长期记忆存两类东西:用户偏好画像与事实知识。偏好如“喜欢靠窗”“回复要简洁”;事实如“公司规模 300 人”“合同 2026 年 3 月到期”。这些信息要从对话里抽取出来,结构化成条目,而不是把聊天原文整段塞进去——档案柜里放的是卡片,不是流水账。

实现上通常用关系型数据库或文档存储落盘,按用户 ID 组织。每次对话产生新线索时,画像需要“平滑更新”:既吸收新信息,又避免被一句话推翻长期印象。指数平滑是最常用的方法,它给新信号一个权重,让画像慢慢演化而不是剧烈跳变。

档案柜的价值是“经得起时间”:一次宴会的小本子会翻篇,档案卡不会。但它只能做精确查询,无法联想——这正好引出第三本账。

Pt=α⋅xt+(1−α)⋅Pt−1\displaystyle P_t = \alpha \cdot x_t + (1 - \alpha) \cdot P_{t-1}
这个式子表示:更新后的用户画像 P_t 由“本次观察到的信号 x_t”和“旧画像 P_{t-1}”按比例混合而成。\alpha 越大,新信号对画像的影响越大。
  1. 更新后画像P_t 是吸收本次观察后新的偏好画像,类比档案卡被管家用新墨水修订后的版本。
    Pt=α⋅xt+(1−α)⋅Pt−1P_t = \alpha \cdot x_t + (1 - \alpha) \cdot P_{t-1}
  2. 本次信号x_t 是这一轮对话里抽取到的偏好信号,例如用户说“我喜欢靠窗的位置”。
    xt=本次抽取的用户偏好值x_t = \text{本次抽取的用户偏好值}
  3. 学习率\alpha 是 0 到 1 之间的学习率,决定新信号对画像的影响权重。
    0<α<10 < \alpha < 1
  4. 惯性保留(1 − \alpha) \cdot P_{t-1} 表示旧画像按比例保留,避免一次口误就推翻长期形成的印象。
    (1−α)⋅Pt−1(1 - \alpha) \cdot P_{t-1}

向量记忆:意义的联想网

主人说“上次那道辣辣的炖菜真不错”。管家不会去档案柜按“炖菜”两个字翻卡——他脑海里会浮起一串相似的旧事:去年在成都点过水煮鱼、在重庆夸过火锅。这种“按意思找”的能力,就是向量记忆。

向量记忆把每条记忆(对话片段、知识条目)用嵌入模型编码成一个高维向量。可以朴素地理解为:把一条文本的“意思”压缩成一串坐标,意思越接近,坐标在空间里越靠近。于是“辣辣的炖菜”“水煮鱼”“麻辣火锅”这些字面不同、含义相近的内容会聚成一团。

查询时,把当前问题也编码成向量,再在向量库里寻找“最近邻”——即余弦相似度最高的 Top-K 条历史记忆。它不怕字面不同、意思相同的情况,是真正的语义检索。实现上依赖向量数据库对海量向量建索引,才能在毫秒级完成近似最近邻搜索。

联想网让管家“见多识广”:见过越多,联想越准。但它只能给出“候选”,不能给出“事实”——所以它必须和档案柜、小本子配合使用。

sim(q,d)=q⋅d∥q∥⋅∥d∥\displaystyle \mathrm{sim}(\mathbf{q}, \mathbf{d}) = \frac{\mathbf{q} \cdot \mathbf{d}}{\|\mathbf{q}\| \cdot \|\mathbf{d}\|}
这个式子表示:两个向量的余弦相似度等于它们点积除以两个模长的乘积,结果越接近 1,表示两条记忆在语义上越接近。
  1. 查询向量\mathbf{q} 是当前问题经过嵌入模型编码后的向量,类比管家把“辣辣的炖菜”变成一串“气味标签”。
    q=Embed(用户问题)\mathbf{q} = \mathrm{Embed}(\text{用户问题})
  2. 候选向量\mathbf{d} 是历史记忆中某个片段编码后的向量,是联想网上的一个坐标点。
    d=Embed(历史片段j)\mathbf{d} = \mathrm{Embed}(\text{历史片段}_j)
  3. 点积分子\mathbf{q} \cdot \mathbf{d} 衡量两个向量方向的一致性,方向越一致数值越大。
    q⋅d=∑kqk dk\mathbf{q} \cdot \mathbf{d} = \sum_{k} q_k \, d_k
  4. 模长归一化分母是两个向量模长的乘积,把结果压到 [−1, 1] 区间,便于设定阈值。
    sim(q,d)∈[−1, 1]\mathrm{sim}(\mathbf{q}, \mathbf{d}) \in [-1,\ 1]

存储介质选择:三类东西三个口袋

管家从来不会把“此刻要用的酒水单”锁进书房保险柜,也不会把“主人的房产证”塞在围裙口袋里。不同信息放不同地方,取用速度才跟得上宴会的节奏。存储介质的选择,本质是“把对的东西放在对的口袋里”。

短期记忆的特点是高频读写、生命周期短,放在内存缓存或键值存储(如 Redis)里最划算,读写延迟在毫秒以内,还能设置过期时间自动清理。长期记忆的要点是结构化、可精确查询、需要持久化,关系型数据库(PostgreSQL、MySQL)最合适。向量记忆则要支撑海量向量的近似最近邻搜索,必须交给专门的向量数据库(Milvus、pgvector、Chroma)。

选型本质上是一笔账:单位存储成本、读写延迟、容量上限、查询模式。内存最快也最贵,磁盘最便宜但只能做精确查询,向量库则为相似度检索做了极致优化。聪明的工程师会按数据特性分派存储,而不是把所有记忆塞进同一个数据库。

管家分口袋装东西是几十年的职业经验,Agent 选介质是工程上的成本与延迟权衡。选对了,三类记忆才能各得其所、各展所长。

Cost=ns⋅cs+nl⋅cl+nv⋅cv\displaystyle \text{Cost} = n_s \cdot c_s + n_l \cdot c_l + n_v \cdot c_v
这个式子表示:记忆系统总成本等于三类记忆各自的条目数乘以单位存储维护成本后再相加。算清这笔账,选型就有了依据。
  1. 条目数量n_s、n_l、n_v 分别是短期、长期、向量记忆中的条目数,通常短期条目远少于长期。
    ns≪nl,nv≈nln_s \ll n_l,\quad n_v \approx n_l
  2. 单位成本c_s、c_l、c_v 是每一条记忆在对应介质上的存储与维护成本,内存类介质单价更高。
    cs>cv>clc_s > c_v > c_l
  3. 单项成本每类记忆的条目数乘以单位成本,得到该类记忆的总开销。
    ns⋅cs,nl⋅cl,nv⋅cvn_s \cdot c_s,\quad n_l \cdot c_l,\quad n_v \cdot c_v
  4. 总成本求和三个单项相加得到整体存储成本,用来指导“哪些记忆放哪种库”。
    Cost=nscs+nlcl+nvcv\text{Cost} = n_s c_s + n_l c_l + n_v c_v

写入更新策略:记什么、忘什么

管家记档案有条铁律:不是每句话都值得进档案柜。客人随口说“今天有点热”不必记;主人说“以后订酒店都选无烟房”必须记。这个“值不值得记”的判断,就是记忆系统里的重要度评分。

每条记忆都会有一个强度分数。每次被再次提及或使用,强度增加,这叫“强化”;长时间没被用到,强度按比例衰减,这就是“遗忘”。强度越高的记忆,越可能被写进长期、越可能在检索中排到前面。正是这种“强化与衰减”并存,让记忆系统既有韧性又有流动性。

实操中,重要度来源于三类信号:规则命中(出现“喜欢、不要、总是”等信号词)、模型抽取(LLM 判断这句话是否值得记住)、用户显式反馈(“请记住我这句话”)。更新时通常采用“衰减 + 命中强化”的公式:旧强度先打个折,若本轮被命中则追加奖励分。

管家既不会什么都记,也不会什么都忘。重要度、强化、衰减构成的“记与忘的平衡”,正是 Agent 记忆区别于普通日志文件的关键。

St+1=St⋅γ+α⋅hitt\displaystyle S_{t+1} = S_t \cdot \gamma + \alpha \cdot \mathrm{hit}_t
这个式子表示:一条记忆的新强度 S_{t+1} 等于旧强度 S_t 乘以衰减因子 \gamma,再加上本轮是否被命中带来的强化量 \alpha \cdot \mathrm{hit}_t。
  1. 当前强度S_t 是某条记忆此刻的强度分数,类比档案卡上管家用铅笔标注的“重要星数”。
    St∈[0, 1]S_t \in [0,\ 1]
  2. 衰减因子\gamma 是小于 1 的衰减因子,让旧记忆强度随时间打折,防止永不遗忘。
    0<γ<10 < \gamma < 1
  3. 命中信号\mathrm{hit}_t 表示这条记忆本轮是否被再次提及或使用,命中为 1,否则为 0。
    hitt∈{0, 1}\mathrm{hit}_t \in \{0,\ 1\}
  4. 强化增量\alpha 是每次命中带来的强度增量,反复被用到的高价值记忆会“升格”。
    St+1=St⋅γ+α⋅hittS_{t+1} = S_t \cdot \gamma + \alpha \cdot \mathrm{hit}_t

协同检索机制:三本账一起翻

客人问“上次那款红酒还有吗”?管家不会只翻一个地方:先看小本子(今天开过没有),再看档案柜(上次买的是哪款),再用联想网(类似口味的备选)。三处信息拼在一起,才是完整的回答。Agent 的检索也一样——不是把三类记忆的候选堆在一起,而是有层次、有优先级地融合。

检索的第一步是分层召回:先查短期记忆,拿到精确的上下文——用户刚才说了什么、当前任务进行到哪一步;再查长期记忆,拿到用户画像与事实——他偏好什么、是什么身份;最后用向量记忆做语义召回,补充“相似的历史经验”。三层结果汇成候选池。

融合的关键是优先级:短期最可信,因为它就是“此刻的事实”;长期其次,因为它稳定可靠;向量最后,因为它只是发散候选。最终排序常用加权融合,短期权重最高。这样设计能避免“张冠李戴”——比如向量库召回了一条相似但不属于当前用户的旧记忆,权重和优先级机制会压低它的分数。

小本子最靠谱、档案柜最权威、联想网最发散。管家“三本账一起翻但心里有数”的判断力,映射到系统里就是分层召回、优先级排序与加权融合。

Score=ws⋅Sshort+wl⋅Slong+wv⋅Svector\displaystyle \mathrm{Score} = w_s \cdot S_{\mathrm{short}} + w_l \cdot S_{\mathrm{long}} + w_v \cdot S_{\mathrm{vector}}
这个式子表示:最终相关度分数等于短期、长期、向量三种子分数按权重加权求和,权重之和为 1,按综合分排序取 Top-K 作为模型上下文。
  1. 三个子分数S_short、S_long、S_vector 分别是短期精确匹配、长期画像命中、向量语义相似给出的相关度。
    Sshort, Slong, Svector∈[0, 1]S_{\mathrm{short}},\ S_{\mathrm{long}},\ S_{\mathrm{vector}} \in [0,\ 1]
  2. 融合权重w_s、w_l、w_v 表示三类记忆在最终排序中的话语权,通常短期最高。
    ws+wl+wv=1w_s + w_l + w_v = 1
  3. 加权求和每个子分数乘以对应权重后相加,得到综合相关度,权重大的来源主导排序。
    Score=∑k∈{s,l,v}wk Sk\mathrm{Score} = \sum_{k \in \{\mathrm{s,l,v}\}} w_k \, S_k
  4. 排序输出按 Score 从高到低取前 K 条,作为送入模型的记忆上下文。
    Top-K(Score)\mathrm{Top\text{-}K}(\mathrm{Score})

记忆迁移整合:从流水到档案

宴会散场,管家不会把小本子随手一扔——他会把“主人下个月去巴黎出差”这样的重要事项誊抄进档案柜。这个动作就是记忆迁移:短期记忆经过筛选,变成长期记忆。

誊抄之前,管家会先查重:档案里已有“2024 年去过巴黎”,新信息是“2026 年再去”,那就合并、更新时间,而不是另起一张互相矛盾的卡片。这就是去重与冲突消解。实现上常用向量相似度判断“新旧记忆是不是同一件事”,相似度超过阈值就合并;若是互相冲突的事实,则以时间更近的为准。

迁移的触发条件有三类:重要度超过阈值(系统自动判断)、用户显式要求(“记住这句话”)、会话结束时的总结流程。迁移后的记忆还要再做一次向量化,写入联想网,让这条新档案同时能被语义检索命中。整个迁移、去重、归档闭环,构成了记忆的生命周期管理。

管家每隔一段时间还会清理档案柜:褪色的旧卡要么归档,要么扔掉。Agent 同样需要整理机制——迁移、去重、归档、衰减全部闭环,才是真正完整的记忆生命周期。

promote(m)={1Sm≥θ0Sm<θ\displaystyle \mathrm{promote}(m) =\begin{cases}1 & S_m \ge \theta \\0 & S_m < \theta\end{cases}
这个式子表示:迁移函数 promote(m) 是一个 0-1 决策——当记忆强度 S_m 达到阈值 \theta 时输出 1(从短期誊入长期),否则输出 0(继续留在短期)。
  1. 判定函数promote(m) 是迁移决策,输出 1 表示“值得誊进档案柜”。
    promote(m)∈{0, 1}\mathrm{promote}(m) \in \{0,\ 1\}
  2. 强度阈值\theta 是迁入门槛,一般取 0.5 到 0.7,防止低价值噪音写入长期。
    θ∈[0.5, 0.7]\theta \in [0.5,\ 0.7]
  3. 达标迁移当记忆强度不低于阈值时输出 1,触发“誊写档案 + 建联想索引”流程。
    Sm≥θ  ⇒  promote(m)=1S_m \ge \theta \;\Rightarrow\; \mathrm{promote}(m) = 1
  4. 未达保留当强度低于阈值时输出 0,记忆留在短期窗口,等待后续强化。
    Sm<θ  ⇒  promote(m)=0S_m < \theta \;\Rightarrow\; \mathrm{promote}(m) = 0

AI 应用场景

智能客服与私域助理

阶段:记忆构建与个性化服务

解决的问题:传统客服每次会话都从零开始,用户反复描述自己的公司和需求。AI 用短期记忆跟住当前工单上下文,用长期记忆记住用户公司规模、对接人称呼、合同状态,用向量记忆召回相似历史工单的解决方案,形成“老熟人式”的服务体验。

具体案例:某 SaaS 企业的客服 Agent,在用户接入后 0.5 秒内查回“该客户上次反馈的 bug、合同到期日、对接人偏好”,将平均沟通时长缩短约 40%,客户满意度显著提升。

企业知识库 RAG 问答助手

阶段:向量记忆召回与知识整合

解决的问题:传统关键词搜索找不到“意思一样、字面不同”的内容。用向量记忆对制度文档切片做语义召回,再结合长期事实库中的权限信息做融合回答,让员工用口语提问也能命中规范条款。

具体案例:某券商内部合规问答 Agent,将 5 万条制度文档编码进向量库。员工问“能不能带手机进机房”这类口语化问题,能自动召回《电子设备管理规范》相关条款并标注权限,替代人工反复答疑。

个性化学习辅导 Agent

阶段:记忆写入与迁移整合

解决的问题:传统教辅工具不记得学生上次错在哪,每次辅导都像第一次见面。短期记忆跟踪当前解题步骤,长期记忆沉淀孩子的薄弱知识点画像,向量记忆寻找相似错题组卷,实现“越教越懂”的个性化陪练。

具体案例:某教育科技公司的数学陪练 Agent,连续辅导七次后自动生成“该生函数图像题失误模式报告”,并从三年内的相似错题中筛选复习题推送给学生家长。

Python 演示

python72 行
import mathimport jsonfrom collections import deque  def bigram_vector(text):    """Encode text into a bag-of-bigram vector (demo embedding)."""    text = text.lower()    vec = {}    for i in range(len(text) - 1):        g = text[i:i + 2]        vec[g] = vec.get(g, 0) + 1    return vec  def cosine(a, b):    """Cosine similarity between two dict-style vectors."""    keys = set(a) | set(b)    dot = sum(a.get(k, 0) * b.get(k, 0) for k in keys)    na = math.sqrt(sum(v * v for v in a.values()))    nb = math.sqrt(sum(v * v for v in b.values()))    return dot / (na * nb) if na and nb else 0.0  class AgentMemory:    def __init__(self, window=6):        self.short_memory = deque(maxlen=window)   # 短期: 滑动窗口        self.long_memory = {}                      # 长期: 偏好与事实        self.vector_memory = []                    # 向量: 历史片段        self.vector_keys = []                      # 向量: 对应坐标     def remember(self, role, text):        """写入短期记忆,并按规则整合到长期与向量记忆。"""        self.short_memory.append((role, text))        self._maybe_promote(role, text)     def _maybe_promote(self, role, text):        """重要度规则: 命中偏好词则写入长期,同时入库向量记忆。"""        pref = None        for word in ("喜欢", "不吃", "希望", "是"):            if word in text:                pref = text                break        if pref is not None:            self.long_memory[text[:6]] = pref        self.vector_memory.append({"role": role, "text": text})        self.vector_keys.append(bigram_vector(text))     def recall(self, query, top_k=2):        """协同检索: 短期字面命中 + 向量语义召回。"""        answers = []        q_bigrams = [query[i:i + 2] for i in range(len(query) - 1)]        for role, text in list(self.short_memory)[-2:]:            if any(bg in text for bg in q_bigrams):                answers.append(text)        qv = bigram_vector(query)        scored = [(cosine(qv, k), i) for i, k in enumerate(self.vector_keys)]        scored.sort(reverse=True)        for score, idx in scored[:top_k]:            if score > 0.15:                answers.append(self.vector_memory[idx]["text"])        return answers  if __name__ == "__main__":    mem = AgentMemory()    mem.remember("user", "我喜欢靠窗的位置")    mem.remember("user", "我不吃海鲜")    mem.remember("assistant", "好的,已为您备注")    print(json.dumps(mem.long_memory, ensure_ascii=False))    print(mem.recall("想坐窗户旁边"))    print(mem.recall("有什么推荐菜"))
目的:用不到一百行实现一个“三类记忆原型”——短期滑动窗口、长期偏好档案、向量语义召回,演示 Agent 记忆系统的核心闭环。
  1. self.short_memory = deque(maxlen=window)短期记忆用有界队列实现滑动窗口,超窗消息自动挤出,对应小本子翻页。
  2. def _maybe_promote(self, role, text)迁移决策函数:判断这条消息要不要从短期升级到长期与向量记忆,对应管家誊写档案。
  3. self.long_memory[text[:6]] = pref命中偏好关键词即写入长期画像,键取消息前 6 字避免覆盖旧档案。
  4. self.vector_keys.append(bigram_vector(text))用相邻字符对构造简易“语义向量”,演示记忆片段如何进入联想网。
  5. scored = [(cosine(qv, k), i) for i, k in enumerate(self.vector_keys)]对每条向量记忆计算余弦相似度并排序,是向量检索的核心。
  6. if score > 0.15相似度低于阈值的候选被过滤,防止无关记忆混入回答,即“联想要有底线”。
  7. mem.recall("想坐窗户旁边")一次协同检索:先从短期窗口找字面线索,再做向量语义召回,验证“按意思找”。

常见误区

误区我以为短期记忆就是把用户的每一句话原样保存下来,聊天记录有多长,短期记忆就有多大。

正解短期记忆是“有界的上下文窗口”,通常只保留最近 N 轮消息,超出的要么被丢弃,要么被摘要压缩。因为模型上下文长度有限,塞满无关旧消息会稀释注意力——就像管家小本子写满旧页,反而找不到今天的流水。真正该做的事,是让“值得记的”沉淀到长期与向量记忆,而不是无脑全存。

为何常见:对话系统的日志天然就是“完整聊天记录”,初学者容易把日志存储和记忆机制混为一谈。

误区长期记忆和向量记忆都是“很久以前的信息”,我以为它们只是同一种东西的两种叫法。

正解两者定位完全不同:长期记忆存“结构化的稳定事实与偏好”,用数据库做精确查询;向量记忆存“语义化的历史片段”,用余弦相似度做召回。前者是档案柜里的卡片,后者是联想网上的坐标。一个精确、一个发散,互相补充,不可互相替代。

为何常见:因为两者都跨会话、都持久化,抽象层面相似,初学者极易混淆。

误区向量检索只是把关键词搜索升级了一下,本质上还是看文字里有没有匹配的词。

正解向量检索比的是“语义距离”而非字面匹配。用户问“带辣味的炖菜”,关键词搜索可能一无所获,向量检索却能召回“水煮鱼”“麻辣火锅”等语义相近的记忆。它的代价是需要嵌入模型编码,且结果是近似召回,不能替代精确事实查询——联想网再强,也不能当档案柜用。

为何常见:传统搜索的“匹配”思维根深蒂固,初学者习惯用旧框架理解新机制。

误区为了让 Agent 更聪明,我把所有历史对话都写进记忆,存得越多它就越懂我。

正解记忆是“信噪比”的游戏。无关记忆会污染检索结果、挤占上下文窗口、拖慢推理速度。正确的做法是重要度评分 + 衰减 + 去重:只保留高价值、强相关、不重复的记忆。管家档案柜的精髓从来不是大,而是“关键时刻翻得出对的那张卡”。

为何常见:“更多 = 更好”是直觉思维,而记忆系统恰恰反直觉——质量和组织比数量更重要。

面试题

Q1. 短期记忆最合适的存储介质是以下哪种?

单选题 · easy

  • A.内存缓存或键值存储← 正确答案
  • B.大规模向量数据库
  • C.分布式文件存储集群
  • D.离线冷数据仓库备份

答案:A。短期记忆读写频繁、延迟要求高、生命周期短,用内存或 Redis 这类键值存储最合适,就像管家把小本子揣在怀里随取随翻。向量数据库擅长语义检索而不是快速缓存,文件集群和冷仓库延迟过高,都不适合“此刻”级的高频读写。

Q2. 滑动窗口管理对话上下文的主要目的是什么?

单选题 · easy

  • A.无限扩大对话记忆容量
  • B.控制规模并淘汰过时信息← 正确答案
  • C.大幅提升模型训练速度
  • D.对用户隐私进行加密

答案:B。滑动窗口把上下文限制在最近 W 条,控制送入模型的规模并自动淘汰过时消息——相当于管家小本子只保留现在这几页。它不会无限扩大容量,也不负责训练速度和隐私加密,那些是另一层面的工程问题。

Q3. 向量记忆检索最常用的相似度度量是哪一个?

单选题 · mid

  • A.字符编辑距离
  • B.余弦相似度← 正确答案
  • C.哈希碰撞次数
  • D.字节长度差值

答案:B。向量记忆把文本编码成向量后,用余弦相似度衡量两个向量的方向一致性,结果越接近 1 语义越接近。编辑距离比较的是字面差异,哈希碰撞和长度差值都不能表达语义远近,它们都不是“按意思找”的正确度量。

Q4. 短期记忆迁移到长期记忆的合理触发条件是哪个?

单选题 · mid

  • A.每一轮对话都写入长期记忆
  • B.重要度超阈值或用户明确要求← 正确答案
  • C.只要向量相似度大于零就迁移
  • D.在系统重启时一次性全部迁移

答案:B。迁移要“值得才记”:要么重要度评分达到阈值(管家觉得这事重要),要么用户显式说“记住这句话”。每轮都写会让档案柜塞满噪音;相似度大于零太宽松;系统重启才迁移则丢失了迁移的时机价值。

Q5. 记忆强度公式 S_{t+1} = S_t × γ + α × hit_t 中,γ 的作用是什么?

单选题 · mid

  • A.让记忆随时间自然衰减← 正确答案
  • B.扩大上下文窗口规模
  • C.增加向量表示的维度
  • D.压缩存储空间成本

答案:A。γ 是小于 1 的衰减因子,每次更新先把旧强度打个折,让记忆随时间自然衰减,防止永不遗忘。它和窗口大小、向量维度、存储成本都没有关系。衰减是“管家会忘记不重要的事”的数学表达。

Q6. 协同检索时短期、长期、向量三者的优先级通常如何安排?

单选题 · hard

  • A.向量最高因为它语义范围最广
  • B.长期最高因为它最稳定可靠
  • C.短期最高其次长期向量兜底← 正确答案
  • D.三者完全随机不设优先级

答案:C。短期记忆是“此刻的事实”最可信,长期记忆是“稳定的画像”次之,向量记忆只是“相似的候选”最后补充。如果向量优先,就可能用别人的相似经历覆盖当前用户的真实上下文——管家再能联想,也不会拿联想结果否定小本子上刚记的话。

Q7. 指数平滑画像公式 P_t = α·x_t + (1−α)·P_{t−1} 中,α 越接近 1 表示什么?

单选题 · mid

  • A.画像越依赖历史更新越缓慢
  • B.画像越信任本次信号更新越快← 正确答案
  • C.模型的参数量随之变得越大
  • D.向量表示的维度也会变得越高

答案:B。α 是学习率,越接近 1,新信号 x_t 的权重越大,画像更新越快;越接近 0,越依赖历史画像、更新越慢。它与模型参数和向量维度无关。类比管家:α 大就像他“听一次就改档案”,α 小就像他“非要确认三次才动笔”。

Q8. 长期记忆适合存储以下哪些内容?(多选)

多选题 · easy

  • A.用户的偏好画像(口味习惯)← 正确答案
  • B.稳定的事实知识(会员等级)← 正确答案
  • C.当前轮对话的逐字消息记录
  • D.用户身份与长期契约信息← 正确答案

答案:ABD。长期记忆存的是跨会话稳定、结构化、可精确查询的信息——偏好、事实、身份契约都属于档案柜该收的卡。当前轮对话的逐字消息属于短期记忆的管辖范围,宴会还没散场,不需要急着锁进档案柜。

Q9. 建立向量记忆通常需要哪些步骤?(多选)

多选题 · mid

  • A.用嵌入模型将文本编码为向量← 正确答案
  • B.存入向量数据库并建立索引← 正确答案
  • C.用正则表达式逐字匹配关键词
  • D.查询时编码问题后做相似度检索← 正确答案

答案:ABD。向量记忆的流程是“编码入库建索引、查询编码找最近邻”。正则表达式是字面匹配,属于关键词搜索的范畴,和“按意思找”的向量检索根本不是一条技术路线。联想网的建立靠向量编码,不靠关键词规则。

Q10. 记忆整合去重时通常采用哪些策略?(多选)

多选题 · hard

  • A.向量相似度超阈值则合并记忆← 正确答案
  • B.时间更近的新信息覆盖旧冲突← 正确答案
  • C.立即删除全部历史旧记忆
  • D.定期整理归档并做强度衰减← 正确答案

答案:ABD。去重靠向量相似度判断“新旧是不是同一件事”,冲突靠时间新者覆盖旧者,日常靠整理归档和强度衰减维持档案质量。立即删除全部历史记忆是毁灭性操作,管家不会因为要整理档案就把整个书房烧掉。