Agent的记忆怎么设计?上下文窗口满了怎么办?
本文目录
30秒版回答
我会把 Agent 的记忆分成三层:工作上下文(本轮推理真正放进窗口的内容)、短期记忆(当前会话或当前任务的历史和中间结果)、长期记忆(跨会话可复用的用户偏好、事实和经验)。另外,长任务还要有可恢复的任务状态,类似 Checkpoint。
上下文快满时,不是简单截断,而是分级处理:大的工具返回结果先外置,只保留摘要和引用;早期对话做滚动摘要;长期信息按需检索,而不是一直带着。
长期记忆最难的不是存,而是更新和遗忘:每条记忆要有来源、时间和置信度,新信息和旧信息冲突时要能覆盖,过期的要能失效。记住过期信息比忘记更危险。
先把这段讲顺;面试官追问时,再展开下面的原理和细节。
详细讲解
面试官:「Agent 跑长任务或者多轮对话的时候,上下文会越来越长。你说说 Agent 的记忆应该怎么设计?上下文窗口满了怎么办?」
这道题听起来像算法题,其实是一道非常典型的数据工程题:什么数据该留在“内存”,什么数据该落到“存储”,什么时候加载、什么时候淘汰、过期了怎么处理——这不就是我们天天做的缓存和分层存储吗?
很多候选人只会说“用向量库存长期记忆”,这只答对了一小部分。面试官真正想听的是:你能不能把记忆分层,并且处理好写入、检索、更新和遗忘。
面试官在考什么
• 是否理解上下文窗口是稀缺资源,不是越长越好——长上下文成本高、延迟高,而且中间的信息容易被模型忽略。
• 能否把记忆分层:当前工作上下文、会话级短期记忆、跨会话长期记忆、可恢复的任务状态。
• 是否考虑记忆的写入策略和过期问题:什么值得记、记错了怎么改、过期了怎么删。
• 能否给出上下文压缩的具体手段:摘要、裁剪、外置到文件、按需检索。
展开一:用数据工程的视角给记忆分层
| 层次 | 存什么 | 生命周期 | 存储方式 | 数据工程类比 |
|---|---|---|---|---|
| 工作上下文 | 系统提示、当前问题、本轮需要的证据 | 单次模型调用 | 上下文窗口 | CPU 寄存器 / 内存 |
| 短期记忆 | 本会话历史、工具调用结果、中间结论 | 会话或任务结束 | 内存 / Redis / 会话表 | 本地缓存 |
| 任务状态 | 计划、已完成步骤、待办、产出物位置 | 任务完成前 | 状态表 / 文件 | Flink Checkpoint |
| 长期记忆 | 用户偏好、稳定事实、历史经验 | 跨会话,需主动失效 | KV + 向量库 + 关系表 | 数仓 + 索引 |
分层的意义在于:每一层的写入频率、读取方式和淘汰策略都不一样。把所有东西都塞进上下文,就像把整个数仓都 cache 进内存,既贵又慢。
展开二:上下文满了的四种处理手段
上下文管理的目标是:在有限窗口里放最相关的信息。我常用四种手段,按优先级排列:
• ① 工具结果外置:一次 SQL 返回 5000 行,不要全塞进对话。把完整结果写到文件或临时表,上下文里只放“共 5000 行,前 20 行如下,完整结果在 xxx”。模型需要时再用工具去读。这一招通常收益最大。
• ② 滚动摘要:对话超过一定长度时,把较早的轮次压缩成一段结构化摘要(目标、已确认的事实、已做的决定、未解决的问题),保留最近几轮原文。
• ③ 按需检索:长期记忆不常驻上下文,每轮根据当前问题检索最相关的几条注入。
• ④ 子任务隔离:把一个需要大量阅读的子任务交给子 Agent,子 Agent 在自己的上下文里读完,只把结论返回给主 Agent。
# 上下文组装:按优先级装填,超预算就降级
def build_context(task, budget=60_000):
parts = [system_prompt(), task.goal] # 必须保留
parts += task.state.summary() # 任务状态摘要(计划/进度)
parts += retrieve_memory(task.user, task.goal, k=5) # 长期记忆按需检索
recent = task.history[-6:] # 最近几轮保留原文
older = task.history[:-6]
if older:
parts.append(summarize(older)) # 早期对话滚动摘要
for msg in recent:
if msg.is_tool_result and tokens(msg) > 2_000:
ref = offload_to_file(msg) # 大结果外置
msg = f"[结果已保存到 {ref},摘要:{preview(msg)}]"
parts.append(msg)
assert tokens(parts) <= budget, "仍超预算则继续压缩摘要"
return parts
这里有一个经常被追问的点:为什么不直接用超长上下文模型,把所有东西都放进去? 答案是三个字:贵、慢、乱。成本和延迟随 Token 线性甚至更快增长;而且研究和实践都发现,信息放在长上下文的中间位置时,模型利用它的效果会变差。
展开三:长期记忆的数据模型
长期记忆千万不要只存一堆向量。我会把它设计成一张有结构的表,向量只是其中一个检索字段:
-- 长期记忆表:每条记忆可追溯、可更新、可失效
CREATE TABLE agent_memory (
memory_id STRING, -- 主键
user_id STRING, -- 归属用户(权限隔离)
memory_type STRING, -- preference / fact / experience
subject_key STRING, -- 主题键,如 'report.timezone',用于冲突覆盖
content STRING, -- 记忆内容
embedding ARRAY<FLOAT>,-- 语义检索用
source STRING, -- 来源:哪次会话、哪条消息
confidence DOUBLE, -- 置信度:用户明确说的 > 模型推断的
valid_from TIMESTAMP,
valid_to TIMESTAMP, -- 失效时间,NULL 表示当前有效
last_used_at TIMESTAMP -- 用于冷数据淘汰
);
这个设计借鉴了数仓里的拉链表思路:用户说“以后报表都按北京时间”,过了一个月又说“改成按 UTC”,不能两条都留着让模型自己猜,而是用 subject_key 找到旧记忆,把它的 valid_to 置为当前时间,再插入新记忆。检索时只取有效记忆。
记忆写入:不是什么都值得记
| 类型 | 举例 | 要不要记 | 原因 |
|---|---|---|---|
| 明确的长期偏好 | “周报都按 UTC 时区出” | 要 | 稳定、可复用、用户明确表达 |
| 稳定的事实 | “我负责广告归因这块数据” | 要 | 影响后续回答方式 |
| 一次性的任务细节 | “这次只看上周的数据” | 不要进长期记忆 | 只在当前任务有效 |
| 模型自己的推断 | “用户可能是新人” | 谨慎,低置信度 | 容易形成偏见,且难以纠正 |
| 敏感信息 | 身份证号、密码、薪资 | 不要 | 合规风险,必须过滤 |
典型场景:Agent 记住了过期口径
想象一个问数 Agent:某次对话里业务方说“GMV 不含退款”,Agent 把这句话写进了长期记忆。三个月后公司统一口径,GMV 改为含退款,指标平台已经更新,但 Agent 的长期记忆里还躺着旧说法,于是它每次都优先相信“记忆”,给出和看板对不上的数字。
这个问题的根因是:业务口径这种有权威来源的信息,不应该进 Agent 的个人记忆,而应该每次从指标平台实时读取。记忆只存“用户偏好”和“个人上下文”,组织级的事实以权威系统为准。这个原则和数仓里“单一可信源(Single Source of Truth)”是一回事。
追问链
追问一:长任务跑到一半进程挂了,怎么恢复?
要把任务状态显式持久化,而不是只存在对话历史里。状态里包括:任务目标、计划步骤、每一步的完成情况和产出位置、关键中间结论。每完成一步就写一次,类似 Checkpoint。恢复时从状态表重建一个精简上下文,而不是把几百轮对话原样重放。
另外,有副作用的步骤(比如重跑任务、写表)要记录幂等键,恢复后不会重复执行。
追问二:滚动摘要会不会丢信息?怎么控制?
会,所以摘要要结构化而不是自由文本:固定包含目标、约束条件、已确认事实、已做决定、未解决问题这几项。对于用户明确给出的约束(比如“不要动生产表”),要单独提取出来放在一个不会被压缩的区域。另外原始对话要完整落库,摘要出问题时可以回溯。
追问三:多个用户共享一个 Agent,记忆怎么隔离?
长期记忆表必须带 user_id 或 tenant_id,检索时作为强制过滤条件,而不是检索完再过滤。团队级共享的知识(比如团队规范)单独存一份,和个人记忆分开,权限分开管理。这和多租户数仓的隔离思路完全一致。
加分说法 vs 减分说法
| ✔ 加分说法 | ✘ 减分说法 |
|---|---|
| 记忆分层:工作上下文 / 短期 / 任务状态 / 长期 | 上下文不够就换更长窗口的模型 |
| 大工具结果外置,上下文只放摘要和引用 | 长期记忆就是把对话存进向量库 |
| 长期记忆要有来源、置信度、有效期,像拉链表 | 满了就把前面的截断 |
| 组织级事实以权威系统为准,不进个人记忆 | 所有对话内容都记下来 |
| 长任务状态要像 Checkpoint 一样持久化 | 不考虑记忆过期和冲突 |
怎么挂到你的项目上
话术模板:“我会用数据工程的思路设计 Agent 记忆:上下文是内存,长期记忆是存储,中间要有加载、淘汰和失效机制。长期记忆我参考了拉链表的设计,每条记忆有主题键和有效期,新信息覆盖旧信息,而不是两条并存让模型去猜。”
这个回答的好处是:把你熟悉的数仓经验(缓存、拉链表、Checkpoint、单一可信源)自然迁移到 Agent 场景,面试官会觉得你的底子扎实。
总结
Agent 记忆本质是一个分层存储 + 缓存淘汰 + 数据版本管理问题。答题时先分层,再讲上下文压缩的四个手段,最后落到“长期记忆要可更新、可失效”。用拉链表、Checkpoint、单一可信源这些数据人熟悉的概念去类比,是数据开发答这道题最大的优势。
下一篇聊一个面试官很爱问的“关系题”:RAG 和 Agent 是什么关系?都有 Agent 了还需要 RAG 吗?
你用过的 AI 助手里,有没有被它“记错”坑过的经历?评论区聊聊~
关注「Wood聊大数据」| 9年大数据老兵,聊技术、聊职场、聊成长