老城街角的茶馆,柜台后的老掌柜手边总摆着两样东西:一张写满名字的便签纸,和一本锁在抽屉里的牛皮账本。便签记着正在招待的几位客人——谁刚续了水、谁还欠着找零;账本里却躺着几十年的老主顾——王姐忌辣、赵叔爱坐窗边、张先生每次都要听评弹。便签用完就撕,账本却要跟人一辈子。AI Agent(智能体)想成为真正“懂你”的助手,也需要同样两套记忆:对话中随手可取的短期工作记忆,和跨越时间沉淀的长期持久记忆。这篇教程,我们就借这位掌柜的账房,把记忆编码、检索增强回忆、遗忘策略、多会话管理、隐私边界与工程落地,一件件讲明白。

岗位关联

掌握 Agent 记忆系统,你就掌握了让 AI 从“每次都是陌生人”到“比你还记得你”的核心能力——这是智能客服、个人助理、陪伴型 Agent 等一切个性化产品的基石,也是 AI 工程师绕不开的工程必修课。

目录
  1. 可视化演示
  2. 记忆系统架构:便签与账本双轨并行
  3. 记忆编码:把闲谈写成档案
  4. 检索增强回忆:翻账本的学问
  5. 遗忘清理策略:旧档案的归宿
  6. 多会话记忆管理:跨天的待客之道
  7. 隐私安全边界:什么该记、什么不该记
  8. 工程落地实践:从账房到收银系统
  9. AI 应用场景
  10. Python 演示
  11. 常见误区
  12. 面试题

可视化演示

动态展示短期长期双层架构、记忆编码存储、检索增强回忆与遗忘隐私管理机制。

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

记忆系统架构:便签与账本双轨并行

茶馆掌柜能同时招待十几桌客人,靠的是两样东西分工明确。柜台上那张便签纸,记着正在进行的对话:这桌刚点了什么、那桌催了几遍——它又小又薄,随时更新、随时撕掉。抽屉里那本牛皮账本,却记着几十年不变的“大事”:王姐的忌口、赵叔的靠窗偏好、张先生对评弹的痴迷。便签管“当下”,账本管“长久”,缺一不可。

AI Agent 也一样。一次对话里,它需要“柜台便签”——短期工作记忆(Working Memory),保存最近几轮对话和当前任务状态;也需要“牛皮账本”——长期持久记忆(Long-Term Memory),让用户第二天回来时还记得昨天提过的需求。绝大多数大模型本身没有记忆,每次调用都像新来的伙计,什么都不记得。

Agent 记忆系统的核心是一条流水线:感知(接收输入)→ 编码(整理成可存储条目)→ 检索(从长期记忆找回相关片段)→ 生成(拼进上下文回答)→ 回写(把新信息归档)。短期工作记忆通常放在上下文窗口里,以一个固定大小的消息队列实现;长期记忆放在外部存储(如向量数据库)中,按需检索。每一轮对话,Agent 都先用检索结果扩充“便签”,结束时再把有价值的“闲谈”归档进“账本”。

就像掌柜倒茶时先扫一眼便签、再联想账本里这位客人的旧偏好,Agent 也是先“看”眼前对话,再“翻”记忆库,最后才开口。双系统协同,便是 Agent 记忆的骨架。

