🧠 AI Agent 核心架构:短期记忆(Working Memory)全解析与面试通关指南

在开发 AI Agent 时,我们经常会遇到这样一个尴尬的场景:大模型虽然“上知天文下知地理”,但它居然记不住你上一句话说了什么!

为了让大模型具备连贯对话的能力,我们需要为它装上“记忆系统”。在 Agent 的记忆分类中,最基础、最直接、也是面试最常考的就是短期记忆(Short-term / Working Memory)

这篇博客将用最通俗的大白话,带你彻底搞懂短期记忆的核心机制、主流工程落地策略,并附带大厂面试级别的实战代码。无论是入门学习还是面试突击,看这一篇就够了!


在这里插入图片描述

💡 一、 什么是短期记忆(大白话秒懂)

通俗概念
短期记忆,在认知科学中也被称为工作记忆(Working Memory)
对于大语言模型(LLM)来说,短期记忆就是当前对话的上下文(Chat History)。它就像是你的“办公桌”——你需要处理什么文件,就得把文件放在桌子上。大模型需要结合什么历史信息来回答问题,你就得把这些历史信息拼接在本次提问的 Prompt 里发给它。

核心痛点
大模型的“办公桌”大小是有限的,这被称为上下文窗口限制(Context Window Limit)
如果你和 AI 聊了三天三夜,产生的文本有几百万字,你不可能把这些聊天记录全塞进 Prompt 里。即使强行塞进去,也会面临成本爆炸(Token 极其昂贵)中间遗忘现象(模型记不住中间的内容)

因此,短期记忆工程的核心,就是“如何在有限的 Token 预算内,保留最有价值的历史对话”。


🗂️ 二、 短期记忆的四大主流策略(面试必背)

面对越来越长的聊天记录,业界通常采用以下四种策略来管理短期记忆。在面试时,请务必清晰地对比它们的优缺点:

记忆策略 💡 原理与机制 🎯 优点 ⚠️ 缺点与适用场景
全量记忆 (Buffer Memory) 简单粗暴,把历史以来的所有对话原封不动地全部保留,每次全部发给大模型。 信息 100% 完整,绝对不会漏掉任何历史细节。 极易爆 Token,成本极高。仅适用于极短的单次单点任务
滑动窗口 (Window Memory) 只保留最近的 N 轮对话(比如最近 5 轮),把更早的对话直接丢弃(滑走)。 极其轻量,永远不会超出上下文限制,成本完全可控。 会发生“硬遗忘”。比如 10 轮前你告诉它你叫张三,它现在已经忘了。适合普通的闲聊机器人
Token 滑动窗口 (Token Window) 不按“轮数”截断,而是按“Token 数量”截断。严格保证历史记录总数不超过设定的 Token 阈值。 比按轮数截断更精确,防止某几轮回复过长导致 API 报错。 依然存在硬遗忘问题。适合对 API 预算控制极严的生产环境
摘要记忆 (Summary Memory) 当对话长度达到阈值时,在后台悄悄调一个小模型,把之前的对话提炼成一段精简的摘要,替换掉长长的原始记录。 兼顾了“保存远古记忆”和“节省当前 Token”两大需求。 后台生成摘要需要消耗额外算力和时间。适合超长周期的虚拟伴侣或个人助理

🎯 三、 高频面试 Q&A 实战演练

Q1:滑动窗口记忆(Window Memory)和摘要记忆(Summary Memory)在实际业务中该如何取舍?

标准答案
在真实的工业落地中,我们通常不会二选一,而是将两者结合使用
架构设计通常是:设定一个最近 N 轮的滑动窗口来保存“高清的近期原话”(保证接话的连贯性);而对于被滑出窗口的更早期的对话,我们不直接丢弃,而是交给一个后台守护进程(Daemon)去生成摘要,作为“模糊的远期记忆”放在 Prompt 顶部。这种结合方案兼顾了当前对话的流畅度和对历史设定的遵循度。

Q2:对于大模型来说,把历史对话作为短期记忆传入时,格式上有什么讲究?

标准答案
现代的大模型 API(如 OpenAI, Anthropic)已经原生支持了统一的 messages 数组格式。在传入短期记忆时,必须严格区分角色:

  • system:存放最高指令和人设。
  • user:存放人类用户的历史提问。
  • assistant:存放 AI 历史生成的回答。
    这种严谨的结构化历史记录,能极大降低大模型产生幻觉的概率,防止大模型在角色扮演中“串戏”。

