编程 Cloudflare Durable Objects 2.0 + Meerkat:边缘计算领域的「不可能三角」破局者

2026-08-13 11:46:07 +0800 CST views 9

Cloudflare Durable Objects 2.0 + Meerkat:边缘计算领域的「不可能三角」破局者

2026 年最值得开发者关注的基础设施升级,没有之一

2026年8月,Cloudflare接连发布重磅技术更新:先是8月4日推出Cloudflare OS平台,随后在8月11日开源Meerkat——一个基于QuePaxa共识算法的全球一致性控制平面。这两件事看似独立,实则指向同一个技术目标:在边缘计算环境下,打破分布式系统中的CAP不可能三角

Durable Objects(DO)作为Cloudflare最具差异化的产品之一,诞生三年来主要用于实时协作场景(如在线文档的多人编辑、游戏的房间管理)。而DO 2.0的向量存储原语,让它从「状态管理工具」一跃成为「AI Agent记忆层」。Meerkat则为这个记忆层提供了全球强一致性的底层保证。

本文将从架构原理、共识算法、代码实战三个维度,深度拆解这两项技术组合的工程价值与局限。


一、背景:边缘计算的状态管理困境

1.1 无状态 vs 有状态:Serverless 的阿喀琉斯之踵

Cloudflare Workers和AWS Lambda代表了Serverless的极致:无容器、冷启动毫秒级、全球分布、按调用计费。但这套架构有一个根本性的软肋——状态管理

当你需要构建一个实时协作应用时,Serverless的无状态特性反而成了负担。每个函数调用都是独立的,你无法在一个调用中保存状态供下一个调用使用。这意味着:

  • 多人实时编辑:无法维护一个「当前光标位置」的共享状态
  • 游戏房间:无法跟踪玩家的加入/离开和房间状态
  • AI Agent:无法让Agent记住之前对话的上下文

传统解决方案是引入外部状态存储(Redis、PostgreSQL、MongoDB),但这会带来三个新问题:

  1. 延迟爆炸:从边缘节点到中心数据中心的往返延迟通常在100-300ms
  2. 架构复杂度:需要处理连接池、超时、重试、分布式锁
  3. 冷启动恶化:每次访问外部存储,都可能触发函数的「冷启动」

1.2 Durable Objects 的破局思路

Durable Objects的设计哲学是:「把状态管理下沉到离用户最近的数据中心」

每个DO实例都是一个单线程的JavaScript运行时,运行在Cloudflare全球网络的某个数据中心里。当客户端连接到一个DO时,Cloudflare的Anycast路由会自动将请求导向距离该客户端最近的数据中心。如果该数据中心的DO实例不可用(比如实例被缩容),Cloudflare会在最近的可用数据中心创建一个新实例,并将请求透明迁移过去。

客户端 (北京) 
    ↓ Anycast路由 (延迟 <5ms)
最近的边缘数据中心 (上海)
    ↓
Durable Object 实例 (单线程JS运行时)
    ↓ 持久的 SQLite 数据库
本地磁盘

这种架构的优势是显著的:状态就保存在处理请求的同一台机器上,读写延迟从100-300ms降低到0.5-2ms。但DO 1.0有一个限制:跨实例的一致性

1.3 DO 1.0 的一致性痛点

DO 1.0保证的是「单实例内部的一致性」——每个DO实例内部的状态是强一致的。但当你有多个DO实例需要协调时(比如一个聊天应用中的多个「房间」),这些实例之间缺乏一个全局一致性保证。

具体来说:

  • 场景A:两个用户分别连接到不同数据中心的DO实例,他们对「某个共享状态」的读取可能不一致
  • 场景B:一个DO实例崩溃后重启,它需要从某个「权威来源」恢复状态,而不是依赖可能已经过时的本地数据
  • 场景C:一个需要跨多个DO实例的分布式锁(比如全局限流),DO 1.0没有原生支持

这就是Meerkat要解决的问题。


二、核心概念:从 Raft 到 QuePaxa

2.1 为什么 Raft 在广域网中表现不佳

要理解QuePaxa的价值,先要理解Raft在广域网(WAN)中的局限性。