Mt+1(WM)=fθ(Mt(WM), R(M(LTM),qt), xt)M_{t+1}^{(WM)} = f_{\theta}\left(M_t^{(WM)},\ \mathcal{R}(M^{(LTM)}, q_t),\ x_t\right)
新便签 = 旧便签 + 账本里翻出的回忆 + 刚听到的话,三者一起交给模型加工,得出本轮回答。
  1. M_t^{(WM)} 当前便签第 t 轮对话时短期工作记忆的内容,也就是“柜台便签”上现在写着什么
    Mt(WM)M_t^{(WM)}
  2. R(M^{(LTM)}, q_t) 账本回忆用当前问题 q_t 去长期记忆里检索出的相关片段,相当于掌柜凭问题翻账本
    R(M(LTM), qt)\mathcal{R}(M^{(LTM)},\ q_t)
  3. x_t 新来的话用户本轮新输入的原文,是刚听到的新信息
    xtx_t
  4. f_θ(……) 掌柜的思考大模型把这些信息拼在一起推理,生成本轮回答
    fθ(⋅)f_{\theta}(\cdot)
  5. M_{t+1}^{(WM)} 更新后的便签本轮结束后的新短期记忆,下一轮就从这里开始
    Mt+1(WM)M_{t+1}^{(WM)}

记忆编码:把闲谈写成档案

客人随口一句“最近胃不大好,少放点辣”,掌柜不会把整句话背下来,而是掏出小本写“赵叔——忌辣、胃不适”。这里发生了两次转化:从语音到文字,从文字到要点。这就是记忆编码——把生活里的一句闲谈,变成账本里一个有用的条目。

Agent 的记忆编码,就是把原始对话整理成“能存、好找、用得上”的记录。一条好的记忆条目通常由三部分组成:语义内容(这条记忆讲什么)、向量表示(让机器能算相似度的“数字指纹”)、元数据(时间、来源、重要度、关联实体)。编码流程分两步:先用大模型或信息抽取器做结构化,提取关键实体并填写元数据;再用嵌入模型把文本映射成一个高维向量。此后所有检索都在向量空间中进行——两个文本语义有多接近,就看两个向量夹角有多小。

掌柜写“忌辣”而不是“今天他说他最近胃不好”,因为前者更好检索、更省空间。Agent 的编码目标也一样:话要短、信息量要大,还要保证以后“凭一个关键词就能翻出来”。

sim(q,m)=q⃗⋅m⃗∥q⃗∥×∥m⃗∥\text{sim}(q,m) = \frac{\vec{q} \cdot \vec{m}}{\|\vec{q}\| \times \|\vec{m}\|}
余弦相似度:把查询和记忆各自抽象成一根“方向箭头”,箭头方向越一致,语义越接近。
  1. q、m 语义指纹查询文本和记忆条目各自编码出的向量,像两个人的“气味标签”
    q⃗, m⃗\vec{q},\ \vec{m}
  2. q·m 方向一致性点积,逐坐标相乘再求和,衡量两个向量是否“同向”
    q⃗⋅m⃗=∑iqi mi\vec{q} \cdot \vec{m} = \sum_{i} q_i \, m_i
  3. ‖q‖×‖m‖ 长度归一用模长做除法,抵消文本长短带来的偏差
    ∥q⃗∥×∥m⃗∥\|\vec{q}\| \times \|\vec{m}\|
  4. 结果越接近 1 越像值域在 [-1, 1],接近 1 表示语义高度相似
    sim(q,m)≈1\text{sim}(q,m) \approx 1

检索增强回忆:翻账本的学问

王姐跨进店门,掌柜扫一眼,脑子里立刻浮出几行账本条目:“王姐——靠窗、普洱、怕吵。”他不是把整本账从头背到尾,而是凭几个特征快速定位。这个“定位”的动作,就是检索。

Agent 的检索增强回忆(Retrieval-Augmented Recall)解决一个核心矛盾:长期记忆可能存了成千上万条,上下文窗口塞不下,只能“挑最相关的几条”。挑的方法是:把当前问题向量化,去记忆库里找最相似的若干条。但实践中,纯向量检索(语义)会漏掉名字、编号这类精确信息,所以生产系统常用“混合检索”:向量检索负责语义相近,关键词/全文检索负责字面精确,两路得分加权融合。拿到候选后还要重排(Rerank),过滤低相关条目,再组装成上下文片段注入提示词。

就像掌柜既会凭“那位常喝普洱的女士”这种模糊描述找人(语义),也会凭“王姐”两个字直接翻档案(字面)。两路并用,关键时刻才不会掉链子。

