用数据库思维设计Cloudflare Durable Objects:构建可信AI智能体记忆

用数据库设计思维构建Cloudflare Durable Objects记忆层,让AI智能体兼顾低延迟、强一致与隐私安全。
本文系统阐述了如何将Cloudflare Durable Objects(DO)作为AI智能体的可靠记忆层进行工程化设计。核心出发点是:DO必须像数据库一样被对待,否则将面临隐私泄露或记忆失效两种灾难。文章从隔离模型选择(Per-Session/Per-User/Per-Tenant)出发,详细说明了SQL持久化的边界——内存变量、未提交事务、临时表在休眠后均会消失,所有关键状态必须通过显式事务写入存储。DO的自动休眠机制使空闲时成本趋近于零,但要求架构不能依赖内存状态跨请求存活。文章还对比了DO与自建Qdrant/RAG方案的定位差异,提出短期上下文与长期知识库分层的混合架构,并介绍了基于MCP协议的elicitation模式实现人机协同。最后通过咨询预约智能体的参考架构,将上述原则串联为可落地的工程实践。
有状态的AI智能体正成为新一代应用的核心,而Cloudflare的Durable Objects(DO)为这类智能体提供了近乎理想的记忆层——边缘低延迟、强一致性、按需休眠。但原始素材提出了一个尖锐观点:只有当你像设计数据库一样对待Durable Objects时,它才能真正成为智能体可靠的记忆。 设计失误会导致两种灾难——要么智能体记住了不该记的东西(隐私噩梦),要么什么都记不住(毫无价值)。

