订阅更新

Agent的记忆怎么设计?上下文窗口满了怎么办?

Agent手册 第 3 题 约 10 分钟读完 1 次阅读

本文目录
  1. 30秒版回答
  2. 面试官在考什么
  3. 展开一:用数据工程的视角给记忆分层
  4. 展开二:上下文满了的四种处理手段
  5. 展开三:长期记忆的数据模型
  6. 记忆写入:不是什么都值得记
  7. 典型场景:Agent 记住了过期口径
  8. 追问链
  9. 加分说法 vs 减分说法
  10. 怎么挂到你的项目上
  11. 总结

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年大数据老兵,聊技术、聊职场、聊成长

刷题进度保存在当前浏览器