score(q,m)=α⋅simsem(q,m)+(1−α)⋅simlex(q,m)\text{score}(q,m) = \alpha \cdot \text{sim}_{\text{sem}}(q,m) + (1-\alpha) \cdot \text{sim}_{\text{lex}}(q,m)
综合得分 = 语义路的分数 × α + 字面路的分数 × (1−α),两条路互补,避免单腿走路。
  1. sim_sem 语义路向量余弦相似度,找到“意思差不多”的记忆
    simsem(q,m)\text{sim}_{\text{sem}}(q,m)
  2. sim_lex 字面路关键词与全文匹配,找到“字一模一样”的记忆
    simlex(q,m)\text{sim}_{\text{lex}}(q,m)
  3. α 两条路的权重0 到 1 之间的系数,调大 α 就更依赖语义理解
    α∈[0,1]\alpha \in [0,1]
  4. 加权和综合排名两路得分融合后排序,取前 K 条进入上下文
    score(q,m)\text{score}(q,m)

遗忘清理策略:旧档案的归宿

账本不能无限厚。掌柜每月都会翻一遍:泛黄的、三年没来的客人归档进旧箱;与现状明显不符的直接划掉。遗忘,从来不是记忆系统的缺陷,而是它的健康机制。

Agent 的记忆库若不清理,会带来两个问题:噪声——无关记忆干扰检索精度;成本——存储和检索越来越慢、越来越贵。因此需要遗忘策略:把“旧、弱、不重要”的记忆降权或删除。常见策略有三:时间衰减(越久权重越低)、重要度评估(按访问频率和用户反馈打分)、容量上限(超出阈值就淘汰最低分或最久未用的)。工程上常用指数衰减:记忆强度随时间指数下滑,每被访问一次就“续命”一次。

好的遗忘是“有选择的遗忘”:贵客的信息十年不忘,一次性的问路当天就清。Agent 也应学会区分“值得记一辈子”和“说完就忘”。

w(t)=w0⋅e−λ(t−t0)w(t) = w_0 \cdot e^{-\lambda (t - t_0)}
记忆强度从初始值 w0 出发,随时间指数下降;λ 控制忘得快慢,关键时刻被“重新想起”就能续命。
  1. w0 初始强度记忆刚写入时的强度,通常设为 1.0
    w0=1.0w_0 = 1.0
  2. λ 遗忘速率衰减系数,λ 越大忘得越快
    λ>0\lambda > 0
  3. t−t0 时间差当前时间与上次强化(写入/访问)之间的间隔
    (t−t0)(t - t_0)
  4. e 的负指数强度随时间指数下滑,越久越弱
    e−λ(t−t0)e^{-\lambda (t-t_0)}
  5. 低于阈值就清理当 w(t) 小于设定阈值时触发归档或删除
    w(t)<θw(t) < \theta

多会话记忆管理:跨天的待客之道

王姐上周来喝过普洱,这周又来。掌柜不但记得她喝什么,还记得她上次抱怨空调太冷,这次提前调开了风口。从上周到这周,“记忆”跨越了两次会话——这是长期记忆最值钱的时刻,也是最难管理的时刻。

多会话场景下,Agent 要解决三个问题:会话边界(什么时候算一次会话结束)、记忆更新(新信息来了怎么改旧档案)、冲突消解(新信息与旧记忆矛盾时信谁)。工程上常见做法:用 session_id 标识会话;会话结束时把短期记忆里尚未入库的内容批量归档;更新采用“增量覆盖”——新观察以一定权重修正旧记忆,而不是直接删除旧记录;当新旧冲突严重时,保留两条并标注时间戳,或让用户明确确认。跨会话继承通过检索实现:新会话开始时,先用问候语检索旧记忆,把相关条目作为“开场白背景”注入上下文。