Raft是目前最流行的分布式共识算法,被etcd、CockroachDB、TiKV等系统广泛采用。Raft的核心机制是领导者选举 + 日志复制

客户端 → Leader → 复制到多数派节点 → 响应客户端
           ↑
      (若Leader崩溃)
      Follower超时 → 发起选举 → 新Leader当选

问题出在领导者依赖超时机制上:

领导者是唯一的写入点。所有写请求必须经过Leader,Leader崩溃后系统进入「不可用」状态,直到新Leader当选。这个过程在局域网环境中通常只需要几十毫秒,但在广域网中可能需要几秒甚至几十秒

超时机制在WAN中不可靠。Raft依赖Follower的选举超时(通常是150-300ms)来触发Leader选举。但在跨数据中心的高延迟网络环境中,超时的判断变得不可靠——可能是Leader真的崩溃了,也仅仅是网络延迟导致的虚假超时。这会导致频繁的不必要选举(又称「脑裂」),严重影响系统吞吐量。

Cloudflare在博文中描述了他们的切肤之痛:

遗憾的是,像 Raft 这样广泛部署的共识算法在 Cloudflare 这样的广域网中表现欠佳,因为它们依赖于领导者和超时机制。领导者是唯一被允许进行写入操作的副本,如果它因崩溃或网络质量下降而出现故障,系统将无法正常运行,直到其他某个副本超时并选出新的领导者为止。

2.2 QuePaxa:无领导者的强一致性

QuePaxa是Cloudflare团队自研的共识算法,其核心创新是完全去掉领导者依赖和超时机制,实现在广域网中的强一致性保证。

2.2.1 核心原理:槽位投票 + 多数派确认

QuePaxa将共识日志组织成一系列槽位(slot),每个槽位可以包含一条已提交的消息(或为空)。关键在于任何一个副本都可以作为提议者,不需要选举领导者。

传统 Raft:
[Client] → [Leader] → (广播到所有Follower) → (等待多数派确认) → [提交]

QuePaxa:
[Client] → [任意副本] → (向所有副本发送提案) → (各副本独立投票) → (达到多数派时自动提交)
           ↑
      无Leader,任何副本均可提议

2.2.2 安全性证明:两条关键不变式

QuePaxa的安全性由两条数学不变式保证:

不变式1(一致性):如果任意两个副本对某个槽位的值做了决定,那么这些值必须相同。换言之,任何两个副本都不会对已决槽的值产生分歧

不变式2(活跃性):只要网络最终能够传递消息(即使延迟波动剧烈),共识过程就能推进。这与Raft的「部分同步」假设不同——QuePaxa是纯异步共识,不依赖任何超时。

这两条不变式的数学证明可以在QuePaxa的原始论文中找到,这里不展开。重点是:在工程层面,这意味着QuePaxa可以在全球分布的副本之间,即使在网络延迟剧烈波动的情况下,仍然保证强一致性

2.3 Meerkat:QuePaxa 的生产实现

Meerkat是Cloudflare基于QuePaxa实现的全球一致性控制平面,目前已经在Cloudflare内部完成概念验证,最多时包含50个分布于全球各地的副本。

Meerkat提供三个核心服务:

  1. 共识日志(Consensus Log):所有已提交操作的全局有序记录
  2. 事务型键值存储(Transactional KV):基于共识日志的强一致性键值操作
  3. 租约管理(Lease Management):分布式锁和领导者选举的底层支持
Meerkat 架构概览:
┌─────────────────────────────────────────────────┐
│                  Cloudflare 全球网络             │
│                                                  │
│  ┌──────────┐  ┌──────────┐  ┌──────────┐       │
│  │ 副本 1   │  │ 副本 2   │  │ 副本 N   │       │
│  │ (亚洲)   │  │ (欧洲)   │  │ (美洲)   │       │
│  └────┬─────┘  └────┬─────┘  └────┬─────┘       │
│       │             │             │              │
│       └─────────────┼─────────────┘              │
│                     │ QuePaxa 共识               │
│              ┌──────┴──────┐                     │
│              │  共识日志    │                     │
│              │ (全局有序)   │                     │
│              └─────────────┘                     │
└─────────────────────────────────────────────────┘

