Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

添加到现有项目

本指南介绍如何在已有的 Cloudflare Workers 项目中加入 agents。如果你是从零开始,请改用 构建一个聊天 agent

前置条件

  • 一个已存在并带有 Wrangler 配置文件的 Cloudflare Workers 项目
  • Node.js 18 或更新版本

1. 安装依赖包

npm yarn pnpm bun

npm i agents
yarn add agents
pnpm add agents
bun add agents

对于 React 应用,无需额外安装包 —— React 绑定已包含在内。

对于 Hono 应用:

npm yarn pnpm bun

npm i agents hono-agents
yarn add agents hono-agents
pnpm add agents hono-agents
bun add agents hono-agents

2. 创建一个 Agent

为你的 agent 新建一个文件(例如 src/agents/counter.ts):

JavaScript


import { Agent, callable } from "agents";


export class CounterAgent extends Agent {

  initialState = { count: 0 };


  @callable()

  increment() {

    this.setState({ count: this.state.count + 1 });

    return this.state.count;

  }


  @callable()

  decrement() {

    this.setState({ count: this.state.count - 1 });

    return this.state.count;

  }

}


Explain Code

TypeScript


import { Agent, callable } from "agents";


export type CounterState = {

  count: number;

};


export class CounterAgent extends Agent<Env, CounterState> {

  initialState: CounterState = { count: 0 };


  @callable()

  increment() {

    this.setState({ count: this.state.count + 1 });

    return this.state.count;

  }


  @callable()

  decrement() {

    this.setState({ count: this.state.count - 1 });

    return this.state.count;

  }

}


Explain Code

3. 更新 Wrangler 配置

添加 Durable Object 绑定与 migration:

JSONC


{

  "name": "my-existing-project",

  "main": "src/index.ts",

  // Set this to today's date

  "compatibility_date": "2026-04-29",

  "compatibility_flags": ["nodejs_compat"],


  "durable_objects": {

    "bindings": [

      {

        "name": "CounterAgent",

        "class_name": "CounterAgent",

      },

    ],

  },


  "migrations": [

    {

      "tag": "v1",

      "new_sqlite_classes": ["CounterAgent"],

    },

  ],

}


Explain Code

TOML


name = "my-existing-project"

main = "src/index.ts"

# Set this to today's date

compatibility_date = "2026-04-29"

compatibility_flags = [ "nodejs_compat" ]


[[durable_objects.bindings]]

name = "CounterAgent"

class_name = "CounterAgent"


[[migrations]]

tag = "v1"

new_sqlite_classes = [ "CounterAgent" ]


Explain Code

要点:

  • 绑定中的 name 会成为 env 上的属性(例如 env.CounterAgent)
  • class_name 必须与你导出的类名完全一致
  • new_sqlite_classes 启用 SQLite 存储以持久化 state
  • nodejs_compat flag 是 agents 包的必备项

4. 配置 TypeScript 与 Vite

如果你使用 @callable() 装饰器(如上例所示),你需要两份构建配置。

tsconfig.json —— 继承 agents/tsconfig(或手动设置 "target": "ES2021"):


{

  "extends": "agents/tsconfig"

}


如果你已有 tsconfig.json 并带自定义设置,可以继承并覆盖:


{

  "extends": "agents/tsconfig",

  "compilerOptions": {

    "paths": { "~/*": ["./src/*"] }

  }

}


vite.config.ts —— 添加 agents() 插件(为 Vite 8 处理 TC39 装饰器转换):

JavaScript


import agents from "agents/vite";


export default defineConfig({

  plugins: [

    agents(),

    // ... your existing plugins

  ],

});


TypeScript


import agents from "agents/vite";


export default defineConfig({

  plugins: [

    agents(),

    // ... your existing plugins

  ],

});


如果项目不使用 Vite,只更新 tsconfig.json 也足够 —— 你的打包工具必须支持 TC39 装饰器(stage 3,版本 2023-11)。

更多细节请参阅 TypeScript 配置Vite 配置 参考。

5. 导出 Agent 类

