编程 Cloudflare Agents 深度拆解:当 AI Agent 成为边缘一等公民——从 Durable Objects 持久状态、Agents SDK 到 Workflows 持久执行与 @cloudflare/computer 的全链路实战

2026-08-19 04:12:25 +0800 CST views 8

Cloudflare Agents 深度拆解:当 AI Agent 成为边缘一等公民——从 Durable Objects 持久状态、Agents SDK 到 Workflows 持久执行与 @cloudflare/computer 的全链路实战

2026 年,AI Agent 已经不再是「跑在笔记本上的脚本」。当 Agent 需要记住跨数天的对话、在数百万人同时在线时保持状态一致、还能调用浏览器和真实命令行时,传统的「无状态函数 + 外部数据库」组合就开始露怯了。Cloudflare 在 2025 年开源了 Agents SDK,又在 2026 年连续抛出 Agent Development Lifecycle、@cloudflare/computer 等重磅原语——它想做的,是把「有状态的 AI Agent」做成边缘平台上的一等公民。本文从底层存储模型、运行时架构,到可运行的 Agents SDK + Workflows 代码,再到生产级性能调优,进行全链路拆解。

一、背景介绍:为什么 Agent 需要「边缘态」

1.1 无状态浪潮留下的坑

过去十年,云原生把「无状态(stateless)」奉为圭臬。容器、函数、微服务,统统追求可以随时被杀掉、随时被调度的纯计算单元。这种范式对 Web API 极其友好——请求进来,处理,返回,上下文随进程一起消失。

但 AI Agent 的天性恰恰是有状态

  • 它需要记忆:上一轮对话说了什么,用户的偏好是什么,任务的进度到了哪一步;
  • 它需要协调:多个工具调用之间要共享中间结果,多个并发请求要串行访问同一份资源;
  • 它需要恢复:一个跑几分钟到几小时的任务,中途宕机、超时、限流了,得能从最后一个成功的检查点继续,而不是从头再来。

当这些需求叠加到「无状态函数」上时,工程师被迫自己拼接一套外部状态层:Redis 存会话、Postgres 存业务数据、消息队列做任务编排、再加一层锁服务解决并发。结果是——你 80% 的代码都在和「状态一致性」搏斗,只有 20% 在解决真正的业务问题。

1.2 为什么是边缘,而不是中心机房

把 Agent 放在传统云上,最大的痛是延迟冷启动

  • 一个来自东京的用户,请求要漂洋过海到 us-east-1 的机房,Round Trip 就是一两百毫秒起步;
  • Serverless 函数冷启动动辄几百毫秒到几秒,而 Agent 的推理本身就要等模型吐字,叠加起来体感极慢;
  • 数据合规要求数据留在本地司法辖区,集中式部署天然违规。

Cloudfloudflare 的卖点恰好命中这两点:全球 300+ 个 PoP(入网点),代码以 V8 Isolate 方式运行,冷启动 < 1ms(不是毫秒级「预热」,而是几乎没有冷启动),并且原生支持「请求落到离用户最近的节点」。

但光有边缘计算还不够。边缘节点的核心难题是:计算无处不在,可状态该放哪儿? 如果每个 PoP 都是无状态的,那状态就只能回源到中心数据库,边缘的低延迟优势瞬间被一次跨洲 DB 查询抹平。

Cloudflare 的答案就是 Durable Objects:一个「地址唯一、单实例、强一致」的计算+存储单元,把状态锚定在某一个确定的位置,同时让计算结果尽可能靠近用户。

1.3 Cloudflare 与 LangChain / Vercel AI SDK 的根本区别

很多同学会问:我用 LangChain.js 或者 Vercel AI SDK 不也能写 Agent 吗?区别在于状态归谁管

  • LangChain / Vercel AI SDK 是「客户端框架」:它们管的是「怎么调模型、怎么组 prompt、怎么串 tool」。至于状态存在哪、并发怎么协调、宕机怎么恢复——你自己想办法(通常是一个外部的 memory store)。
  • Cloudflare Agents 是「运行时 + 状态层」:它把 Agent 直接建在 Durable Objects 之上,this.state 就是持久化的内存,单实例模型天然解决了并发写冲突,WebSocket hibernation 让百万连接不再烧钱,Workflows 负责长任务的持久执行。