掌柜不会被顾客一句“今天不想喝茶”就推翻“她爱普洱”的档案,而是记一笔“今日例外”。Agent 也一样:一次异常不推翻长期规律,但也不能完全无视——折中更新,才是跨会话服务的精髓。

M′=M+γ⋅(Mobs−M)M' = M + \gamma \cdot (M_{\text{obs}} - M)
更新后的记忆 = 旧记忆 + 更新速率 ×(新观察 − 旧记忆):新信息越离谱,修正幅度越大;但旧经验也不会被一笔勾销。
  1. M 旧档案之前记录的旧记忆,比如“王姐忌辣”
    MM
  2. M_obs 新观察用户最新的表述,比如“我胃好了可以吃辣了”
    MobsM_{\text{obs}}
  3. γ 更新速率0 到 1 之间的系数,控制新信息多大程度改写旧档案
    γ∈(0,1)\gamma \in (0, 1)
  4. M_obs − M 差异量新旧信息差得越多,修正幅度越大
    Mobs−MM_{\text{obs}} - M
  5. M' 折中结果更新后的记忆,既保留经验又吸收新变化
    M′M'

隐私安全边界:什么该记、什么不该记

掌柜最守规矩的地方,恰恰是他“记事的分寸”。客人酒后失言的事,他绝不落笔;记下的口味偏好,也绝不在别的客人面前提起。记忆系统的边界,决定了用户敢不敢信任它。

Agent 的记忆系统会接触到大量个人信息:姓名、习惯、健康状态、支付意愿。隐私保护不是“不存数据”,而是“分级存储、按需脱敏、授权使用”。设计上要做三件事:识别敏感信息、入库前脱敏、读取时权限控制。具体手段包括:用敏感信息识别器检测姓名、电话、地址等 PII(个人身份信息);入库前做掩码/泛化(“1381234“);给每条记忆打隐私级别标签,只有具备对应权限的调用方才能读取;同时设定保留期限,到期自动删除。用户还应拥有”查看/删除”自己记忆的入口。

掌柜的账房先生也看不到所有账——不同伙计有不同的“可见范围”。Agent 记忆系统亦然:把“能看”和“该看”分开,信任才有基础。

Access(u,m)=1[perm(u)≥level(m)]⋅mask(m)\text{Access}(u, m) = \mathbb{1}[\text{perm}(u) \ge \text{level}(m)] \cdot \text{mask}(m)
能否读取一条记忆 = “权限够不够” 和 “脱敏过没有” 两个条件同时满足。
  1. perm(u) 权限等级调用方 u 拥有的访问权限等级
    perm(u)\text{perm}(u)
  2. level(m) 敏感等级记忆条目 m 的隐私分级(如 0-3 级)
    level(m)\text{level}(m)
  3. 指示函数门槛权限 ≥ 敏感等级才放行,否则返回 0
    1[perm(u)≥level(m)]\mathbb{1}[\text{perm}(u) \ge \text{level}(m)]
  4. mask(m) 脱敏兜底即使能读,敏感字段也要被掩码处理
    mask(m)\text{mask}(m)

工程落地实践:从账房到收银系统

手工账本能用几十年,但一天要接待一万个客人时,就得换成收银系统。Agent 记忆系统从原型到生产,同样要经历一场“从账房到系统”的升级。

生产环境有四个关键决策。存储选型:记忆规模百万条以内可选 Redis、pgvector;规模更大换专用向量库(如 Milvus、Qdrant)。性能优化:检索延迟要控制在几十毫秒内,手段包括近似最近邻索引(如 HNSW)和热记忆缓存(常访问的条目驻留内存)。评估监控:上线前用 Recall@K、NDCG 等指标评估检索质量,上线后持续监控记忆命中率与用户反馈。生产经验:记忆写入要防重与去抖(同一信息反复写入时合并),批量异步入库,定期做一致性校验。