你的 agent 类必须从主入口文件导出。更新 src/index.ts:

JavaScript


// Export the agent class (required for Durable Objects)

export { CounterAgent } from "./agents/counter";


// Your existing exports...

export default {

  // ...

};


TypeScript


// Export the agent class (required for Durable Objects)

export { CounterAgent } from "./agents/counter";


// Your existing exports...

export default {

  // ...

} satisfies ExportedHandler<Env>;


6. 接入路由

根据你的项目结构选择对应方式:

纯 Workers(fetch handler)

JavaScript


import { routeAgentRequest } from "agents";

export { CounterAgent } from "./agents/counter";


export default {

  async fetch(request, env, ctx) {

    // Try agent routing first

    const agentResponse = await routeAgentRequest(request, env);

    if (agentResponse) return agentResponse;


    // Your existing routing logic

    const url = new URL(request.url);

    if (url.pathname === "/api/hello") {

      return Response.json({ message: "Hello!" });

    }


    return new Response("Not found", { status: 404 });

  },

};


Explain Code

TypeScript


import { routeAgentRequest } from "agents";

export { CounterAgent } from "./agents/counter";


export default {

  async fetch(request: Request, env: Env, ctx: ExecutionContext) {

    // Try agent routing first

    const agentResponse = await routeAgentRequest(request, env);

    if (agentResponse) return agentResponse;


    // Your existing routing logic

    const url = new URL(request.url);

    if (url.pathname === "/api/hello") {

      return Response.json({ message: "Hello!" });

    }


    return new Response("Not found", { status: 404 });

  },

} satisfies ExportedHandler<Env>;


Explain Code

Hono

JavaScript


import { Hono } from "hono";

import { agentsMiddleware } from "hono-agents";

export { CounterAgent } from "./agents/counter";


const app = new Hono();


// Add agents middleware - handles WebSocket upgrades and agent HTTP requests

app.use("*", agentsMiddleware());


// Your existing routes continue to work

app.get("/api/hello", (c) => c.json({ message: "Hello!" }));


export default app;


Explain Code

TypeScript


import { Hono } from "hono";

import { agentsMiddleware } from "hono-agents";

export { CounterAgent } from "./agents/counter";


const app = new Hono<{ Bindings: Env }>();


// Add agents middleware - handles WebSocket upgrades and agent HTTP requests

app.use("*", agentsMiddleware());


// Your existing routes continue to work

app.get("/api/hello", (c) => c.json({ message: "Hello!" }));


export default app;


Explain Code

与静态资源一起使用

如果你在提供 agents 的同时也提供静态资源,默认情况下静态资源优先返回。Worker 代码只在路径未匹配静态资源时才运行:

JavaScript


import { routeAgentRequest } from "agents";

export { CounterAgent } from "./agents/counter";


export default {

  async fetch(request, env, ctx) {

    // Static assets are served automatically before this runs

    // This only handles non-asset requests


    // Route to agents

    const agentResponse = await routeAgentRequest(request, env);

    if (agentResponse) return agentResponse;


    return new Response("Not found", { status: 404 });

  },

};


Explain Code

TypeScript


import { routeAgentRequest } from "agents";

export { CounterAgent } from "./agents/counter";


export default {

  async fetch(request: Request, env: Env, ctx: ExecutionContext) {

    // Static assets are served automatically before this runs

    // This only handles non-asset requests


    // Route to agents

    const agentResponse = await routeAgentRequest(request, env);

    if (agentResponse) return agentResponse;


    return new Response("Not found", { status: 404 });

  },

} satisfies ExportedHandler<Env>;


Explain Code

在 Wrangler 配置文件中配置静态资源:

JSONC


{

  "assets": {

    "directory": "./public",

  },

}


TOML


[assets]

directory = "./public"


7. 生成 TypeScript 类型

不要手写 Env 接口。运行 wrangler types 生成与 Wrangler 配置一致的类型定义文件。这样能在编译期、而非部署期发现配置和代码之间的差异。

每次新增或重命名绑定后,请重新运行 wrangler types

