第5章:记忆与状态管理

想象一下,如果你的朋友每次见面都忘记你是谁,那会是多么糟糕的体验!同样,智能体也需要"记忆"来记住用户、记住对话、记住任务进展。本章将带你掌握智能体的记忆与状态管理技术。

5.1 为什么智能体需要记忆

记忆是智能体变得"聪明"的关键。没有记忆的智能体就像金鱼的7秒记忆,无法提供连贯、个性化的服务。

记忆带来的好处:
  • 保持对话连贯 - 智能体记得之前说了什么,不会出现"前言不搭后语"
  • 避免重复询问 - 已经问过的问题不需要再问第二遍
  • 个性化服务 - 根据用户偏好和历史行为提供定制化回答
  • 提升效率 - 不需要每次都从头开始了解上下文

5.2 记忆的类型

智能体的记忆和人类一样,也有不同的类型。我们可以将记忆分为三大类:

┌─────────────────────────────────────────────────────┐
│                   智能体记忆架构                      │
├─────────────────────────────────────────────────────┤
│                                                     │
│  ┌─────────────────┐    ┌─────────────────────┐    │
│  │   短期记忆       │    │      长期记忆        │    │
│  │  (对话历史)      │    │    (知识库)          │    │
│  │                 │    │                     │    │
│  │ • 当前对话上下文 │    │ • 用户基本信息       │    │
│  │ • 最近的几轮对话 │    │ • 用户偏好设置       │    │
│  │ • 临时变量      │    │ • 历史交互记录       │    │
│  │                 │    │ • 重要事实知识       │    │
│  └────────┬────────┘    └──────────┬──────────┘    │
│           │                        │               │
│           └────────┬───────────────┘               │
│                    │                               │
│           ┌────────▼────────┐                      │
│           │    工作记忆      │                      │
│           │                 │                      │
│           │ • 当前任务状态   │                      │
│           │ • 中间计算结果   │                      │
│           │ • 待办事项      │                      │
│           └─────────────────┘                      │
│                                                     │
└─────────────────────────────────────────────────────┘
          

5.2.1 短期记忆(对话历史)

短期记忆就像你的大脑正在处理的信息,随时可用但容量有限。

短期记忆特点:
  • 存储当前对话的上下文
  • 通常保存最近10-20轮对话
  • 会话结束后可能清空
  • 直接参与每次AI请求

5.2.2 长期记忆(知识库)

长期记忆就像你的长期知识储备,容量大、持久保存。

长期记忆存储内容举例:
  • 用户姓名、职业、兴趣爱好
  • 用户之前表达过的偏好("我不喜欢辣"、"我对花生过敏")
  • 重要的历史事件("去年我们讨论过这个方案")
  • 常用知识库(产品文档、公司制度等)

5.2.3 工作记忆

工作记忆是智能体的"工作台",存放当前任务的临时数据。

工作记忆示例:订餐任务

┌─────────────────────────────────┐
│         工作记忆区域             │
├─────────────────────────────────┤
│ 任务: 帮用户订外卖               │
│ 状态: 选择餐厅中                 │
│                                 │
│ 已收集信息:                     │
│   • 用户位置: 朝阳区             │
│   • 预算: 50元以内               │
│   • 口味: 川菜                   │
│   • 人数: 2人                    │
│                                 │
│ 待确认: 具体送餐地址             │
│ 待执行: 搜索餐厅 → 展示菜单      │
└─────────────────────────────────┘
          

5.3 短期记忆的实现

短期记忆的实现最简单:维护一个对话历史列表即可。

// 对话历史结构
const conversationHistory = [
  { role: "user", content: "你好,我想订外卖" },
  { role: "assistant", content: "好的!请问您想吃什么类型的美食?" },
  { role: "user", content: "想吃川菜" },
  { role: "assistant", content: "明白了,川菜很受欢迎呢!请问您几位用餐?" }
];

// 发送请求时带上对话历史
async function sendMessage(userMessage) {
  // 1. 添加用户消息到历史
  conversationHistory.push({ role: "user", content: userMessage });
  
  // 2. 调用AI,带上完整对话历史
  const response = await callAI({
    messages: conversationHistory,  // ← 关键!带上历史
    model: "gpt-4"
  });
  
  // 3. 添加AI回复到历史
  conversationHistory.push({
    role: "assistant", 
    content: response.content
  });
  
  return response;
}
          
⚠️ 注意事项:
  • 对话历史太长会消耗更多Token,增加成本
  • 不同模型有最大上下文长度限制(如GPT-4是8K/32K)
  • 超出限制时需要压缩或截断历史

5.4 长期记忆的实现

长期记忆需要持久化存储,并且要能快速检索。通常使用向量数据库来实现。

5.4.1 向量数据库原理

向量数据库工作流程:

存储阶段:                    检索阶段:
┌──────────┐                 ┌──────────┐
│ 文本信息  │                 │ 用户提问 │
└────┬─────┘                 └────┬─────┘
     │                            │
     ▼                            ▼
┌──────────┐                 ┌──────────┐
│ Embedding│                 │ Embedding│
│  编码    │                 │  编码    │
│ (向量化) │                 │ (向量化) │
└────┬─────┘                 └────┬─────┘
     │                            │
     ▼                            ▼
┌──────────┐                 ┌──────────┐
│ 向量数据库│                 │ 相似度计算│
│ 存储向量  │◄────────────────│ 召回相关 │
└──────────┘                 └──────────┘
                                    │
                                    ▼
                              ┌──────────┐
                              │ 返回相关 │
                              │ 记忆片段 │
                              └──────────┘
          

5.4.2 代码实现示例

// 使用向量数据库(如 Pinecone、Chroma、Milvus)

class LongTermMemory {
  constructor(vectorDB) {
    this.db = vectorDB;
  }

  // 存储记忆
  async store(memory, metadata = {}) {
    // 1. 将文本转换为向量
    const vector = await embeddingModel.encode(memory);
    
    // 2. 存入向量数据库
    await this.db.upsert({
      id: generateId(),
      vector: vector,
      metadata: {
        content: memory,
        timestamp: Date.now(),
        ...metadata
      }
    });
  }

  // 检索相关记忆
  async retrieve(query, topK = 5) {
    // 1. 将查询转为向量
    const queryVector = await embeddingModel.encode(query);
    
    // 2. 搜索最相似的向量
    const results = await this.db.query({
      vector: queryVector,
      topK: topK
    });
    
    // 3. 返回记忆内容
    return results.map(r => r.metadata.content);
  }
}

// 使用示例
const memory = new LongTermMemory(vectorDB);

// 存储用户喜好
await memory.store("用户喜欢吃川菜,尤其是麻婆豆腐", {
  type: "preference",
  category: "food"
});

// 之后检索
const relevantMemories = await memory.retrieve("推荐餐厅");
// 返回:["用户喜欢吃川菜,尤其是麻婆豆腐"]
          

5.5 记忆的管理策略

记忆不是越多越好,需要合理的管理策略。

5.5.1 记忆摘要(压缩)

当对话历史太长时,可以压缩成摘要。

对话历史压缩示例:

原始对话(100轮):
┌────────────────────────────────────┐
│ 用户: 你好                         │
│ AI: 你好!有什么可以帮您的?        │
│ 用户: 我想学编程                    │
│ AI: 太好了!您想学哪种语言?        │
│ ...(中间96轮)...                 │
│ 用户: Python的列表怎么用            │
│ AI: 列表是Python中...              │
└────────────────────────────────────┘

压缩后(摘要 + 最近几轮):
┌────────────────────────────────────┐
│ 【摘要】用户是编程初学者,想学习    │
│ Python编程。已了解基础语法,目前    │
│ 在学习数据结构。                    │
│                                     │
│ 用户: Python的列表怎么用            │
│ AI: 列表是Python中...               │
└────────────────────────────────────┘
          

5.5.2 记忆遗忘(清理)

// 记忆遗忘策略

class MemoryManager {
  // 按时间遗忘:清理超过30天的短期记忆
  async forgetByTime(maxAgeDays = 30) {
    const cutoff = Date.now() - maxAgeDays * 24 * 60 * 60 * 1000;
    await this.db.deleteWhere({ timestamp: { $lt: cutoff } });
  }

  // 按重要性遗忘:保留重要记忆,删除次要记忆
  async forgetByImportance() {
    const memories = await this.db.getAll();
    const toDelete = memories.filter(m => 
      m.importance < 3 && m.lastAccess < Date.now() - 7 * 24 * 60 * 60 * 1000
    );
    await this.db.deleteMany(toDelete.map(m => m.id));
  }

  // 手动标记重要记忆永不删除
  async markImportant(memoryId) {
    await this.db.update(memoryId, { neverForget: true });
  }
}
          

5.5.3 记忆更新

记忆更新场景:
  • 用户改变了偏好("我现在不吃辣了")
  • 信息过期("我搬到上海了")
  • 纠正错误信息
  • 增加新属性("对了,我还有个女儿")

5.6 状态管理

状态管理是智能体的"工作区",存储当前任务的临时数据和执行进度。

智能体状态管理架构:

┌─────────────────────────────────────────────┐
│              全局状态 (Global State)          │
│  • 当前会话ID                                │
│  • 用户信息                                  │
│  • 配置参数                                  │
└─────────────────────────────────────────────┘
                      │
                      ▼
┌─────────────────────────────────────────────┐
│              任务状态 (Task State)            │
│  • 当前执行的任务                            │
│  • 任务进度 (0-100%)                         │
│  • 已完成的步骤                              │
│  • 待执行的步骤                              │
└─────────────────────────────────────────────┘
                      │
                      ▼
┌─────────────────────────────────────────────┐
│              上下文状态 (Context State)       │
│  • 工具调用结果                              │
│  • API返回数据                               │
│  • 中间计算结果                              │
└─────────────────────────────────────────────┘
          

5.6.1 状态管理代码示例

class AgentState {
  constructor() {
    this.state = {
      // 用户相关
      user: {
        id: null,
        name: null,
        preferences: {}
      },
      
      // 当前任务
      currentTask: {
        id: null,
        name: null,
        status: 'idle',  // idle, running, paused, completed
        progress: 0,
        currentStep: 0,
        totalSteps: 0
      },
      
      // 工作上下文
      context: {
        collectedData: {},    // 已收集的信息
        toolResults: [],      // 工具调用结果
        tempVariables: {}     // 临时变量
      }
    };
  }

  // 更新任务进度
  updateProgress(step, total) {
    this.state.currentTask.currentStep = step;
    this.state.currentTask.totalSteps = total;
    this.state.currentTask.progress = Math.round(step / total * 100);
  }

  // 存储临时数据
  setTempData(key, value) {
    this.state.context.tempVariables[key] = value;
  }

  // 获取临时数据
  getTempData(key) {
    return this.state.context.tempVariables[key];
  }

  // 完成任务
  completeTask() {
    this.state.currentTask.status = 'completed';
    this.state.currentTask.progress = 100;
    // 清理临时数据
    this.state.context.tempVariables = {};
  }
}
          

5.7 实战示例:个人助手

让我们做一个能记住用户喜好的"个人助手"。

5.7.1 完整代码实现

class PersonalAssistant {
  constructor() {
    this.shortTerm = [];           // 短期记忆:对话历史
    this.longTerm = new Map();     // 长期记忆:用户偏好
    this.workingMemory = {};       // 工作记忆:当前任务
  }

