You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Synced from cloudflare/agents PR #636
This adds comprehensive documentation explaining the internal architecture
of the Agent class, including its layered structure (DurableObject -> Server -> Agent)
and detailed explanations of all core features and APIs.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <[email protected]>
Copy file name to clipboardExpand all lines: src/content/docs/agents/concepts/agent-class.mdx
+40-40Lines changed: 40 additions & 40 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,29 +1,27 @@
1
1
---
2
-
title: Understanding the Agent class
2
+
title: Agent class internals
3
3
pcx_content_type: concept
4
4
sidebar:
5
-
order: 10
5
+
order: 5
6
6
---
7
7
8
-
import { Render } from"~/components";
8
+
The core of the `agents` library is the exported `Agent` class. Following the pattern from [Durable Objects](/durable-objects/api/), the main API for developers is to extend the `Agent` so those classes inherit all the built-in features. While this effectively is a supercharged primitive that allows developers to only write the logic they need in their agents, it obscures the inner workings.
9
9
10
-
The core of the `agents` library is the exported `Agent` class. Following the pattern from [Durable Objects](/durable-objects/api/), the main API for developers is to extend the `Agent` class so those classes inherit all the built-in features. While this effectively is a supercharged primitive that allows developers to only write the logic they need in their agents, it obscures the inner workings.
10
+
This document tries to bridge that gap, empowering any developer aiming to get started writing agents to get the full picture and avoid common pitfalls. The snippets shown here are primarily illustrative and don't necessarily represent best practices. For a more in-depth look at the inner workings of the `Agent` class, check out the [API reference](/agents/api-reference/) and the [source code](https://github.com/cloudflare/agents/blob/main/packages/agents/src/index.ts).
11
11
12
-
This document tries to bridge that gap, empowering any developer aiming to get started writing agents to get the full picture and avoid common pitfalls. The snippets shown here are primarily illustrative and don't necessarily represent best practices.
13
-
14
-
## What is the Agent class?
12
+
## What is the Agent?
15
13
16
14
The `Agent` class is an extension of `DurableObject`. That is to say, they _are_**Durable Objects**. If you're not familiar with Durable Objects, it is highly recommended that you read [What are Durable Objects](/durable-objects/) but at their core, Durable Objects are globally addressable (each instance has a unique ID) single-threaded compute instances with long term storage (KV/SQLite).
17
15
18
16
That being said, `Agent` does **not** extend `DurableObject` directly but instead `Server`. `Server` is a class provided by [PartyKit](https://github.com/cloudflare/partykit/tree/main/packages/partyserver).
19
17
20
-
You can visualize the logic as a Matryoshka doll: **DurableObject**→**Server**→**Agent**.
18
+
You can visualize the logic as a Matryoshka doll: **DurableObject**->**Server**->**Agent**.
21
19
22
20
## Layer 0: Durable Object
23
21
24
22
This won't cover Durable Objects in detail, but it's good to know what primitives they expose so we understand how the outer layers make use of them. The Durable Object class comes with:
Durable Objects can take a `Request` from a Worker and send a `Response` back. This can **only** be done through the [`fetch`](/durable-objects/best-practices/create-durable-object-stubs-and-send-requests/#invoking-the-fetch-handler) method (which the developer must implement).
54
52
@@ -81,17 +79,17 @@ export class MyDurableObject extends DurableObject {
81
79
}
82
80
```
83
81
84
-
### alarm()
82
+
### `alarm()`
85
83
86
-
HTTP and RPC requests are not the only entrypoints for a DO. Alarms allow developers to schedule tasks to run at a later time. Whenever the runtime knows an alarm is due, it will call the `alarm()` method, which is left to the developer to implement.
84
+
HTTP and RPC requests are not the only entrypoints for a DO. Alarms allow developers to schedule an event to trigger at a later time. Whenever the next alarm is due, the runtime will call the `alarm()` method, which is left to the developer to implement.
87
85
88
86
To schedule an alarm, you can use the `this.ctx.storage.setAlarm()` method. For more information, refer to [Alarms](/durable-objects/api/alarms/).
89
87
90
-
### this.ctx
88
+
### `this.ctx`
91
89
92
90
The base `DurableObject` class sets the [DurableObjectState](/durable-objects/api/state/) into `this.ctx`. There are a lot of interesting methods and properties, but we'll focus on `this.ctx.storage`.
93
91
94
-
### this.ctx.storage
92
+
### `this.ctx.storage`
95
93
96
94
[DurableObjectStorage](/durable-objects/api/sqlite-storage-api/) is the main interface with the DO's persistence mechanisms, which include both a KV and SQLITE **synchronous** APIs.
97
95
@@ -106,15 +104,15 @@ const rows = sql.exec("SELECT * FROM contacts WHERE country = ?", "US");
106
104
const token =kv.get("someToken");
107
105
```
108
106
109
-
### this.ctx.env
107
+
### `this.ctx.env`
110
108
111
-
Lastly, it's worth mentioning that the DO also has the Worker `Env` in `this.env`. Read more about [bindings](/workers/runtime-apis/bindings).
109
+
Lastly, it's worth mentioning that the DO also has the Worker `Env` in `this.env`. Read more in [Bindings](/workers/runtime-apis/bindings).
112
110
113
-
## Layer 1: PartyKit Server
111
+
## Layer 1: Partykit `Server`
114
112
115
113
Now that you've seen what Durable Objects come with out-of-the-box, what [PartyKit](https://github.com/cloudflare/partykit)'s `Server` (package `partyserver`) implements will be clearer. It's an **opinionated `DurableObject` wrapper that improves DX by hiding away DO primitives in favor of more developer friendly callbacks**.
116
114
117
-
An important note is that `Server`**does NOT make use any of the DO storage** so you will not see extra operations.
115
+
An important note is that `Server`**does NOT persist to the DO storage** so you will not see extra storage operations by using it.
118
116
119
117
### Addressing
120
118
@@ -133,20 +131,20 @@ await stub.bar();
133
131
Since we have a URL addressing scheme, we also get access to `routePartykitRequest()`.
// Behind the scenes, PartyKit normalizes your DO binding names
136
+
// and tries to do some pattern matching.
137
+
const res =awaitroutePartykitRequest(request, env);
140
138
141
-
if (res) returnres;
139
+
if (res) returnres;
142
140
143
-
returnResponse("Not found", { status: 404 });
144
-
}
141
+
returnResponse("Not found", { status: 404 });
142
+
}
145
143
```
146
144
147
145
You can have a look at [the implementation](https://github.com/cloudflare/partykit/blob/main/packages/partyserver/src/index.ts#L122) if you're interested.
148
146
149
-
### onStart
147
+
### `onStart`
150
148
151
149
The extra plumbing that `Server` includes on addressing allows it to expose an `onStart` callback that is **executed every time the DO starts up** (the DO was evicted, hibernated or never created at all) and **before any `fetch` or RPC**.
152
150
@@ -161,7 +159,7 @@ class MyServer extends Server {
161
159
}
162
160
```
163
161
164
-
### onRequest and onConnect
162
+
### `onRequest` and `onConnect`
165
163
166
164
`Server` already implements `fetch` for the underlying Durable Object and exposes 2 different callbacks that developers can make use of, `onRequest` and `onConnect` for HTTP requests and incoming WS connections, respectively (**WebSocket connections are accepted by default**).
167
165
@@ -189,15 +187,15 @@ Just as `onConnect` is the callback for every new connection, `Server` also prov
189
187
190
188
There's also `this.broadcast` that sends a WS message to all connected clients (no magic, just a loop over `this.getConnections()`!).
191
189
192
-
### this.name
190
+
### `this.name`
193
191
194
-
It's hard to get a Durable Object's `name` from within it. `partyserver` tries to make it available in `this.name` but it's not a perfect solution. Read more about it [here](https://github.com/cloudflare/workerd/issues/2240).
192
+
It's hard to get a Durable Object's `name` from within it. `partyserver` tries to make it available in `this.name` but it's not a perfect solution. Read more about it in [this GitHub issue](https://github.com/cloudflare/workerd/issues/2240).
195
193
196
194
## Layer 2: Agent
197
195
198
196
Now finally, the `Agent` class. `Agent` extends `Server` and provides opinionated primitives for stateful, schedulable, and observable agents that can communicate via RPC, WebSockets, and (even!) email.
199
197
200
-
### this.state and this.setState()
198
+
### `this.state` and `this.setState()`
201
199
202
200
One of the core features of `Agent` is **automatic state persistence**. Developers define the shape of their state via the generic parameter and `initialState` (which is only used if no state exists in storage), and the Agent handles loading, saving, and broadcasting state changes (check `Server`'s `this.broadcast()` above).
203
201
@@ -221,7 +219,7 @@ class MyAgent extends Agent<Env, { count: number }> {
221
219
222
220
State is stored in the `cf_agents_state` SQL table. State messages are sent with `type: "cf_agent_state"` (both from the client and the server). Since the `agents` provides [JS and React clients](/agents/api-reference/store-and-sync-state/#synchronizing-state), real-time state updates are available out of the box.
223
221
224
-
### this.sql
222
+
### `this.sql`
225
223
226
224
The Agent provides a convenient `sql` template tag for executing queries against the Durable Object's SQL storage. It constructs parameterized queries and executes them. This uses the **synchronous** SQL API from `this.ctx.storage.sql`.
227
225
@@ -249,7 +247,7 @@ class MyAgent extends Agent {
249
247
250
248
### RPC and Callable Methods
251
249
252
-
`agents` take Durable Objects RPC one step forward by implementing RPC through WebSockets, so clients can also call methods on the Agent directly. To make a method callable, developers can use the `@callable` decorator. Methods can return a serializable value or a stream (when using `@callable({ stream: true })`).
250
+
`agents` take Durable Objects RPC one step forward by implementing RPC through WebSockets, so clients can also call methods on the Agent directly. To make a method callable through WS, developers can use the `@callable` decorator. Methods can return a serializable value or a stream (when using `@callable({ stream: true })`).
253
251
254
252
```ts
255
253
classMyAgentextendsAgent {
@@ -279,7 +277,7 @@ const result = await stub.add(2, 3);
279
277
console.log(result); // 5
280
278
```
281
279
282
-
### this.queue and friends
280
+
### `this.queue` and friends
283
281
284
282
Agents include a built-in task queue for deferred execution. This is useful for offloading work or retrying operations. The available methods are `this.queue`, `this.dequeue`, `this.dequeueAll`, `this.dequeueAllByCallback`, `this.getQueue`, and `this.getQueues`.
285
283
@@ -298,13 +296,15 @@ class MyAgent extends Agent {
298
296
299
297
Tasks are stored in the `cf_agents_queues` SQL table and are automatically flushed in sequence. If a task succeeds, it's automatically dequeued.
300
298
301
-
### this.schedule and friends
299
+
### `this.schedule` and friends
302
300
303
301
Agents support scheduled execution of methods by wrapping the Durable Object's `alarm()`. The available methods are `this.schedule`, `this.getSchedule`, `this.getSchedules`, `this.cancelSchedule`. Schedules can be one-time, delayed, or recurring (using cron expressions).
304
302
303
+
Since DOs only allow one alarm at a time, the `Agent` class works around this by managing multiple schedules in SQL and using a single alarm.
Schedules are stored in the `cf_agents_schedules` SQL table. Cron schedules automatically reschedule themselves after execution, while one-time schedules are deleted.
335
335
336
-
### this.mcp and friends
336
+
### `this.mcp` and friends
337
337
338
-
`Agent` includes a multi-server MCP client. This enables your Agent to interact with external services that expose MCP interfaces. The MCP client is documented in detail at [MCP Client API](/agents/model-context-protocol/mcp-client-api/).
338
+
`Agent` includes a multi-server MCP client. This enables your Agent to interact with external services that expose MCP interfaces. The MCP client is properly documented in [MCP client API](/agents/model-context-protocol/mcp-client-api/).
339
339
340
340
```ts
341
341
classMyAgentextendsAgent {
@@ -407,7 +407,7 @@ function someUtilityFunction() {
407
407
}
408
408
```
409
409
410
-
### this.onError
410
+
### `this.onError`
411
411
412
412
`Agent` extends `Server`'s `onError` so it can be used to handle errors that are not necessarily WebSocket errors. It is called with a `Connection` or `unknown` error.
413
413
@@ -428,9 +428,9 @@ class MyAgent extends Agent {
428
428
}
429
429
```
430
430
431
-
### this.destroy
431
+
### `this.destroy`
432
432
433
-
`destroy()` drops all tables, deletes alarms, clears storage, and aborts the context. The `this.abort` method that is called by`this.destroy` comes from `DurableObject` and ensures that the Agent is fully evicted. In order to do so, it throws an uncatchable error that will show up in your logs (read more at[abort()](/durable-objects/api/state/#abort)).
433
+
`this.destroy()` drops all tables, deletes alarms, clears storage, and aborts the context. To ensure that the DO is fully evicted,`this.ctx.abort()`is called, which throws an uncatchable error that will show up in your logs (read more about it in[abort()](/durable-objects/api/state/#abort)).
0 commit comments