一句话:前者给你乐高积木,后者直接给你一座带地基的房子。 这就是为什么 Cloudflare 在 2026 年的宣传里反复强调「Agent 是一等公民,而不是你自己拼出来的上层应用」。


二、核心概念:把 Agent 拆成四块积木

要在边缘上跑 Agent,Cloudflare 提供了一组彼此咬合的原语。我们逐一拆开。

2.1 Workers:V8 Isolate 上的无冷启动计算

Workers 是 Cloudflare 的计算底座。它和你熟悉的 Node.js / Docker 函数最大的不同是运行单元不是容器,而是 V8 Isolate

  • 每个 Isolate 是 V8 引擎里的一个独立上下文,启动只需微秒级,没有容器镜像加载;
  • 同一台物理机的上百个 Isolate 共享同一个 V8 实例,内存开销极低;
  • 计费按 CPU 时间(实际消耗的计算),而不是 wall-clock 挂钟时间,空闲等待(比如等模型流式返回)不怎么花钱。

代价是受限的运行时:没有原生文件系统、不能用 process、不能用动态 requireCPU 时间 有硬上限(免费版每请求 10ms,付费版可到 30s 级 CPU + 更长的 duration)。对 Agent 来说,这意味着「重活」(跑浏览器、编译、长进程)不能只靠 Isolate,这正是后面 @cloudflare/computer 要补的洞。

2.2 Durable Objects:状态的地基

Durable Objects(简称 DO)是整套 Agent 架构的「定海神针」。它的三个核心特性:

① 单实例强一致。 每个 DO 由「命名空间 + ID」唯一确定,Cloudflare 保证全局只有一个活跃的该实例。所有对该对象的请求,无论来自哪个 PoP,都会被路由到这同一个实例上串行执行。这意味着你不需要分布式锁——DO 本身就是锁。这是 Agent 协调能力的来源。

② SQLite 存储。 现代 DO 的存储后端是事务型 SQLitethis.ctx.storage 的底层)。你可以像写关系表一样 INSERT/SELECT,ACID 保证。Agents SDK 把这套存储封装成了 this.state(自动持久化的结构化状态)和 this.sql(直接跑 SQL)。

③ WebSocket Hibernation(休眠)。 这是 DO 最被低估的能力。传统 WebSocket 服务,每个连接都拽着一个进程/线程不撒手,10 万连接就是 10 万份内存占用。DO 的 Hibernation API 允许连接休眠:消息到来时 Isolate 可以被整体驱逐,只在有新消息时唤醒。一个 DO 实例轻松扛住海量休眠连接,醒来时再从存储里恢复上下文。

2.3 Agents SDK:把 DO 包装成「Agent」

@cloudflare/agents(npm 上的 agents 包)是一个薄而关键的上层封装:它让你继承一个 Agent 类,而这个类本质就是一个 Durable Object,但额外提供了一套 Agent 友好的生命周期与状态接口。

关键接口一览:

能力API说明
持久化状态this.state / this.setState(partial)结构化状态,自动落盘到 DO 的 SQLite
关系存储this.sql\...``直接在 DO 存储里跑 SQL
连接管理this.broadcast() / this.getConnection()向所有 WebSocket 连接广播
定时唤醒this.scheduleAlarm() / this.ctx.waitUntil类似 cron,用于定时任务、心跳
生命周期onStart / onConnect / onMessage / onDisconnect / onClose事件钩子
工具调用内置 tool / MCP 集成让 Agent 调用外部工具或 MCP server

最妙的是:你写的 Agent 子类,既是「业务逻辑」,又是「Durable Object 定义」。Cloudflare 的运行时自动把它路由成单实例、强一致、可休眠的 Agent。你不用碰一行分布式协调代码。

2.4 Workflows:长任务的「持久执行」

Agent 经常要干「多步、长时、不容失败」的活儿:调研 → 起草 → 评审 → 发布,任何一步崩了都得从断点续跑。Workflows 就是为这种场景设计的持久执行引擎

它的心智模型非常简单:一个 Workflow 由若干 step 组成,每个 step 是「可独立重试的最小单元」。Workflows 引擎会在每个 step 完成后持久化状态,如果进程崩溃、网络中断或代码报错,它会从最后一个成功的 step 自动恢复,而不是从头重跑。你不需要自己写状态机、不需要外挂数据库记录进度。