2.3.1 性能代价:1-3次往返通信

Cloudflare团队坦诚地指出了Meerkat的性能代价:

特别是 QuePaxa,在初始提议者与多数派副本之间,需要进行一到三次往返通信(通常如此,尽管有时可能更多),才能就一项提议达成一致并将事件添加到日志中。

这意味着在跨区域的写操作中,每条消息需要额外的1-3次网络往返。考虑到Cloudflare边缘节点之间的典型延迟(20-100ms),一次写操作可能需要40-300ms。

这与Raft的性能对比:

指标Raft(局域网)Raft(WAN)QuePaxa/Meerkat
单次写入延迟5-20ms50-500ms40-300ms
领导者崩溃恢复时间50-200ms1-30s0(无Leader)
网络抖动容忍度很低
无领导者写入

关键洞察:在正常网络条件下,QuePaxa的延迟与Raft相当。但由于没有Leader依赖,QuePaxa在网络抖动时不会发生「系统不可用」的情况——这是Meerkat的核心价值主张。

2.4 Durable Objects 2.0:向量存储原语

DO 2.0在原有DO的基础上,新增了两项关键能力:

  1. 向量存储(Vector Store):在每个DO实例内部存储文本嵌入向量
  2. 全文搜索索引:基于向量相似度的语义搜索

这对于AI Agent的记忆系统意味着什么?让我们看一个具体的架构对比:

传统 AI Agent 记忆架构:
┌────────────────┐      ┌──────────────────┐      ┌────────────────┐
│   AI Agent     │ ───→ │  外部向量数据库   │ ───→ │   Pinecone/    │
│  (边缘运行)    │      │   (集中式部署)   │      │   Weaviate     │
└────────────────┘      └──────────────────┘      └────────────────┘
        │                         ↑                         │
        │                      100-300ms                    │
        └───────────────────────────────────────────────────┘
                              延迟瓶颈

DO 2.0 + Meerkat 记忆架构:
┌────────────────┐      ┌──────────────────┐      ┌────────────────┐
│   AI Agent     │ ───→ │  Durable Objects │ ───→ │    本地向量    │
│  (边缘运行)    │      │  (单实例运行)    │      │    存储        │
└────────────────┘      └──────────────────┘      └────────────────┘
        │                         ↑                         │
        │                       <2ms                         │
        │                         │                          │
        │                  ┌──────┴──────┐                   │
        │                  │   Meerkat   │ ← 强一致性保证    │
        │                  │ (全球协调)   │                   │
        │                  └──────────────┘                   │
        └───────────────────────────────────────────────────┘
                          低延迟 + 强一致性

向量存储的具体使用方式如下:

// Durable Objects 2.0 向量存储示例
export class AgentMemory {
  constructor(state, env) {
    this.state = state;
    this.env = env;
    // DO 2.0: 本地向量存储
    this.vectorStore = state.storage.vectorIndex("memory");
  }

  // 存储一段记忆(文本 + 向量嵌入)
  async remember(text, embedding, metadata = {}) {
    // 文本直接存储在 SQLite 中
    await this.state.storage.put(`text:${metadata.id}`, text);
    
    // 向量存储在向量索引中,用于语义搜索
    await this.vectorStore.insert(embedding, {
      id: metadata.id,
      timestamp: Date.now(),
      ...metadata
    });
    
    // 如果需要跨DO实例共享,使用Meerkat协调
    if (metadata.global) {
      await this.replicateToMeerkat(metadata.id, text, embedding);
    }
  }

  // 语义搜索记忆
  async recall(queryEmbedding, topK = 5) {
    const results = await this.vectorStore.search(queryEmbedding, { topK });
    
    // 从 SQLite 中还原完整文本
    const memories = await Promise.all(
      results.map(async (r) => {
        const text = await this.state.storage.get(`text:${r.id}`);
        return { ...r, text };
      })
    );
    
    return memories;
  }

  // 通过 Meerkat 复制到全局
  async replicateToMeerkat(id, text, embedding) {
    await this.env.MEERKAT.put(`memory:${id}`, JSON.stringify({
      text,
      embedding: Array.from(embedding),
      timestamp: Date.now()
    }), { durability: "strong" });
  }
}

