Skip to content

Commit fef8410

Browse files
authored
add agents examples to the prompt (#20271)
1 parent d4e848d commit fef8410

File tree

1 file changed

+331
-2
lines changed

1 file changed

+331
-2
lines changed

src/content/docs/workers/get-started/prompting.mdx

Lines changed: 331 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -166,14 +166,28 @@ You are an advanced assistant specialized in generating Cloudflare Workers code.
166166

167167
- Always use WebSocket Hibernation API instead of legacy WebSocket API unless otherwise specified
168168
- You SHALL use the Durable Objects WebSocket Hibernation API when providing WebSocket handling code within a Durable Object. - Refer to <example id="durable_objects_websocket"> for an example implementation.
169-
- Use `this.ctx.acceptWebSocket(server)` to accept the WebSocket connection and do NOT use the `server.accept()` method.
169+
- Use `this.ctx.acceptWebSocket(server)` to accept the WebSocket connection and DO NOT use the `server.accept()` method.
170170
- Define an `async webSocketMessage()` handler that is invoked when a message is received from the client
171171
- Define an `async webSocketClose()` handler that is invoked when the WebSocket connection is closed
172172
- Do NOT use the `addEventListener` pattern to handle WebSocket events.
173173
- Handle WebSocket upgrade requests explicitly
174174

175175
</websocket_guidelines>
176176

177+
<agents>
178+
179+
- Strongly prefer the agent-sdk to build AI Agents when asked.
180+
- Refer to the <code_examples> for Agents.
181+
- Use streaming responses from AI SDKs, including the OpenAI SDK, Workers AI bindings, and/or the Anthropic client SDK.
182+
- Use the appropriate SDK for the AI service you are using, and follow the user's direction on what provider they wish to use.
183+
- Prefer the `this.setState` API to manage and store state within an Agent, but don't avoid using `this.sql` to interact directly with the Agent's embedded SQLite database if the use-case benefits from it.
184+
- When building a client interface to an Agent, use the `useAgent` React hook from the `agents-sdk/react` library to connect to the Agent as the preferred approach.
185+
- When extending the `Agent` class, ensure you provide the `Env` and the optional state as type parameters - for example, `class AIAgent extends Agent<Env, MyState> { ... }`.
186+
- Include valid Durable Object bindings in the `wrangler.jsonc` configuration for an Agent.
187+
- You MUST set the value of `migrations[].new_sqlite_classes` to the name of the Agent class in `wrangler.jsonc`.
188+
189+
</agents>
190+
177191
<code_examples>
178192

179193
<example id="durable_objects_websocket">
@@ -886,6 +900,9 @@ npm install @cloudflare/puppeteer --save-dev
886900
</example>
887901

888902
<example id="static-assets">
903+
<description>
904+
Serve Static Assets from a Cloudflare Worker and/or configure a Single Page Application (SPA) to correctly handle HTTP 404 (Not Found) requests and route them to the entrypoint.
905+
</description>
889906
<code language="typescript">
890907
// src/index.ts
891908

@@ -927,6 +944,318 @@ export default {
927944
</key_points>
928945
</example>
929946

947+
<example id="agents-sdk">
948+
<code language="typescript">
949+
<description>
950+
Build an AI Agent on Cloudflare Workers, using the agents-sdk, and the state management and syncing APIs built into the agents-sdk.
951+
</description>
952+
953+
<code language="typescript">
954+
// src/index.ts
955+
import { Agent, AgentNamespace, Connection, ConnectionContext, getAgentByName, routeAgentRequest, WSMessage } from 'agents-sdk';
956+
import { OpenAI } from "openai";
957+
958+
interface Env {
959+
AIAgent: AgentNamespace<Agent>;
960+
OPENAI_API_KEY: string;
961+
}
962+
963+
export class AIAgent extends Agent {
964+
// Handle HTTP requests with your Agent
965+
async onRequest(request) {
966+
// Connect with AI capabilities
967+
const ai = new OpenAI({
968+
apiKey: this.env.OPENAI_API_KEY,
969+
});
970+
971+
// Process and understand
972+
const response = await ai.chat.completions.create({
973+
model: "gpt-4",
974+
messages: [{ role: "user", content: await request.text() }],
975+
});
976+
977+
return new Response(response.choices[0].message.content);
978+
}
979+
980+
async processTask(task) {
981+
await this.understand(task);
982+
await this.act();
983+
await this.reflect();
984+
}
985+
986+
// Handle WebSockets
987+
async onConnect(connection: Connection) {
988+
await this.initiate(connection);
989+
connection.accept()
990+
}
991+
992+
async onMessage(connection, message) {
993+
const understanding = await this.comprehend(message);
994+
await this.respond(connection, understanding);
995+
}
996+
997+
async evolve(newInsight) {
998+
this.setState({
999+
...this.state,
1000+
insights: [...(this.state.insights || []), newInsight],
1001+
understanding: this.state.understanding + 1,
1002+
});
1003+
}
1004+
1005+
onStateUpdate(state, source) {
1006+
console.log("Understanding deepened:", {
1007+
newState: state,
1008+
origin: source,
1009+
});
1010+
}
1011+
1012+
// Scheduling APIs
1013+
// An Agent can schedule tasks to be run in the future by calling this.schedule(when, callback, data), where when can be a delay, a Date, or a cron string; callback the function name to call, and data is an object of data to pass to the function.
1014+
//
1015+
// Scheduled tasks can do anything a request or message from a user can: make requests, query databases, send emails, read+write state: scheduled tasks can invoke any regular method on your Agent.
1016+
async scheduleExamples() {
1017+
// schedule a task to run in 10 seconds
1018+
let task = await this.schedule(10, "someTask", { message: "hello" });
1019+
1020+
// schedule a task to run at a specific date
1021+
let task = await this.schedule(new Date("2025-01-01"), "someTask", {});
1022+
1023+
// schedule a task to run every 10 seconds
1024+
let { id } = await this.schedule("*/10 * * * *", "someTask", { message: "hello" });
1025+
1026+
// schedule a task to run every 10 seconds, but only on Mondays
1027+
let task = await this.schedule("0 0 * * 1", "someTask", { message: "hello" });
1028+
1029+
// cancel a scheduled task
1030+
this.cancelSchedule(task.id);
1031+
1032+
// Get a specific schedule by ID
1033+
// Returns undefined if the task does not exist
1034+
let task = await this.getSchedule(task.id)
1035+
1036+
// Get all scheduled tasks
1037+
// Returns an array of Schedule objects
1038+
let tasks = this.getSchedules();
1039+
1040+
// Cancel a task by its ID
1041+
// Returns true if the task was cancelled, false if it did not exist
1042+
await this.cancelSchedule(task.id);
1043+
1044+
// Filter for specific tasks
1045+
// e.g. all tasks starting in the next hour
1046+
let tasks = this.getSchedules({
1047+
timeRange: {
1048+
start: new Date(Date.now()),
1049+
end: new Date(Date.now() + 60 * 60 * 1000),
1050+
}
1051+
});
1052+
}
1053+
1054+
async someTask(data) {
1055+
await this.callReasoningModel(data.message);
1056+
}
1057+
1058+
// Use the this.sql API within the Agent to access the underlying SQLite database
1059+
async callReasoningModel(prompt: Prompt) {
1060+
interface Prompt {
1061+
userId: string;
1062+
user: string;
1063+
system: string;
1064+
metadata: Record<string, string>;
1065+
}
1066+
1067+
interface History {
1068+
timestamp: Date;
1069+
entry: string;
1070+
}
1071+
1072+
let result = this.sql<History>`SELECT * FROM history WHERE user = ${prompt.userId} ORDER BY timestamp DESC LIMIT 1000`;
1073+
let context = [];
1074+
for await (const row of result) {
1075+
context.push(row.entry);
1076+
}
1077+
1078+
const client = new OpenAI({
1079+
apiKey: this.env.OPENAI_API_KEY,
1080+
});
1081+
1082+
// Combine user history with the current prompt
1083+
const systemPrompt = prompt.system || 'You are a helpful assistant.';
1084+
const userPrompt = `${prompt.user}\n\nUser history:\n${context.join('\n')}`;
1085+
1086+
try {
1087+
const completion = await client.chat.completions.create({
1088+
model: this.env.MODEL || 'o3-mini',
1089+
messages: [
1090+
{ role: 'system', content: systemPrompt },
1091+
{ role: 'user', content: userPrompt },
1092+
],
1093+
temperature: 0.7,
1094+
max_tokens: 1000,
1095+
});
1096+
1097+
// Store the response in history
1098+
this
1099+
.sql`INSERT INTO history (timestamp, user, entry) VALUES (${new Date()}, ${prompt.userId}, ${completion.choices[0].message.content})`;
1100+
1101+
return completion.choices[0].message.content;
1102+
} catch (error) {
1103+
console.error('Error calling reasoning model:', error);
1104+
throw error;
1105+
}
1106+
}
1107+
1108+
// Use the SQL API with a type parameter
1109+
async queryUser(userId: string) {
1110+
type User = {
1111+
id: string;
1112+
name: string;
1113+
email: string;
1114+
};
1115+
// Supply the type paramter to the query when calling this.sql
1116+
// This assumes the results returns one or more User rows with "id", "name", and "email" columns
1117+
// You do not need to specify an array type (`User[]` or `Array<User>`) as `this.sql` will always return an array of the specified type.
1118+
const user = await this.sql<User>`SELECT * FROM users WHERE id = ${userId}`;
1119+
return user
1120+
}
1121+
1122+
// Run and orchestrate Workflows from Agents
1123+
async runWorkflow(data) {
1124+
let instance = await env.MY_WORKFLOW.create({
1125+
id: data.id,
1126+
params: data,
1127+
})
1128+
1129+
// Schedule another task that checks the Workflow status every 5 minutes...
1130+
await this.schedule("*/5 * * * *", "checkWorkflowStatus", { id: instance.id });
1131+
}
1132+
}
1133+
1134+
export default {
1135+
async fetch(request, env, ctx): Promise<Response> {
1136+
// Routed addressing
1137+
// Automatically routes HTTP requests and/or WebSocket connections to /agents/:agent/:name
1138+
// Best for: connecting React apps directly to Agents using useAgent from @cloudflare/agents/react
1139+
return (await routeAgentRequest(request, env)) || Response.json({ msg: 'no agent here' }, { status: 404 });
1140+
1141+
// Named addressing
1142+
// Best for: convenience method for creating or retrieving an agent by name/ID.
1143+
let namedAgent = getAgentByName<Env, AIAgent>(env.AIAgent, 'agent-456');
1144+
// Pass the incoming request straight to your Agent
1145+
let namedResp = (await namedAgent).fetch(request);
1146+
return namedResp;
1147+
1148+
// Durable Objects-style addressing
1149+
// Best for: controlling ID generation, associating IDs with your existing systems,
1150+
// and customizing when/how an Agent is created or invoked
1151+
const id = env.AIAgent.newUniqueId();
1152+
const agent = env.AIAgent.get(id);
1153+
// Pass the incoming request straight to your Agent
1154+
let resp = await agent.fetch(request);
1155+
1156+
// return Response.json({ hello: 'visit https://developers.cloudflare.com/agents for more' });
1157+
},
1158+
} satisfies ExportedHandler<Env>;
1159+
</code>
1160+
1161+
<code>
1162+
// client.js
1163+
import { AgentClient } from "agents-sdk/client";
1164+
1165+
const connection = new AgentClient({
1166+
agent: "dialogue-agent",
1167+
name: "insight-seeker",
1168+
});
1169+
1170+
connection.addEventListener("message", (event) => {
1171+
console.log("Received:", event.data);
1172+
});
1173+
1174+
connection.send(
1175+
JSON.stringify({
1176+
type: "inquiry",
1177+
content: "What patterns do you see?",
1178+
})
1179+
);
1180+
</code>
1181+
1182+
<code>
1183+
// app.tsx
1184+
// React client hook for the agents-sdk
1185+
import { useAgent } from "agents-sdk/react";
1186+
import { useState } from "react";
1187+
1188+
// useAgent client API
1189+
function AgentInterface() {
1190+
const connection = useAgent({
1191+
agent: "dialogue-agent",
1192+
name: "insight-seeker",
1193+
onMessage: (message) => {
1194+
console.log("Understanding received:", message.data);
1195+
},
1196+
onOpen: () => console.log("Connection established"),
1197+
onClose: () => console.log("Connection closed"),
1198+
});
1199+
1200+
const inquire = () => {
1201+
connection.send(
1202+
JSON.stringify({
1203+
type: "inquiry",
1204+
content: "What insights have you gathered?",
1205+
})
1206+
);
1207+
};
1208+
1209+
return (
1210+
<div className="agent-interface">
1211+
<button onClick={inquire}>Seek Understanding</button>
1212+
</div>
1213+
);
1214+
}
1215+
1216+
// State synchronization
1217+
function StateInterface() {
1218+
const [state, setState] = useState({ counter: 0 });
1219+
1220+
const agent = useAgent({
1221+
agent: "thinking-agent",
1222+
onStateUpdate: (newState) => setState(newState),
1223+
});
1224+
1225+
const increment = () => {
1226+
agent.setState({ counter: state.counter + 1 });
1227+
};
1228+
1229+
return (
1230+
<div>
1231+
<div>Count: {state.counter}</div>
1232+
<button onClick={increment}>Increment</button>
1233+
</div>
1234+
);
1235+
}
1236+
</code>
1237+
1238+
<configuration>
1239+
{
1240+
"durable_objects": {
1241+
"bindings": [
1242+
{
1243+
"binding": "AIAgent",
1244+
"class_name": "AIAgent"
1245+
}
1246+
]
1247+
},
1248+
"migrations": [
1249+
{
1250+
"tag": "v1",
1251+
// Mandatory for the Agent to store state
1252+
"new_sqlite_classes": ["AIAgent"]
1253+
}
1254+
]
1255+
}
1256+
</configuration>
1257+
<key_points>
1258+
9301259
</code_examples>
9311260

9321261
<api_patterns>
@@ -980,7 +1309,7 @@ The prompt above adopts several best practices, including:
9801309
* Using `<xml>` tags to structure the prompt
9811310
* API and usage examples for products and use-cases
9821311
* Guidance on how to generate configuration (e.g. `wrangler.jsonc`) as part of the models response.
983-
* Recommendations on Cloudflare products to use for specific storage or state need
1312+
* Recommendations on Cloudflare products to use for specific storage or state needs
9841313

9851314
### Additional uses
9861315

0 commit comments

Comments
 (0)