const outline = await step.do("research", async () => { /* 调用 LLM 做调研 */ });
const draft   = await step.do("draft",   async () => { /* 基于 outline 起草 */ });
const review  = await step.do("review",  async () => { /* 评审并打回或放行 */ });

注意 step.do 的 handler 必须是幂等的——因为引擎可能在重试时多次调用它(虽然它保证最终只提交一次结果,但执行可能被重放)。这是用 Workflows 的第一条铁律。

2.5 周边绑定:AI / Vectorize / R2 / KV

Agent 要干活,还得有「感官」和「手脚」,Cloudflare 用 binding(绑定) 把这些能力挂到 Worker 上:

  • Workers AI:直接 env.AI.run("@cf/...", {...}) 调用托管模型(Llama、Phi、嵌入模型等),免运维、按量计费;
  • Vectorize:托管的向量数据库,给 RAG 提供语义检索;
  • R2:零出口费用的对象存储,存大文件、知识库、产物;
  • KV:全球低延迟键值缓存,适合存「读多写少」的配置/会话索引。

2.6 @cloudflare/computer:给 Agent 一台「真机」

2026 年 Cloudflare 推出的 @cloudflare/computer,是补齐 Agent 能力版图的最后一块。它的核心洞察是:Agent 需要的不是一个容器,而是一台「计算机」——能开浏览器、能跑 shell、能装依赖、能操作真实 GUI。

@cloudflare/computer 是一个 Agent 运行时,它动态地在「快但受限的 Isolate」和「完整但重的 Linux 容器」之间编排:轻量推理和路由走 Isolate(毫秒级、几乎零成本),需要浏览器自动化、代码编译、长进程的重活则调度到全功能容器。Agent 于是同时拥有了「边缘的速度」和「主机的完整能力」。下文会给出它的集成思路。


三、架构分析:一个请求是怎么变成「有状态的 Agent」的

3.1 路由全景

                        全球用户请求
                             │
                    Cloudfloudflare 边缘网络 (300+ PoP)
                             │  (就近接入,<1ms 冷启动)
                             ▼
                   Worker (路由层 / index.ts)
                             │  根据 room/topic 算出 DO id
                             ▼
            Durable Object 单实例  (Agents SDK 的 Agent 子类)
                ┌─────────────────────────────────┐
                │  this.state   ← 自动持久化的状态  │
                │  this.sql     ← SQLite 关系存储    │
                │  WebSocket 连接池 (可休眠)         │
                │  Alarm / 定时任务                 │
                └─────────────────────────────────┘
                   │              │               │
            env.AI.run()     Vectorize 查询     R2/KV 读写
            (Workers AI)     (语义检索)         (知识库/缓存)
                   │
            长任务 → Workflows (持久执行: research→draft→review)
                   │
            重活   → @cloudflare/computer (Isolate↔容器编排)

3.2 协调模型:一个 Agent = 一个 DO 实例 = 一个线程

这是理解整套架构的钥匙。每个逻辑上的 Agent(比如「房间 A 的对话助手」「用户 X 的长期记忆体」)都映射到一个唯一的 Durable Object 实例。Cloudflare 保证这个实例全局唯一且串行执行。于是:

  • 多用户同时给「房间 A」发消息 → 全部被路由到同一个 DO 实例 → 天然串行,没有并发写冲突;
  • Agent 的 memory 就是 this.state,读写都在单线程内进行,不需要任何锁;
  • 状态变更通过 SQLite 事务持久化,进程挂了重启后从存储恢复,memory 不丢。

对比一下传统方案:你要在 Redis + Postgres 之上自己实现「同一房间请求串行化」,通常得用分布式锁(Redlock 之类的),而 Redlock 在真实网络分区下并不绝对安全。Cloudflare 直接把这个问题从「工程难题」降级成了「架构默认」。

3.3 状态存储:SQLite 才是真相

很多人以为 this.state 是个内存变量。其实它的真相是:DO 的 SQLite 存储里的一张表。当你 this.setState(partial) 时,Agents SDK 会把结构化状态序列化后写入事务型存储;唤醒时再读回来。

这意味着你可以对 Agent 的状态做真正的 SQL 查询。比如一个客服 Agent,你可以:

-- 查这个用户最近 7 天的高优先级会话
SELECT * FROM messages
WHERE role = 'user'
  AND created_at > datetime('now', '-7 days')
ORDER BY created_at DESC;

this.sql 标签模板直接在这个 SQLite 上执行,ACID、事务、索引一应俱全。这是把 Agent 从「玩具」推向「生产系统」的关键——你的 Agent 记忆是可查询、可分析、可治理的数据,而不是一团腌在内存里的 JSON。

3.4 长连接与休眠:百万 WebSocket 不烧钱

实时 Agent(比如边想边吐字的聊天界面)离不开 WebSocket。DO 的 Hibernation 机制让这件事变得便宜:

  1. 客户端连上来,DO acceptWebSocket 接住;
  2. 没有消息的空闲期,Isolate 可以被整体驱逐,但连接由 Cloudfloudflare 边缘维持
  3. 新消息到达,对应 DO 实例被唤醒,从存储恢复上下文,处理并 getWebSockets().forEach 广播。

这样你付的钱是「被唤醒时处理消息的 CPU」,而不是「24 小时养着 10 万个进程」。对实时协作、直播字幕、多人 Agent 会话这类场景,这是成本结构的质变。

3.5 Workflows 在架构中的定位

DO / Agent 擅长「有状态、长连接、低延迟」;Workflows 擅长「长时、多步、必须不丢」。二者是互补层:

  • Agent 收到一个「帮我写一份竞品分析报告」的大任务 → 不直接在 DO 里跑(怕超时、怕中途崩)→ 转交给 Workflows;
  • Workflows 用 step 把任务拆成调研/起草/评审,每步持久化,任何失败自动从断点续跑;
  • 每步内部可以回调 Agent / 调 Workers AI / 读写 R2,形成「Agent 管状态、Workflow 管流程」的分工。

四、代码实战:从零搭一个边缘智能体

下面我们动手实现一个**「带长期记忆的多人聊天 Agent」+「竞品分析长任务 Workflow」**,并给出 @cloudflare/computer 的集成思路。所有代码面向 Cloudflare Workers + Agents SDK + Workflows。

4.1 工程骨架与 wrangler.toml

name = "edge-agent-demo"
main = "src/index.ts"
compatibility_date = "2025-08-01"
workers_dev = true

# 把 Agent 同时作为 Durable Object 和 Agent 命名空间暴露
[[durable_objects.bindings]]
name = "ChatAgent"
class_name = "ChatAgent"

[[migrations]]
tag = "v1"
new_classes = ["ChatAgent"]

# 让 DO 自动选择离用户/离存储最近的部署位置,降低延迟
[[durable_objects.bindings]]
name = "ChatAgent"
class_name = "ChatAgent"
# 在代码中通过 binding 的 smart_placement 控制;此处给出 DO 级别的提示

# Workers AI 绑定
[ai]
binding = "AI"

# 向量库(用于 RAG 检索)
[[vectorize.bindings]]
name = "VECTORIZE"
index_name = "knowledge-base"

# R2 对象存储(存放长文档/产物)
[[r2_buckets.bindings]]
name = "DOCS"
bucket_name = "edge-agent-docs"

# Workflows 绑定
[[workflows]]
name = "research-workflow"
binding = "RESEARCH_WORKFLOW"
class_name = "ResearchWorkflow"

注意:smart_placement = "smart" 也可以写在 DO 绑定上,让 Cloudfloudflare 自动把实例调度到「离其存储与调用方最近」的位置,对跨大洲的低延迟至关重要。

4.2 一个有长期记忆的 Agent(Agents SDK)

// src/chat-agent.ts
import { Agent, type Connection } from "agents";

interface ChatMessage {
  role: "user" | "assistant" | "system";
  content: string;
  ts: number;
}

interface ChatState {
  room: string;
  messages: ChatMessage[];
}

export class ChatAgent extends Agent<Env, ChatState> {
  // 初始状态,首次创建实例时写入存储
  initialState: ChatState = { room: "default", messages: [] };

  // 新连接建立
  async onConnect(connection: Connection) {
    // 把当前历史推给刚连上的客户端,实现「断线重连不丢上下文」
    connection.send(
      JSON.stringify({ type: "history", messages: this.state.messages })
    );
  }