三、架构分析:DO 2.0 + Meerkat 的技术定位

3.1 三层存储架构

Cloudflare在边缘计算领域实际上构建了一个三层存储架构,每层解决不同的问题:

┌──────────────────────────────────────────────────────┐
│                   边缘存储层                          │
│            Workers KV (最终一致性 KV)                 │
│  - 全球分布、高吞吐、最终一致                         │
│  - 适合配置、静态资源、CDN缓存                       │
│  - 延迟:读取 10-50ms                                │
├──────────────────────────────────────────────────────┤
│                  单实例存储层                          │
│         Durable Objects (强一致性单实例)               │
│  - 每个实例独立的状态存储                            │
│  - SQLite + DO 2.0向量存储                          │
│  - 适合实时协作、AI Agent记忆、游戏房间               │
│  - 延迟:< 2ms                                       │
├──────────────────────────────────────────────────────┤
│                  全球协调层                            │
│            Meerkat (强一致性全局协调)                 │
│  - 基于 QuePaxa 的跨数据中心共识                    │
│  - 事务型 KV、分布式锁、领导者选举                   │
│  - 适合:跨DO实例的分布式锁、全局序列号、配置同步    │
│  - 延迟:40-300ms(跨区域)                          │
└──────────────────────────────────────────────────────┘

理解这个分层模型至关重要:不是所有数据都需要放到Meerkat中。Meerkat的每次写入需要1-3次跨区域往返,成本高昂。正确的做法是:

  • 大多数状态:放在DO单实例内(最低延迟)
  • 需要跨实例共享但不要求强一致:使用Workers KV(高吞吐)
  • 必须强一致的关键数据:使用Meerkat(最低频率、最高价值)

3.2 DO 2.0 的向量存储技术细节

DO 2.0的向量存储底层实现值得深入了解,因为它直接决定了AI Agent记忆系统的性能上限。

向量存储的核心操作是最近邻搜索(ANN - Approximate Nearest Neighbor)。Cloudflare DO 2.0实现了一个基于**分层可导航小世界图(HNSW)**的向量索引:

# DO 2.0 向量存储底层:HNSW 索引原理(Python 伪代码)

class HNSWIndex:
    """
    Hierarchical Navigable Small World Graph
    DO 2.0 用于实现毫秒级语义搜索的核心数据结构
    """
    
    def __init__(self, max_m=16, ef_construction=200):
        self.max_m = max_m
        self.ef_construction = ef_construction
        self.layers = []  # 多层图,上层稀疏,下层稠密
        self.entrance = None  # 入口节点
    
    def insert(self, vector, metadata):
        # 第一步:从顶层到底层,选择每层的最近邻作为下一层入口
        level = self._select_random_level()
        
        # 第二步:在每一层执行贪婪搜索,找到最近邻
        for l in range(level, -1, -1):
            candidates = self._greedy_search(vector, self.ef_construction, layer=l)
            neighbors = self._search_neighborhood(candidates, self.max_m, l)
            self._add_connections(vector, neighbors, l)
    
    def search(self, query_vector, top_k=5):
        # 从顶层入口开始,逐层向下贪婪搜索
        for l in range(len(self.layers) - 1, -1, -1):
            current = self._greedy_search_in_layer(query_vector, ef=100, layer=l, start=current)
        
        # 底层执行k-NN搜索,返回top_k个最近邻
        return self._ksearch(query_vector, top_k)
    
    def _greedy_search(self, query, ef, layer):
        """贪婪搜索:在当前层迭代向最近邻方向移动"""
        visited = set()
        candidates = [(self._distance(query, self.entrance), self.entrance)]
        
        while len(visited) < len(candidates):
            _, current = heapq.heappop(candidates)
            if current in visited:
                continue
            visited.add(current)
            
            for neighbor in self.layers[layer].get(current, []):
                dist = self._distance(query, neighbor)
                if len(candidates) < ef or dist < candidates[0][0]:
                    heapq.heappush(candidates, (dist, neighbor))
        
        return [n for _, n in heapq.nsmallest(ef, candidates)]