从手写纸条到收银系统,变的是万倍的吞吐和毫秒级的响应,不变的是同一个道理:记住该记住的,需要时一秒翻出来。工程化的全部努力,都指向四个字——可靠记忆。

Recall@K=∣R∩TK∣∣R∣\text{Recall@K} = \frac{|\mathcal{R} \cap \mathcal{T}_K|}{|\mathcal{R}|}
召回率 = “该找到的记忆里,系统真的找回了几条”,越接近 1 说明检索越不遗漏。
  1. R 应该被找到的所有与当前问题相关的记忆集合
    R\mathcal{R}
  2. T_K 实际返回的系统取回的前 K 条候选记忆
    TK\mathcal{T}_K
  3. 交集分子该找到的里面真正被找到几条
    ∣R∩TK∣|\mathcal{R} \cap \mathcal{T}_K|
  4. 比值越接近 1 越好召回率越高,检索越不遗漏关键记忆
    Recall@K\text{Recall@K}

AI 应用场景

智能客服:隔月还认得你

阶段:长期持久记忆 + 检索增强回忆

解决的问题:传统客服系统每通对话都从零开始,用户要反复报设备型号与工单号,体验差、效率低。AI 记忆系统把每次服务的要点编码入库,下次对话自动检索历史,实现“续接式服务”。

具体案例:某智能硬件厂商的客服 Agent,用户 3 月报修“笔记本散热异响”,系统存入设备型号、问题、处理进度等记忆条目;4 月用户再次来访说“还是有点烫”,检索模块命中旧条目并组装上下文,Agent 直接接上话题:“您上次更换的散热垫效果如何?”并给出升级方案,用户重复描述的频次下降约 60%。

教育辅导 Agent:记住每个孩子的“口味”

阶段:多会话记忆管理 + 记忆更新

解决的问题:一对一辅导最怕“每次重新摸底”。AI 辅导 Agent 用记忆系统记录学生对各知识点的掌握度,跨会话更新学情画像,像掌柜记住客人忌口一样记住孩子的薄弱环节。

具体案例:数学辅导 Agent 在第一次课记录“分数运算薄弱、计算粗心”;第三次课开始时检索到该记忆,自动降低运算题难度并提示检查步骤;当学生连续两次正确完成同类题后,系统把记忆更新为“已掌握分数加法”,让画像保持“保鲜”。

健康助手:守口如瓶的边界感

阶段:隐私安全边界 + 记忆编码

解决的问题:健康类 Agent 会接触血压、体重、用药等高度敏感信息。它既要“记住”以提供个性化建议,又要“守口如瓶”防止泄露。

具体案例:某慢病管理助手在编码阶段对手机号、姓名等字段做掩码,给每条记忆打隐私等级标签;用户本人通过认证后可查看全部记忆并一键清空,客服角色只能读取脱敏后的统计信息,所有记忆 90 天后自动过期。

Python 演示