Terminal window


npx wrangler types


这会生成一个类型定义文件,把所有绑定都加上类型,包括你的 agent Durable Object 命名空间。Agent 类默认使用生成的 Env 类型,因此通常不需要显式传类型参数 —— extends Agent 就够了,除非你需要传第二个类型参数表示 state(例如 Agent<Env, CounterState>)。

更多类型生成细节,请参阅 Configuration

8. 在前端建立连接

React

JavaScript


import { useState } from "react";

import { useAgent } from "agents/react";

function CounterWidget() {

  const [count, setCount] = useState(0);


  const agent = useAgent({

    agent: "CounterAgent",

    onStateUpdate: (state) => setCount(state.count),

  });


  return (

    <>

      {count}

      <button onClick={() => agent.stub.increment()}>+</button>

      <button onClick={() => agent.stub.decrement()}>-</button>

    </>

  );

}


Explain Code

TypeScript


import { useState } from "react";

import { useAgent } from "agents/react";

import type { CounterAgent, CounterState } from "./agents/counter";


function CounterWidget() {

  const [count, setCount] = useState(0);


  const agent = useAgent<CounterAgent, CounterState>({

    agent: "CounterAgent",

    onStateUpdate: (state) => setCount(state.count),

  });


  return (

    <>

      {count}

      <button onClick={() => agent.stub.increment()}>+</button>

      <button onClick={() => agent.stub.decrement()}>-</button>

    </>

  );

}


Explain Code

原生 JavaScript

JavaScript


import { AgentClient } from "agents/client";


const agent = new AgentClient({

  agent: "CounterAgent",

  name: "user-123", // Optional: unique instance name

  onStateUpdate: (state) => {

    document.getElementById("count").textContent = state.count;

  },

});


// Call methods

document.getElementById("increment").onclick = () => agent.call("increment");


Explain Code

TypeScript


import { AgentClient } from "agents/client";


const agent = new AgentClient({

  agent: "CounterAgent",

  name: "user-123", // Optional: unique instance name

  onStateUpdate: (state) => {

    document.getElementById("count").textContent = state.count;

  },

});


// Call methods

document.getElementById("increment").onclick = () => agent.call("increment");


Explain Code

添加多个 agent

通过扩展配置即可添加更多 agent:

JavaScript


// src/agents/chat.ts

export class Chat extends Agent {

  // ...

}


// src/agents/scheduler.ts

export class Scheduler extends Agent {

  // ...

}


TypeScript


// src/agents/chat.ts

export class Chat extends Agent {

  // ...

}


// src/agents/scheduler.ts

export class Scheduler extends Agent {

  // ...

}


更新 Wrangler 配置文件:

JSONC


{

  "durable_objects": {

    "bindings": [

      { "name": "CounterAgent", "class_name": "CounterAgent" },

      { "name": "Chat", "class_name": "Chat" },

      { "name": "Scheduler", "class_name": "Scheduler" },

    ],

  },

  "migrations": [

    {

      "tag": "v1",

      "new_sqlite_classes": ["CounterAgent", "Chat", "Scheduler"],

    },

  ],

}


Explain Code

TOML


[[durable_objects.bindings]]

name = "CounterAgent"

class_name = "CounterAgent"


[[durable_objects.bindings]]

name = "Chat"

class_name = "Chat"


[[durable_objects.bindings]]

name = "Scheduler"

class_name = "Scheduler"


[[migrations]]

tag = "v1"

new_sqlite_classes = [ "CounterAgent", "Chat", "Scheduler" ]


Explain Code

在入口文件中导出所有 agent:

JavaScript


export { CounterAgent } from "./agents/counter";

export { Chat } from "./agents/chat";

export { Scheduler } from "./agents/scheduler";


TypeScript


export { CounterAgent } from "./agents/counter";

export { Chat } from "./agents/chat";

export { Scheduler } from "./agents/scheduler";


常见集成模式

在认证之后接入 agent

在路由到 agent 之前先做认证:

JavaScript


