
1. 项目概述当AI Agent拥有了一台永不关机的云电脑最近在GitHub上看到一个挺有意思的开源项目叫my_ai_town目前有3.9K的Stars。它的核心卖点很直接利用Cloudflare Workers为你的AI Agent智能体提供一台持久化、可编程的“云电脑”。这听起来有点抽象但如果你正在捣鼓AI应用尤其是需要让AI能“记住”上下文、执行周期性任务或者与外部API持续交互的Agent这个概念就非常实用了。简单来说我们平时开发的AI应用比如基于LangChain或AutoGPT的Agent其“记忆”和“状态”通常是临时的。一次对话结束或者服务器重启Agent的“大脑”就清零了。而my_ai_town项目则巧妙地利用了Cloudflare Workers的Durable Objects特性为每个Agent实例创建了一个独立的、状态持久化的执行环境。你可以把它想象成在云端为每个AI角色分配了一个永不关机的虚拟机里面运行着它的专属代码和内存可以7x24小时处理任务、维护状态、甚至与其他Agent进行异步通信。这个项目之所以能火正是因为它精准地戳中了当前AI Agent开发的一个核心痛点如何实现有状态的、长期运行的智能体。无论是构建一个虚拟小镇里的NPC一个自动处理邮件的助手还是一个监控数据并定期报告的机器人持久化状态都是刚需。而Cloudflare Workers的边缘计算网络又为这种需求提供了近乎零运维、全球低延迟的完美基础设施。项目用TypeScript写成结构清晰对于想深入Agent架构和Serverless实践的开发者来说是个绝佳的学习和起步模板。2. 核心架构与设计思路拆解2.1 为什么是Cloudflare Workers Durable Objects要理解这个项目首先得弄明白它依赖的两大Cloudflare核心技术Workers和Durable Objects。Cloudflare Workers是一个无服务器Serverless函数计算平台。和我们熟悉的AWS Lambda或Vercel Edge Functions类似它允许你将代码部署到全球数百个边缘节点上运行。其最大特点是启动速度极快冷启动在毫秒级并且按请求次数计费成本极低对于轻量级、高并发的应用场景非常友好。但普通的Workers是无状态的每次调用都是独立的。这对于需要记忆的Agent来说不够用。这时Durable Objects就登场了。它是Cloudflare提供的一种状态化、全局唯一的对象存储与执行模型。你可以把一个Durable Object理解为一个有状态的、单线程的JavaScript环境它拥有自己独立的存储内存和持久化存储并且可以通过一个唯一的ID从全球任何地方的Worker请求中访问到它。my_ai_town项目的设计精髓就在于此它将每一个AI Agent实例映射为一个Durable Object。这个Durable Object内部封装了Agent的状态记忆、目标、库存等、行为逻辑LLM调用、工具使用以及与其他Agent或外部服务的通信接口。因为Durable Object是持久化的所以即使没有外部请求这个Agent对象及其状态也会被保留在Cloudflare的存储中直到你主动删除它或它因闲置而被回收可配置。这种架构带来了几个显著优势真正的持久化Agent的状态跨越请求和会话而存在实现了长期记忆和连续性任务。简化并发Durable Object是单线程的天然避免了多线程环境下的状态同步难题简化了Agent内部逻辑的编写。全球低延迟访问通过Worker网络可以就近访问部署在边缘的Agent实例响应迅速。极低的运维负担无需管理服务器、容器或Kubernetes集群Cloudflare负责一切基础设施的伸缩、部署和可用性。2.2 项目整体架构与数据流项目的架构可以清晰地分为三层接口层、Agent核心层和持久化层。接口层通常是一个或多个普通的Cloudflare Worker它们充当了HTTP API网关或WebSocket网关的角色。当外部请求比如用户指令、定时触发器、Webhook到达时接口层Worker会根据请求参数如Agent ID定位到对应的Durable Object Stub存根然后将请求转发给具体的Agent实例进行处理。Agent核心层就是运行在Durable Object内部的代码。这是项目的核心。每个Durable Object即每个Agent都是一个独立的类实例。在这个类中开发者需要定义state 通过Durable Object的存储API如state.storage持久化的状态数据。fetch(request)方法 处理所有传入的HTTP请求这是与外界通信的主入口。Agent的行为逻辑 例如如何解析输入、如何调用大型语言模型LLM、如何使用工具如搜索、计算、调用API、如何更新自身状态、如何生成响应或执行动作。持久化层由Durable Objects底层自动处理。状态可以存储在内存中也可以通过state.storageAPI写入到Cloudflare的持久化KV存储中确保即使Durable Object实例被暂时卸载由于闲置状态也能在下次被访问时恢复。数据流大致如下用户通过HTTP或WebSocket发送一个消息给接口Worker。接口Worker解析消息提取目标Agent ID。接口Worker通过env.AI_TOWN.get(id)获取该ID对应的Durable Object Stub。通过Stub调用其fetch方法将用户消息传入。Agent的Durable Object实例被唤醒或已在运行在其fetch方法中处理消息可能调用LLM、执行工具、更新内部状态。Agent生成响应通过fetch方法的返回值返回给接口Worker。接口Worker将响应最终返回给用户。注意Durable Objects有请求并发限制。同一时刻对一个特定ID的Durable Object的请求会被序列化处理。这意味着你的Agent逻辑不需要考虑线程安全但也意味着高并发场景下请求可能需要排队。设计时要考虑Agent处理单个请求的耗时。3. 核心细节解析与实操要点3.1 Agent状态设计与存储策略在my_ai_town这类项目中Agent状态的设计是重中之重。状态决定了Agent的“记忆”能力和行为连续性。一个典型的状态对象可能包含interface AgentState { // 身份与元数据 id: string; name: string; role: string; // 例如shopkeeper, assistant createdAt: number; // 记忆系统 conversationHistory: Array{role: user | assistant | system, content: string}; longTermMemory: Array{fact: string, timestamp: number}; // 关键事实记忆 goals: Array{description: string, completed: boolean}; // 当前目标 // 环境与上下文 inventory: Mapstring, number; // 物品库存 location: string; // 当前所在位置对于游戏或模拟场景 relationships: Mapstring, number; // 与其他Agent的关系值 // 运行时状态 currentTask: string | null; lastActiveTime: number; }存储策略Durable Objects提供了state.storageAPI它像一个简单的键值数据库。你可以选择将整个状态对象序列化如用JSON.stringify后存储在一个键下也可以将不同部分拆分存储。对于频繁更新的部分如conversationHistory单独存储可能更高效但会带来一致性问题。项目通常采用混合策略将核心、高频更新的状态如对话历史保存在Durable Object的实例变量内存中以获得最快访问速度同时定期或在某些检查点将完整状态快照持久化到state.storage防止数据丢失。实操心得状态序列化时要小心处理Map、Set等原生JavaScript对象它们直接JSON.stringify会变成空对象{}。一个常见的技巧是将其转换为数组再存储例如storage.put(inventory, Array.from(inventory.entries()))读取时再new Map(JSON.parse(storedData))。3.2 与大型语言模型LLM的集成Agent的“智能”来源于LLM。在Cloudflare Worker环境中集成LLM主要有两种方式1. 调用外部LLM API如OpenAI, Anthropic Claude 这是最直接的方式。在你的Agent代码中使用fetch向这些服务的API端点发起请求。Cloudflare Workers的优势在于其全球边缘网络可以优化到这些API服务的网络延迟。async function callOpenAI(messages: any[]) { const response await fetch(https://api.openai.com/v1/chat/completions, { method: POST, headers: { Authorization: Bearer ${env.OPENAI_API_KEY}, Content-Type: application/json, }, body: JSON.stringify({ model: gpt-4, messages: messages, temperature: 0.7, }), }); return await response.json(); }2. 使用在边缘运行的轻量级模型如通过ONNX Runtime Cloudflare Workers支持WebAssembly理论上可以运行一些轻量级的、经过优化的模型例如一些较小的开源模型。但这需要复杂的模型转换、裁剪和优化工作且Worker的内存和CPU限制较严格目前还不是主流方案。my_ai_town这类项目通常采用第一种方式。关键点Prompt工程与上下文管理。由于每次调用LLM API都需要携带上下文而Durable Object内存中的对话历史可能很长你需要设计一个上下文窗口管理策略。常见做法是维护一个固定长度的滑动窗口只保留最近N轮对话或者更智能地使用LLM本身来总结之前的对话历史将“摘要”作为系统提示的一部分从而在有限的token内保留更长期的记忆。3.3 工具Tools调用与动作执行一个强大的Agent不仅能聊天还能“做事”。这就需要为其装备“工具”。工具可以是任何函数例如查询天气、搜索网络、操作数据库、调用某个REST API、进行数学计算等。在Agent的循环中通常的流程是LLM根据用户输入和当前状态决定是否需要调用工具以及调用哪个工具及其参数。Agent代码执行对应的工具函数。将工具执行的结果作为新的上下文再次交给LLM由LLM生成面向用户的自然语言回答或决定下一个动作。在my_ai_town的架构下工具函数的实现需要注意无状态性工具函数本身应尽量是无状态的纯函数其所需的所有数据都通过参数传入。状态的变化应通过更新Agent的state来完成。异步安全所有I/O操作网络请求、存储读写都必须是异步的async/await并做好错误处理避免一个工具调用失败导致整个Agent实例崩溃。权限与隔离由于多个用户或Agent可能共享同一套后端工具要确保工具调用有适当的权限检查和资源隔离防止越权操作。4. 从零开始构建一个简易持久化Agent4.1 环境准备与项目初始化首先确保你已安装Node.js和npm。然后我们需要使用Cloudflare的官方命令行工具wrangler。# 全局安装wrangler npm install -g wrangler # 登录到你的Cloudflare账户 wrangler login # 创建一个新的Workers项目 mkdir my-persistent-agent cd my-persistent-agent wrangler init在初始化过程中选择“TypeScript”模板。这会在目录下生成wrangler.toml配置文件、src/index.ts主Worker入口等文件。接下来我们需要定义一个Durable Object。在src目录下创建一个新文件例如src/agent.ts。4.2 定义Durable Object (Agent类)src/agent.ts文件将包含我们Agent的核心逻辑。// src/agent.ts export class Agent implements DurableObject { state: DurableObjectState; env: Env; // 环境变量如API密钥 memory: string[]; // 简单的内存数组 constructor(state: DurableObjectState, env: Env) { this.state state; this.env env; // 初始化时尝试从持久化存储加载记忆 this.state.blockConcurrencyWhile(async () { this.memory (await this.state.storage.getstring[](memory)) || []; }); } async fetch(request: Request): PromiseResponse { const url new URL(request.url); const path url.pathname; if (request.method POST path /chat) { // 处理聊天请求 const { message } await request.json{ message: string }(); // 1. 将新消息加入记忆 this.memory.push(User: ${message}); // 2. 模拟调用LLM此处简化实际应调用OpenAI等API // 构建上下文将最近5条记忆作为提示 const context this.memory.slice(-5).join(\n); const llmResponse await this.simulateLLM(context, message); // 3. 将LLM回复加入记忆 this.memory.push(Assistant: ${llmResponse}); // 4. 持久化更新后的记忆异步不阻塞返回 this.state.storage.put(memory, this.memory); // 5. 返回响应 return new Response(JSON.stringify({ response: llmResponse }), { headers: { Content-Type: application/json }, }); } return new Response(Not Found, { status: 404 }); } private async simulateLLM(context: string, userMessage: string): Promisestring { // 这里是模拟实际项目中替换为真实的LLM API调用 // 例如调用OpenAI GPT // const response await fetch(https://api.openai.com/v1/chat/completions, {...}); // return response.choices[0].message.content; // 简单模拟回声并加上记忆上下文提示 return I remember our chat context: ${context}. You said: ${userMessage}. How can I assist you further?; } } // 导出类型供其他文件使用 export interface Env { AGENT: DurableObjectNamespaceAgent; // 其他环境变量如OPENAI_API_KEY }4.3 配置Wrangler与路由Worker接下来我们需要在wrangler.toml中配置这个Durable Object并设置主Worker作为路由。首先更新wrangler.toml# wrangler.toml name my-persistent-agent compatibility_date 2024-03-01 [[durable_objects.bindings]] name AGENT # 在Worker代码中使用的变量名 class_name Agent # 对应的Durable Object类名 [[migrations]] tag v1 new_classes [Agent] # 声明要创建的Durable Object类然后修改主Worker文件src/index.ts将其作为路由// src/index.ts export interface Env { AGENT: DurableObjectNamespace; } export default { async fetch(request: Request, env: Env, ctx: ExecutionContext): PromiseResponse { const url new URL(request.url); const pathname url.pathname; // 路由/agent/:id/chat - 转发到对应ID的Agent Durable Object const agentChatMatch pathname.match(/^\/agent\/([^\/])\/chat$/); if (agentChatMatch request.method POST) { const agentId agentChatMatch[1]; // 获取或创建该ID对应的Durable Object Stub const agentIdObj env.AGENT.idFromName(agentId); const agentStub env.AGENT.get(agentIdObj); // 将请求转发给Durable Object实例处理 return agentStub.fetch(request); } // 默认响应 return new Response(Persistent Agent Service. POST to /agent/{id}/chat to chat., { headers: { content-type: text/plain }, }); }, };4.4 本地测试与部署在本地开发服务器上测试# 启动本地开发环境 wrangler dev启动后你可以使用curl或Postman等工具测试curl -X POST http://localhost:8787/agent/alice/chat \ -H Content-Type: application/json \ -d {message: Hello, what do you remember?}多次向同一个ID如alice发送消息你会看到模拟的LLM回复中包含了之前的对话上下文证明了状态的持久性。测试无误后部署到Cloudflare全球网络wrangler deploy部署成功后你会获得一个*.workers.dev的域名你的持久化Agent服务就正式上线了。5. 性能优化与成本控制实战5.1 理解计费模型与限制Cloudflare Workers的计费主要基于请求次数和CPU执行时间。Durable Objects在此基础上增加了存储的读写操作次数和存储的数据量计费。对于长期运行、状态复杂的Agent成本控制的关键在于状态存储优化避免频繁写入大量数据。不要每次交互都将整个状态对象写入存储。采用增量更新或设置写入节流例如每10次交互或状态变化超过一定阈值才持久化一次。请求合并如果前端或客户端可能高频发送请求考虑在客户端或接入层进行请求合并减少对Durable Object的调用次数。闲置实例处理Durable Object实例在闲置一段时间可配置后会被卸载但状态已持久化。这是节省持续CPU时间的关键。确保你的Agent逻辑能妥善处理“唤醒”后的状态恢复。5.2 实现状态快照与懒加载一个高效的策略是区分“热数据”和“冷数据”。将当前会话频繁访问的数据如最近10轮对话保存在Durable Object的内存中热数据。将完整的历史记录、日志等不常访问的数据以“快照”形式定期序列化并存储到state.storage的一个独立键中冷数据。class OptimizedAgent implements DurableObject { private hotMemory: string[] []; // 内存中的热数据 private snapshotKey memory_snapshot_v1; async fetch(request: Request) { // 处理请求更新 hotMemory... this.hotMemory.push(newMessage); // 每处理10个请求或热数据达到100条触发一次快照保存 if (this.hotMemory.length % 10 0 || this.hotMemory.length 100) { // 异步保存快照不阻塞请求响应 this.state.storage.put(this.snapshotKey, JSON.stringify(this.hotMemory)); } // ... 返回响应 } // 初始化时加载快照 async initialize() { const snapshot await this.state.storage.getstring(this.snapshotKey); if (snapshot) { this.hotMemory JSON.parse(snapshot); } } }5.3 利用Alarms实现定时任务Durable Objects支持Alarms API允许你在未来的某个时间点唤醒自己。这对于需要定时执行任务的Agent如每日报告、定期数据抓取非常有用。export class ScheduledAgent implements DurableObject { async fetch(request: Request) { const url new URL(request.url); if (url.pathname /schedule-daily-report) { // 设置一个24小时后触发的闹钟 const currentAlarm await this.state.storage.getAlarm(); if (currentAlarm null) { // 避免重复设置 // 24小时 86400秒 * 1000毫秒 const nextAlarmTime Date.now() 86400 * 1000; await this.state.storage.setAlarm(nextAlarmTime); } return new Response(Daily report scheduled.); } return new Response(Not found, { status: 404 }); } // 当闹钟触发时会自动调用此方法 async alarm() { console.log(Alarm triggered! Time to generate the daily report.); // 在这里执行你的定时任务逻辑例如调用LLM生成报告并发送 await this.generateAndSendReport(); // 任务完成后可以再次设置下一个闹钟实现循环 const nextAlarmTime Date.now() 86400 * 1000; await this.state.storage.setAlarm(nextAlarmTime); } }注意alarm()方法必须快速执行通常建议在30秒内完成否则可能被强制终止。对于长时间运行的任务应考虑将其拆分成多个步骤或者触发一个外部服务如另一个Worker来处理。6. 常见问题排查与调试技巧6.1 调试与日志记录在Cloudflare Workers环境中调试console.log是你的好朋友。所有日志都会输出到wrangler dev的终端或者可以在Cloudflare Dashboard的Workers部分的“日志”中查看。更结构化的日志考虑使用一个轻量级的日志库或者简单地封装一个日志函数为每条日志添加上下文如Agent ID、请求ID便于追踪。class AgentWithLogging implements DurableObject { id: string; constructor(state: DurableObjectState, env: Env) { this.id state.id.toString(); } private log(level: info | error, message: string, data?: any) { console.log(JSON.stringify({ timestamp: new Date().toISOString(), agentId: this.id, level, message, data })); } async fetch(request: Request) { this.log(info, Fetch request received, { path: request.url }); // ... 处理逻辑 } }6.2 典型错误与解决方案1. 错误Durable Object reset because of an unhandled exception原因Durable Object内部代码抛出了未捕获的异常。解决确保所有异步操作都有.catch()错误处理或者在顶层用try...catch包裹。特别是调用外部API、读写存储时。2. 错误Durable Object storage operation timed out原因对state.storage的读写操作耗时过长默认有操作超时限制。解决优化存储操作避免单次写入过大对象。大对象可以分块存储。检查网络延迟如果存储操作间接依赖网络。考虑将非关键的批量写入操作改为异步、非阻塞的方式。3. 问题Agent响应缓慢排查首先检查LLM API的响应时间。这是最常见的瓶颈。使用wrangler tail命令实时查看请求日志和耗时。检查Durable Object内部是否有复杂的同步计算阻塞了事件循环。优化为LLM调用设置合理的超时和重试机制。如果逻辑允许将一些准备工作如下一步的提示词生成与LLM调用并行执行。4. 问题状态不一致或丢失排查确认状态更新逻辑是否正确。特别是在并发环境下虽然Durable Object是单线程但快速连续的请求可能交织要确保状态读取、计算、写入的原子性。使用state.blockConcurrencyWhile()方法在初始化或关键状态更新时防止竞态条件。示例async updateInventory(item: string, delta: number) { await this.state.blockConcurrencyWhile(async () { let inventory (await this.state.storage.getMapstring, number(inventory)) || new Map(); let current inventory.get(item) || 0; inventory.set(item, current delta); await this.state.storage.put(inventory, Array.from(inventory.entries())); }); }6.3 监控与告警对于生产环境监控至关重要。使用Cloudflare Dashboard观察Worker的请求量、错误率、CPU时间。自定义指标在代码中发送自定义指标到外部监控服务如Datadog, Prometheus记录每个Agent的处理延迟、工具调用成功率等。设置告警在Cloudflare Dashboard中可以为Worker的错误率、未处理异常等配置告警以便及时发现问题。构建一个基于Cloudflare Workers和Durable Objects的持久化AI Agent系统将无服务器架构的优势与AI的长期记忆、主动执行能力相结合为开发智能应用打开了新的大门。从简单的聊天机器人到复杂的模拟环境其架构模式具有很强的通用性。在实际开发中关键在于精细设计状态模型、审慎管理存储I/O以控制成本并充分利用Durable Objects的Alarms等特性来实现更复杂的自治行为。随着边缘计算和AI模型的进一步发展这种“边缘智能体”的范式可能会变得越来越普遍。