Skip to content
Cloudflare Docs
非官方翻译 - 此文档为非官方中文翻译版本,仅供参考。如有疑问请以 英文官方文档 为准。

存储和同步状态

每个 Agent 都有内置的状态管理功能,包括内置存储和 Agent 与前端应用程序之间的同步。

Agent 内的状态具有以下特性:

  • 在 Agent 重启时持久化:数据永久存储在 Agent 内。
  • 自动序列化/反序列化:您可以存储任何 JSON 可序列化的数据。
  • 在 Agent 内立即一致:读取您自己的写入。
  • 并发更新的线程安全
  • 快速:状态与 Agent 运行的位置位于同一位置。读取和写入不需要穿越网络。

Agent 状态存储在嵌入每个单独 Agent 实例内的 SQL 数据库中:您可以使用更高级别的 this.setState API(推荐)与其交互,它允许您同步状态并在状态更改时触发事件,或直接使用 this.sql 查询数据库。

状态 API

每个 Agent 都有内置的状态管理功能。您可以直接使用 this.setState 设置和更新 Agent 的状态:

import { Agent } from "agents";
export class MyAgent extends Agent {
// 响应事件更新状态
async incrementCounter() {
this.setState({
...this.state,
counter: this.state.counter + 1,
});
}
// 处理传入消息
async onMessage(message) {
if (message.type === "update") {
this.setState({
...this.state,
...message.data,
});
}
}
// 处理状态更新
onStateUpdate(state, source) {
console.log("状态已更新", state);
}
}

如果您使用 TypeScript,您还可以通过将类型作为 类型参数 传递给 Agent 类定义的第二个类型参数来为您的 Agent 状态提供类型。

import { Agent } from "agents";
// 为您的 Agent 状态定义类型
// 传入您的 Agent 状态的类型
export class MyAgent extends Agent {
// 这允许 this.setState 和 onStateUpdate 方法
// 被类型化:
async onStateUpdate(state) {
console.log("状态已更新", state);
}
async someOtherMethod() {
this.setState({
...this.state,
price: this.state.price + 10,
});
}
}

为 Agent 设置初始状态

您还可以通过 Agent 类上的 initialState 属性为 Agent 设置初始状态:

class MyAgent extends Agent {
// 设置默认的初始状态
initialState = {
counter: 0,
text: "",
color: "#3B82F6",
};
doSomething() {
console.log(this.state); // {counter: 0, text: "", color: "#3B82F6"},如果您还没有设置状态
}
}

任何初始状态都会同步到通过useAgent hook连接的客户端。

同步状态

客户端可以连接到 Agent 并使用作为 agents/react 一部分提供的 React hooks 与其状态保持同步。

React 应用程序可以调用 useAgent 通过 WebSockets 连接到命名的 Agent

import { useState } from "react";
import { useAgent } from "agents/react";
function StateInterface() {
const [state, setState] = useState({ counter: 0 });
const agent = useAgent({
agent: "thinking-agent",
name: "my-agent",
onStateUpdate: (newState) => setState(newState),
});
const increment = () => {
agent.setState({ counter: state.counter + 1 });
};
return (
<div>
<div>计数: {state.counter}</div>
<button onClick={increment}>增加</button>
</div>
);
}

状态同步系统:

  • 自动将 Agent 的状态同步到所有连接的客户端
  • 优雅地处理客户端断开连接和重新连接
  • 提供立即的本地更新
  • 支持多个同时的客户端连接

常见用例:

  • 实时协作功能
  • 多窗口/选项卡同步
  • 跨多个设备的实时更新
  • 在客户端之间维护一致的 UI 状态
  • 当新客户端连接时,它们自动从 Agent 接收当前状态,确保所有客户端都从最新数据开始。

SQL API

每个单独的 Agent 实例都有自己的 SQL(SQLite)数据库,运行在与 Agent 本身相同的上下文中。这意味着在您的 Agent 内插入或查询数据实际上是零延迟的:Agent 不必跨越大陆或世界来访问自己的数据。

您可以通过 this.sql 在 Agent 的任何方法中访问 SQL API。SQL API 接受模板字符串,并且

export class MyAgent extends Agent {
async onRequest(request) {
let userId = new URL(request.url).searchParams.get("userId");
// 'users' 这里只是一个例子:您可以创建任意表并在每个 Agent 的数据库中
// 使用 SQL(SQLite 语法)定义您自己的架构。
let user = await this.sql`SELECT * FROM users WHERE id = ${userId}`;
return Response.json(user);
}
}

您还可以为查询提供一个 TypeScript 类型参数,它将用于推断结果的类型:

type User = {
id: string;
name: string;
email: string;
};
export class MyAgent extends Agent<Env> {
async onRequest(request: Request) {
let userId = new URL(request.url).searchParams.get("userId");
// Supply the type paramter to the query when calling this.sql
// This assumes the results returns one or more User rows with "id", "name", and "email" columns
const user = await this.sql<User>`SELECT * FROM users WHERE id = ${userId}`;
return Response.json(user);
}
}

您不需要指定数组类型 (User[]Array<User>),因为 this.sql 将始终返回指定类型的数组。

提供类型参数不会验证结果是否匹配您的类型定义。在 TypeScript 中,不存在的属性(字段)或不符合您提供的类型定义的属性将被删除。如果您需要验证传入的事件,我们建议使用库,例如 zod 或您自己的验证逻辑。

The SQL API exposed to an Agent is similar to the one within Durable Objects: Durable Object SQL methods available on this.ctx.storage.sql. You can use the same SQL queries with the Agent's database, create tables, and query data, just as you would with Durable Objects or D1.

Use Agent state as model context

You can combine the state and SQL APIs in your Agent with its ability to call AI models to include historical context within your prompts to a model. Modern Large Language Models (LLMs) often have very large context windows (up to millions of tokens), which allows you to pull relevant context into your prompt directly.

For example, you can use an Agent's built-in SQL database to pull history, query a model with it, and append to that history ahead of the next call to the model:

export class ReasoningAgent extends Agent {
async callReasoningModel(prompt) {
let result = this
.sql`SELECT * FROM history WHERE user = ${prompt.userId} ORDER BY timestamp DESC LIMIT 1000`;
let context = [];
for await (const row of result) {
context.push(row.entry);
}
const client = new OpenAI({
apiKey: this.env.OPENAI_API_KEY,
});
// Combine user history with the current prompt
const systemPrompt = prompt.system || "You are a helpful assistant.";
const userPrompt = `${prompt.user}\n\nUser history:\n${context.join("\n")}`;
try {
const completion = await client.chat.completions.create({
model: this.env.MODEL || "o3-mini",
messages: [
{ role: "system", content: systemPrompt },
{ role: "user", content: userPrompt },
],
temperature: 0.7,
max_tokens: 1000,
});
// Store the response in history
this
.sql`INSERT INTO history (timestamp, user, entry) VALUES (${new Date()}, ${prompt.userId}, ${completion.choices[0].message.content})`;
return completion.choices[0].message.content;
} catch (error) {
console.error("Error calling reasoning model:", error);
throw error;
}
}
}

This works because each instance of an Agent has its own database, the state stored in that database is private to that Agent: whether it's acting on behalf of a single user, a room or channel, or a deep research tool. By default, you don't have to manage contention or reach out over the network to a centralized database to retrieve and store state.

Next steps