Q3:在生产环境中,短期记忆存在哪里?

标准答案
短期记忆是具有会话级别(Session-level)特征的高频读写数据。通常绝对不存放在关系型数据库(如 MySQL)中
工业界的标准做法是存放在 Redis 等内存数据库中,设置一个过期时间(TTL,比如 24 小时无交互自动清空),以保证极高的读写速度和合理的服务器资源利用。


💻 四、 面试加分代码:手写工业级“Token 滑动窗口记忆”

在面试白板环节,如果面试官让你写一个记忆管理模块,千万别只写一个简单的 List append。写出一个带**长度保护(滑动截断)**的记忆类,能完美展现你的工程健壮性意识!

import json
from collections import deque

class TokenWindowMemory:
    """
    工业级短期记忆管理器 (Token 滑动窗口策略)
    为了方便演示,这里用“字符长度”来近似模拟“Token 数量”。
    """
    def __init__(self, max_tokens: int = 1000):
        # 使用 deque (双端队列) 方便我们在超过阈值时从头部(最早的记忆)弹出数据
        self.chat_history = deque()
        self.max_tokens = max_tokens
        self.current_token_count = 0
        
    def _estimate_tokens(self, text: str) -> int:
        """
        估算文本的 Token 数量。
        真实生产环境中,这里应该调用 tiktoken 库等专用的分词器。
        此处简单使用字符长度来近似模拟。
        """
        return len(text)

    def add_message(self, role: str, content: str):
        """
        向短期记忆中添加一条新消息,并触发滑动窗口裁剪机制。
        """
        # 1. 构建标准的消息体
        new_message = {"role": role, "content": content}
        msg_tokens = self._estimate_tokens(content)
        
        # 2. 将新消息加入队列,并更新当前总 Token 数
        self.chat_history.append(new_message)
        self.current_token_count += msg_tokens
        
        print(f"📥 [新记忆存入] 角色: {role} | 消耗 Token: {msg_tokens}")
        
        # 3. 核心滑动窗口逻辑:如果总 Token 超标了,就开始遗忘最早的记忆
        self._trim_memory()

    def _trim_memory(self):
        """
        私有方法:裁剪超出的记忆,保证不爆 Token
        """
        while self.current_token_count > self.max_tokens and len(self.chat_history) > 1:
            # 从队列左侧(头部)弹出一个最古老的消息
            oldest_message = self.chat_history.popleft()
            freed_tokens = self._estimate_tokens(oldest_message["content"])
            self.current_token_count -= freed_tokens
            
            print(f"🗑️ [触发遗忘] 为了节省空间,已遗忘早期对话。释放 Token: {freed_tokens}")
            
    def get_context(self) -> list:
        """
        获取当前经过裁剪的、可以直接发给 LLM API 的上下文列表
        """
        return list(self.chat_history)


# ==========================================
# 模拟运行与面试讲解
# ==========================================
if __name__ == "__main__":
    # 面试展示:故意把最大 Token 设得很小(限制为 50 个字符),以此触发滑动遗忘机制
    memory_system = TokenWindowMemory(max_tokens=50)
    
    # 回合 1:简单的问候,安全存入
    memory_system.add_message("user", "你好,我是张三。")
    memory_system.add_message("assistant", "你好张三,很高兴认识你!")
    
    # 此时查看一下记忆
    print("\n目前的上下文:", json.dumps(memory_system.get_context(), ensure_ascii=False, indent=2))
    print("-" * 40)
    
    # 回合 2:用户发送了一大段话,导致 Token 超出阈值
    # 这会触发 _trim_memory,把回合 1 的对话从工作记忆中“滑走”
    long_text = "今天天气真不错,我想去打篮球,但是发现球鞋坏了,你能帮我推荐一双性价比高的实战篮球鞋吗?"
    memory_system.add_message("user", long_text)
    
    print("\n目前的上下文:", json.dumps(memory_system.get_context(), ensure_ascii=False, indent=2))
    
    # 💡 面试讲解要点:
    # "正如您所见,在生产环境中直接传递无限延长的 List 是极其危险的。
    # 我在这里实现了一个基于 Token 估算的滑动窗口队列。
    # 当用户的长段落涌入时,系统会自动将最初的问候语丢弃,确保最终返回的 context 列表
    # 永远处于大模型 API 的安全容量限制内,不仅避免了请求报错,还实现了成本的精准控制。"
Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