export default {

  async fetch(request, env) {

    // Check auth for agent routes

    if (request.url.includes("/agents/")) {

      const authResult = await checkAuth(request, env);

      if (!authResult.valid) {

        return new Response("Unauthorized", { status: 401 });

      }

    }


    const agentResponse = await routeAgentRequest(request, env);

    if (agentResponse) return agentResponse;


    // ... rest of routing

  },

};


Explain Code

TypeScript


export default {

  async fetch(request: Request, env: Env) {

    // Check auth for agent routes

    if (request.url.includes("/agents/")) {

      const authResult = await checkAuth(request, env);

      if (!authResult.valid) {

        return new Response("Unauthorized", { status: 401 });

      }

    }


    const agentResponse = await routeAgentRequest(request, env);

    if (agentResponse) return agentResponse;


    // ... rest of routing

  },

} satisfies ExportedHandler<Env>;


Explain Code

自定义 agent 路径前缀

默认情况下,agent 的路由是 /agents/{agent-name}/{instance-name}。你可以自定义:

JavaScript


import { routeAgentRequest } from "agents";


const agentResponse = await routeAgentRequest(request, env, {

  prefix: "/api/agents", // Now routes at /api/agents/{agent-name}/{instance-name}

});


TypeScript


import { routeAgentRequest } from "agents";


const agentResponse = await routeAgentRequest(request, env, {

  prefix: "/api/agents", // Now routes at /api/agents/{agent-name}/{instance-name}

});


更多选项(包括 CORS、自定义实例命名、location hints)请参阅 Routing

在服务端代码中访问 agent

你可以在 Worker 代码中直接与 agent 交互:

JavaScript


import { getAgentByName } from "agents";


export default {

  async fetch(request, env) {

    if (request.url.endsWith("/api/increment")) {

      // Get a specific agent instance

      const counter = await getAgentByName(env.CounterAgent, "shared-counter");

      const newCount = await counter.increment();

      return Response.json({ count: newCount });

    }

    // ...

  },

};


Explain Code

TypeScript


import { getAgentByName } from "agents";


export default {

  async fetch(request: Request, env: Env) {

    if (request.url.endsWith("/api/increment")) {

      // Get a specific agent instance

      const counter = await getAgentByName(env.CounterAgent, "shared-counter");

      const newCount = await counter.increment();

      return Response.json({ count: newCount });

    }

    // ...

  },

} satisfies ExportedHandler<Env>;


Explain Code

故障排查

Agent not found,或 404 错误

  1. 检查导出 —— Agent 类必须从主入口文件导出。
  2. 检查绑定 —— Wrangler 配置中的 class_name 必须与导出的类名完全一致。
  3. 检查路由 —— 默认路由是 /agents/{agent-name}/{instance-name}

No such Durable Object class 错误

在 Wrangler 配置文件中加上 migration:

JSONC


{

  "migrations": [

    {

      "tag": "v1",

      "new_sqlite_classes": ["YourAgentClass"],

    },

  ],

}


TOML


[[migrations]]

tag = "v1"

new_sqlite_classes = [ "YourAgentClass" ]


WebSocket 连接失败

确保你的路由把响应原样返回:

JavaScript


// Correct - return the response directly

const agentResponse = await routeAgentRequest(request, env);

if (agentResponse) return agentResponse;


// Wrong - this breaks WebSocket connections

if (agentResponse) return new Response(agentResponse.body);


TypeScript


// Correct - return the response directly

const agentResponse = await routeAgentRequest(request, env);

if (agentResponse) return agentResponse;


// Wrong - this breaks WebSocket connections

if (agentResponse) return new Response(agentResponse.body);


state 没有持久化

请检查:

  1. 你使用的是 this.setState(),而不是直接修改 this.state
  2. agent 类已加入 migrations 中的 new_sqlite_classes
  3. 你正在连接同一个 agent 实例名。

后续步骤

State 管理 管理并同步 agent 的 state。

计划任务 后台任务与 cron 作业。

Agent 类内部细节 完整生命周期与方法参考。

Agents API Agents SDK 的完整 API 参考。