@mastra/cloudflare 深度解析:基于 Cloudflare KV 与 Durable Objects 构建 Mastra 无服务器存储层

发布时间:2026/9/15 17:57:59
@mastra/cloudflare 深度解析:基于 Cloudflare KV 与 Durable Objects 构建 Mastra 无服务器存储层 mastra/cloudflare 深度解析基于 Cloudflare KV 与 Durable Objects 构建 Mastra 无服务器存储层【免费下载链接】mastraMastra is the modern TypeScript framework for AI-powered applications and agents.项目地址: https://gitcode.com/GitHub_Trending/ma/mastraMastra 是面向 AI 应用与 Agent 的现代 TypeScript 框架而mastra/cloudflare是它官方的 Cloudflare 存储适配器为线程threads、消息messages、工作流快照workflow snapshots与评估结果scores/evals提供可扩展、无服务器化的持久化能力。本文以该包的完整变更历史CHANGELOG.md为主线结合 README.md 与源码实现系统讲解如何接入 Cloudflare KVREST API 与 Workers Binding 两种模式和 Durable ObjectsSqlStorage并剖析其领域化存储架构、关键正确性修复与 API 演进帮助你规避线上数据丢失与版本兼容陷阱。一、包定位与安装前提mastra/cloudflare是 Mastra 存储Store生态中的 Cloudflare 提供方其定位从 package.json 的描述可见一斑Cloudflare provider for Mastra - includes db storage capabilities即它并非只做 KV 键值读写而是把数据库式的多表存储能力线程、消息、工作流快照、评分映射到 Cloudflare 的无服务器基础设施之上。安装与运行前提如下npm install mastra/cloudflare运行时要求Node.js 22.13.0见 package.json 的engines字段peerDependenciesmastra/core 1.53.0-0 2.0.0-0同时需要cloudflare/workers-types ^4.20240919.0底层 SDK依赖cloudflare^5.2.0官方 TypeScript SDK。其中mastra/core的 peer 版本下限1.53.0并非随意设定适配器在初始化时需要mastra/core/storage子路径导出storageMessageMatchesMetadataFilter等工具函数若 core 版本过低包管理器不会在安装期报错却会在导入期抛出SyntaxError: The requested module mastra/core/storage does not provide an export named storageMessageMatchesMetadataFilter见 CHANGELOG 1.6.1 的修复记录。因此升级mastra/cloudflare时务必同步升级mastra/core。二、两种后端、三条连接路径从源码入口 src/index.ts 与 src/kv/index.ts、src/do/index.ts 可以看出该包提供两个存储实现共三种接入方式后端主类别名已废弃连接方式Cloudflare KVCloudflareKVStorageCloudflareStoreREST APIaccountIdapiTokenCloudflare KVCloudflareKVStorageCloudflareStoreWorkers KV BindingsbindingsDurable ObjectsCloudflareDOStorageDOStoreDO 内置的同步SqlStoragectx.storage.sql1. KV REST API适合 Node.js / serverless 混合环境这是 README.md 中的标准用法也是对外文档主推的示例import { CloudflareStore } from mastra/cloudflare; const store new CloudflareStore({ accountId: process.env.CLOUDFLARE_ACCOUNT_ID!, apiToken: process.env.CLOUDFLARE_API_TOKEN!, namespacePrefix: myapp_, }); // 保存一个线程 await store.saveThread({ thread: { id: thread-123, resourceId: resource-456, title: My Thread, metadata: { key: value }, createdAt: new Date(), }, }); // 追加消息 await store.saveMessages({ messages: [ { id: msg-1, threadId: thread-123, content: Hello Cloudflare!, role: user, createdAt: new Date(), }, ], }); // 查询消息 const messages await store.listMessages({ threadId: thread-123 });2. KV Workers Bindings适合 Cloudflare Workers 原生环境在 Workers 中更高效的做法是直接使用env上的 KV namespace 绑定省去每次请求的网络往返。构造时传入bindings与可选的keyPrefixsrc/kv/index.ts 会校验threads、messages、workflow_snapshot、scorers四张表的绑定是否齐全缺失即抛错import { CloudflareKVStorage } from mastra/cloudflare; const storage new CloudflareKVStorage({ id: my-store, bindings: { threads: env.THREADS_KV, messages: env.MESSAGES_KV, workflow_snapshot: env.WORKFLOW_SNAPSHOT_KV, scorers: env.SCORERS_KV, }, keyPrefix: app_, });3. Durable Objects SqlStorage需要事务与并发一致性时Durable Objects 自带同步的 SQLite 式SqlStorage适合对一致性要求更高的场景。CHANGELOG 1.3.0 明确记录Adds a new Durable Objects-based storage implementation alongside the existing KV store. Includes SQL-backed persistence via DOs SQLite storage, batch operations, and proper table/column validation. 其典型接入方式src/do/index.ts 内注释示例import { DurableObject } from cloudflare:workers; import { CloudflareDOStorage } from mastra/cloudflare/do; class AgentDurableObject extends DurableObjectEnv { private storage: CloudflareDOStorage; constructor(ctx: DurableObjectState, env: Env) { super(ctx, env); this.storage new CloudflareDOStorage({ sql: ctx.storage.sql, tablePrefix: mastra_, }); } async run() { const memory await this.storage.getStore(memory); await memory?.saveThread({ thread: { id: thread-1, /* ... */ } }); } }注意 DO 模式要求tablePrefix符合^[A-Za-z_][A-Za-z0-9_]*$的命名规范源码中有正则校验且 KV 模式通过namespacePrefix/keyPrefix实现多实例数据隔离。配置参数速查表汇总自 src/kv/storage/types.ts 与 src/do/index.ts参数适用模式类型说明id全部string存储实例标识必填accountIdKV RESTstringCloudflare 账户 IDREST 模式必填apiTokenKV RESTstring具备 KV 权限的 API TokenREST 模式必填namespacePrefixKV RESTstringKV namespace 名前缀生产环境推荐用于实例间数据隔离bindingsKV BindingRecordTABLE_NAMES, KVNamespaceWorkers 环境注入的 KV 绑定对象keyPrefixKV Bindingstringnamespace 内 key 的可选前缀sqlDOSqlStorage来自ctx.storage.sql的 DO 存储句柄必填tablePrefixDOstring表名前缀需符合标识符命名规范disableInit全部boolean为true时关闭运行期自动建表/迁移详见第六节三、领域化Domain存储架构一切走 getStore()这是理解mastra/cloudflare的关键设计。从 CHANGELOG 1.0.0 时代起Mastra 存储层完成了从直通方法passthrough到领域商店domain store的重构MastraStorage基类上的getThreadById、persistWorkflowSnapshot、createSpan等方法被移除统一改为通过storage.getStore(xxx)获取对应领域实例。CloudflareKVStorage内部装配了三个领域见 src/kv/index.ts 的stores属性this.stores { workflows, // WorkflowsStorageCloudflare工作流快照/运行状态 memory, // MemoryStorageCloudflare线程与消息 scores, // ScoresStorageCloudflare评估评分 };而CloudflareDOStorage在此基础上多实现了backgroundTasks领域BackgroundTasksStorageDO因为 Durable Objects 的SqlStorage支持原子条件更新compare-and-set这是 KV 不具备的能力——详见下文第五节。新版用法迁移示意来自 CHANGELOG 1.0.0-beta// 旧写法已移除 const thread await storage.getThreadById({ threadId }); await storage.persistWorkflowSnapshot({ workflowName, runId, snapshot }); // 新写法 const memory await storage.getStore(memory); const thread await memory?.getThreadById({ threadId }); const workflows await storage.getStore(workflows); await workflows?.persistWorkflowSnapshot({ workflowName, runId, snapshot });这一架构还支持复合存储可以把 memory 领域放到一个数据库、把 scores 领域放到另一个数据库通过MastraCompositeStore组装CHANGELOG 1.0.0 的 StorageDomain 基类引入后改名MastraCompositeStore并保留废弃别名。各领域类MemoryStorageCloudflare、ScoresStorageCloudflare、WorkflowsStorageCloudflare、BackgroundTasksStorageDO也都独立导出可直接用于复合存储组合。四、KV 数据布局与关键正确性修复Cloudflare KV 是最终一致的键值存储把它抽象成多表数据库需要解决分页、元数据合法性、孤儿数据等工程问题。CHANGELOG 中 1.6.3 一节记录了三个关键修复值得任何在生产中使用 KV 的团队关注1. 超过 1000 个 key 后静默丢数据Fixed Cloudflare KV storage silently dropping data once a table grows past 1000 keys. Listing threads, deleting threads, and clearing tables now read every page of keys from Cloudflare instead of only the first one...Cloudflare KV 的list()接口默认每次只返回一页 key一页约 1000 条旧实现只读了第一页导致大存储中的线程列不出来、删不掉、清空后留下孤儿消息。修复后列线程、删线程、清表等操作会逐页遍历全部 key源码测试见 src/kv/storage/db/index.test.ts保证大数据量下的行为正确。2. REST 写入的 JSON 元数据校验Also fixed writes through the REST API, which Cloudflare rejected with a metadata must be valid json error.KV 的metadata字段要求严格的 JSON 结构旧实现对消息/线程中的 metadata 序列化不严格REST 写入会被 Cloudflare 拒绝。该修复确保所有经 REST 写入的值都满足metadata must be valid json约束。3. 后台任务存储从 KV 中移除CAS 语义缺失Enforce atomic conditional background task state updates so cancellation cannot be overwritten during dispatch. Background task storage is no longer exposed by Cloudflare KV or ClickHouse, which cannot provide the required compare-and-set semantics.后台任务的状态推进如调度中→已取消要求原子条件更新否则并发分发时取消操作会被覆盖。Cloudflare KV 与 ClickHouse 都无法提供 compare-and-set 语义因此mastra/cloudflare的 KV 实现不再暴露backgroundTasks领域只有 Durable Objects 模式提供见 src/do/index.ts。选择后端时应先确认你的工作负载是否依赖后台任务存储——这是 KV 与 DO 模式最本质的能力分界线。五、记忆Memory相关的行为与修复1. 线程标题不被覆盖1.6.2Fixed generated thread titles being clobbered during a turn ...updateThreadrequired bothtitleandmetadata, so callers that only needed to change metadata had to read the thread and pass its title back. When title generation finished between that read and the write, the freshly generated title was overwritten with the stale one.旧版updateThread强制要求同时传title与metadata。当工作记忆、观测记忆、消息持久化等调用方只想改 metadata 时必须先读再写而读与写之间如果刚好有线程标题生成完成新标题就会被旧值覆盖。修复后title与metadata相互独立可选——省略哪个字段就保持哪一列不动。测试见 src/do/storage/domains/memory/update-thread.test.ts。配套地存储适配器开始声明支持部分线程更新以便新版本mastra/memory在旧版存储包上回填已有标题保证混合版本部署不崩。2. 资源边界隔离1.6.2 / 1.3.3getThreadById尊重可选的resourceId当线程属于其他资源时返回null避免越权读取1.3.3Fixed resource-scoped message includes across storage adapters so included context cannot cross resource boundaries1.6.2消息包含include上下文的查询不会再跨资源边界。3. 元数据精确过滤1.6.0消息历史查询支持精确的 metadata 过滤多字段为 AND 语义支持字符串、有限数字、布尔值与nullconst messages await memory.recall({ threadId: thread-1, filter: { metadata: { status: done, priority: high, }, }, });对应测试见 src/do/storage/domains/memory/metadata-filter.test.ts。4. 语义召回与消息排序1.2.3 优化了大规模消息历史的语义召回——Semantic recall no longer loads entire threads when only the recalled messages are needed不再把整个线程加载进内存。1.0.0 时代还修复了listMessages在语义召回include参数场景下必须按createdAt排序而非存储顺序的问题保证会话历史时间序正确。六、工作流持久化workflowDefinitions 与快照语义CHANGELOG 1.6.1 记录了workflowDefinitions存储领域的落地POST /stored/workflows与Mastra.addStoredWorkflow之前只能依赖 core 的内存存储持久化适配器返回undefined并抛错。现在该领域支持upsert/get/list/deleteconst workflowDefinitions await storage.getStore(workflowDefinitions); if (!workflowDefinitions) { throw new Error(This storage adapter does not support the workflowDefinitions domain); } await workflowDefinitions.upsert({ id: greeting-workflow, inputSchema: { type: object, properties: { name: { type: string } }, required: [name] }, outputSchema: { type: object, properties: { text: { type: string } }, required: [text] }, graph: [{ type: agent, id: greet, agentId: greeter-agent }], }); const { definitions, total } await workflowDefinitions.list({ status: active }); const definition await workflowDefinitions.get(greeting-workflow); await workflowDefinitions.delete(greeting-workflow);mastra/cloudflare在此版本中为共享表常量注册了mastra_workflow_definitions表映射register the newmastra_workflow_definitionstable in their table/type maps so shared table constants stay exhaustive但目前尚未实现该领域的读写实现调用前需用上面的守卫判断。与此相关1.2.2 明确说明 KV 后端不支持并发工作流更新updateWorkflowResults与updateWorkflowState会抛出 not-implemented 错误1.6.1 还引入了deleteWorkflowRunById(runId)删除运行记录的 API。七、存储 API 演进与迁移要点CHANGELOG 跨版本记录了多次 API 破坏性演进升级时需逐一核对1.getMessages()→listMessages()1.0.0getMessages被移除统一走带分页的listMessages// 旧写法 const messages await storage.getMessages({ threadId: thread-1 }); // 新写法 const result await storage.listMessages({ threadId: thread-1, page: 0, perPage: 50, }); // result.messages / result.total / result.hasMore默认按createdAt升序旧消息在前需要倒序时传orderBy: { field: createdAt, direction: DESC }。getMessagesPaginated()一并移除且listMessages对空/纯空白threadId会直接抛错而非返回空结果。2.offset/limit→page/perPage1.0.0所有存储与记忆分页接口改为 0 起始的pageperPage并增加了对负数 page、非法 perPage负数、0、false的校验。perPage: false表示一次取回全部记录HTTP 层对应?perPagefalse。3. 类型与命名清理MastraMessageV2→MastraDBMessage所有返回 db 消息的方法统一为{ messages: MastraDBMessage[] }消息查询输入统一为StorageListMessagesInputgetThreadsByResourceId→listThreadsByResourceId分页改为page/perPage排序用嵌套orderBy: { field, direction }存储领域方法统一为泛型 APIcreateAgent→create、getAgentById→getById、deleteAgent→delete1.1.1错误 ID 全局标准化为MASTRA_STORAGE_{STORE}_{OPERATION}_{STATUS}1.0.0。八、评估、收藏与工具连接等扩展领域除核心三领域外适配器逐步跟进 core 层新增的存储能力评分多租户1.5.1saveScore支持可选的organizationId/projectIdlistScoresBy*接受filters限定租户范围projectId标识项目范围与代表 Agent 记忆资源的resourceId相互独立收藏1.4.0通过storage.getStore(favorites)对存储的 Agent / Skill 做收藏与可见性过滤ToolProvider 连接1.4.1tool_provider_connections领域可持久化每个 Agent 的 OAuth 连接配置读/写/创建均完整往返MCP 服务器表1.2.1MCP server 配置可随 Agent 与工作流一起持久化CRUD数据集与实验1.2.0数据集 CRUD、条目 SCD-2 版本化、实验运行与 scorer 结果存储要求 core 1.4.0后台任务领域1.3.2 引入、1.6.3 从 KV/ClickHouse 撤回仅 DO 模式保留。九、运维实践disableInit、前缀隔离与版本管理1.disableInit分离迁移与运行所有存储适配器含 Cloudflare支持disableInitCHANGELOG 1.0.0 引入在 CI/CD 阶段用高权限执行await storage.init()完成建表/迁移运行期应用则以disableInit: true启动避免运行时 Schema 变更带来权限与安全风险。DO 模式中该选项同样生效见 src/do/index.ts。2. 前缀隔离KV REST 模式namespacePrefix为每个环境dev/staging/prod使用独立 namespaceKV Binding 模式keyPrefix在同一 namespace 内做键级隔离DO 模式tablePrefix做表级隔离。3. 版本与依赖纪律保持mastra/core 1.53.0-0避免导入期SyntaxErrorKV 模式不要依赖backgroundTasks领域与并发工作流状态更新大数据量线程/消息表依赖 1.6.3 及以上的分页修复低于该版本必须升级混合部署时确认存储包与mastra/memory版本兼容部分线程更新声明机制自 1.6.2 起。十、测试与验证仓库内置了覆盖两种后端的测试资产是理解行为契约的最佳入口KV 数据库层src/kv/storage/db/index.test.tsKV 接入方式验证binding-api.test.tsWorkers Binding、rest-api.test.tsREST APIKV 日志验证logger-verification.test.tsDO 记忆行为metadata-filter.test.ts、update-thread.test.ts本地可通过pnpm --filter mastra/cloudflare test等价于包内vitest run运行这些测试见 package.json 的 scripts。结语mastra/cloudflare用一套统一的领域化存储 API把 Cloudflare KV 的简单可扩展与 Durable Objects 的强一致能力同时纳入 Mastra 生态轻量、追求极致 serverless 的会话/评估存储选 KVREST 或 Binding 均可需要后台任务、事务与并发正确性的场景选 DO。其变更历史中反复出现的分页遍历全部 keymetadata 必须是合法 JSONCAS 语义缺失即不暴露领域等修复正是无服务器键值存储落地为关系型抽象时的工程共识。结合源码与测试继续深挖你可以在选型、升级与排障时少踩大量暗坑。【免费下载链接】mastraMastra is the modern TypeScript framework for AI-powered applications and agents.项目地址: https://gitcode.com/GitHub_Trending/ma/mastra创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考