HNSW的核心优势在于构建了一个多层高速公路网络:顶层是稀疏的「高速公路」,底层是稠密的「本地道路」。搜索时从顶层快速定位到一个大致区域,然后在底层精细搜索。

在DO 2.0的实际部署中,这个索引就运行在边缘节点的内存中,不需要任何网络往返。实测算例:100万个128维向量,搜索延迟在0.5-2ms之间。


四、代码实战:构建一个 AI Agent 记忆系统

4.1 场景设计:多 Agent 协作的记忆共享

让我们设计一个具体的应用场景:多Agent协作的记忆共享。多个AI Agent运行在不同的边缘节点,它们需要共享一个全局记忆,同时每个Agent也有自己的私有记忆。

┌─────────────────────────────────────────────────────┐
│                   Cloudflare 边缘网络               │
│                                                      │
│  Agent A (北京)          Agent B (上海)             │
│  ┌─────────────┐         ┌─────────────┐          │
│  │ 私有记忆 DO  │         │ 私有记忆 DO  │          │
│  │  (本地向量)  │         │  (本地向量)  │          │
│  └──────┬──────┘         └──────┬──────┘          │
│         │                        │                   │
│         └───────────┬────────────┘                   │
│                     │                                  │
│              ┌──────▼──────┐                          │
│              │  共享记忆 DO │                          │
│              │ (全局向量)   │                          │
│              └──────┬──────┘                          │
│                     │                                  │
│              ┌──────▼──────┐                          │
│              │   Meerkat   │ ← 全局强一致性           │
│              │  (协调层)    │                          │
│              └─────────────┘                          │
└─────────────────────────────────────────────────────┘

4.2 完整实现代码

4.2.1 私有记忆 Durable Object

// private-memory.ts — 私有记忆:每个Agent独立管理
// 延迟最低,不涉及跨区域通信

export class PrivateMemory {
  constructor(state, env) {
    this.state = state;
    this.env = env;
    
    // DO 2.0: 本地向量索引
    this.vectorIndex = state.storage.vectorIndex("private_vectors");
    
    // SQLite: 结构化数据存储
    this.db = state.storage.sql;
    
    // 初始化数据库表
    this.db.exec(`
      CREATE TABLE IF NOT EXISTS episodes (
        id TEXT PRIMARY KEY,
        content TEXT NOT NULL,
        importance INTEGER DEFAULT 1,
        tags TEXT,
        created_at INTEGER
      )
    `);
  }

  // 写入一段记忆
  async memorize(content, embedding, options = {}) {
    const id = options.id || crypto.randomUUID();
    const importance = options.importance || 1;
    const tags = options.tags || [];

    // 存储向量(用于语义搜索)
    await this.vectorIndex.insert(embedding, {
      id,
      importance,
      createdAt: Date.now()
    });

    // 存储结构化数据
    this.db.exec(
      `INSERT OR REPLACE INTO episodes VALUES (?, ?, ?, ?, ?)`,
      [id, content, importance, JSON.stringify(tags), Date.now()]
    );

    return { id, memory: content };
  }

  // 语义检索
  async recall(queryEmbedding, options = {}) {
    const topK = options.topK || 5;
    const minImportance = options.minImportance || 0;

    // DO 2.0: 向量相似度搜索,<2ms
    const candidates = await this.vectorIndex.search(queryEmbedding, { topK: topK * 2 });
    
    // 过滤低重要性记忆
    const filtered = candidates.filter(c => c.metadata.importance >= minImportance);
    
    // 还原完整内容
    const results = [];
    for (const c of filtered.slice(0, topK)) {
      const row = this.db.exec(
        `SELECT * FROM episodes WHERE id = ?`,
        [c.id]
      ).next();
      
      if (row) {
        results.push({
          id: c.id,
          content: row.content,
          relevance: 1 - c.distance,
          importance: row.importance,
          tags: JSON.parse(row.tags || "[]"),
          createdAt: row.created_at
        });
      }
    }

    return results;
  }

