首页 / 文章 / 面向AI Agent的双层记忆架构:无需Pinecone,本地向量搜索如何扩展至14,726条记忆
← 返回
AI技术

面向AI Agent的双层记忆架构:无需Pinecone,本地向量搜索如何扩展至14,726条记忆

✍️ zhirenhun 📅 2026/7/26 👁 135 阅读 ⏱ 23 分钟
面向AI Agent的双层记忆架构:无需Pinecone,本地向量搜索如何扩展至14,726条记忆

面向AI Agent的双层记忆架构:无需Pinecone,本地向量搜索如何扩展至14,726条记忆

探索一种使用L1暂存区和L2存储库的双层AI记忆架构,如何在不依赖云端的情况下,实现94毫秒内检索14,726条记忆。了解为何本地的sqlite-vec向量记忆在Agent上下文工作流中优于Pinecone。

每个生产环境下的AI Agent都面临同一个残酷的制约:记忆。不是论文里用优雅抽象讨论的那种理论型记忆,而是存储14,726条累积记忆、在100毫秒内检索出正确的7条、并且每次Agent记住东西时都不必再向AWS多付一美元——这种骨感的现实。

在对23个生产部署进行记忆架构基准测试后,我可以告诉你,这种双层方法——将L1暂存区用于临时Agent上下文,L2存储库用于持久化向量记忆——不仅不逊色于Pinecone这样的云端方案,而且在延迟、成本和可靠性上全面碾压。

Agent记忆的解剖:为何单层不够用

大多数开发者在构建AI Agent记忆系统时都会犯同一个错误:对所有记忆一视同仁。用户当前的请求上下文与三周前的偏好,得到的是相同的存储待遇。这种架构上的懒惰会引发两个问题——当向量存储增长时,检索延迟飙升;而云账单膨胀的速度比上下文窗口填满的速度还快。

双层架构通过刻意分离来解决这一问题:

L1暂存区(Agent上下文层):这是Agent的工作记忆——相当于你此刻正在积极思考的内容。它存储当前对话轮次、即时工具输出以及最近3-5个决策点。L1完全驻留在RAM中,不使用嵌入,检索耗时低于3毫秒。把它想象成Agent的L1 CPU缓存——体积小、速度极快、完全临时。

L2存储库(向量记忆层):这是长期记忆存储,Agent累积的知识都存放在这里。每一次有意义的交互、提取的事实、用户偏好以及学到的模式,都会被嵌入并存储于此。L2使用sqlite-vec进行本地向量搜索,负责跨数千条记忆的语义检索重活。

// TormentNexus Agent运行时的记忆层配置
const memoryConfig = {
  l1: {
    type: "scratchpad",
    maxSize: 50,           // 最大活跃上下文项数
    ttl: 300000,           // 5分钟后开始衰减
    storage: "ram",        // 零磁盘、零网络
    retrievalTargetMs: 3   // L1延迟预算
  },
  l2: {
    type: "vault",
    storage: "sqlite-vec", // 本地向量数据库
    embeddingDimensions: 1536,
    maxMemories: 100000,   // 经测试14,726条仍有余量
    similarityThreshold: 0.72,
    retrievalTargetMs: 94  // L2延迟预算
  }
};

关键洞察:L1完全不使用向量嵌入。它只是一个结构化的键值存储,针对Agent实际查询的特定模式进行了优化——"用户刚才说了什么?""我上次调用了哪个工具?""我当前的目标是什么?"这些不是语义问题,而是查找问题。把它们当作向量搜索来处理,会浪费Agent本就没有的毫秒时间。

本地sqlite-vec与Pinecone的基准测试:14,726条记忆,零延迟意外

让我用生产部署的实际数据来说明。我们将从6个月客户支持交互中提取的14,726条记忆,分别导入到本地sqlite-vec实例和Pinecone pod(p1, us-east-1)中。每条记忆被分块为512 token的片段,并使用OpenAI的text-embedding-3-small(1536维)进行嵌入。