  // 收到一条消息
  async onMessage(connection: Connection, raw: string) {
    const { text } = JSON.parse(raw) as { text: string };

    // 1) 写入持久化状态(自动落盘到 DO 的 SQLite)
    this.setState({
      ...this.state,
      messages: [
        ...this.state.messages,
        { role: "user", content: text, ts: Date.now() },
      ],
    });

    // 2) 调用 Workers AI 做推理( Messages 格式)
    const resp = await this.env.AI.run("@cf/meta/llama-3.1-8b-instruct", {
      messages: this.state.messages.map((m) => ({
        role: m.role,
        content: m.content,
      })),
      max_tokens: 512,
      temperature: 0.7,
    });

    const reply = (resp as { response: string }).response;

    // 3) 把回复也持久化,并广播给房间内所有连接(含休眠连接)
    this.setState({
      ...this.state,
      messages: [
        ...this.state.messages,
        { role: "assistant", content: reply, ts: Date.now() },
      ],
    });
    this.broadcast(JSON.stringify({ type: "message", role: "assistant", content: reply }));
  }

  // 定时任务:每天凌晨清理 30 天前的历史,避免存储无限膨胀
  async scheduled(controller: ScheduledController) {
    this.sql`
      DELETE FROM messages
      WHERE ts < ${Date.now() - 30 * 24 * 3600 * 1000}
    `;
  }
}

这段代码看似简单,背后却藏着整套架构红利:

  • this.setState 的写入是事务型、自动持久化的,进程被杀也不丢;
  • this.broadcast 通过 DO 的 WebSocket 连接池广播,休眠连接也会被唤醒投递
  • onConnect 里回放历史,天然支持「刷新页面 / 断线重连后记忆还在」;
  • scheduled 定时清理,配合 DO 的 Alarm 能力,无需外部 cron 服务。

4.3 直接在 DO 存储上跑 SQL:把记忆变成可查询数据

上面 scheduled 里用到的 this.sql 就是 DO 的 SQLite。你也可以在任何方法里做分析型查询,比如统计一个房间今天的高频话题:

async topTopicsToday(): Promise<string[]> {
  const rows = this.sql<{ topic: string; cnt: number }>`
    SELECT json_extract(content, '$.topic') AS topic, COUNT(*) AS cnt
    FROM messages
    WHERE role = 'user'
      AND ts > ${Date.now() - 24 * 3600 * 1000}
    GROUP BY topic
    ORDER BY cnt DESC
    LIMIT 10
  `;
  return rows.map((r) => r.topic);
}

this.sql标签模板函数,参数用 ${} 插值会被安全地参数化,天然防 SQL 注入。json_extract 是 SQLite 内置的 JSON 函数,说明 DO 存储就是正经的 SQLite,不是什么阉割版 KV。

4.4 底层透视:Durable Objects 的 Hibernatable WebSocket

如果你想绕开 Agents SDK、直接吃透 DO 的休眠能力,可以手写一个 Room 类(Agents SDK 的连接管理底层就是这套):

// src/room.ts
export class Room extends DurableObject<Env> {
  async fetch(request: Request): Promise<Response> {
    const url = new URL(request.url);
    if (url.pathname === "/ws") {
      const pair = new WebSocketPair();
      // 关键:acceptWebSocket 让这个连接进入「可休眠」状态
      this.ctx.acceptWebSocket(pair[1]);
      return new Response(null, { status: 101, webSocket: pair[0] });
    }
    return new Response("not found", { status: 404 });
  }

  // 休眠期间连接不占 Isolate;新消息到达才唤醒实例
  async webSocketMessage(ws: WebSocket, message: string) {
    // 唤醒后从存储恢复房间状态(示意)
    const state = (await this.ctx.storage.get("state")) ?? { users: [] };
    // 广播给该 DO 下所有连接(包括其他休眠连接)
    for (const c of this.ctx.getWebSockets()) {
      c.send(JSON.stringify({ from: "server", message }));
    }
  }

  async webSocketClose(ws: WebSocket, code: number, reason: string, wasClean: boolean) {
    ws.close(code, reason);
  }
}

acceptWebSocket / getWebSockets / webSocketMessage / webSocketClose 这套 Hibernatable API,是支撑「百万长连接不烧钱」的底层机制。Agents SDK 帮你封装了它,但你理解了底层,才能在出问题时精准排障。