  // 基于关键词的记忆检索
  async recallByKeyword(keyword, topK = 5) {
    const rows = this.db.exec(
      `SELECT * FROM episodes 
       WHERE content LIKE ? 
       ORDER BY importance DESC, created_at DESC 
       LIMIT ?`,
      [`%${keyword}%`, topK]
    ).toArray();
    
    return rows.map(row => ({
      id: row.id,
      content: row.content,
      importance: row.importance,
      tags: JSON.parse(row.tags || "[]"),
      createdAt: row.created_at
    }));
  }

  // 遗忘(删除)记忆
  async forget(id) {
    await this.vectorIndex.removeById(id);
    this.db.exec(`DELETE FROM episodes WHERE id = ?`, [id]);
  }
}

4.2.2 共享记忆 Durable Object(使用 Meerkat 协调)

// shared-memory.ts — 共享记忆:跨Agent协调,使用Meerkat保证一致性
// 用于Agent之间需要共享的关键信息

export class SharedMemory {
  constructor(state, env) {
    this.state = state;
    this.env = env;
    this.vectorIndex = state.storage.vectorIndex("shared_vectors");
  }

  // 写入共享记忆(带Meerkat强一致性保证)
  async share(content, embedding, metadata = {}) {
    const id = metadata.id || crypto.randomUUID();
    const agentId = metadata.agentId || "unknown";

    // 第一步:在本地DO存储(最低延迟)
    await this.vectorIndex.insert(embedding, {
      id,
      agentId,
      timestamp: Date.now(),
      ...metadata
    });
    await this.state.storage.put(`content:${id}`, content);

    // 第二步:通过Meerkat发布到全局协调层
    await this.env.MEERKAT.put(`shared:${id}`, JSON.stringify({
      contentHash: await this._hash(content),
      agentId,
      timestamp: Date.now(),
      vector: Array.from(embedding)
    }), { 
      durability: "strong",
      metadata: { id, agentId }
    });

    return { id, status: "shared" };
  }

  // Meerkat 全局查询:获取所有Agent发布的共享记忆摘要
  async listSharedSummaries() {
    const summaries = [];
    let cursor = null;
    
    do {
      const result = await this.env.MEERKAT.list({
        prefix: "shared:",
        cursor,
        limit: 100
      });
      
      for (const item of result.keys) {
        const meta = JSON.parse(item.value);
        summaries.push({
          id: meta.id,
          agentId: meta.agentId,
          timestamp: meta.timestamp,
          contentHash: meta.contentHash
        });
      }
      
      cursor = result.cursor;
    } while (cursor);
    
    return summaries;
  }

  // 检索共享记忆
  async searchShared(queryEmbedding, options = {}) {
    const topK = options.topK || 5;
    const agentId = options.agentId;

    const validSummaries = await this.listSharedSummaries();
    
    const filteredIds = agentId 
      ? validSummaries.filter(s => s.agentId === agentId).map(s => s.id)
      : validSummaries.map(s => s.id);

    const candidates = await this.vectorIndex.search(queryEmbedding, { topK: topK * 2 });
    
    const results = [];
    for (const c of candidates) {
      if (filteredIds.includes(c.id)) {
        const content = await this.state.storage.get(`content:${c.id}`);
        if (content) {
          results.push({
            id: c.id,
            content,
            relevance: 1 - c.distance,
            metadata: c.metadata
          });
        }
      }
    }

    return results.slice(0, topK);
  }

  // 分布式锁:保护共享资源
  async acquireLock(resourceId, ttlMs = 30000) {
    const lockKey = `lock:${resourceId}`;
    const ownerId = this.env.MEERKAT.id;
    
    const acquired = await this.env.MEERKAT.put(
      lockKey, 
      JSON.stringify({ owner: ownerId, expires: Date.now() + ttlMs }),
      { 
        durability: "strong",
        onlyIfNotExists: true
      }
    );
    
    if (acquired) {
      this.state.storage.queueMicrotask(async () => {
        await this.releaseLock(resourceId, ownerId);
      }, ttlMs);
    }
    
    return acquired;
  }