python106 行
"""老茶馆记忆系统:演示 Agent 记忆的编码、检索、遗忘与更新一条对话先进"柜台便签",满了归档入"牛皮账本";新会话凭问题检索旧账本,旧记忆随时间衰减,弱记忆被清理。"""import mathimport re  def tokenize(text):    """最简编码:把中英文句子切成词条"""    return re.findall(r'[\w\u4e00-\u9fa5]+', text)  def cosine_sim(a, b):    """两个文本的向量余弦,衡量语义相似度"""    va = {}    for w in tokenize(a):        va[w] = va.get(w, 0) + 1    vb = {}    for w in tokenize(b):        vb[w] = vb.get(w, 0) + 1    inter = set(va) & set(vb)    dot = sum(va[w] * vb[w] for w in inter)    na = math.sqrt(sum(v * v for v in va.values()))    nb = math.sqrt(sum(v * v for v in vb.values()))    return dot / (na * nb) if na and nb else 0.0  class AgentMemory:    """掌柜的记忆系统:短期便签 + 长期账本"""     def __init__(self, max_short=2, decay=0.05):        self.max_short = max_short        self.decay = decay        self.short_term = []      # 柜台便签:最近的对话轮次        self.long_term = []       # 牛皮账本:长期记忆条目        self.gc_count = 0         # 全局时钟,用于时间衰减     def observe(self, speaker, text):        """听到一句话:先进便签,便签满了就把最老的归档"""        self.short_term.append((speaker, text))        if len(self.short_term) > self.max_short:            self._commit(self.short_term.pop(0))        self.gc_count += 1     def _commit(self, item):        """记忆编码:原始对话 -> 结构化条目 + 元数据"""        self.long_term.append({            'speaker': item[0],            'text': item[1],            't': self.gc_count,            'strength': 1.0,        })     def _match(self, query, m):        """混合匹配:语义相似为主,名字命中则直接加分"""        sim = cosine_sim(query, m['text'])        if m['speaker'] in query or query in m['text']:            sim = max(sim, 0.7)        return sim     def recall(self, query, top_k=2):        """检索增强回忆:相关度 x 强度 x 新鲜度,取 Top-K"""        scored = []        for m in self.long_term:            m['strength'] *= (1 - self.decay)            age = math.exp(-0.02 * (self.gc_count - m['t']))            score = self._match(query, m) * m['strength'] * age            scored.append((m, score))        scored.sort(key=lambda x: -x[1])        return [m for m, _ in scored[:top_k]]     def forget(self, threshold=0.3):        """遗忘策略:强度低于阈值的旧记忆被清理"""        self.long_term = [m for m in self.long_term                          if m['strength'] >= threshold]  def main():    mem = AgentMemory(max_short=2)    mem.observe('王姐', '最近胃不舒服,少放点辣的')    mem.observe('伙计', '今日茶点:桂花糕、绿豆酥')    mem.observe('王姐', '来一壶普洱,谢谢')    print('短期便签:', [t for _, t in mem.short_term])    print('长期账本:', [(m['speaker'], m['text']) for m in mem.long_term])     hits = mem.recall('今天给王姐准备什么茶')    print('检索回忆:', [(m['speaker'], m['text']) for m in hits])     mem.observe('王姐', '我胃好了,可以重新吃辣了')    for m in mem.long_term:        if '少放点辣的' in m['text']:            m['text'] = '喜欢吃辣'            m['strength'] = min(1.0, m['strength'] * 0.5 + 0.8)    mem.long_term.append({'speaker': '路人甲',                          'text': '请问厕所在哪',                          't': 0,                          'strength': 0.01})    mem.forget(threshold=0.3)    print('更新与遗忘后:', [(m['speaker'], m['text'])                            for m in mem.long_term])  if __name__ == '__main__':    main()
目的:用 100 行不到的“老茶馆记忆系统”,把 Agent 记忆的编码、检索、更新、遗忘四个核心环节完整跑通。
  1. return re.findall(r'[\w\u4e00-\u9fa5]+', text)把中文英文句子切成词条,这是最朴素的“编码”
  2. return dot / (na * nb) if na and nb else 0.0余弦相似度公式落地,空文本时安全返回 0
  3. self.short_term.append((speaker, text))新信息先进“柜台便签”,短期工作记忆
  4. self._commit(self.short_term.pop(0))便签超限,最老的一条归档进“牛皮账本”
  5. m['strength'] *= (1 - self.decay)每次检索都让记忆强度轻微衰减,模拟“遗忘”
  6. score = self._match(query, m) * m['strength'] * age综合得分 = 语义相关度 × 强度 × 新鲜度
  7. scored.sort(key=lambda x: -x[1])按综合得分降序排列,取前 top_k 条作为“回忆”
  8. m['text'] = '喜欢吃辣'增量更新:用新观察修正旧档案,而不是删除重建
  9. self.long_term = [m for m in self.long_term if ...]遗忘策略:按阈值清理弱记忆,账本保持“干净”

