ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

在 Cloudflare Workers 上为 VoltAgent 接入 D1 持久化:@voltagent/cloudflare-d1 存储适配器实战指南

在 Cloudflare Workers 上为 VoltAgent 接入 D1 持久化:@voltagent/cloudflare-d1 存储适配器实战指南 人工智能AI AgentAgent 框架后端多智能体RAG工具调用Agent 记忆【免费下载链接】voltagentAI Agent Engineering Platform built on an Open Source TypeScript AI Agent Framework项目地址https://gitcode.com/gh_mirrors/vo/voltagent点击查看免费下载本篇技术指南围绕 VoltAgent 官方存储适配器voltagent/cloudflare-d1展开讲解如何将 VoltAgent Memory V2 的会话、消息、工作记忆与工作流状态持久化到 Cloudflare D1基于 SQLite 的边缘数据库并完整覆盖安装、Worker 集成、全部配置项以及底层表结构与重试机制。读完本文你将掌握在 Cloudflare Workers 无服务器环境中为 VoltAgent Agent 搭建 D1 记忆存储的完整方案并理解适配器源码级别的实现原理。背景VoltAgent Memory V2 与存储适配器VoltAgent 是构建在 TypeScript AI Agent Framework 之上的 Agent 工程平台Memory是其记忆模块负责管理对话历史、会话元数据、工作记忆working memory以及工作流运行状态。Memory本身不关心数据落在哪里而是通过StorageAdapter接口定义于 packages/core/src/memory/types.ts抽象持久化层该接口声明了消息读写、会话增删改查、工作记忆读写以及工作流状态持久化等一组能力。voltagent/cloudflare-d1正是这套抽象在 Cloudflare 生态下的官方实现它把上述能力翻译为针对 D1Cloudflare 的 serverless SQLite的 SQL 语句让开发者可以在 Worker 里直接获得与内存适配器、PostgreSQL、LibSQL、Supabase 等适配器完全一致的语义实现一次编写多端持久化。安装voltagent/cloudflare-d1作为独立的 npm 包发布使用 pnpm 安装pnpm add voltagent/cloudflare-d1从 package.json 可以看到它声明了三个 peerDependenciesvoltagent/core^2.0.0、voltagent/logger^2.0.0与ai^6.0.0说明它依赖 VoltAgent 核心的类型与日志体系以及 AI SDK 的UIMessage消息模型同时依赖cloudflare/workers-types提供D1Database类型。包采用双格式导出ESM 的dist/index.mjs与 CJS 的dist/index.js并提供了dist/index.d.mts/dist/index.d.ts类型声明可以直接接入 tsup 等构建链。包入口非常精简src/index.ts 只导出两样东西export { D1MemoryAdapter } from ./memory-adapter; export type { D1MemoryOptions } from ./memory-adapter;快速开始在 Worker 中接入 D1 记忆存储README 给出了一个完整的 Cloudflare Worker 示例它把D1MemoryAdapter作为Memory的存储层再把它挂载到Agent上最终通过serverlessHono()输出 Worker 入口。核心代码与 packages/cloudflare-d1/README.md 保持一致import { openai } from ai-sdk/openai; import { Agent, Memory, VoltAgent } from voltagent/core; import { D1MemoryAdapter } from voltagent/cloudflare-d1; import { serverlessHono } from voltagent/serverless-hono; import type { D1Database } from cloudflare/workers-types; import { weatherTool } from ./tools; type Env { DB: D1Database; OPENAI_API_KEY: string; }; const createWorker (env: Env) { const memory new Memory({ storage: new D1MemoryAdapter({ binding: env.DB, tablePrefix: voltagent_memory, }), }); const agent new Agent({ name: serverless-assistant, instructions: Answer user questions quickly., model: openai(gpt-4o-mini), tools: [weatherTool], memory, }); const voltAgent new VoltAgent({ agents: { agent }, serverless: serverlessHono(), }); return voltAgent.serverless().toCloudflareWorker(); }; let cached: ReturnTypetypeof createWorker | undefined; export default { fetch: (request: Request, env: Env, ctx: ExecutionContext) { if (!cached) { cached createWorker(env); } return cached.fetch(request, env, ctx); }, };这个示例包含了几个值得注意的工程细节Worker 全局复用实例createWorker的结果被缓存在模块级变量cached中只在首次请求时构建。这是因为 Worker 的模块级状态在无冷启动期间会被复用而D1MemoryAdapter构造时就会触发建表初始化见下文懒初始化避免每个请求都重复建表是更优做法。通过serverlessHono输出 WorkertoCloudflareWorker()返回标准的fetchhandler 形态与env/ctx解构相匹配。仓库中的 examples/with-cloudflare-workers/src/index.ts 展示了同一模式的简化版本。TypeScript 类型Env中的DB: D1Database需要来自cloudflare/workers-types这正是适配器构造参数binding所期望的类型。配套的 wrangler.toml 配置要让上面的代码真正跑起来还需要在 Worker 的wrangler.toml中声明 D1 数据库绑定。仓库 examples/with-cloudflare-workers/wrangler.toml 给出了完整的示例name voltagent-worker main dist/index.js compatibility_date 2025-01-01 account_id workers_dev true compatibility_flags [ nodejs_compat, nodejs_compat_populate_process_env, no_handle_cross_request_promise_resolution, ] [[d1_databases]] binding DB database_name my-db-name database_id b4712095-3b98-4834-ad03-edf70aef9eb3其中binding DB必须与代码中env.DB的字段名一致database_id需要替换为你自己通过wrangler d1 create database-name创建的真实数据库 ID。若在本地开发还需要用wrangler d1 migrations apply或wrangler dev --local保证本地 D1 可用。注意适配器本身会在运行时自动建表因此你不需要手工编写 SQL migration 文件但需要在 Cloudflare 控制台或通过 wrangler 提前创建好 D1 数据库资源本身。配置项全解D1MemoryAdapter的构造参数定义在 packages/cloudflare-d1/src/memory-adapter.ts 的D1MemoryOptions接口中README 的 Options 一节列出了全部字段。下面结合源码补充默认值与底层影响参数是否必填默认值作用binding必填—Worker 环境中的 Cloudflare D1 绑定env.DB。构造时若缺失会直接抛出Error(D1MemoryAdapter requires a D1 binding)tablePrefix可选voltagent_memory所有表名的前缀用于在同一 D1 数据库中隔离多套记忆数据或避免与既有表冲突maxRetries可选3针对 busy/locked 数据库操作的最大重试次数retryDelayMs可选100指数退避的初始延迟毫秒debug可选false是否开启调试级日志logger可选自动降级链自定义日志实例各参数在源码中的实际效果binding必填构造函数首先校验它见 memory-adapter.ts未传入时立即抛错避免在后续 SQL 调用中产生难以排查的运行时错误。tablePrefix它直接参与所有 SQL 的拼装。初始化时适配器会基于前缀派生出五张表名${prefix}_users${prefix}_conversations${prefix}_messages${prefix}_workflow_states${prefix}_steps以及多张索引见 initializeSchema。也就是说同一个 D1 数据库里可以同时容纳多套 VoltAgent 应用只要它们使用不同的前缀。maxRetries与retryDelayMs共同控制executeWithRetry的行为见 memory-adapter.ts。D1 底层是 SQLite在并发写入时可能出现SQLITE_BUSY或database is locked错误该封装会在检测到这类错误码/错误消息时以retryDelayMs * 2 ** attempt的指数退避策略重试最多尝试maxRetries次非锁错误则立即抛出不做无谓重试。debug与logger日志解析遵循一个优先级链——先使用options.logger否则尝试从AgentRegistry.getInstance().getGlobalLogger()获取全局日志器再退化为createPinoLogger({ name: cloudflare-d1-memory, level: options.debug ? debug : info })来自voltagent/logger。也就是说debug: true的效果是让兜底的 pino logger 以debug级别输出方便在wrangler tail中观察建表、重试等内部行为。底层原理一自动建表与五张核心表适配器在首次使用时构造时异步触发见ensureInitialized会自动执行CREATE TABLE IF NOT EXISTS因此无需用户手工维护 schema。完整的 DDL 位于 initializeSchema五张表的职责如下users表仅id、metadata与时间戳字段主要用于存储用户级工作记忆working memory。当以scope: user调用工作记忆读写时数据会落到metadata.workingMemory字段中。conversations表保存会话的resource_id、user_id、title、metadata与时间戳。会话级工作记忆scope: conversation就存放在该表的metadata.workingMemory中。表上建有user_id与resource_id两个索引支撑按用户、按资源分页拉取会话列表。messages表以(conversation_id, message_id)为联合主键存储role、partsAI SDKUIMessage的多模态内容部分以 JSON 序列化、metadata与format_version当前固定为 2并通过外键关联conversations启用ON DELETE CASCADE。索引覆盖conversation_id与created_at便于按会话取消息并支持before/after时间游标。workflow_states表存储工作流执行状态字段极其丰富——workflow_id、status、input、context、workflow_state、suspension、events、output、cancellation、user_id、conversation_id、metadata等。这些字段在 2.x 系列版本中陆续通过迁移补齐详见 CHANGELOG.md用于支持工作流挂起/恢复、多租户元数据过滤等能力。索引覆盖workflow_id与status其中status索引直接服务于查询某工作流所有 suspended 运行这类高频操作。steps表存储对话中的逐步执行记录agent 调用、工具调用、子代理调用等包含agent_id、agent_name、operation_id、step_index、type、content、arguments、result、usage、sub_agent_id等字段。索引为(conversation_id, step_index)与(conversation_id, operation_id)分别支撑按顺序回放步骤与按操作追溯步骤。底层原理二懒初始化、幂等迁移与升级兼容初始化逻辑被刻意设计为懒加载 幂等ensureInitialized()使用initPromise保证并发调用只触发一次建表失败时会把initPromise置回null允许下次重试memory-adapter.ts。建表完成后还会执行一组幂等迁移addV2ColumnsToMessagesTable、migrateDefaultUserIds、addWorkflowStateColumns见 memory-adapter.ts通过PRAGMA table_info检查列是否存在缺失时才ALTER TABLE ADD COLUMN并用 try/catch 容忍列已存在的竞争条件若旧表的content/type列为NOT NULL会采用加临时列 → 拷贝数据 → 删旧列 → 重命名的方式迁移到可空版本若当前 D1 不支持DROP COLUMN则保留两列属于非关键路径将历史遗留的user_id default消息按所属会话的用户 ID 批量回填并记录剩余孤儿数据警告。这意味着从早期版本升级到 Memory V2 时适配器会自动完成 schema 演进无需手动执行迁移脚本——这在新安装场景下同样安全CREATE TABLE IF NOT EXISTS 缺列检测天然幂等。底层原理三完整的 StorageAdapter 能力矩阵D1MemoryAdapter实现了 packages/core/src/memory/types.ts 中定义的StorageAdapter接口的全部方法按职责可分为四组消息操作addMessage/addMessages/getMessages/clearMessages/deleteMessages以及步骤相关的saveConversationSteps/getConversationSteps。实现细节上写入统一走INSERT ... ON CONFLICT(conversation_id, message_id) DO UPDATE SET ...的 upsert 语义保证同一消息 ID 重放时幂等addMessages与saveConversationSteps使用 D1 的batch()一次性提交多条预编译语句减少网络往返getMessages通过子查询实现取最近 N 条再按时间正序返回支持limit、before、after、roles过滤读取parts时会对 JSON 做容错解析兼容老版本遗留的content字段自动将其转换为[{ type: text, text }]的 parts 形态clearMessages支持按单个会话清理也支持按用户清空该用户所有会话的消息与步骤。会话操作createConversation/getConversation/getConversations/getConversationsByUserId/queryConversations/countConversations/updateConversation/deleteConversation。其中queryConversations支持按userId/resourceId过滤、白名单校验的orderBy仅允许created_at/updated_at/title、orderDirection、limit/offset分页重复创建会话会抛出ConversationAlreadyExistsError更新不存在的会话会抛出ConversationNotFoundError与核心包的错误语义保持一致。工作记忆操作getWorkingMemory/setWorkingMemory/deleteWorkingMemory。以scope区分存储位置——conversation作用域写进会话metadata.workingMemoryuser作用域写进users表的metadata.workingMemory用户不存在时自动插入一行。工作记忆是 Agent 跨对话保留长期上下文的关键机制。工作流状态操作getWorkflowState/setWorkflowState/updateWorkflowState/queryWorkflowRuns/getSuspendedWorkflowStates。setWorkflowState使用INSERT OR REPLACE整行覆盖queryWorkflowRuns支持workflowId、status、from/to时间范围、userId以及元数据过滤见下节getSuspendedWorkflowStates用于恢复挂起的工作流。多租户元数据过滤JSON 感知的查询从 2.1.x 开始queryWorkflowRuns支持对metadata做多租户过滤见 CHANGELOG.md 中关于multi-tenant filters to workflow execution listing的条目。当查询条件携带metadata: { tenantId: acme }时适配器会生成这样的 SQL 片段json_extract(metadata, ?) json_extract(json(?), $)即利用 SQLite/D1 内置的 JSON 函数将metadata字段中的键值与查询值做类型安全的比较键路径会先做反斜杠与引号的转义$.tenantId值为null时则改用json_type(metadata, ?) null显式匹配键存在且为空的情形。这一行为在 memory-adapter.spec.ts 中有直接的单测佐证测试断言生成的 SQL 同时包含workflow_id ?、status ?、created_at 、user_id ?、json_extract(...)以及ORDER BY created_at DESC并逐项校验了绑定的参数数组包括转义后的键路径$.tenantId与序列化后的值acme。这套设计让/workflows/executions之类的管理接口可以在 D1 上直接按租户/用户元数据检索工作流执行历史而无需应用层全量扫描。测试与质量保障voltagent/cloudflare-d1的测试使用 Vitestpnpm test运行见 package.json测试文件为 memory-adapter.spec.ts。测试通过vi.fn()构造一个 mock 的D1Databaseprepare/bind/run/all/batch再 spy 掉ensureInitialized与all从而在不依赖真实 D1 环境的前提下验证 SQL 生成与参数绑定、JSON 字段解析回填等行为。这种 mock 手法也是你在自己项目中为基于 D1 的存储层编写单测时可参考的范式。总结voltagent/cloudflare-d1以极小的包面仅一个适配器类与一个选项类型为 VoltAgent Memory V2 提供了完整的 Cloudflare D1 持久化能力自动建表与幂等迁移让你零维护 schema指数退避重试缓解了 D1 的锁竞争StorageAdapter的完整实现让会话、消息、步骤、工作记忆与工作流状态都能跨请求持久化。结合 wrangler.toml 的 D1 绑定配置与 示例 Worker你可以在数分钟内把一个无状态 Agent 升级为具备完整记忆能力的边缘 AI 应用。赞分享人工智能AI AgentAgent 框架后端多智能体RAG工具调用Agent 记忆【免费下载链接】voltagentAI Agent Engineering Platform built on an Open Source TypeScript AI Agent Framework项目地址https://gitcode.com/gh_mirrors/vo/voltagent点击查看免费下载相关推荐基于 Cloudflare D1 构建 Mastra 存储层mastra/cloudflare-d1 完整实战与版本演进解读基于 Cloudflare D1 构建 Mastra 存储层mastra/cloudflare d1 完整实战与版本演进解读 mastra/cloudfl人工智能Agent 框架AI AgentRAG后端Mastra 集成 Cloudflare D1 存储指南从 Workers Binding 到 REST API 的完整实战Mastra 集成 Cloudflare D1 存储指南从 Workers Binding 到 REST API 的完整实战 Mastra 是基于 TypeS人工智能Agent 框架AI AgentRAG后端EmDash Cloudflare Demo 实战指南在 Workers D1 上运行 Astro 驱动的全栈 CMSEmDash Cloudflare Demo 实战指南在 Workers D1 上运行 Astro 驱动的全栈 CMS 本指南以仓库中的 demos/clCMS后端前端插件系统上一篇开源项目《CS-Ebook》使用教程下一篇Blender插件跨版本兼容性终极指南构建未来可靠的扩展生态创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表