  async releaseLock(resourceId, ownerId) {
    const lockKey = `lock:${resourceId}`;
    const lockData = await this.env.MEERKAT.get(lockKey);
    if (lockData && JSON.parse(lockData).owner === ownerId) {
      await this.env.MEERKAT.delete(lockKey);
    }
  }

  async _hash(data) {
    const encoder = new TextEncoder();
    const dataBuffer = encoder.encode(data);
    const hashBuffer = await crypto.subtle.digest("SHA-256", dataBuffer);
    const hashArray = Array.from(new Uint8Array(hashBuffer));
    return hashArray.map(b => b.toString(16).padStart(2, "0")).join("");
  }
}

4.2.3 Agent 入口:编排私有记忆 + 共享记忆

// agent.ts — AI Agent 主逻辑

export class AgentMemoryCoordinator {
  constructor(env) {
    this.env = env;
  }

  async getPrivateMemory(agentId) {
    const id = `private:${agentId}`;
    return this.env.privateMemories.get(id);
  }

  async getSharedMemory() {
    return this.env.sharedMemory.get("shared");
  }

  async think(agentId, userMessage, embedding) {
    const [privateMem, sharedMem] = await Promise.all([
      this.getPrivateMemory(agentId),
      this.getSharedMemory()
    ]);

    const [privateRecall, sharedRecall] = await Promise.all([
      privateMem.recall(embedding, { topK: 5, minImportance: 1 }),
      sharedMem.searchShared(embedding, { topK: 3 })
    ]);

    const context = {
      private: privateRecall.map(r => ({
        type: "private",
        content: r.content,
        relevance: r.relevance,
        importance: r.importance
      })),
      shared: sharedRecall.map(r => ({
        type: "shared",
        content: r.content,
        relevance: r.relevance,
        fromAgent: r.metadata.agentId
      }))
    };

    return context;
  }

  async memorize(agentId, content, embedding, options = {}) {
    const [privateMem, sharedMem] = await Promise.all([
      this.getPrivateMemory(agentId),
      this.getSharedMemory()
    ]);

    const privateResult = await privateMem.memorize(content, embedding, {
      importance: options.importance || 1,
      tags: options.tags || []
    });

    if (options.shared) {
      const lockAcquired = await sharedMem.acquireLock(`write:${agentId}`, 5000);
      
      if (lockAcquired) {
        try {
          await sharedMem.share(content, embedding, {
            agentId,
            importance: options.importance || 1
          });
        } finally {
          await sharedMem.releaseLock(`write:${agentId}`, sharedMem.env.MEERKAT.id);
        }
      }
    }

    return {
      privateId: privateResult.id,
      sharedId: options.shared ? privateResult.id : null
    };
  }
}

五、性能优化:生产环境踩坑清单

5.1 Durable Objects 相关

1. 避免过度创建 DO 实例
每个DO实例都会消耗资源,不使用时要及时关闭。对于短生命周期任务(<30秒),考虑使用普通Workers。

// ❌ 错误:每个请求都创建一个新的DO实例
const doInstance = await env.MY_DO.newUniqueId();

// ✅ 正确:使用确定性ID或从ID列表中复用
const stub = await env.MY_DO.getFromExistingId(deterministicId);

2. SQLite 事务批量提交
单个SQLite事务的提交开销约1-5ms,如果有大量写入,批量提交可以显著降低开销。

// ❌ 错误:每条记录一个事务
for (const record of records) {
  this.db.exec(`INSERT INTO events VALUES (?)`, [record]);
}

// ✅ 正确:批量事务
this.db.exec(`BEGIN TRANSACTION`);
for (const record of records) {
  this.db.exec(`INSERT INTO events VALUES (?)`, [record]);
}
this.db.exec(`COMMIT`);

3. 向量维度不是越大越好
HNSW的搜索性能与向量维度成反比。768维与1536维的语义搜索质量差异可能不超过5%,但搜索延迟可能相差2-3倍。

4. DO 实例的预热
冷启动时DO的首次向量搜索可能慢10-20ms。使用alarm定时触发预热操作。

5. DO 实例的全局唯一ID设计
使用合理的ID策略:type:region:sequence格式,如chatroom:us-east:1234

5.2 Meerkat 相关