常见误区

误区想让 Agent 记住更多?把上下文窗口调大,把所有聊天记录都塞进提示词不就行了?

正解加长上下文只是“便签纸变大”,不等于记忆系统。它有三个问题:成本随长度线性爆炸(每轮都要重算所有 token);噪声干扰(无关旧对话稀释注意力);完全没有结构——无法回答“这位用户最喜欢什么”这类聚合问题。真正的记忆系统需要编码、存储、检索、遗忘的完整闭环,而不是一味把纸加大。掌柜再厉害,也不可能把十年对话都摊在柜台上。

为何常见:大模型长上下文能力宣传很响,“塞得下”很容易让人误以为 “记得住”,忽略了调度与检索才是关键。

误区长期记忆库越大,Agent 就越了解用户;宁可多存也不删,删了就是丢用户信息。

正解存储是成本,检索是噪声放大器。百万条记忆里塞了 90% 的垃圾,检索出的 Top-K 大概率也是垃圾。遗忘策略(时间衰减、重要度评估、容量上限)恰恰是保障检索精度的关键。就像掌柜不会把客人每个喷嚏都记进账本——记太多,真正要找的那条反而被埋住。

为何常见:直觉上“大数据=更聪明”,但记忆系统里“精准的小数据”远胜“噪声里大海捞针”。

误区把对话写进数据库存好,以后要查的时候 SQL 一搜就出来了,跟文件归档一样可靠。

正解记忆写入是“有损编码”——原始对话被压缩成要点 + 向量 + 元数据,细节必然丢失;读取是“相似度检索”——只有与当前问题向量足够接近的记忆才会被召回,换个问法可能就找不到了。这就是为什么需要混合检索和重排,且关键信息在编码时要保留原文摘要。掌柜记录时会用简写,久了连自己也认不全,所以重要的账他写得格外完整。

为何常见:人容易把“数据库存储”类比成复印机,忽略了编码和检索都是概率性的、有损的。

误区隐私保护就是提供删除按钮,用户点一下,把数据库记录删了就算合规。

正解删除只解决“留存”,记忆还散落在日志、备份、第三方模型调用记录里——曾经发给模型 API 的内容可能已无法撤回。真正的边界要前置:入库前脱敏、分级授权、最小化采集、保留期限自动过期。就像掌柜的规矩不在“客人问才撕纸条”,而在“不该记的当场就不落笔”。

为何常见:“删库”是能想象到的具体动作,而权限、脱敏、传播链条是看不见的,最容易忽略。

面试题

Q1. Agent 的短期工作记忆主要负责什么?

单选题 · easy

  • A.跨会话保存用户的长期偏好
  • B.保存当前对话最近几轮信息← 正确答案
  • C.存储模型训练好的全部参数
  • D.对记忆做向量化与相似度检索

答案:B。短期工作记忆对应“柜台便签”,只保存当前对话的最近几轮,用于本轮理解和应答。A 是长期持久记忆的职责,C 是模型训练产物与记忆无关,D 是检索模块的工作。

Q2. 长期持久记忆通常存放在哪里?

单选题 · easy

  • A.大模型的上下文窗口里
  • B.外部存储如向量数据库← 正确答案
  • C.模型的梯度参数之中
  • D.浏览器本地缓存数据

答案:B。长期记忆必须放在模型之外的持久化存储中,向量数据库是主流选择。上下文窗口容量有限且每轮重算,不能充当长期记忆;梯度参数是训练产物;浏览器缓存与 Agent 服务端存储无关。

Q3. 余弦相似度衡量的本质是什么?

单选题 · mid

  • A.两个向量长度相差的倒数
  • B.两个向量在方向上的接近度← 正确答案
  • C.两个向量逐元素乘积之和
  • D.文本在语料中出现的频率