隔离模型:Per-Session、Per-User 还是 Per-Tenant
在写第一行代码之前,必须先决定智能体的记忆作用域。这是整个架构中最关键的决策,没有之一。
Per-Session(会话级隔离) 让记忆只存活于单次对话。用户离开再回来时,面对的是一张白纸。它最适合面向公众的客服机器人等场景——你不希望历史对话污染后续用户的体验。实现方式是为每个连接创建新的DO实例,会话结束即销毁。风险最低,但成本较高,因为频繁创建和销毁实例。
Per-User(用户级隔离) 为每个用户保留跨会话的持久记忆。个人助手、辅导系统、生产力工具都依赖这种随时间积累的上下文。实现上通过用户ID(经哈希处理以保护隐私)派生DO ID,并在多次会话间复用同一个DO。风险属中等,关键在于严格防止用户之间的数据泄露。
Per-Tenant(租户级隔离) 在组织内部共享记忆,但在不同客户之间严格隔离,是企业软件和SaaS应用的典型模式。它由租户/组织ID派生DO ID,并在应用层校验访问权限。这种模式风险最高——一旦配置错误,就可能发生跨租户数据暴露。
素材中的提醒值得警惕:选错隔离模型,结果不是「它为什么还记得我上周的医疗问题」这种令人毛骨悚然的隐私侵犯,就是「它完全不记得我昨天说过的话」这种毫无用处的尴尬。
Durable Objects的隔离能力来自其独特的寻址机制:每个DO实例通过一个字符串ID(或名称)来标识,同一ID始终路由到同一个实例,不同ID则完全独立。这意味着隔离模型在代码层面的实现,本质上是如何派生DO的ID。Per-Session模式通常用crypto.randomUUID()生成随机ID;Per-User模式用HMAC-SHA256(userId, secret)生成确定性但不可逆的ID;Per-Tenant则用组织ID加上固定前缀派生。值得注意的是,Cloudflare保证同一ID的请求在全球范围内只路由到唯一一个DO实例(单写者语义),这是其强一致性的根基,也是它与分布式数据库最本质的区别。错误的ID派生策略(例如直接用明文用户邮箱)会同时带来隐私风险和潜在的ID冲突问题。
SQL 存储:什么会持久化,什么会消失
Agents SDK为Durable Objects内置了SQL接口,但它的行为与传统数据库存在微妙差异,理解这些差异能避免大量隐蔽的bug。
会被自动持久化的内容包括:显式提交的事务、事务块内成功写入的数据,以及未被回滚的事务外更改。而真正容易踩坑的,是那些不会持久化的东西:
- 内存中的JavaScript变量:存在
this或局部变量里的任何数据,在DO休眠时都会消失 - 未提交的事务:如果DO在
COMMIT之前被驱逐,更改会丢失 - 临时表:会话专属的临时表无法在休眠周期中存活
- 预处理语句:每次从休眠唤醒后都需要重新准备
可靠持久化的核心实践是:把所有修改状态的操作都包进显式事务,并且在异步函数返回前务必await提交或回滚。
class AgentDurableObject extends DurableObject {
constructor(state, env) {
super(state, env);
// DO 首次创建时初始化 schema
this.storage.sql.exec(`
CREATE TABLE IF NOT EXISTS conversations (
id INTEGER PRIMARY KEY AUTOINCREMENT,
session_id TEXT,
timestamp REAL,
role TEXT,
content TEXT
)
`);
}
async addMessage(sessionId, role, content) {
// 永远用事务保证持久化
await this.storage.sql.exec(`
BEGIN;
INSERT INTO conversations (session_id, timestamp, role, content)
VALUES ($1, $2, $3, $4);
COMMIT;
`, [sessionId, Date.now(), role, content]);
}
}
边缘记忆 vs Homelab:各自的擅长领域
把Cloudflare DO的记忆能力,与自建的Qdrant/RAG homelab方案做对比,能看出两者定位完全不同。
Cloudflare Workers + Durable Objects的优势在边缘体验:延迟是个位数毫秒级,DO内部强一致,全球自动分布,零服务器运维。代价是查询能力受限(SQL子集,不支持join和复杂事务),容量也受套餐限制。它最适合存储会话状态、用户偏好、对话历史。
自建的Home Lab(VPS + Qdrant + PostgreSQL)则是另一个极端:延迟通常50-200ms,一致性可调,需要手动分片和复制,并承担完整的服务器运维责任。但它拥有完整的SQL与向量检索能力,容量只受硬件限制,适合长期知识库、embeddings和复杂分析。
素材给出的最优解是混合架构:用Durable Objects保存短期上下文(当前对话、用户会话),用homelab/Qdrant保存长期知识(FAQ、文档、embeddings),并建立一套「记忆层级」——DO中沉淀出的重要洞见定期同步到长期存储,同时用TTL策略让过期的会话数据自动清理。这本质上是在模拟人类的短期记忆与长期记忆分工。
向量数据库(如Qdrant)与传统SQL数据库在检索范式上存在根本差异,理解这一点是规划混合架构的前提。SQL基于精确匹配和结构化查询,擅长「找出用户ID为X的所有预约记录」;向量数据库基于语义相似度检索,擅长「找出与当前问题语义最接近的历史知识片段」。在RAG(Retrieval-Augmented Generation)流程中,文档被切分为chunk并通过embedding模型转化为高维向量存储在Qdrant中,查询时将用户问题同样转为向量后做近邻搜索,从而召回语义相关的上下文注入LLM的提示词。这类操作对延迟不敏感(50-200ms可接受),但对计算和存储资源需求较高,因此不适合放在边缘的DO中执行。混合架构的分工逻辑由此清晰:DO处理「我在和这个用户聊什么」,Qdrant处理「关于这个话题有哪些相关知识」,两者通过智能体的编排层协同工作。
休眠机制:会睡觉的智能体才省钱
Durable Objects最强大却最常被误解的特性,是自动休眠。当一个DO在数秒内没有收到请求时,它会挂起执行,内存状态通过检查点保存到持久化存储,CPU占用降为零——空闲时间你几乎不付费。下一个请求到来时自动重载恢复,典型唤醒时间视状态大小在10-100ms之间。
成本意义重大:你主要为活跃计算时间和存储付费,空闲的DO实例几乎零成本。这与传统VPS无论用不用都7×24小时计费形成鲜明对比,让「随时可用但只在被使用时消耗资源」的智能体成为经济可行的选择。
但休眠也带来了设计约束的硬性要求:永远不要依赖内存状态在请求之间存活。所有关键状态必须在返回前写入持久化存储;任何内存缓存都要在初始化时从持久化存储重建。
class SmartAgentDO extends DurableObject {
constructor(state, env) {
super(state, env);
this.cache = new Map(); // 休眠时会丢失!
}
async initialize() {
// 每次唤醒都从持久化存储重建缓存
const rows = await this.storage.sql.exec(
"SELECT key, value FROM cache"
).results;
this.cache.clear();
for (const row of rows) {
this.cache.set(row.key, JSON.parse(row.value));
}
}
async setCachedValue(key, value) {
// 先持久化
await this.storage.sql.exec(`
INSERT OR REPLACE INTO cache (key, value)
VALUES ($1, $2)
`, [key, JSON.stringify(value)]);
// 再更新缓存
this.cache.set(key, value);
}
}
理解DO休眠的底层机制有助于写出更健壮的代码。Cloudflare使用的是「协作式检查点」模型:当DO判断自己即将进入空闲时,运行时会将所有已提交的SQL数据flush到持久化存储,然后挂起V8 isolate。这与操作系统的进程挂起不同——被挂起的不是进程快照,而只有存储层的数据。这意味着所有只存在于堆内存中的JavaScript对象(包括Map、Set、类实例的字段)在唤醒后都不复存在,必须从SQL存储重新构建。另一个常被忽视的细节是:Durable Objects的WebSocket连接在休眠期间会被Cloudflare的边缘节点「代持」——客户端保持连接,但DO本身处于挂起状态,只有新消息到达时才会唤醒DO来处理。这使得长连接的智能体交互既能保持实时性,又不会持续消耗计算资源。
人机协同:MCP 流程中的 Elicitation
有时候,智能体最聪明的行为是开口求助。素材强调,在高风险决策(金融交易、医疗建议)、意图模糊可能导致有害操作、需要智能体不具备的上下文知识、合规要求,或置信度低于预设阈值时,都应该引入人类输入。
基于MCP的elicitation模式流程清晰:智能体通过置信度打分或规则匹配检测到需要人类介入,便返回一个特殊的「elicitation」响应类型,而不是直接执行;客户端UI把这个请求呈现给用户,用户的回复通过标准MCP消息返回,智能体整合后继续处理。
async handleMessage(message) {
if (this.needsHumanApproval(message)) {
return {
type: "elicitation",
requestId: generateId(),
prompt: "Please confirm you want to proceed with this action:",
suggestedActions: ["Approve", "Reject", "Modify"]
};
}
return await this.processNormally(message);
}
设计有效的elicitation有几个要点:清晰说明需要什么决策以及为什么;提供建议动作或约束来引导用户;让响应尽量简单(是/否、多选、有界输入);为未完成的elicitation设置超时以免对话挂死;并记录所有elicitation事件用于审计和改进。
MCP(Model Context Protocol)是Anthropic主导推动的一项开放协议,旨在标准化AI模型与外部工具、数据源之间的交互方式。在MCP的架构中,「elicitation」是一种特殊的请求类型,允许服务端(即运行智能体的DO)在处理流程中途暂停并向客户端请求额外输入,而不是强制客户端预先提供所有信息。这与传统的表单提交或API调用范式有本质区别:elicitation是在一次有状态的会话流中动态插入的人工决策节点。对Durable Objects而言,实现elicitation需要特别注意:等待用户响应的期间DO可能进入休眠,因此requestId和当前处理上下文必须持久化到SQL存储,而不能仅靠内存中的Promise链来维持状态。这正是把DO当数据库设计的理念在人机协同场景中的直接体现。
反模式:智能体记忆中千万别踩的坑
素材列举了四个会稳定导致麻烦的反模式,极具实战参考价值。
把原始LLM输出当作事实存储:智能体幻觉出一条金融法规,当作事实存下来,再用这个「事实」去给几十个用户建议。修复方法是只把用户确认过或经外部验证的信息作为永久知识存储。
无节制的上下文累积:对话历史无限增长而不做摘要或裁剪。超过100条消息后,智能体会变慢、变贵,并丢失对相关上下文的关注。应实施滑动窗口保留或定期摘要。
通过共享状态造成交叉污染:用一个DO服务多个用户/租户却没有合理隔离,导致用户A的医疗询问影响了用户B的股票建议。必须用运行时校验严格执行你选定的隔离模型。
忽视数据新鲜度:使用过时信息而不检查有效期或来源。智能体可能基于已被废止的法规给出法律建议。修复方法是为所有存储的知识打时间戳,并实现自动过期或定期刷新。
参考架构:咨询预约智能体
素材用一个预约咨询智能体把上述原则串联落地。该智能体的职责是帮助用户检查空闲时段、推荐时间并完成预约。
其数据模型包含三张表:availability(定期从日历同步的可用时段)、conversation(对话上下文)、preferences(随时间学习到的用户偏好)。关键设计决策体现了全文思路:隔离模型选用Per-User,让偏好跨会话延续;所有预约事务采用显式事务保证持久化;高频访问的时段数据以内存缓存加数据库备份;唤醒后从存储重建可用性缓存;对非正常时段的预约触发人工确认;可用性每60秒与日历同步一次;并对每个用户做限流和并发预约上限控制。
一次典型交互是:用户说「下周我想预约一次30分钟咨询」,智能体读取用户偏好、查询匹配时段、给出2-3个建议时间;用户选择后,若时间在正常工作时间之外则触发确认;随后用事务写入预约、更新偏好,并返回带日历链接的确认。这个模式同时做到了记住偏好(有用)、不在用户间泄露(安全)、休眠下保持一致(可靠)、知道何时求助(稳妥)。
生产落地建议
开发阶段用wrangler dev配合本地SQLite数据库镜像DO的schema,并灌入真实测试数据提前暴露边界情况。测试不仅要覆盖happy path,还要验证:DO在事务中途休眠会怎样?对同一DO的并发请求如何处理?接近存储上限时行为如何?状态损坏时能否恢复?
生产监控应追踪DO唤醒时间分布(警惕状态膨胀导致的爬升)、事务成败率、内存使用趋势、按类型分类的错误率,以及内存缓存命中率。上线采用渐进式发布:先导流10%到新实现,对比关键指标,逐步放量,并随时保留瞬时回滚能力。
正如素材的结语所言:世界上最精巧的智能体,如果它的记忆无法被信任,也毫无价值。把你的Durable Objects当作珍贵且需严格管控的资源来对待——用户会以持续的信任和参与来回报你。
相关推荐

WorkBuddy全流程教程:从零搭建AI办公自动化智能体
WorkBuddy 全流程教程精讲:从下载安装、三种工作模式到多模态绘本视频实战,再到专家团搭建智能体与工作流配置,帮你用低成本 AI Agent 实现办公自动化。

WorkBuddy智能体全攻略:7天从入门到自动化实战
基于B站256集WorkBuddy全套精讲课程,系统拆解腾讯AI办公工作台的核心能力:Agent原理、Skill封装、连接器跨平台、定时自动化、知识库与零代码开发,附学习路径建议。

8个AI账号翻车7个:我用AI干活的真实复盘
B站8个AI运营账号仅1个跑通。本文拆解"演AI"与"用AI干活"的本质区别,分析5条视频217万播放背后的产能控制、评论区运营和交付节奏策略。