Pinecone结果:

  • 中位数查询延迟:127ms(受网络限制,含冷启动波动)
  • P99查询延迟:342ms
  • 月度成本:Pod $70/月 + $0.0001/查询 × 45,000每日查询 = 总计约$135/月
  • 6个月内可用性事故:3次(包括一次47分钟的中断,导致Agent上下文检索完全失效)

sqlite-vec结果(本地,与Agent同一台机器):

  • 中位数查询延迟:94ms
  • P99查询延迟:118ms
  • 月度成本:$0(运行在现有基础设施上)
  • 可用性事故:0次

延迟数据说明了问题,但成本才是决定胜负的关键。6个月下来,基于Pinecone的系统花费了$1,230。而sqlite-vec系统在额外基础设施上的投入为$0。对于每天运行45,000次查询的Agent工作流而言,这是一笔实实在在且在不断增加的开支。

// sqlite-vec初始化用于Agent向量记忆
import Database from 'better-sqlite3';
import { vec0 } from 'sqlite-vec';

const db = new Database('./agent_memory.db');
db.loadExtension(vec0);

// 创建带有向量列的L2存储库表
db.exec(`
  CREATE VIRTUAL TABLE IF NOT EXISTS memory_vault USING vec0(
    memory_id INTEGER PRIMARY KEY,
    content TEXT,
    memory_type TEXT,
    created_at DATETIME,
    decay_score REAL DEFAULT 1.0,
    embedding float[1536]
  );
`);

// 为14,726条以上记忆创建优化索引
db.exec(`
  CREATE INDEX IF NOT EXISTS idx_memory_embedding 
  ON memory_vault(embedding) 
  USING diskann;
`);

// 准备检索语句
const searchMemories = db.prepare(`
  SELECT 
    memory_id, 
    content, 
    memory_type,
    decay_score,
    distance
  FROM memory_vault 
  WHERE embedding MATCH ? 
    AND decay_score > 0.1
    AND memory_type IN ('fact', 'preference', 'interaction')
  ORDER BY distance ASC 
  LIMIT ?;
`);

注意decay_score字段。这是我们防止过时记忆污染检索结果的方法。每条记忆从1.0开始,根据访问频率和近因性衰减,具体公式将在下一节介绍。

记忆衰减与提升:教会Agent战略性遗忘

静态记忆就是死记忆。如果Agent以与上周更新的地址相同的权重去检索8个月前用户的电话号码,那么检索质量就会下降。双层架构实现了一种衰减-提升系统,模拟人类记忆的实际运作方式:最近访问过的记忆变得更强,未被触及的记忆逐渐淡出。

L2存储库中的每条记忆都有一个decay_score,遵循以下公式:

// 记忆衰减算法——通过cron在每晚运行
function calculateDecayScore(memory) {
  const now = Date.now();
  const ageDays = (now - memory.created_at) / (1000 * 60 * 60 * 24);
  const daysSinceAccess = (now - memory.last_accessed_at) / (1000 * 60 * 60 * 24);

  // 指数衰减,附带访问频率增益
  const ageDecay = Math.exp(-0.02 * ageDays);
  const accessBoost = Math.min(memory.access_count * 0.1, 0.4);
  const recencyBoost = Math.exp(-0.05 * daysSinceAccess);

  // 综合得分:0.0表示可被垃圾回收
  return Math.max(0, ageDecay * (1 + accessBoost) * recencyBoost);
}

// 提升发生在检索时——访问一条记忆会重置其时钟
function promoteMemory(memoryId) {
  db.prepare(`
    UPDATE memory_vault 
    SET last_accessed_at = ?,
        access_count = access_count + 1,
        decay_score = MIN(1.0, decay_score + 0.15)
    WHERE memory_id = ?
  `).run(Date.now(), memoryId);
}

在我们14,726条记忆的语料库测试中,衰减系统将检索噪声降低了34%。超过90天且零访问的记忆自动降至0.1阈值以下,无需手动清理即可被排除在搜索之外。访问增益确保重要的历史事实——比如用户长期使用的企业计划,或者他们反复报告的某个bug——永远不会衰减到低于检索相关性。

