Agent 面试通关 / 04
记忆与上下文:长对话不丢信息的实战方案
记忆和上下文管理是 Agent 面试中“看起来简单但很容易答浅”的维度。面试官不想听你说“用向量数据库”——他想知道的是存什么、怎么查、怎么融合,以及面对模糊输入时 Agent 怎么利用记忆给出好体验。
Q:长上下文里,怎么让 Agent 不忘记关键信息?
来源:腾讯 Agent 岗终面
新手答:“用向量数据库存起来。”
高手答:
单一向量检索在长对话中很容易丢关键信息。我们的方案是三段式记忆:
- 滚动窗口记忆:最近 3-5 轮对话原样保留,保证强相关
- 关键实体记忆:用 NER 实时抽取用户提到的时间、地点、人物、任务,存入一个类似知识图谱的结构,随时可查
- 周期性摘要记忆:每 10 轮对话,让模型生成一段结构化摘要(“用户想做什么,目前进展到哪,遇到了什么障碍”)
查询时,三段信息同时召回,按权重融合。这样既能记住“用户对花生过敏”这种细节,又不会被 50 轮前的闲聊干扰。
差距在哪:新手的“向量数据库”是一个工具,不是方案。它解决了存储问题,但没解决“存什么、怎么查、怎么融合”的问题。高手的三段式记忆在不同时间粒度上各有分工——短期精确、中期结构化、长期摘要——这是 Memory 系统设计的核心思路。面试官考的是“你理不理解 Agent Memory 的多层次需求”。
Q:用户说“按老样子帮我订一下”,这种模糊需求怎么处理?
来源:腾讯 Agent 岗终面
新手答:“问用户‘老样子’具体指什么。”
高手答:
分两步处理,先“猜”再“问”:
- 猜:立刻检索用户的历史订单,找出时间最近、频次最高、或用户标记过“喜欢”的订单,作为候选
- 问:把候选的 1-3 个选项(例如“是订上次的 XX 酒店吗?”)清晰地抛给用户确认
绝不能模型自己去“猜”一个就执行。这个流程把模糊需求转化成了一个清晰的选择题,体验好,且不会错。
差距在哪:新手的“直接问”看起来安全,但体验差——用户说“老样子”就是不想再描述一遍,你让他重新说等于没理解他的意图。高手的“先猜再确认”是更好的交互模式:展示你理解了他的意思(通过历史数据),同时用确认保证不出错。面试官考的是“你有没有产品思维”,Agent 不只是技术系统,也是用户交互系统。
Q:上下文窗口不够用,对话太长了怎么办?
来源:Agent 岗面试高频题 / 字节 Agent 实习二面
新手答:“截断早期消息,只保留最近几轮。”
高手答:
截断是最粗暴的做法,会丢失关键上下文。我们的处理分四层:
- 早期对话压缩成摘要:不是直接丢掉,而是每隔一段对话,让模型把已完成的部分压缩成结构化摘要(“用户需求是什么、已确认的参数、当前进展”),只保留摘要,释放原始 token
- 大任务拆子任务:一个 30 步的复杂任务,不要在一个上下文里硬撑。拆成 5 个子任务,每个子任务用独立对话完成,中间结果通过数据库传递,而不是全靠上下文承载
- 中间结果落库:工具调用的返回值、中间计算结果,不要全留在对话里。提取关键字段存数据库,需要时再查回来,上下文里只保留一句“已完成 XX,结果存储在 task_123”
- 按需回溯而非全量携带:不是每轮都把所有历史带上。建一个索引,标记每段对话的主题和关键实体,需要时按主题精准召回,而不是全量塞回去
核心思路是:先用工程手段省 token,实在不够再考虑模型层面的压缩。
差距在哪:新手的“截断”是单一策略,且会造成信息丢失。高手的四层方案各有分工——摘要保留语义、拆任务降低单次复杂度、落库减少冗余、按需召回精准补充。面试官考的是“你有没有在生产环境里处理过长对话的工程经验”,这不是模型能力问题,是系统设计问题。
Q:多 Agent / 多异步任务下,如何防止上下文污染?
来源:字节后端开发 Agent 一面
新手答:“每个 Agent 用独立的上下文。”
高手答:
上下文污染是多 Agent 系统最隐蔽的 bug——一个 Agent 的中间状态泄漏到另一个 Agent 的上下文里,导致输出偏离。
污染来源:
- 共享上下文变量:多个 Agent 读写同一个 conversation history,A 的中间推理过程被 B 当成了事实
- 异步竞态:并行 Agent 同时更新共享状态,后写入的覆盖先写入的
- prompt 拼接错误:动态拼接 Prompt 时,把不属于当前 Agent 的信息混了进去
防治方案:
- 上下文隔离:每个 Agent 维护独立的消息历史,不共享 conversation history。需要传递信息时,通过显式的结构化消息(而不是共享上下文)
- 作用域控制:定义每个 Agent 能看到什么——只传入它的 System Prompt、当前任务描述、和上游传来的结构化结果,不传无关历史
- 状态快照:并行任务启动前,对共享状态做快照。每个 Agent 基于快照执行,结果通过合并策略写回,不直接覆盖
- 消息签名:每条消息标记来源 Agent ID,下游 Agent 可以按来源过滤,只接收自己应该看到的信息
核心原则:Agent 之间传递"结论",不传递"过程"
对比后端思维:这和微服务之间不共享数据库、用 API 通信是同一个原则——进程隔离,接口通信。
差距在哪:新手只说了“独立上下文”——知道方向但没有方案。高手先分析了三种污染来源,再给出四种防治方案,且类比了后端微服务的隔离原则。面试官用“并发安全”视角考你对多 Agent 系统的理解——上下文污染本质就是“共享可变状态”问题。
Q:讲一下 Agent 中的“长短期记忆”
来源:字节后端开发 Agent 一面
新手答:“短期记忆是当前对话,长期记忆存数据库。”
高手答:
Agent 的记忆系统可以类比人类认知模型,分三层:
| 记忆类型 | 对应人类认知 | Agent 中的实现 | 生命周期 |
|---|---|---|---|
| 感知记忆 | 瞬时记忆 | 当前输入 + 最近 1-2 轮对话 | 当前请求 |
| 短期记忆 | 工作记忆 | 上下文窗口内的全部对话历史 | 当前会话 |
| 长期记忆 | 长时记忆 | 外部存储(向量库、数据库、文件) | 跨会话持久 |
短期记忆的工程挑战:
上下文窗口有限。对话轮次多了,早期信息被截断或被淹没。解决方案:
- 滚动窗口:只保留最近 N 轮原始对话
- 摘要压缩:定期让模型把已完成的对话压缩成结构化摘要
- 关键信息外置:实时提取用户提到的关键约束(预算、日期、偏好),存到独立的状态变量里,每轮强制注入
长期记忆的工程挑战:
存什么、怎么查、怎么融合:
- 存什么:不是把所有对话都存,而是提取有价值的信息——用户偏好、历史决策、学到的经验
- 怎么查:用 embedding 做语义检索,但要控制召回数量——塞太多长期记忆会干扰当前任务
- 怎么融合:长期记忆和短期上下文可能矛盾(用户偏好变了),需要有时效性权重——最近的记忆优先级更高
进阶:还有一类“工作记忆”——Agent 在执行多步任务时的中间状态(to-do list、已完成步骤、中间结果)。它不属于对话历史,也不属于长期记忆,是任务级别的临时状态。
差距在哪:新手只分了两层且没有展开。高手分了三层(感知/短期/长期),每层都说了工程挑战和解法,还补充了“工作记忆”这个进阶概念。面试官要的不是“存数据库”三个字,而是你能不能把记忆按层次拆清楚。
Q:对于上下文工程有什么经验?有没有做过 to-do list?为什么让模型更聚焦?
来源:抖音基础架构 Agent 一面 / 字节 Agent 实习二面
新手答:“就是把相关信息塞到上下文里。”
高手答:
上下文工程的核心不是“塞更多信息”,是让模型在每一步都能看到恰好需要的信息,不多不少。
To-do list 机制是上下文工程的一个重要实践:
- 任务开始时,让模型把复杂任务拆解成一个结构化的 to-do list,每个 item 有状态标记(pending / in_progress / completed)
- 每一步执行后,更新对应 item 的状态,并把当前 to-do list 注入到下一步的上下文中
- 模型在每一步都能看到“全局做到哪了、当前该做什么、还剩什么没做”
为什么这样能让模型更聚焦:
大模型的一个核心问题是上下文越长越容易迷失——长对话中,模型会忘记最初的任务目标,或者在中间步骤偏离方向。To-do list 起到锚点(Anchor)的作用:
- 它把隐式的任务进度变成了显式的结构化状态,模型不需要从历史消息中推断“我做到哪了”
- 每一步都有明确的当前任务,减少了模型的决策空间
- 已完成的 item 可以压缩或折叠,只保留结论,释放上下文空间给当前任务
实现方式:
- 在 System Prompt 中定义 to-do list 的格式规范
- 用工具调用(如
task_update)让模型显式更新任务状态 - 每轮对话前,把最新的 to-do list 作为上下文的固定部分注入,位置放在历史消息之前、当前任务之前
差距在哪:新手把上下文工程等同于“塞信息”。高手展示了 to-do list 这个具体的上下文管理手段,且解释了它为什么有效——把隐式进度变成显式状态。面试官考的是你有没有超越“Prompt Engineering”的认知——上下文工程是系统级的信息管理。
Q:Agent 需要同时读知识库、调外部 API、结合用户历史偏好,怎么处理这三类上下文的优先级?
来源:腾讯大模型应用开发二面
新手答:“都放进上下文里让模型自己判断。”
高手答:
三类信息不能混着塞,要先定义优先级:
优先级从高到低:
1. 系统规则(最高)
2. 当前轮用户明确输入
3. 外部工具返回 + 知识库证据
4. 用户历史偏好(最低)
因为偏好只影响表达方式或默认选择,不能覆盖当前事实。比如用户历史里一直偏好 Python,但这轮明确说“用 Java 给我写”,那当前轮约定一定优先。又比如知识库里有旧规则,外部 API 返回的是实时状态,那实时状态优先于静态知识。
真正做 Prompt 组装时,最好按槽位拼接,把“当前目标”“实时证据”“历史画像”分开,而不是混成一段自然语言。这样模型能清楚地看到每类信息的边界和权重。
差距在哪:新手把所有信息混在一起丢给模型——模型没有优先级意识,容易被低优先级信息干扰。高手定义了四级优先级,且在 Prompt 组装时按槽位分离。面试官考的是你有没有对多源上下文做过系统性的优先级设计。
Q:你怎么理解 Agent 里的“状态”而不是“上下文”?
来源:腾讯大模型应用开发二面
新手答:“状态就是上下文的一部分。”
高手答:
上下文更像模型看到的输入材料,状态则是系统对任务推进过程的结构化刻画。Agent 做得深一点以后,不能只靠大段对话历史维持执行,因为模型并不天然擅长长期状态一致性。
状态通常包括:
当前阶段、已完成任务、失败次数、已调用工具、关键中间结果、待确认信息
这样做的好处是,模型不用每次从自然语言自己猜任务进行到哪一步,系统可以明确告诉它现在在什么节点。很多所谓 Agent 不稳定,本质上不是上下文不够,而是没有显式状态。
差距在哪:新手把状态和上下文混为一谈。高手区分了两者——上下文是“模型看到的输入”,状态是“系统对任务进度的结构化刻画”。面试官考的是你理不理解 Agent 的核心工程挑战:把隐式的执行进度变成显式的结构化状态。
Q:一个 Agent 系统里,什么时候应该追问用户,什么时候应该自己继续推理?
来源:腾讯大模型应用开发二面
新手答:“不确定就问用户。”
高手答:
判断标准主要有两个:信息缺口是否影响正确执行,以及这个缺口能不能通过工具或外部知识补上。
| 情况 | 策略 |
|---|---|
| 缺的是执行必需参数(如订单号) | 追问用户 |
| 缺的是可由外部系统补齐的信息(如城市从画像获取) | 自己继续推理 |
| 能猜到但猜错代价极高(如支付、删除) | 宁愿追问,不擅自补全 |
追问不是因为模型“不聪明”,而是因为系统要在体验和风险之间做平衡。无脑追问体验差,无脑猜测风险高——好的 Agent 需要根据操作的可逆性和代价来决定。
差距在哪:新手的“不确定就问”看起来安全,但会导致过度追问、体验打折。高手用两个判断标准(是否影响执行 + 能否自动补齐)加一个风险维度(猜错代价)构建了完整的决策框架。面试官考的是你能不能在“用户体验”和“执行安全”之间做工程化的平衡。
Q:设计一个能支持亿级用户、千亿级记忆条目的 Agent 记忆系统,你会如何做技术选型和架构设计?
来源:后端 AI 八股 / Memory 系统
新手答:“用向量数据库存记忆,加个缓存层。”
高手答:
亿级用户 × 千亿条目,核心挑战是存储成本、检索延迟和写入吞吐三角平衡。架构分三层:
- 热记忆层(最近 7 天 / 高频访问):Redis Cluster 或 MemoryDB,按 user_id 分片,毫秒级读写。容量小但访问频繁
- 温记忆层(近期 + 中等频率):向量数据库(Milvus / Qdrant)+ 倒排索引(Elasticsearch),支持语义检索和关键词混合召回
- 冷记忆层(历史归档):对象存储(S3 / OSS)+ 列式数据库(ClickHouse),压缩存储,按需加载
关键设计决策:
- 分片策略:按 user_id 一致性哈希分片,同一用户的记忆在同一分片上,减少跨分片查询
- 冷热分离:记忆条目带 TTL 和访问计数器,定期冷热迁移。大部分记忆 30 天后几乎不再被访问
- 写入优化:记忆写入走异步队列(Kafka),批量写入存储层,避免高并发写入打爆数据库
- 索引策略:不是所有记忆都建向量索引——高频查询的建索引,低频的只做全文检索或按时间范围查
- Embedding 服务独立部署:向量化计算是 CPU/GPU 密集型,和业务服务混部会互相影响。独立部署 Embedding 服务(K8s 上按需扩缩),写入时异步调用,避免阻塞主链路
- 两阶段检索:第一阶段用 ANN(近似最近邻)从百万级候选中快速召回 top-100,第二阶段用 Re-ranking 模型(Cross-Encoder)对候选集做精排,兼顾速度和精度
千亿级的核心不是“用什么数据库”,而是分层存储 + 冷热分离 + 异步写入的架构设计。
差距在哪:新手只想到了一个组件。高手按访问频率做了三层分离,每层有不同的存储引擎和访问模式,且点出了分片、冷热迁移、异步写入三个关键工程决策。面试官考的是你能不能把“用什么数据库”的问题升级成一个完整的系统架构设计。
Q:如何处理记忆的“新鲜度”与“重要性”之间的冲突?
来源:后端 AI 八股 / Memory 系统
新手答:“最新的优先。”
高手答:
新鲜度和重要性是记忆排序的两个独立维度,不能简单让一个压过另一个。处理思路是多因子加权 + 衰减函数:
FinalScore = w1 × Relevance + w2 × Importance + w3 × Recency
- 相关性(Relevance):当前查询和记忆条目的语义匹配度,用 embedding 相似度或 Cross-Encoder 打分
- 重要性(Importance):由记忆的语义权重决定——用户明确表达的偏好(“我对花生过敏”)权重高;闲聊、过渡性对话权重低。可以用分类器或规则标注
- 新鲜度(Recency):用时间衰减函数,比如指数衰减
decay = e^(-λt),越久越低
权重 w1、w2、w3 不是拍脑袋定的——通过 A/B 测试在线上调优,不同业务场景的最优权重可能完全不同(客服场景重要性权重高,新闻场景新鲜度权重高)。
冲突消解的关键是按记忆类型定不同的衰减策略:
- 事实性记忆(如过敏信息):重要性权重极高,几乎不衰减
- 偏好性记忆(如“喜欢中餐”):中等衰减,允许被新偏好覆盖
- 上下文性记忆(如“上次聊到了旅行”):快速衰减,主要靠新鲜度
核心是不同类型的记忆用不同的衰减策略,而不是一刀切。
差距在哪:新手用单一维度做排序。高手引入了三因子加权模型(相关性 + 重要性 + 新鲜度),按记忆类型定义不同的衰减策略,且用 A/B 测试调优权重。面试官考的是你有没有把“排序”问题建模成一个可量化、可调优的工程方案。
Q:Agent 的记忆可能存在偏见(Bias)或事实性错误,如何发现并纠正?
来源:后端 AI 八股 / Memory 系统
新手答:“定期清理过期记忆。”
高手答:
记忆偏见主要有三种来源:
- 采样偏差:如果用户只在不满时反馈,Agent 记住的全是负面信息,导致后续交互过度谨慎
- 确认偏差:Agent 用已有记忆过滤新信息,只保留和已有记忆一致的内容,形成“信息茧房”
- 事实过期:用户之前说“在北京工作”,后来搬去了上海,旧记忆变成了错误记忆
发现机制:
- 矛盾检测:新记忆写入时,和已有记忆做冲突检查。如果新信息和旧记忆矛盾(“我现在在上海” vs 记忆中的“用户在北京”),触发更新流程
- 置信度衰减:事实性记忆带置信度分数,长时间未被验证的逐步降低置信度
- 周期性审计:定期用模型对关键记忆做事实性检查——“这条记忆是否仍然有效?”
纠正机制:
- 用户主动纠正时,不只更新当前记忆,还要级联更新所有基于错误记忆推导出的下游记忆
- 高置信度新信息覆盖低置信度旧信息,但旧信息不删除,做版本化存储,方便溯源
差距在哪:新手只想到了“清理过期”——这是最粗暴的方式。高手从偏见来源、发现机制、纠正策略三个层面构建了完整方案,且提到了级联更新和版本化存储。面试官考的是你有没有意识到记忆系统的“数据质量”问题。
Q:什么是记忆的 Reflection 机制?它与简单的 Summarization 有何不同?
来源:后端 AI 八股 / Memory 系统
新手答:“Reflection 就是对记忆做总结。”
高手答:
Summarization 是压缩——把 10 段对话压成 1 段摘要,信息量减少但核心保留。Reflection 是提炼 + 推理——不只压缩,还要从记忆中抽象出更高层次的认知。这个概念最早在 Stanford 的 Generative Agents 论文中被系统化提出。
原始记忆:
- "用户问了三次 Python 装饰器的用法"
- "用户在 Java 泛型问题上回答得很快"
- "用户说自己是后端开发"
Summarization 输出:
"用户是后端开发,问过 Python 装饰器和 Java 泛型"
Reflection 输出:
"用户是有经验的 Java 后端开发,正在学习 Python。
Python 基础语法可能已掌握,但高级特性(装饰器、元编程)是薄弱点。
后续交互建议:Python 解释可以类比 Java 概念来辅助理解。"
Reflection 的价值在于它产生了原始记忆中不存在的新知识——“正在学习 Python”“可以用 Java 类比”这些推断不是任何一条原始记忆直接包含的。本质上,Reflection 做了三件 Summarization 做不到的事:归纳(从多条记忆中提取共性)、抽象(从具体事件上升到模式识别)、策略推导(基于认知产出行动建议)。
实现上,Reflection 通常是定期运行的后台任务:积累一批记忆后,让模型做一次反思性分析,产出高级认知,存入独立的“反思记忆层”。Generative Agents 的做法是设置一个“重要性累计阈值”——当最近记忆的重要性总分超过阈值时触发一次 Reflection,而不是简单按时间间隔。
差距在哪:新手把 Reflection 等同于 Summarization。高手用具体例子展示了本质区别——Summarization 压缩信息,Reflection 通过归纳、抽象、策略推导产生新认知,并引用了 Generative Agents 的触发机制。面试官考的是你对记忆系统“知识蒸馏”能力的理解。
Q:在实现长期记忆时,什么情况选向量数据库,什么情况选传统的 KV 或关系数据库?
来源:后端 AI 八股 / Memory 系统
新手答:“记忆都用向量数据库。”
高手答:
选什么取决于查询模式,不是记忆类型:
| 查询模式 | 适用存储 | 示例 |
|---|---|---|
| 语义相似性搜索 | 向量数据库 | “用户之前说过关于旅行的偏好” |
| 精确键值查找 | KV 存储 | user_id → 最近 5 条对话(情景记忆 / Episodic Memory) |
| 结构化条件过滤 + 事务 | 关系数据库 | “最近 7 天内、重要性 > 0.8 的记忆”,或需要跨表 JOIN、ACID 事务的场景 |
| 图关系查询 | 图数据库 | “和用户提到的‘张三’相关的所有记忆” |
实际系统通常是混合存储:
- 用户画像和结构化偏好 → 关系数据库(PostgreSQL),支持精确查询、复杂 JOIN 和事务——当记忆条目之间有强关联关系(如偏好变更历史、记忆版本链)时,关系数据库的事务保证不可替代
- 最近对话和热记忆 → KV 存储(Redis / DynamoDB),毫秒级读取,适合存储情景记忆(Episodic Memory)——按时间线组织的对话片段
- 语义检索场景 → 向量数据库(Milvus / Qdrant),支持 embedding 相似度召回
- 实体关系 → 图数据库(Neo4j),支持多跳关联查询
向量数据库的局限:不擅长精确匹配、不支持事务、更新成本高。如果你的查询是“查用户的手机号”,向量数据库反而不如 KV 直查。
差距在哪:新手“全用向量数据库”是一刀切。高手按查询模式选存储,区分了情景记忆(KV)、语义记忆(向量库)、结构化记忆(关系库)、关联记忆(图库),且指出实际系统是混合架构。面试官考的是你有没有数据存储选型的工程经验——不同查询模式对应不同的最优存储引擎。
Q:什么是记忆的幻觉问题?它和 LLM 本身的幻觉有何区别?如何缓解?
来源:后端 AI 八股 / Memory 系统
新手答:“都是模型编造信息。”
高手答:
两者的来源不同,治理手段也不同:
- LLM 幻觉:模型在生成时凭空编造事实,来源是模型参数中的统计偏差。比如问“某公司 CEO 是谁”,模型编了一个不存在的名字
- 记忆幻觉:Agent 从记忆中检索到了信息,但这条记忆本身是错的、过期的、或被错误关联的。比如检索到“用户喜欢辣”(其实是另一个用户的记忆),然后基于这条错误记忆做了推荐
关键区别:LLM 幻觉是“无中生有”,记忆幻觉是“有据但据错”。记忆幻觉更难发现——因为 Agent 确实引用了一条“记忆”,看起来有理有据,但根基是错的。
缓解手段:
- 置信度过滤:记忆条目带来源标签和置信度分数,检索时设相关性阈值,低置信度记忆降权或标注“不确定”
- 交叉验证:关键记忆在使用前和外部数据源做一致性校验
- 用户确认:基于记忆做高风险操作前,先向用户确认“我记得你上次说过 XX,是否正确?”
差距在哪:新手混淆了两种幻觉。高手区分了来源(模型参数 vs 记忆存储)和表现(无中生有 vs 有据但据错),且针对记忆幻觉给出了置信度过滤、交叉验证、用户确认三种缓解手段。面试官考的是你对 Agent 系统中不同类型错误的精确认知。
Q:什么是“工具态记忆”(Tool-state Memory)?它在 Agent 工作流中如何发挥作用?
来源:后端 AI 八股 / Memory 系统
新手答:“就是记住工具的使用方法。”
高手答:
工具态记忆(也叫 Procedural Memory,程序性记忆)是指 Agent 在调用工具过程中产生的中间状态和执行上下文,包括四类:
- 工具调用历史:哪些工具被调用过、参数是什么、返回了什么——避免重复调用
- 工具执行状态:每次工具调用的生命周期状态(
pending → running → success/failed),供编排层做流转控制和超时检测 - 工具能力记忆:某个工具在特定条件下的成功率、平均延迟、已知限制——辅助工具选择决策
- 失败记忆:哪些参数组合导致过工具调用失败,失败原因是什么——避免重复犯错,支持自动重试策略调整
在工作流中的作用:
- 避免重复调用:查过一次天气就不需要再查,除非时间过了很久
- 上下文传递:前一个工具的输出是后一个工具的输入参数,中间需要工具态记忆做承接
- 动态工具选型:根据历史失败记忆,自动跳过当前不可用的工具,选择备选方案
- 执行状态追踪:编排层通过工具执行状态判断当前步骤是否完成、是否需要超时干预,这是实现可靠多步工作流的基础
和对话记忆的区别:对话记忆是“用户说了什么”,工具态记忆是“系统做了什么”。很多 Agent 只维护对话记忆,丢失了工具态——导致重复调用、参数丢失、无法从失败中学习。
差距在哪:新手把工具态记忆理解成“记住工具说明书”。高手用 Procedural Memory 概念框架,区分了四类工具态记忆(调用历史、执行状态、能力记忆、失败记忆),且说清了它在避免重复、上下文传递、动态选型、状态追踪四个场景的作用。面试官考的是你对 Agent 工作流中“执行态信息”管理的理解。
Q:记忆的容量规划需要考虑哪些因素?如何估算存储成本?
来源:后端 AI 八股 / Memory 系统
新手答:“按用户数乘以平均记忆条数估算。”
高手答:
容量规划要考虑三类因素:
单条记忆存储拆解:
原始文本:200-500 字,UTF-8 编码约 0.6-1.5 KB
Embedding 向量:768 维 float32 约 3 KB,1536 维约 6 KB
元数据索引:时间戳、标签、置信度等约 0.5 KB
─────────────────────────────────
单条记忆总存储 ≈ 5-8 KB
规模估算(两种口径交叉验证):
口径一:按总用户估算
1 亿用户 × 平均 1000 条/用户 = 1000 亿条
1000 亿条 × 7 KB/条 ≈ 700 TB 原始存储
加上索引、副本、冗余 → 约 2-3 PB
口径二:按 DAU 增长估算
100 万 DAU × 50 条/天 × 365 天 = 182.5 亿条/年
第一年原始存储 ≈ 128 TB,三年累计 ≈ 400 TB
成本估算(以 AWS 为例):
冷存储(S3 Standard):$0.023/GB/月 → 400 TB ≈ $9,200/月
热存储(Redis):按 50 TB 热数据,r6g.xlarge 集群 ≈ $15,000/月
向量索引(Milvus on EKS):100 TB 向量 ≈ $8,000/月
Embedding 计算:按 5000 万条/天 × $0.0001/条 ≈ $5,000/月
成本优化手段:
- 冷热分离:80% 的记忆超过 30 天不再被访问,转冷存储成本降 10 倍+
- 向量量化:int8 量化可以把向量存储减半,精度损失约 1-2%
- 记忆合并:相似记忆做去重和合并,减少冗余条目
- TTL 策略:低重要性记忆设过期时间,自动清理
还要考虑读写比(记忆系统通常读多写少 10:1 以上,读扩展比写扩展更重要)和增长速率(每天新增多少条记忆,决定扩容节奏)。
差距在哪:新手只做了简单乘法。高手从单条存储拆解、双口径规模估算、具体云服务成本核算、优化手段四个层面做了完整规划,且给出了可落地的数字(精确到美元/月)。面试官考的是你有没有做过大规模存储系统的容量规划,能不能把一个抽象的“多大”变成具体的数字和优化策略。
Q:如何判断当前对话与历史对话是否相关?
来源:字节 Agent 实习二面
新手答:“看关键词有没有重叠。”
高手答:
相关性判断是上下文管理的前置环节——Agent 需要决定是否召回历史对话来辅助当前回答。做错了要么遗漏关键信息,要么引入无关噪声。
判断方法分三个层次:
1. 实体和意图匹配(快、粗):
- 提取当前对话的关键实体(人名、产品名、任务名)和意图标签
- 与历史对话的实体/意图做交集匹配
- 有交集 → 可能相关,进入下一步精判
- 无交集 → 大概率无关,跳过
2. 语义相似度(中等精度):
- 对当前对话和历史对话分别做 embedding
- 计算余弦相似度,设阈值(如 0.7)
- 注意:不能只拿最后一句话做 embedding,应该取当前对话的主题摘要
3. 模型精判(慢、准):
- 把当前对话和候选历史对话组合成一个 prompt,让小模型(7B)判断“这两段对话是否讨论同一主题或有因果关系”
- 适合高价值场景(如客服接续、任务继续),不适合每次都调
工程实践中的组合策略:
当前对话 → 实体/意图提取(规则/小模型)
→ 用实体做倒排检索,召回候选历史
→ embedding 相似度粗排,保留 top-K
→ (可选)模型精排
→ 判断是否关联
关键设计点:
- 时间衰减:越近的历史对话越可能相关,给相似度分数加时间衰减权重
- 主题切换检测:如果用户明确说“换个话题”或提了一个完全无关的新需求,直接判定与历史无关
- 会话级 vs 跨会话级:同一会话内的历史默认相关,跨会话的才需要判断
差距在哪:新手只想到关键词匹配,这在语义相近但用词不同时会失效。高手用三层递进的判断方法(实体匹配 → 语义相似 → 模型精判),且考虑了时间衰减和主题切换等实际场景。面试官考的是你能不能把“相关性判断”设计成一个可工程化的方案,而不是拍脑袋设阈值。
Q:你会如何判断一条历史信息该进入长期记忆,还是只留在当前会话里?
来源:Agent 开发面试 30 题
新手答:“重要的存长期,不重要的只留会话。”
高手答:
“重要不重要”太主观了。需要一套可程序化的判断标准:
进入长期记忆的条件(满足任一):
| 条件 | 示例 | 原因 |
|---|---|---|
| 用户显式声明的偏好 | “我不吃辣”“预算一般在 5000 以内” | 跨会话复用价值高 |
| 反复出现的模式 | 连续三次对话都查某只股票 | 隐式偏好,值得记住 |
| 身份性信息 | 姓名、角色、所在城市、公司 | 个性化服务的基础 |
| 已确认的事实性结论 | “上次排查发现是 Redis 配置问题” | 避免重复排查 |
只留在当前会话的条件:
| 条件 | 示例 | 原因 |
|---|---|---|
| 一次性的任务上下文 | “帮我写封邮件”的具体内容 | 任务完成后无复用价值 |
| 中间推理过程 | 模型的 chain-of-thought | 占空间大、复用价值低 |
| 临时性的环境信息 | “现在网络不好” | 下次对话时已过期 |
| 未经确认的猜测 | Agent 推测用户可能想要 X | 未验证的信息不应持久化 |
工程实现:每轮对话结束后,用一个轻量模型做“记忆准入判断”——从当前对话中提取候选记忆条目,按上述条件过滤,通过的写入长期存储。关键是去重——新记忆和已有记忆做语义相似度检查,重复的合并而非追加。
差距在哪:新手用“重要性”做主观判断。高手给出了可程序化的准入条件表——显式偏好、反复模式、身份信息、确认事实四类进长期,一次性上下文、中间过程、临时信息、未确认猜测只留会话。面试官考的是你能不能把模糊的“重要性”转化成可执行的工程规则。
Q:长期记忆检索时,怎么避免把“语义相关但当前无用”的内容召回进来污染理解?
来源:Agent 开发面试 30 题
新手答:“调高相似度阈值。”
高手答:
单纯调高阈值会漏掉真正有用的记忆。问题的根源不是“检索到了不相关的”,而是“语义相关”不等于“当前有用”。
比如用户以前聊过北京的餐厅推荐,现在在聊北京的天气——“北京”“推荐”这些词语义上确实相关,但餐厅记忆对天气问题毫无帮助。
解决方案是多阶段过滤:
flowchart LR
A["长期记忆库"] --> B["第一阶段:\n向量召回\n(语义相似 top-K)"]
B --> C["第二阶段:\n意图匹配过滤\n(当前意图 vs 记忆标签)"]
C --> D["第三阶段:\n时效性过滤\n(过期信息降权/丢弃)"]
D --> E["第四阶段:\n相关性精排\n(模型判断是否真正有用)"]
E --> F["注入上下文"]
- 意图匹配过滤:每条长期记忆存储时打上意图标签(美食、出行、技术、偏好等),召回时只取和当前意图匹配的类别
- 时效性过滤:带时间戳的记忆检查是否过期——“上周的天气”对本周没用,但“用户不吃辣”长期有效
- 相关性精排:用一个轻量模型或 Cross-Encoder 判断:“这条记忆对回答当前问题是否有帮助?”
关键原则:宁可少召回,不要多召回。注入一条无关记忆的危害比漏掉一条有用记忆更大——因为无关信息会干扰模型的推理方向,而缺少信息模型会主动追问。
差距在哪:新手靠调阈值——这是一维调节,解决不了“语义相关但无用”的核心矛盾。高手用四阶段过滤(向量召回 → 意图匹配 → 时效性 → 精排)逐步缩小范围,且给出了“宁少勿多”的设计原则。面试官考的是你对记忆检索质量的工程化思考。
Q:记忆摘要、压缩、去重、合并这几件事,你会怎么设计触发时机?
来源:Agent 开发面试 30 题
新手答:“定时跑一个清理任务。”
高手答:
定时清理是最简单但最粗糙的方案——可能还没到清理时间记忆就爆了,也可能频繁清理浪费资源。更合理的做法是事件驱动 + 阈值触发的组合:
| 操作 | 触发时机 | 原因 |
|---|---|---|
| 摘要 | 单次会话结束时 | 会话原始内容太长,摘要后再存入长期记忆 |
| 压缩 | 当前上下文 token 数达到窗口 70% | 预留 30% 给模型生成和新信息,不等到 100% 才压缩 |
| 去重 | 新记忆写入时 | 写入前和已有记忆做相似度检查,发现重复就合并而非追加 |
| 合并 | 同一主题的记忆条目超过 N 条时 | 5 条关于“用户喜欢日料”的记忆可以合并成 1 条 |
设计关键点:
- 压缩不是简单截断:不能直接砍掉前面的对话。用模型对历史消息做摘要压缩——保留关键决策和约束条件,丢弃中间推理过程
- 去重要看语义不是看字面:“用户偏好日料”和“用户喜欢吃日本菜”字面不同但语义重复,需要用 embedding 相似度判断
- 合并要保留时间线:合并后的记忆应保留“最早/最近出现时间”,方便后续做时效性判断
触发策略优先级:
写入时去重(实时)> 会话结束时摘要(会话级)> 上下文压缩(对话中)> 主题合并(后台异步)
差距在哪:新手用定时任务一刀切。高手按操作类型分别设计了不同的触发时机——摘要在会话结束、压缩在 token 阈值、去重在写入时、合并在条目超限时,且说清了每个操作的关键约束。面试官考的是你对记忆生命周期管理的工程化设计能力。
Q:如果用户偏好、事实记忆、系统状态三者冲突了,Agent 应该信谁?
来源:Agent 开发面试 30 题
新手答:“听用户的。”
高手答:
不能无条件听用户的。三者的优先级要按冲突类型分别处理:
冲突类型 1:用户偏好 vs 事实记忆
用户说“我预算不限”,但历史记忆显示用户每次都选最便宜的 → 以当前偏好为准,但可以柔性提醒:“好的,预算不限。顺便说一下,之前您几次选的都是经济型方案,需要我也推荐一些高端选项吗?”
原则:当前会话的显式声明 > 历史隐式行为模式。
冲突类型 2:用户偏好 vs 系统状态
用户说“帮我订明天的机票”,但系统状态显示用户账户已被风控冻结 → 系统状态优先,必须拦截。不能因为用户想做就帮他做——安全约束、权限约束、业务规则是硬性边界。
原则:系统安全/合规约束 > 一切用户偏好。
冲突类型 3:事实记忆 vs 系统状态
记忆里存的是“用户 VIP 等级为金卡”,但系统实时查询显示已降级为银卡 → 以系统实时状态为准。记忆可能过期,系统状态是当前事实。
原则:实时系统状态 > 历史记忆快照。
总结优先级:
系统安全/合规约束(不可违反)
> 系统实时状态(当前事实)
> 用户当前显式声明(当前意愿)
> 历史记忆/行为模式(参考但可被覆盖)
差距在哪:新手无条件听用户的——但如果用户要做违反安全规则的事呢?高手按冲突类型分别处理,给出了明确的优先级链:安全 > 实时状态 > 当前声明 > 历史记忆。面试官考的是你在设计 Agent 决策逻辑时有没有考虑过“信息源冲突”这个关键问题。
Q:Code Agent 的上下文工程,和普通对话 Agent 相比有哪些独特挑战?
来源:蚂蚁集团 Agent 开发一面
新手答:“代码多了上下文放不下,截断就行。”
高手答:
Code Agent 的上下文工程比普通对话 Agent 难得多,因为代码的上下文结构是网状的,不是线性的。
挑战一:依赖关系是隐式的
普通对话的上下文是线性的——前几轮说了什么。但代码的上下文是跨文件、跨模块的依赖图。要修改一个函数,可能需要知道:调用它的地方、它依赖的类型定义、相关的配置文件、对应的测试文件。这些信息分散在整个项目里,不在对话历史中。
普通对话 Agent:上下文 = 最近 N 轮对话(线性)
Code Agent:上下文 = 当前文件 + 依赖文件 + 类型定义 + 测试 + 配置 + git history(网状)
挑战二:精度要求极高
自然语言对话“大差不差”也能用,但代码差一个字符就编译不过。上下文里缺少一个类型定义,模型就可能自己编造一个不存在的接口。所以 Code Agent 的上下文不能只求“相关”,必须求精确和完整。
挑战三:上下文随操作动态变化
Code Agent 每做一步操作(写文件、运行测试),项目状态就变了。下一步的上下文必须基于操作后的最新状态,不能用缓存的旧版本。这意味着上下文需要持续刷新,而不是一次组装完就够了。
工程解法:
- 结构化索引:不把整个文件塞进上下文,而是用 AST/LSP 建立项目的结构化索引——函数签名、类型定义、调用关系。需要时按依赖路径精确召回
- 分层加载:当前编辑文件全量加载 → 直接依赖文件加载签名 → 间接依赖只加载类型定义
- 操作后刷新:每次文件修改或命令执行后,增量更新受影响文件的上下文,而非全量重建
- 项目规则注入:用 CLAUDE.md / .cursorrules 等机制注入编码规范和架构约束,让模型在生成代码时遵守项目约定
差距在哪:新手把 Code Agent 的上下文当成普通对话来处理。高手看到了代码上下文的三个独特挑战(网状依赖、精度要求、动态变化),并给出了结构化索引、分层加载、操作后刷新、规则注入四个工程解法。面试官考的是你对 Code Agent 这个垂直场景的深度理解。
Q:做上下文工程最关键的工作是什么?
来源:蚂蚁集团 Agent 开发二面
新手答:“把相关信息尽量多地塞进上下文。”
高手答:
上下文工程最关键的工作不是“塞更多信息”,而是在有限的窗口里,让模型在每一步都能看到恰好需要的信息——不多不少。
这个工作可以拆成三个关键环节:
1. 信息分级——决定“什么信息该在上下文里”
必须在(每轮注入):系统规则、当前任务目标、关键约束条件
按需在(检索注入):相关知识、历史记录、工具返回结果
不该在(外置存储):中间推理过程、已处理的原始数据、过长的工具返回
最常见的错误是把所有东西都往上下文里塞——信息越多,模型越容易在噪声中迷失。
2. 注入位置——决定“信息放在上下文的哪个位置”
大模型对上下文不同位置的信息敏感度不同(Lost in the Middle 现象)。关键信息的最佳位置:
System Prompt:系统规则、角色约束、输出格式要求(最稳定的锚点)
对话开头:任务目标、关键约束(不会被中间对话淹没)
最近几轮:工具返回结果、当前步骤信息(和当前决策最相关)
3. 生命周期管理——决定“信息什么时候进、什么时候出”
上下文不是只进不出的。最关键的工程决策是信息的退出策略:
- 工具返回的原始 JSON:提取关键字段后,原始数据退出上下文
- 已完成的子任务:压缩成一句摘要,详情落库
- 过时的约束条件:用户改了需求后,旧约束必须显式移除,不能让它残留干扰
flowchart LR
A["信息分级\n什么该在"] --> B["注入位置\n放在哪里"]
B --> C["生命周期\n什么时候退出"]
C --> D["每一步都是\n恰好的上下文"]
差距在哪:新手以为上下文工程就是”塞信息”。高手把它拆成信息分级、注入位置、生命周期管理三个关键环节——重点不是”放什么进去”,而是”什么时候让什么退出”。面试官考的是你对上下文工程的系统性认知,而不只是”会用 RAG 注入知识”。
Q:Agent 的 Checkpoint 用什么数据库存?初始化 session 时如何优化加载 Checkpoint 的速度?
来源:AI 工程师面试
新手答:”用 Redis 存,读取快。”
高手答:
Checkpoint 是 Agent 执行状态的持久化快照——包括当前节点、中间变量、已完成步骤、工具返回结果等。选什么数据库存、怎么优化加载速度,取决于数据特征和访问模式。
存储选型:
| 方案 | 适用场景 | 优缺点 |
|---|---|---|
| PostgreSQL(LangGraph 默认) | 生产环境首选 | 事务保证、支持复杂查询、数据持久可靠;延迟略高 |
| SQLite | 本地开发、单用户场景 | 零配置、嵌入式;不支持并发写入 |
| Redis | 高频读写、短期状态 | 极低延迟;但数据持久性弱,重启可能丢失 |
| MongoDB | Checkpoint 结构复杂且多变 | Schema 灵活;但事务支持弱于 PG |
LangGraph 生产部署推荐 PostgreSQL + 异步连接池(AsyncPostgresSaver + asyncpg),兼顾可靠性和性能。
Checkpoint 表结构设计(以 LangGraph 为例):
checkpoints 表:
thread_id -- 会话标识
checkpoint_id -- 检查点版本号(单调递增)
parent_id -- 父检查点 ID(支持版本链)
channel_values -- 序列化的状态数据(JSON/msgpack)
metadata -- 节点名、时间戳、自定义标签
通过 thread_id 定位会话,checkpoint_id 定位最新版本,parent_id 支持状态回溯。
加载速度优化:
初始化 session 时加载 Checkpoint 的瓶颈通常在序列化数据的读取和反序列化。优化分四个方向:
1. 数据库层优化:
- 对 (thread_id, checkpoint_id) 建复合索引,精确命中,避免全表扫描
- 使用连接池(asyncpg pool)复用数据库连接,省掉每次建连的开销
- 对历史 checkpoint 做定期归档,保持主表体积小
2. 缓存层:
flowchart LR
A[“请求加载\nCheckpoint”] --> B{“Redis 缓存\n命中?”}
B -->|”命中”| C[“直接返回”]
B -->|”未命中”| D[“查 PostgreSQL”]
D --> E[“写入 Redis 缓存\n设置 TTL”]
E --> C
热点 session 的最新 Checkpoint 缓存到 Redis,TTL 设为 session 超时时间。命中率高时加载延迟从 10ms 级降到 1ms 级。
3. 懒加载(Lazy Loading):
不一次性加载完整 Checkpoint。先加载元数据(当前节点、步骤数),channel_values 中的大字段(如工具返回的长文本、检索结果)按需加载:
第一步:加载 metadata + 当前节点位置(<1ms)
第二步:用户实际交互时,按需加载具体 channel 的值
4. 状态压缩:
- 历史步骤的完整工具返回可以压缩为摘要——只保留关键结论,丢弃原始 JSON
- 用 msgpack 替代 JSON 做序列化,体积缩小 30-50%,反序列化速度更快
- 超过一定步数的历史 Checkpoint 只保留最近 N 个完整快照,更早的合并为摘要快照
差距在哪:新手只想到 Redis——快但不可靠,且没考虑加载优化。高手从存储选型(PostgreSQL + 异步连接池)、表结构设计、四层加载优化(索引 + 缓存 + 懒加载 + 压缩)给出了完整方案。面试官考的是你对状态持久化的工程化设计能力——Checkpoint 是 Agent 断点恢复和多轮对话的基石。
这类题的答题模式
记忆与上下文题的核心是多层次 + 产品思维:
1. Memory 不是一个组件,是一个多层系统——短期、中期、长期各有分工
2. 不要只说"存起来",要说"存什么、怎么查、怎么融合"
3. 上下文管理的核心是"工程手段省 token",而不是"换更大的窗口"
4. 模糊需求的处理思路是"先猜再问",而不是"直接问"
5. 交互设计和技术方案同样重要——Agent 是用户产品
面试官听到“向量数据库”或“直接问用户”就知道你只想到了技术的一半。听到三段式记忆、NER 实体抽取、结构化摘要、先猜再确认,才会觉得你既懂技术也懂产品。
下一篇建议继续看: