大模型长期记忆的工程化之痛:我是怎么被上下文窗口搞疯的

大模型长期记忆的工程化之痛:我是怎么被上下文窗口搞疯的

前言

做智能客服系统,用户聊到第 10 轮,大模型就开始"失忆"了。翻聊天记录?那是真痛苦。

长期记忆模块的问题,比想象中复杂得多。今天聊聊我们遇到的工程化挑战和应对方案。

一、底层原理

1.1 长期记忆管理的核心难点

大模型的记忆管理有三个层次:

graph TD
    A["对话上下文"] --> B["短期记忆"]
    B --> C["工作记忆"]
    C --> D["长期记忆"]
    D --> E["向量数据库"]
    D --> F["总结压缩"]
    D --> G["分层检索"]
    B --> H["Token 限制"]
    H --> I["滑动窗口"]
    I --> J["信息丢失"]

核心挑战:

  • Token 窗口有限,不能无限塞
  • 记忆压缩时丢失细节
  • 检索时找不到相关信息
  • 记忆的时效性管理

1.2 记忆方案对比

方案 容量 检索速度 信息损失
全部历史 很小
滑动窗口
总结压缩
向量检索 很大

二、快速上手

先看最简单的记忆方案,滑动窗口:

from typing import List, Dict, Any
from collections import deque

class SlidingWindowMemory:
    def __init__(self, window_size=10):
        self.window = deque(maxlen=window_size)
    
    def add(self, message: Dict[str, str]):
        self.window.append(message)
    
    def get_context(self) -> List[Dict[str, str]]:
        return list(self.window)
    
    def clear(self):
        self.window.clear()

memory = SlidingWindowMemory(5)
memory.add({"role": "user", "content": "你好"})
memory.add({"role": "assistant", "content": "你好,有什么可以帮助的?"})
print(memory.get_context())

这够简单,但窗口大小就是记忆瓶颈。

再看更好的实现:

class SummaryMemory:
    def __init__(self, llm, max_tokens=2000):
        self.llm = llm
        self.max_tokens = max_tokens
        self.history = []
        self.summary = ""
    
    def add(self, message: Dict):
        self.history.append(message)
        if self._estimate_tokens() > self.max_tokens:
            self._compress()
    
    def _estimate_tokens(self):
        total = len(self.summary.split())
        for msg in self.history:
            total += len(msg.get("content", "").split())
        return total
    
    def _compress(self):
        text = "\n".join(f"{m['role']}: {m['content']}" for m in self.history)
        prompt = f"压缩以下对话为摘要(保留关键信息):\n{text}"
        self.summary = self.llm(prompt)
        self.history = []
    
    def get_context(self):
        context = [{"role": "system", "content": f"历史摘要:{self.summary}"}]
        context.extend(self.history)
        return context

三、核心 API / 深水区

3.1 记忆管理策略速查

策略 适用场景 优点 缺点
滑动窗口 短期对话 实现简单 丢失历史
总结压缩 长对话 压缩效果好 总结开销
向量检索 知识问答 容量大 检索可能不准
分层记忆 复杂场景 兼顾长短 实现复杂

3.2 向量检索记忆

import json
from typing import List, Dict, Any

class VectorStoreMemory:
    def __init__(self, embedding_func, store):
        self.embedding = embedding_func
        self.store = store
        self.session_memory = []
    
    def add(self, message: Dict):
        self.session_memory.append(message)
    
    def save_to_long_term(self):
        if len(self.session_memory) < 5:
            return
        
        text = "\n".join(m["content"] for m in self.session_memory)
        embedding = self.embedding(text)
        
        self.store.add(
            id=f"mem_{len(self.store)}",
            vector=embedding,
            metadata={
                "text": text,
                "timestamp": time.time()
            }
        )
        self.session_memory = []
    
    def retrieve(self, query: str, k=3) -> List[str]:
        query_embedding = self.embedding(query)
        results = self.store.similarity_search(query_embedding, k=k)
        return [r.metadata["text"] for r in results]

3.3 记忆优先级管理

class PriorityMemory:
    def __init__(self, max_items=100):
        self.max_items = max_items
        self.items = []
    
    def add(self, item: Dict, priority: int = 1):
        self.items.append({"item": item, "priority": priority})
        self.items.sort(key=lambda x: x["priority"], reverse=True)
        self.items = self.items[:self.max_items]
    
    def get_top(self, n=10):
        return [i["item"] for i in self.items[:n]]
    
    def increase_priority(self, key: str, amount: int = 1):
        for item in self.items:
            if item["item"].get("key") == key:
                item["priority"] += amount
                break
        self.items.sort(key=lambda x: x["priority"], reverse=True)

四、实战演练

完整的多层记忆系统:

import time
from typing import List, Dict, Any, Optional
from dataclasses import dataclass, field

@dataclass
class MemoryEntry:
    content: str
    timestamp: float
    type: str
    importance: int = 1