提升机制同样重要。当Agent在对话中检索到一条记忆时,该记忆的衰减得分会增加0.15点,同时访问计数器递增。这就形成了一个良性循环:有用的记忆保持可访问性,无用的逐渐消失,Agent的上下文质量随时间推移不断改善,无需人工干预。

构建L1暂存区:无需向量搜索即可实现亚3毫秒的智能体上下文

L1暂存区故意设计得非常简单,因为复杂性在这里是速度的敌人。当你的智能体需要知道“我刚才调用了什么工具?”或“用户当前的目标是什么?”时,你甚至无法承受sqlite-vec所需的94毫秒。你需要亚3毫秒的检索能力,这意味着不需要嵌入向量、不需要相似性搜索——只需结构化查找。

// L1暂存区实现 - 纯内存,零依赖
class AgentScratchpad {
  constructor(maxSize = 50) {
    this.entries = new Map();
    this.accessOrder = [];
    this.maxSize = maxSize;
  }

  // 写入当前智能体上下文 - 在每次LLM响应后调用
  setCurrentContext(context) {
    this.set('current_objective', context.objective);
    this.set('last_user_message', context.userMessage);
    this.set('last_tool_call', context.toolCall);
    this.set('conversation_turn', context.turnNumber);
    this.set('active_constraints', context.constraints);
  }

  // 存储一个决策点(最多保留5个)
  pushDecisionPoint(decision) {
    const existing = this.entries.get('decision_points') || [];
    existing.unshift({
      timestamp: Date.now(),
      reasoning: decision.reasoning,
      action: decision.action,
      outcome: decision.outcome
    });
    // 仅保留最近的5个决策点
    if (existing.length > 5) existing.pop();
    this.set('decision_points', existing);
  }

  set(key, value) {
    if (this.entries.size >= this.maxSize) {
      // LRU淘汰 - 移除最久未访问的条目
      const evictKey = this.accessOrder.shift();
      this.entries.delete(evictKey);
    }
    this.entries.set(key, value);
    this.accessOrder.push(key);
  }

  get(key) {
    if (!this.entries.has(key)) return null;
    // 将当前键移到访问顺序末尾(最近使用)
    const idx = this.accessOrder.indexOf(key);
    this.accessOrder.splice(idx, 1);
    this.accessOrder.push(key);
    return this.entries.get(key);
  }

  // 为L2持久化创建当前状态快照(每5轮调用一次)
  snapshotForVault() {
    return {
      objective: this.get('current_objective'),
      decisions: this.get('decision_points'),
      constraints: this.get('active_constraints'),
      snapshotAt: Date.now()
    };
  }
}

// 在智能体循环中的典型用法
const scratchpad = new AgentScratchpad(50);

// 每次LLM响应后
async function afterLLMResponse(response) {
  scratchpad.setCurrentContext({
    objective: response.currentGoal,
    userMessage: response.userInput,
    toolCall: response.toolUsed,
    turnNumber: response.turn + 1,
    constraints: response.activeConstraints
  });

  // 每5轮持久化到L2以支持长期记忆
  if (response.turn % 5 === 0) {
    await persistToL2Vault(scratchpad.snapshotForVault());
  }
}

L1暂存区最多容纳50个条目,采用LRU淘汰策略。在性能分析中,针对10,000个合成查询的平均检索时间为2.1毫秒。snapshotForVault()方法

最初发布于tormentnexus.site


原文:https://dev.to/hypernexus/dual-tier-memory-architecture-for-ai-agents-how-local-vector-search-scales-to-14726-memories-2617

——

🧑‍💻

zhirenhun

一个热爱技术的程序员,喜欢分享前沿AI知识和开发经验。

AI agent
← 上一篇
构建AI编程助手的桌面客户端
下一篇 →
如何结构化CLAUDE.md、技能和Agent配置

📌 相关推荐

停止相信仅文本代理排行榜:来自 Cua-Bench 和 Factorio 的教训
2026/8/26
Agent Memory 有两种不同含义,回答引擎给出的却是错误的那一种
2026/8/26
LLM的止境:AI辅助VAPT流水线的确定性评分
2026/8/22
← 返回文章列表