答案:B。余弦相似度 = 点积除以两向量模长,本质是“方向的一致性”,与长度无关。点积只是分子不是全部,词频是词袋编码的统计量,都不构成余弦相似度的本质。

Q4. 混合检索中语义检索主要解决什么问题?

单选题 · mid

  • A.精确匹配用户名字与编号
  • B.找到意思相近但字面不同的记忆← 正确答案
  • C.对检索结果做时间排序去重
  • D.压缩向量降低存储的成本

答案:B。语义检索用向量相似度捕捉“意思差不多”的记忆,比如“想喝点暖胃的”匹配“爱普洱”;字面精确匹配交给关键词检索。时间排序和存储压缩都不是检索的职责。

Q5. 下列哪些属于常见的遗忘清理策略?

多选题 · mid

  • A.用时间衰减降低旧记忆权重← 正确答案
  • B.按重要度评估淘汰弱记忆← 正确答案
  • C.无限扩容保留全部记录
  • D.容量超限触发淘汰清理← 正确答案

答案:ABD。遗忘策略的核心是“有选择地降低或删除”,时间衰减、重要度评估、容量上限淘汰都是常见手段。无限扩容恰恰是遗忘要解决的问题,会让噪声淹没检索。

Q6. 新记忆与旧记忆冲突时,最合理的处理方式是?

单选题 · hard

  • A.直接用新信息覆盖旧记录
  • B.以一定权重修正并保留差异← 正确答案
  • C.完全忽略新信息维持现状
  • D.把新旧内容拼接成一长段

答案:B。增量更新 M' = M + γ(M_obs − M) 体现“折中”:一次异常不推翻长期规律,但也不能无视,保留差异和时间戳供后续判断。直接覆盖可能丢失重要历史,完全忽略则无法适应用户变化。

Q7. 记忆入库前做脱敏处理的核心目的是什么?

单选题 · mid

  • A.压缩存储空间降低磁盘成本
  • B.让检索计算速度变得更快
  • C.降低敏感信息泄露的风险← 正确答案
  • D.提高记忆向量的余弦相似度

答案:C。脱敏(掩码、泛化)是把"1381234"这类处理前置到入库环节,即使存储被窃取或权限越界,敏感字段已经不可读。它不压缩空间、不加速检索,也与向量相似度无关。

Q8. 新会话继承旧记忆主要依赖什么机制?

单选题 · easy

  • A.把所有历史文本写入系统提示词
  • B.会话开始时检索旧记忆注入上下文← 正确答案
  • C.每次会话重新训练一遍模型参数
  • D.对所有会话使用相同的随机种子

答案:B。跨会话继承靠“检索增强”:新会话开始时用用户的话去长期记忆库检索相关条目,作为背景注入上下文。把所有历史塞进提示词成本太高,重训练参数既不现实也非记忆机制。

Q9. 关于记忆系统的隐私边界,下列正确的是?

多选题 · hard

  • A.权限满足要求才允许读取记忆← 正确答案
  • B.敏感字段入库前要做掩码处理← 正确答案
  • C.用户点删除数据就绝对安全
  • D.记忆应设保留期限自动过期← 正确答案

答案:ABD。隐私边界 = 权限门槛 + 脱敏前置 + 保留期限,三者缺一不可。删除按钮只能清掉主库记录,日志、备份、第三方调用链中的残留无法保证绝对安全,所以 C 错。

Q10. 关于评估指标 Recall@K,下列说法正确的是?

多选题 · extreme

  • A.分母是所有应被召回的相关记忆数← 正确答案
  • B.分子是返回结果中的相关记忆数← 正确答案
  • C.K 值增大会让召回率只升不降← 正确答案
  • D.K 值越大则检索的延迟越低

答案:ABC。Recall@K = 返回的前 K 条里相关数 / 全部相关数。K 增大时返回集合只会扩大不会缩小,因此召回率单调不减;但 K 越大要拉取和重排的结果越多,延迟通常更高,所以 D 错。