  // 处理用户消息
  async chat(userMessage) {
    // 1. 检查是否需要更新长期记忆
    await this.extractAndStoreMemory(userMessage);
    
    // 2. 检索相关长期记忆
    const relevantMemories = this.findRelevantMemories(userMessage);
    
    // 3. 构建系统提示
    const systemPrompt = this.buildSystemPrompt(relevantMemories);
    
    // 4. 调用AI(带上短期记忆)
    const messages = [
      { role: "system", content: systemPrompt },
      ...this.shortTerm.slice(-10),  // 最近10轮
      { role: "user", content: userMessage }
    ];
    
    const response = await callAI(messages);
    
    // 5. 更新短期记忆
    this.shortTerm.push({ role: "user", content: userMessage });
    this.shortTerm.push({ role: "assistant", content: response });
    
    return response;
  }

  // 提取并存储记忆
  async extractAndStoreMemory(message) {
    // 简单规则:检测"我喜欢/我讨厌/我是"等表达
    const patterns = [
      { regex: /我喜欢(.+)/, type: 'like' },
      { regex: /我讨厌(.+)/, type: 'dislike' },
      { regex: /我是(.+)/, type: 'identity' },
      { regex: /我(.+)过敏/, type: 'allergy' }
    ];
    
    for (const pattern of patterns) {
      const match = message.match(pattern.regex);
      if (match) {
        this.longTerm.set(`${pattern.type}:${match[1]}`, {
          content: match[1],
          type: pattern.type,
          timestamp: Date.now()
        });
        console.log(`💾 已记忆:${pattern.type} - ${match[1]}`);
      }
    }
  }

  // 查找相关记忆
  findRelevantMemories(query) {
    const memories = [];
    
    // 简单关键词匹配(实际可用向量检索)
    if (query.includes('推荐') || query.includes('吃')) {
      for (const [key, value] of this.longTerm) {
        if (value.type === 'like' || value.type === 'dislike') {
          memories.push(value);
        }
      }
    }
    
    return memories;
  }

  // 构建系统提示
  buildSystemPrompt(memories) {
    let prompt = '你是用户的个人助手。';
    
    if (memories.length > 0) {
      prompt += '\n\n关于用户的已知信息:\n';
      memories.forEach(m => {
        const prefix = {
          like: '喜欢',
          dislike: '不喜欢',
          identity: '身份',
          allergy: '过敏'
        }[m.type];
        prompt += `- 用户${prefix}:${m.content}\n`;
      });
    }
    
    return prompt;
  }
}
          

5.7.2 运行效果演示

交互示例:

用户: 我喜欢吃川菜
助手: 好的,我记下了!您喜欢川菜。
      💾 已记忆:like - 川菜

用户: 我对花生过敏
助手: 了解了,我会记住您对花生过敏,以后推荐食物时会注意避开。
      💾 已记忆:allergy - 花生

用户: 推荐个餐厅
助手: 检索记忆:用户喜欢川菜,对花生过敏
      
      基于您的喜好,我推荐以下川菜馆:
      1. 蜀香园 - 正宗川菜,有明确过敏原标注
      2. 川味轩 - 口碑很好,可定制免花生
      
      需要我帮您查看菜单或预订吗?

用户: 我明天要开会
助手: 好的,请问需要什么时间提醒您?需要我准备会议资料吗?
      (此信息属于临时任务,存入工作记忆)
          
✅ 记忆管理最佳实践:
  • 短期记忆保持精简,只保留必要的对话上下文
  • 长期记忆使用向量数据库存储,支持语义检索
  • 工作记忆及时清理,避免数据堆积
  • 定期对长期记忆进行整理和去重
  • 给用户查看和删除记忆的权限

本章小结

核心知识点回顾:
  • 短期记忆 - 维护对话历史列表,实现对话连贯性
  • 长期记忆 - 使用向量数据库存储和检索用户偏好
  • 工作记忆 - 存储当前任务的临时状态和进度
  • 记忆管理 - 摘要压缩、遗忘清理、更新同步
  • 状态管理 - 分层管理全局、任务、上下文状态

下一章,我们将学习如何让智能体进行规划与任务分解,把复杂任务拆解成可执行的小步骤!