6. 不要把所有数据都放在 Meerkat
Meerkat的每次写入需要1-3次跨区域往返,是所有Cloudflare存储服务中延迟最高的。只把必须强一致性的数据放进去。

7. 使用 Meerkat 的批量API
单次Meerkat写入需要完整的共识过程,批量写入可以摊薄这个成本。

8. 处理 Meerkat 的竞态条件
使用幂等操作和重试机制处理失败情况。

9. 合理设置租约TTL
建议初始TTL设为预期操作时间的10倍。

10. 监控 Meerkat 的往返延迟
跨区域的Meerkat延迟可能从40ms到300ms不等。在生产环境中部署监控,追踪P99延迟。

5.3 AI Agent 记忆系统架构

11. 记忆重要性分层

const importanceSchema = {
  10: "Agent核心能力/偏好(永久存储)",
  7:  "当前项目上下文(会话级)",
  5:  "用户明确要求记住的信息",
  3:  "Agent自动提取的中间结论",
  1:  "临时草稿/探索性思考"
};

const memories = await privateMem.recall(embedding, { minImportance: 3 });

12. 记忆压缩与摘要
定期对低重要性的记忆进行摘要压缩,释放存储空间。

13. 向量索引的增量更新策略
当向量索引中数据量超过10万条时,使用按时间分区策略:每个月一个DO实例。

14. 处理 DO 实例迁移
使用稳定的UUID而不是内部计数器作为记忆ID,便于迁移后恢复。

15. DO 2.0 向量存储的已知限制

  • 单个DO实例的向量存储上限约为500万条
  • HNSW索引不支持更新操作,只能删除后重新插入
  • 向量数据类型目前仅支持float32

六、总结与展望

6.1 技术价值回顾

Cloudflare在2026年8月的这两次更新(Cloudflare OS + Meerkat),实际上解决了一个困扰边缘计算多年的问题:如何在保持Serverless优势的同时,提供数据库级别的一致性保证

Durable Objects 2.0让边缘节点具备了「本地状态」能力,Meerkat则让跨节点的状态协调从「不可靠的最终一致」升级为「强一致」。这两个技术组合,加上向量存储原语,使得在边缘运行AI Agent记忆系统从理论走向了工程可行。

6.2 局限性诚实评估

Meerkat 尚未投入生产。Cloudflare在官方博文中明确表示「目前尚未投入生产环境」,虽然已完成50节点的概念验证。

性能代价不可忽视。QuePaxa的1-3次往返通信意味着,对于写密集型工作负载,Meerkat并不是最佳选择。

学习曲线陡峭。三层存储架构(Workers KV → DO → Meerkat)的选择本身就需要深入理解。

6.3 未来展望

  1. Meerkat 开源:HackerNews上有大量开发者请求Cloudflare开源QuePaxa的规范和Meerkat的实现。

  2. DO 2.0 正式版发布:向量存储目前仍是Beta功能,正式发布后将支持更多向量类型和更大规模索引。

  3. 与主流AI框架的集成:DO 2.0的向量存储可能直接成为AI推理管道的内置组件。

  4. 竞争格局:AWS的Local Zones、Google的Cloud Run、Fastly的Compute@Edge都在追赶。Cloudflare能否凭借DO + Meerkat的技术领先优势,在AI时代延续其在CDN时代的影响力,值得持续关注。


参考来源

  • Cloudflare博客:Meerkat & QuePaxa(2026年8月11日)
  • Cloudflare OS发布博文:Cloudflare CEO Matthew Prince(2026年8月4日)
  • InfoQ:Cloudflare 推出 Meerkat,实现全球强一致性协调(2026年8月)
  • GitHub:cloudflare/computer(Cloudflare Computer项目,2026年8月)
  • HackerNews社区讨论:Cloudflare OS & Meerkat相关帖子

推荐文章

HTML5的 input:file上传类型控制
2024-11-19 07:29:28 +0800 CST
25个实用的JavaScript单行代码片段
2024-11-18 04:59:49 +0800 CST
快速提升Vue3开发者的效率和界面
2025-05-11 23:37:03 +0800 CST
程序员茄子在线接单