4.5 Workflows:把「竞品分析」做成持久执行的长任务

下面这个 Workflow 演示「调研 → 起草 → 评审」三步,任何一步崩了都能从断点恢复:

// src/research-workflow.ts
import { WorkflowEntrypoint, type WorkflowStep, type WorkflowEvent } from "cloudflare:workers";

type Params = { topic: string; depth: "lite" | "full" };

export class ResearchWorkflow extends WorkflowEntrypoint<Env, Params> {
  async run(event: WorkflowEvent<Params>, step: WorkflowStep) {
    const { topic, depth } = event.params;

    // Step 1: 调研(可能要调多次模型 / 抓网页,慢且易失败)
    const research = await step.do("research", async () => {
      const r = await this.env.AI.run("@cf/meta/llama-3.1-8b-instruct", {
        prompt: `针对「${topic}」做一份结构化调研大纲,列出关键维度与数据源。`,
        max_tokens: 1024,
      });
      return (r as { response: string }).response;
    });

    // Step 2: 起草(依赖 Step1 的结果)
    const draft = await step.do("draft", async () => {
      const r = await this.env.AI.run("@cf/meta/llama-3.1-8b-instruct", {
        prompt: `基于以下调研大纲撰写竞品分析草稿:\n${research}`,
        max_tokens: 2048,
      });
      return (r as { response: string }).response;
    });

    // Step 3: 评审(用另一个模型做 critique,可打回)
    const review = await step.do("review", async () => {
      const r = await this.env.AI.run("@cf/microsoft/phi-3-mini-4k-instruct", {
        prompt: `评审以下草稿的事实准确性与结构,给出通过/打回结论:\n${draft}`,
        max_tokens: 512,
      });
      return (r as { response: string }).response;
    });

    // 落库:把最终产物存进 R2,供前端拉取
    const key = `reports/${topic}-${Date.now()}.md`;
    await this.env.DOCS.put(key, `# ${topic}\n\n${draft}\n\n---\n评审:${review}`);

    return { topic, research, draft, review, storedKey: key };
  }
}

触发方式(在 Worker 里):

// src/index.ts
export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const url = new URL(request.url);

    // 触发长任务
    if (url.pathname === "/analyze" && request.method === "POST") {
      const { topic } = await request.json();
      const instance = await env.RESEARCH_WORKFLOW.create({
        params: { topic, depth: "full" },
      });
      return Response.json({ workflowId: instance.id });
    }

    // 把 WebSocket 请求路由到唯一 Agent 实例(按 room 算 id)
    if (url.pathname.startsWith("/ws")) {
      const room = url.searchParams.get("room") ?? "lobby";
      const id = env.ChatAgent.idFromName(room); // 同 room → 同实例 → 强一致
      return env.ChatAgent.get(id).fetch(request);
    }

    return new Response("Edge Agent Demo");
  },
};

注意 env.ChatAgent.idFromName(room) 这一行:它把「房间名」映射成「确定性的 DO id」,从而保证同一个房间的所有请求都落到同一个单实例上。这就是前面说的「一个 Agent = 一个 DO 实例」在工程上的落点。

4.6 给 Agent 一台真机:@cloudflare/computer 集成思路

当 Agent 需要「打开浏览器截图」「跑一段要编译的代码」「操作真实 GUI」时,纯 Isolate 无能为力。@cloudflare/computer 的思路是:轻活走 Isolate,重活调度到全功能 Linux 容器,对 Agent 暴露统一的「计算机」接口。

// src/computer-agent.ts (示意:集成思路,具体 API 以官方文档为准)
import { Computer } from "@cloudflare/computer";

export class BrowserAgent extends Agent<Env, ChatState> {
  private computer?: Computer;

  private getComputer() {
    if (!this.computer) {
      // 把「计算机运行时」绑定挂到 Agent 上
      this.computer = new Computer(this.env.COMPUTER);
    }
    return this.computer;
  }

  async onMessage(_conn: Connection, raw: string) {
    const { task } = JSON.parse(raw);
    // 1) 推理阶段在 Isolate 里完成(毫秒级、零成本)
    const plan = await this.env.AI.run("@cf/meta/llama-3.1-8b-instruct", {
      prompt: `把任务「${task}」拆解成浏览器操作序列`,
      max_tokens: 512,
    });

    // 2) 执行阶段:需要真实浏览器 → 交给 computer 运行时调度到容器
    const session = await this.getComputer().session.create({ image: "browser" });
    const result = await session.run(async (vm) => {
      const page = await vm.browser.open("https://example.com");
      await page.click("#login");
      return await page.screenshot();
    });

    this.broadcast(JSON.stringify({ type: "screenshot", data: result }));
  }
}