class LayeredMemory:
    def __init__(self, llm, vector_store):
        self.llm = llm
        self.working_memory = []
        self.short_term = []
        self.long_term = vector_store
        self.max_working = 20
        self.max_short = 100
    
    def add(self, entry: MemoryEntry):
        # 1. 加到工作记忆
        self.working_memory.append(entry)
        
        if len(self.working_memory) >= self.max_working:
            self._consolidate()
    
    def _consolidate(self):
        # 压缩到短期记忆
        text = "\n".join(e.content for e in self.working_memory)
        if len(text) > 500:
            prompt = f"压缩以下内容:{text[:1000]}"
            summary = self.llm(prompt)
            self.short_term.append(MemoryEntry(
                content=summary,
                timestamp=time.time(),
                type="summary",
                importance=5
            ))
        
        self.working_memory = []
        
        if len(self.short_term) >= self.max_short:
            self._archive()
    
    def _archive(self):
        # 归档到长期记忆
        for entry in self.short_term:
            if entry.importance >= 3:
                self.long_term.add(entry.content)
        self.short_term = []
    
    def retrieve(self, query: str, k=5) -> List[str]:
        results = []
        
        # 1. 工作记忆
        for entry in self.working_memory[-5:]:
            results.append(entry.content)
        
        # 2. 短期记忆
        for entry in self.short_term[-3:]:
            results.append(entry.content)
        
        # 3. 长期记忆
        long_results = self.long_term.similarity_search(query, k=2)
        results.extend(long_results)
        
        return results

memory = LayeredMemory(llm, vector_store)
memory.add(MemoryEntry("用户说想退款", time.time(), "user", 5))
memory.add(MemoryEntry("系统查询到订单", time.time(), "system", 3))
context = memory.retrieve("退款政策")
print(context)

五、避坑指南与最佳实践

💡 **技巧:记忆要有重要性评分
不是所有信息都重要,给记忆打分,优先保留高分的。

⚠️ **警告:压缩太多会丢关键信息
压缩比不要超过 5:1,不然细节全没了。

✅ **推荐:分层记忆架构
工作记忆 + 短期 + 长期,兼顾速度和容量。

六、综合实战演示

生产级长期记忆系统:

import json
import time
from typing import Dict, List, Any, Optional
from collections import OrderedDict

class LongTermMemory:
    def __init__(self, llm, persist_path="memory.json"):
        self.llm = llm
        self.persist_path = persist_path
        self.episodes = []
        self.index = OrderedDict()
    
    def store_episode(self, episode: Dict):
        self.episodes.append({
            "content": episode,
            "timestamp": time.time(),
            "id": len(self.episodes)
        })
        
        # 生成摘要和关键词
        text = json.dumps(episode, ensure_ascii=False)
        keywords = self._extract_keywords(text)
        
        for kw in keywords:
            if kw not in self.index:
                self.index[kw] = []
            self.index[kw].append(len(self.episodes) - 1)
    
    def _extract_keywords(self, text):
        prompt = f"提取关键词(3-5个):{text[:200]}"
        result = self.llm(prompt)
        return [kw.strip() for kw in result.split(",") if kw.strip()]
    
    def recall(self, query: str, k=5) -> List[Dict]:
        # 关键词匹配
        keywords = self._extract_keywords(query)
        episode_ids = set()
        for kw in keywords:
            for eid in self.index.get(kw, []):
                episode_ids.add(eid)
        
        # 关联意图
        matched = []
        for eid in list(episode_ids)[:k]:
            if eid < len(self.episodes):
                matched.append(self.episodes[eid])
        
        return matched
    
    def forget_old(self, max_age_hours=48):
        now = time.time()
        self.episodes = [
            e for e in self.episodes 
            if (now - e["timestamp"]) < max_age_hours * 3600
        ]
    
    def persist(self):
        data = {
            "episodes": self.episodes,
            "index": dict(self.index)
        }
        with open(self.persist_path, "w") as f:
            json.dump(data, f, ensure_ascii=False)
    
    def load(self):
        try:
            with open(self.persist_path) as f:
                data = json.load(f)
            self.episodes = data.get("episodes", [])
            self.index = OrderedDict(data.get("index", {}))
        except:
            pass

memory = LongTermMemory(llm)
memory.store_episode({"action": "查询订单", "result": "已发货"})
memory.store_episode({"action": "退款申请", "result": "已退款"})

# 回忆
recalled = memory.recall("我的订单怎么了?")
print(recalled)

# 持久化
memory.persist()

七、总结

大模型长期记忆管理的工程化挑战:

  • 窗口限制用滑动窗口解决
  • 信息丢失用总结压缩
  • 检索用向量+关键词
  • 效率用分层架构

没有银弹,但组合起来能有效缓解"失忆"问题。

Logo

脑启社区是一个专注类脑智能领域的开发者社区。欢迎加入社区,共建类脑智能生态。社区为开发者提供了丰富的开源类脑工具软件、类脑算法模型及数据集、类脑知识库、类脑技术培训课程以及类脑应用案例等资源。

更多推荐