这里展示的是架构意图:Agent 不再被 Isolate 的能力边界困住——推理与路由的「快」留在边缘,浏览器/编译/长进程的「重」下沉到容器,二者由 computer 运行时无缝编排。对「能逛网页、能点按钮、能跑代码」的自主 Agent 来说,这是从 demo 走向生产力的关键一步。


五、性能优化:把边缘 Agent 榨到极致

代码能跑只是第一步。生产环境里,Agent 要在高并发、长连接、模型慢响应的压力下保持廉价和稳定。下面是经过实战检验的优化清单。

5.1 让 DO 待在「对的位置」:Smart Placement

默认情况下,DO 实例创建在哪个 PoP 是「就近创建时所在节点」。如果你的 Agent 既要读 R2/KV(有固定存储位置),又要服务全球用户,位置选错就意味着每次请求都跨大洲。开启 smart_placement

[durable_objects]
smart_placement = "smart"

Cloudfloudflare 会动态把实例调度到「离其依赖存储与调用方综合最近」的位置,通常能把 P99 延迟砍掉一大截。

5.2 用 Hibernation 扛长连接,而不是堆进程

再次强调:别用「每个连接一个常驻 Isolate」的思路。统一走 acceptWebSocket / Agents SDK 的连接池,让空闲连接休眠。一个 DO 实例扛 10 万休眠连接的成本,远低于 10 万个常驻 Worker。

5.3 用 Alarm 代替轮询

很多同学用「每 5 秒发一个请求来触发检查」的轮询模式——这在边缘上既贵又蠢。改用 DO Alarm:

// 设定 1 小时后唤醒做一次性检查
await this.ctx.storage.setAlarm(Date.now() + 3600_000);
// 引擎会在到点时调用 agent 的 alarm() 钩子
async alarm() {
  // 做定时任务,无需外部 cron
}

Alarm 由平台精确触发,不占持续计算,是做心跳、过期清理、定时摘要的最佳原语。

5.4 状态写入要批量、要事务

Agent 记忆频繁更新时,别一条条 setState。把多个相关变更合并成一次事务写入,既减少存储往返,又保证一致性:

// 好的做法:一次合并更新
this.setState({
  messages: [...this.state.messages, userMsg, assistantMsg],
  lastActive: Date.now(),
});

// 避免:两次独立写,中间崩溃会丢一半
this.setState({ messages: [...] });
this.setState({ lastActive: Date.now() }); // 若此处崩溃,状态不一致

5.5 把「读多写少」的东西塞进 KV / Cache API

Agent 的「系统提示词」「用户画像摘要」「工具目录」这类几乎不变、却被每次请求读取的数据,不该每次都从 DO 存储或模型里拉。用 Workers KV 或 Cache API 做一层边缘缓存:

// 用 Cache API 缓存一次昂贵的检索结果,TTL 60s
const cache = caches.default;
const cacheKey = new Request(`https://cache.local/tools-catalog`);
let catalog = await cache.match(cacheKey);
if (!catalog) {
  catalog = await buildToolCatalog(); // 昂贵操作
  await cache.put(cacheKey, new Response(JSON.stringify(catalog), {
    headers: { "Cache-Control": "max-age=60" },
  }));
}

5.6 Workflow 的 step 必须幂等 + 加超时

Workflows 会在失败时重放 step 的 handler,所以:

  • 不要在 step 里做「副作用不可重入」的事(比如无脑 INSERT 一条订单)。需要幂等就带幂等键去重;
  • 给外部调用设超时,避免一个慢 API 把整个 Workflow 拖死:
const draft = await step.do("draft", {
  retries: { limit: 3, delay: "5s", backoff: "exponential" },
  timeout: "2m",
}, async () => { /* ... */ });

5.7 流式输出,别等模型吐完再回

Agent 聊天体验的关键不是「快」,而是「立刻有反馈」。Workers AI 支持流式,配合 WebSocket 逐 token 广播:

const stream = await this.env.AI.run("@cf/meta/llama-3.1-8b-instruct", {
  messages, stream: true,
});
for await (const chunk of (stream as ReadableStream)) {
  this.broadcast(JSON.stringify({ type: "token", delta: chunk }));
}

用户看到的是「边想边打字」的体感,而不是「转圈 5 秒后啪一下全出来」。

5.8 生产级调优清单(直接抄)

  • DO 开启 smart_placement = "smart"
  • 长连接统一走 hibernation,禁止常驻 Isolate 轮询
  • 定时任务用 Alarm,不用外部 cron
  • 状态变更合并成单次事务写入
  • 系统提示 / 工具目录 / 用户画像 → KV + Cache API 缓存
  • Workflow step 幂等 + 显式超时 + 指数退避
  • 模型输出走流式逐 token 广播
  • 大文件 / 知识库走 R2,别塞进 DO 存储
  • wrangler tail + wrangler dev 的结构化 trace 做故障定位
  • 监控 DO 存储用量与 CPU 时间,设预算告警

六、总结与展望:边缘 Agent 的下半场

6.1 这套架构到底适合谁

强烈适合:

  • 需要长期记忆、跨会话一致的 Agent(客服、个人助理、协作工具);
  • 实时多人场景(直播字幕、协作白板里的 AI、游戏内 NPC);
  • 追求全球低延迟且不想养一堆中心数据库的产品;
  • 长任务、不容失败的自动化(报告生成、数据管道、内容审核流)。

要三思的:

  • 你极度厌恶厂商锁定:这套能力深度绑定 Cloudfloudflare,迁移成本不低;
  • 你的负载是纯 CPU 重计算(视频转码、大规模训练),DO 的 CPU 上限和 Isolate 限制会卡脖子——这类更适合 @cloudflare/computer 的容器通道或干脆回传统云;
  • 你需要跨多个强一致存储做分布式事务:DO 是单实例单存储的强一致,跨 DO 的协调得自己设计(消息 + 最终一致)。

6.2 2026 年的信号:Cloudfloudflare 在赌「Agent 全生命周期」

把 2026 年的几则官方动态串起来,能看出 Cloudfloudflare 的野心不止于「跑 Agent」,而是承包 Agent 的整个生命周期

  • Agent Development Lifecycle:把「编写 → 评审 → 部署 → 维护」做成 Cloudfloudflare 原生原语;wrangler dev 现在能为每个本地请求产出结构化 trace,你的 coding agent 只要调一个 API 就能精确定位「哪一步失败、为什么失败」,无需部署即可排障;
  • @cloudfloudflare/computer:给 Agent 一台真机,弥合 Isolate 与完整 Linux 容器之间的能力鸿沟;
  • Software Factory:Cloudfloudflare 展示用「跑在 GitHub Actions 里的隔离 AI 子 Agent」把 Astro 项目的 open issue 数干到接近零——自动复现 bug、验证补丁、发预览版本。

这背后是一条清晰的产品哲学:未来的 Agent 不该是开发者手搓的上层应用,而应该是平台提供的、带状态、带记忆、带真机、带可观测性的基础设施。 你专注于「Agent 该做什么」,平台负责「Agent 怎么稳稳地活着」。

6.3 写在最后

回到开头那个问题:当 AI Agent 需要记忆、需要协调、需要从断点恢复时,无状态函数为什么不够用?答案现在已经很清楚——不是不够「算」,而是不够「稳」和不够「近」。Cloudfloudflare 用 Durable Objects 把状态钉死在确定的位置,用 Agents SDK 把「有状态」变成默认,用 Workflows 把「长任务」变成持久执行,再用 @cloudfloudflare/computer 把「真机能力」补齐。

对程序员来说,这意味着你终于可以少写 80% 的状态胶水代码,把精力放回真正创造价值的地方。当然,代价是更深地拥抱一个生态。但至少在「让 Agent 稳稳跑在全球边缘」这件事上,Cloudfloudflare 目前给出的,是工程上最自洽、也最省力的一套答案。

技术的尽头不是更复杂的架构,而是让复杂的事情变得理所当然。边缘上的 Agent,正在朝这个方向走。

推荐文章

20个超实用的CSS动画库
2024-11-18 07:23:12 +0800 CST
程序员茄子在线接单